@aptos-labs/ts-sdk
Version:
Aptos TypeScript SDK
214 lines (200 loc) • 9.04 kB
text/typescript
import { Deserializer, Serializer } from "../bcs/index.js";
import { AnyPublicKeyVariant, HexInput, SigningScheme } from "../types/index.js";
import { MultiKeyAccount } from "./MultiKeyAccount.js";
import { Account } from "./Account.js";
import { Ed25519Account } from "./Ed25519Account.js";
import { isSingleKeySigner, SingleKeyAccount, SingleKeySignerOrLegacyEd25519Account } from "./SingleKeyAccount.js";
import { KeylessAccount } from "./KeylessAccount.js";
import { FederatedKeylessAccount } from "./FederatedKeylessAccount.js";
import { AbstractKeylessAccount } from "./AbstractKeylessAccount.js";
import { AccountAddress } from "../core/accountAddress.js";
import { Hex } from "../core/hex.js";
import { Ed25519PrivateKey } from "../core/crypto/ed25519.js";
import { Secp256k1PrivateKey } from "../core/crypto/secp256k1.js";
import { MultiKey } from "../core/crypto/multiKey.js";
import { getIssAudAndUidVal, ZeroKnowledgeSig } from "../core/crypto/keyless.js";
import { deserializeSchemeAndAddress } from "./utils.js";
import { EphemeralKeyPair } from "./EphemeralKeyPair.js";
function serializeKeylessAccountCommon(account: AbstractKeylessAccount, serializer: Serializer): void {
serializer.serializeStr(account.jwt);
serializer.serializeStr(account.uidKey);
serializer.serializeFixedBytes(account.pepper);
account.ephemeralKeyPair.serialize(serializer);
if (account.proof === undefined) {
throw new Error("Cannot serialize - proof undefined");
}
account.proof.serialize(serializer);
serializer.serializeOption(account.verificationKeyHash, 32);
}
function deserializeKeylessAccountCommon(deserializer: Deserializer): {
jwt: string;
uidKey: string;
pepper: Uint8Array;
ephemeralKeyPair: EphemeralKeyPair;
proof: ZeroKnowledgeSig;
verificationKeyHash?: Uint8Array;
} {
const jwt = deserializer.deserializeStr();
const uidKey = deserializer.deserializeStr();
const pepper = deserializer.deserializeFixedBytes(31);
const ephemeralKeyPair = EphemeralKeyPair.deserialize(deserializer);
const proof = ZeroKnowledgeSig.deserialize(deserializer);
const verificationKeyHash = deserializer.deserializeOption("fixedBytes", 32);
return { jwt, uidKey, pepper, ephemeralKeyPair, proof, verificationKeyHash };
}
/**
* Utility functions for working with accounts.
*/
export namespace AccountUtils {
export function toBytes(account: Account): Uint8Array {
const serializer = new Serializer();
serializer.serializeU32AsUleb128(account.signingScheme);
account.accountAddress.serialize(serializer);
switch (account.signingScheme) {
case SigningScheme.Ed25519:
(account as Ed25519Account).privateKey.serialize(serializer);
return serializer.toUint8Array();
case SigningScheme.SingleKey: {
if (!isSingleKeySigner(account)) {
throw new Error("Account is not a SingleKeySigner");
}
const anyPublicKey = account.getAnyPublicKey();
serializer.serializeU32AsUleb128(anyPublicKey.variant);
switch (anyPublicKey.variant) {
case AnyPublicKeyVariant.Keyless: {
const keylessAccount = account as KeylessAccount;
serializeKeylessAccountCommon(keylessAccount, serializer);
return serializer.toUint8Array();
}
case AnyPublicKeyVariant.FederatedKeyless: {
const federatedKeylessAccount = account as FederatedKeylessAccount;
serializeKeylessAccountCommon(federatedKeylessAccount, serializer);
federatedKeylessAccount.publicKey.jwkAddress.serialize(serializer);
serializer.serializeBool(federatedKeylessAccount.audless);
return serializer.toUint8Array();
}
case AnyPublicKeyVariant.Secp256k1:
case AnyPublicKeyVariant.Ed25519: {
const singleKeyAccount = account as SingleKeyAccount;
singleKeyAccount.privateKey.serialize(serializer);
return serializer.toUint8Array();
}
default: {
throw new Error(`Invalid public key variant: ${anyPublicKey.variant}`);
}
}
}
case SigningScheme.MultiKey: {
const multiKeyAccount = account as MultiKeyAccount;
multiKeyAccount.publicKey.serialize(serializer);
serializer.serializeU32AsUleb128(multiKeyAccount.signers.length);
multiKeyAccount.signers.forEach((signer) => {
serializer.serializeFixedBytes(toBytes(signer));
});
return serializer.toUint8Array();
}
default:
throw new Error(`Deserialization of Account failed: invalid signingScheme value ${account.signingScheme}`);
}
}
export function toHexStringWithoutPrefix(account: Account): string {
return Hex.hexInputToStringWithoutPrefix(toBytes(account));
}
export function toHexString(account: Account): string {
return Hex.hexInputToString(toBytes(account));
}
export function deserialize(deserializer: Deserializer): Account {
const { address, signingScheme } = deserializeSchemeAndAddress(deserializer);
switch (signingScheme) {
case SigningScheme.Ed25519: {
const privateKey = Ed25519PrivateKey.deserialize(deserializer);
return new Ed25519Account({ privateKey, address });
}
case SigningScheme.SingleKey: {
const variantIndex = deserializer.deserializeUleb128AsU32();
switch (variantIndex) {
case AnyPublicKeyVariant.Ed25519: {
const privateKey = Ed25519PrivateKey.deserialize(deserializer);
return new SingleKeyAccount({ privateKey, address });
}
case AnyPublicKeyVariant.Secp256k1: {
const privateKey = Secp256k1PrivateKey.deserialize(deserializer);
return new SingleKeyAccount({ privateKey, address });
}
case AnyPublicKeyVariant.Keyless: {
const keylessComponents = deserializeKeylessAccountCommon(deserializer);
const jwtClaims = getIssAudAndUidVal(keylessComponents);
return new KeylessAccount({ ...keylessComponents, ...jwtClaims });
}
case AnyPublicKeyVariant.FederatedKeyless: {
const keylessComponents = deserializeKeylessAccountCommon(deserializer);
const jwkAddress = AccountAddress.deserialize(deserializer);
const audless = deserializer.deserializeBool();
const jwtClaims = getIssAudAndUidVal(keylessComponents);
return new FederatedKeylessAccount({ ...keylessComponents, ...jwtClaims, jwkAddress, audless });
}
default:
throw new Error(`Unsupported public key variant ${variantIndex}`);
}
}
case SigningScheme.MultiKey: {
const multiKey = MultiKey.deserialize(deserializer);
const length = deserializer.deserializeUleb128AsU32();
const signers = new Array<SingleKeySignerOrLegacyEd25519Account>();
for (let i = 0; i < length; i += 1) {
const signer = deserialize(deserializer);
if (!isSingleKeySigner(signer) && !(signer instanceof Ed25519Account)) {
throw new Error(
"Deserialization of MultiKeyAccount failed. Signer is not a SingleKeySigner or Ed25519Account",
);
}
signers.push(signer);
}
return new MultiKeyAccount({ multiKey, signers, address });
}
default:
throw new Error(`Deserialization of Account failed: invalid signingScheme value ${signingScheme}`);
}
}
export function keylessAccountFromHex(hex: HexInput): KeylessAccount {
const account = fromHex(hex);
if (!(account instanceof KeylessAccount)) {
throw new Error("Deserialization of KeylessAccount failed");
}
return account;
}
export function federatedKeylessAccountFromHex(hex: HexInput): FederatedKeylessAccount {
const account = fromHex(hex);
if (!(account instanceof FederatedKeylessAccount)) {
throw new Error("Deserialization of FederatedKeylessAccount failed");
}
return account;
}
export function multiKeyAccountFromHex(hex: HexInput): MultiKeyAccount {
const account = fromHex(hex);
if (!(account instanceof MultiKeyAccount)) {
throw new Error("Deserialization of MultiKeyAccount failed");
}
return account;
}
export function singleKeyAccountFromHex(hex: HexInput): SingleKeyAccount {
const account = fromHex(hex);
if (!(account instanceof SingleKeyAccount)) {
throw new Error("Deserialization of SingleKeyAccount failed");
}
return account;
}
export function ed25519AccountFromHex(hex: HexInput): Ed25519Account {
const account = fromHex(hex);
if (!(account instanceof Ed25519Account)) {
throw new Error("Deserialization of Ed25519Account failed");
}
return account;
}
export function fromHex(hex: HexInput): Account {
return deserialize(Deserializer.fromHex(hex));
}
export function fromBytes(bytes: Uint8Array): Account {
return fromHex(bytes);
}
}