From 640ad63d2904fc521a9302d290b72c257e7d4e55 Mon Sep 17 00:00:00 2001 From: maayan Date: Tue, 17 Oct 2023 13:32:33 -0700 Subject: [PATCH 01/10] secpk --- src/client/core.ts | 1 + src/core/account.ts | 16 ++- src/core/authenticationKey.ts | 30 ++-- src/core/crypto/anyPublicKey.ts | 75 ++++++++++ src/core/crypto/anySignature.ts | 56 ++++++++ src/core/crypto/ed25519.ts | 99 +++++++++++++- src/core/crypto/secp256k1.ts | 22 ++- src/internal/transactionSubmission.ts | 1 + src/transactions/authenticator/account.ts | 31 +++-- src/transactions/authenticator/transaction.ts | 37 ++--- .../transaction_builder.ts | 53 ++++--- src/transactions/types.ts | 1 + src/types/index.ts | 37 +++-- .../transaction_submission.test.ts | 129 ++++++++++++++++++ tests/unit/helper.ts | 4 +- 15 files changed, 500 insertions(+), 92 deletions(-) create mode 100644 src/core/crypto/anyPublicKey.ts create mode 100644 src/core/crypto/anySignature.ts create mode 100644 tests/e2e/transaction/transaction_submission.test.ts diff --git a/src/client/core.ts b/src/client/core.ts index 1340ca44e..ae9d5184e 100644 --- a/src/client/core.ts +++ b/src/client/core.ts @@ -95,6 +95,7 @@ export async function aptosRequest( } const errorMessage = errors[result.status]; + console.log(result); throw new AptosApiError( options, result, diff --git a/src/core/account.ts b/src/core/account.ts index 1a752eb6f..cd461cbb2 100644 --- a/src/core/account.ts +++ b/src/core/account.ts @@ -8,8 +8,9 @@ import { Ed25519PrivateKey, Ed25519PublicKey } from "./crypto/ed25519"; import { MultiEd25519PublicKey } from "./crypto/multiEd25519"; import { Secp256k1PrivateKey, Secp256k1PublicKey } from "./crypto/secp256k1"; import { Hex } from "./hex"; -import { HexInput, SigningScheme } from "../types"; +import { HexInput, SigningScheme, SigningSchemeInput } from "../types"; import { derivePrivateKeyFromMnemonic, KeyType } from "../utils/hdKey"; +import { AnyPublicKey } from "./crypto/anyPublicKey"; /** * Class for creating and managing account on Aptos network @@ -61,7 +62,7 @@ export class Account { } else if (this.publicKey instanceof MultiEd25519PublicKey) { this.signingScheme = SigningScheme.MultiEd25519; } else if (this.publicKey instanceof Secp256k1PublicKey) { - this.signingScheme = SigningScheme.Secp256k1Ecdsa; + this.signingScheme = SigningScheme.SingleKey; } else { throw new Error("Can not create new Account, unsupported public key type"); } @@ -78,11 +79,11 @@ export class Account { * * @returns Account with the given signing scheme */ - static generate(scheme?: SigningScheme): Account { + static generate(scheme?: SigningSchemeInput): Account { let privateKey: PrivateKey; switch (scheme) { - case SigningScheme.Secp256k1Ecdsa: + case SigningSchemeInput.Secp256k1Ecdsa: privateKey = Secp256k1PrivateKey.generate(); break; // TODO: Add support for MultiEd25519 @@ -104,8 +105,11 @@ export class Account { * @param privateKey Hex - private key of the account * @returns Account */ - static fromPrivateKey(privateKey: PrivateKey): Account { - const publicKey = privateKey.publicKey(); + static fromPrivateKey(privateKey: PrivateKey, legacy: boolean = true): Account { + let publicKey = privateKey.publicKey(); + if (!legacy) { + publicKey = new AnyPublicKey(publicKey); + } const authKey = Account.authKey({ publicKey }); const address = new AccountAddress({ data: authKey.toUint8Array() }); return Account.fromPrivateKeyAndAddress({ privateKey, address }); diff --git a/src/core/authenticationKey.ts b/src/core/authenticationKey.ts index 456b8b6cf..755f8d36e 100644 --- a/src/core/authenticationKey.ts +++ b/src/core/authenticationKey.ts @@ -6,9 +6,9 @@ import { AccountAddress } from "./accountAddress"; import { PublicKey } from "./crypto/asymmetricCrypto"; import { Ed25519PublicKey } from "./crypto/ed25519"; import { MultiEd25519PublicKey } from "./crypto/multiEd25519"; -import { Secp256k1PublicKey } from "./crypto/secp256k1"; import { Hex } from "./hex"; import { AuthenticationKeyScheme, HexInput, SigningScheme } from "../types"; +import { AnyPublicKey } from "./crypto/anyPublicKey"; /** * Each account stores an authentication key. Authentication key enables account owners to rotate @@ -53,8 +53,20 @@ export class AuthenticationKey { * This allows for the creation of AuthenticationKeys that are not derived from Public Keys directly * @param args */ - private static fromBytesAndScheme(args: { bytes: HexInput; scheme: AuthenticationKeyScheme }) { - const { bytes, scheme } = args; + private static fromBytesAndScheme(args: { publicKey: PublicKey; scheme: AuthenticationKeyScheme }) { + const { publicKey, scheme } = args; + const bytes = publicKey.toUint8Array(); + + // TODO - check for single key or multi key + if (scheme === SigningScheme.SingleKey) { + let newBytes = publicKey.bcsToBytes(); + const authKeyBytes = new Uint8Array([...newBytes, scheme]); + console.log("authKeyBytes", authKeyBytes); + const hash = sha3Hash.create(); + hash.update(authKeyBytes); + const hashDigest = hash.digest(); + return new AuthenticationKey({ data: hashDigest }); + } const inputBytes = Hex.fromHexInput(bytes).toUint8Array(); const authKeyBytes = new Uint8Array(inputBytes.length + 1); authKeyBytes.set(inputBytes); @@ -62,8 +74,9 @@ export class AuthenticationKey { const hash = sha3Hash.create(); hash.update(authKeyBytes); + const hashDigest = hash.digest(); - return new AuthenticationKey({ data: hash.digest() }); + return new AuthenticationKey({ data: hashDigest }); } /** @@ -77,17 +90,18 @@ export class AuthenticationKey { let scheme: number; if (publicKey instanceof Ed25519PublicKey) { + // for legacy support scheme = SigningScheme.Ed25519.valueOf(); } else if (publicKey instanceof MultiEd25519PublicKey) { + // for legacy support scheme = SigningScheme.MultiEd25519.valueOf(); - } else if (publicKey instanceof Secp256k1PublicKey) { - scheme = SigningScheme.Secp256k1Ecdsa.valueOf(); + } else if (publicKey instanceof AnyPublicKey) { + scheme = SigningScheme.SingleKey.valueOf(); } else { throw new Error("No supported authentication scheme for public key"); } - const pubKeyBytes = publicKey.toUint8Array(); - return AuthenticationKey.fromBytesAndScheme({ bytes: pubKeyBytes, scheme }); + return AuthenticationKey.fromBytesAndScheme({ publicKey: publicKey, scheme }); } /** diff --git a/src/core/crypto/anyPublicKey.ts b/src/core/crypto/anyPublicKey.ts new file mode 100644 index 000000000..50cb05b4b --- /dev/null +++ b/src/core/crypto/anyPublicKey.ts @@ -0,0 +1,75 @@ +import { Serializer, Deserializer } from "../../bcs"; +import { AnyPublicKeyVariant, HexInput } from "../../types"; +import { AnySignature } from "./anySignature"; +import { PublicKey } from "./asymmetric_crypto"; +import { Ed25519PublicKey, Ed25519Signature } from "./ed25519"; +import { Secp256k1PublicKey, Secp256k1Signature } from "./secp256k1"; + +export class AnyPublicKey extends PublicKey { + public readonly publicKey: PublicKey; + + constructor(publicKey: PublicKey) { + super(); + this.publicKey = publicKey; + } + + /** + * Get the public key in bytes (Uint8Array). + * + * @returns Uint8Array representation of the public key + */ + toUint8Array(): Uint8Array { + return this.publicKey.toUint8Array(); + } + + /** + * Get the public key as a hex string with the 0x prefix. + * + * @returns string representation of the public key + */ + toString(): string { + return this.publicKey.toString(); + } + + /** + * Verifies a signed data with a public key + * + * @param args.message message + * @param args.signature The signature + * @returns true if the signature is valid + */ + verifySignature(args: { message: HexInput; signature: AnySignature }): boolean { + const { message, signature } = args; + if (this.publicKey instanceof Ed25519PublicKey && signature.signature instanceof Ed25519Signature) { + return this.publicKey.verifySignature({ message, signature: signature.signature }); + } else if (this.publicKey instanceof Secp256k1PublicKey && signature.signature instanceof Secp256k1Signature) { + return this.publicKey.verifySignature({ message, signature: signature.signature }); + } else { + throw new Error("Unknown public key type"); + } + } + + serialize(serializer: Serializer): void { + if (this.publicKey instanceof Ed25519PublicKey) { + serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Ed25519); + this.publicKey.serialize(serializer); + } else if (this.publicKey instanceof Secp256k1PublicKey) { + serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Secp256k1); + this.publicKey.serialize(serializer); + } else { + throw new Error("Unknown public key type"); + } + } + + static deserialize(deserializer: Deserializer): AnyPublicKey { + const index = deserializer.deserializeUleb128AsU32(); + switch (index) { + case AnyPublicKeyVariant.Ed25519: + return new AnyPublicKey(Ed25519PublicKey.load(deserializer)); + case AnyPublicKeyVariant.Secp256k1: + return new AnyPublicKey(Secp256k1PublicKey.load(deserializer)); + default: + throw new Error(`Unknown variant index for AnyPublicKey: ${index}`); + } + } +} diff --git a/src/core/crypto/anySignature.ts b/src/core/crypto/anySignature.ts new file mode 100644 index 000000000..6a62d375a --- /dev/null +++ b/src/core/crypto/anySignature.ts @@ -0,0 +1,56 @@ +import { Serializer, Deserializer } from "../../bcs"; +import { AnySignatureVariant } from "../../types"; +import { Signature } from "./asymmetric_crypto"; +import { Ed25519PublicKey } from "./ed25519"; +import { Secp256k1PublicKey } from "./secp256k1"; + +export class AnySignature extends Signature { + public readonly signature: Signature; + + constructor(signature: Signature) { + super(); + this.signature = signature; + } + + /** + * Get the public key in bytes (Uint8Array). + * + * @returns Uint8Array representation of the public key + */ + toUint8Array(): Uint8Array { + return this.signature.toUint8Array(); + } + + /** + * Get the public key as a hex string with the 0x prefix. + * + * @returns string representation of the public key + */ + toString(): string { + return this.signature.toString(); + } + + serialize(serializer: Serializer): void { + if (this.signature instanceof Ed25519PublicKey) { + serializer.serializeU32AsUleb128(AnySignatureVariant.Ed25519); + this.signature.serialize(serializer); + } else if (this.signature instanceof Secp256k1PublicKey) { + serializer.serializeU32AsUleb128(AnySignatureVariant.Secp256k1); + this.signature.serialize(serializer); + } else { + throw new Error("Unknown public key type"); + } + } + + static deserialize(deserializer: Deserializer): AnySignature { + const index = deserializer.deserializeUleb128AsU32(); + switch (index) { + case AnySignatureVariant.Ed25519: + return new AnySignature(Ed25519PublicKey.load(deserializer)); + case AnySignatureVariant.Secp256k1: + return new AnySignature(Secp256k1PublicKey.load(deserializer)); + default: + throw new Error(`Unknown variant index for AnyPublicKey: ${index}`); + } + } +} diff --git a/src/core/crypto/ed25519.ts b/src/core/crypto/ed25519.ts index edb91173b..c1009b0d7 100644 --- a/src/core/crypto/ed25519.ts +++ b/src/core/crypto/ed25519.ts @@ -6,7 +6,7 @@ import { PublicKey, PrivateKey, Signature } from "./asymmetricCrypto"; import { Deserializer } from "../../bcs/deserializer"; import { Serializer } from "../../bcs/serializer"; import { Hex } from "../hex"; -import { HexInput } from "../../types"; +import { AnyPublicKeyVariant, HexInput } from "../../types"; /** * Represents the public key of an Ed25519 key pair. @@ -76,6 +76,93 @@ export class Ed25519PublicKey extends PublicKey { const bytes = deserializer.deserializeBytes(); return new Ed25519PublicKey(bytes); } + + serializeForSingleKey(serializer: Serializer): void { + serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Ed25519); + this.serialize(serializer); + } + + static load(deserializer: Deserializer): Ed25519PublicKey { + const bytes = deserializer.deserializeBytes(); + return new Ed25519PublicKey(bytes); + } +} + +export class SingleKey extends PublicKey { + /** + * Length of an Ed25519 public key + */ + static readonly LENGTH: number = 65; + + /** + * Bytes of the public key + * @private + */ + private readonly key: Hex; + + /** + * Create a new PublicKey instance from a Uint8Array or String. + * + * @param hexInput A HexInput (string or Uint8Array) + */ + constructor(hexInput: HexInput) { + super(); + + const hex = Hex.fromHexInput(hexInput); + if (hex.toUint8Array().length !== SingleKey.LENGTH) { + throw new Error(`PublicKey length should be ${SingleKey.LENGTH}`); + } + this.key = hex; + } + + /** + * Get the public key in bytes (Uint8Array). + * + * @returns Uint8Array representation of the public key + */ + toUint8Array(): Uint8Array { + return this.key.toUint8Array(); + } + + /** + * Get the public key as a hex string with the 0x prefix. + * + * @returns string representation of the public key + */ + toString(): string { + return this.key.toString(); + } + + /** + * Verifies a signed data with a public key + * @param args.message a signed message + * @param args.signature the signature of the message + */ + verifySignature(args: { message: HexInput; signature: Ed25519Signature }): boolean { + const { message, signature } = args; + const rawMessage = Hex.fromHexInput(message).toUint8Array(); + const rawSignature = Hex.fromHexInput(signature.toUint8Array()).toUint8Array(); + return nacl.sign.detached.verify(rawMessage, rawSignature, this.key.toUint8Array()); + } + + serialize(serializer: Serializer): void { + serializer.serializeBytes(this.key.toUint8Array()); + } + + static deserialize(deserializer: Deserializer): Ed25519PublicKey { + const bytes = deserializer.deserializeBytes(); + return new Ed25519PublicKey(bytes); + } + + serializeForSingleKey(serializer: Serializer): void { + serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Ed25519); + this.serialize(serializer); + } + + static load(deserializer: Deserializer): Ed25519PublicKey { + const bytes = deserializer.deserializeBytes(); + return new Ed25519PublicKey(bytes); + } } /** @@ -221,4 +308,14 @@ export class Ed25519Signature extends Signature { const bytes = deserializer.deserializeBytes(); return new Ed25519Signature(bytes); } + + serializeForSingleKey(serializer: Serializer): void { + serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Ed25519); + this.serialize(serializer); + } + + static load(deserializer: Deserializer): Ed25519Signature { + const bytes = deserializer.deserializeBytes(); + return new Ed25519Signature(bytes); + } } diff --git a/src/core/crypto/secp256k1.ts b/src/core/crypto/secp256k1.ts index eddb6c40d..47d888027 100644 --- a/src/core/crypto/secp256k1.ts +++ b/src/core/crypto/secp256k1.ts @@ -6,7 +6,7 @@ import { secp256k1 } from "@noble/curves/secp256k1"; import { PrivateKey, PublicKey, Signature } from "./asymmetricCrypto"; import { Deserializer, Serializer } from "../../bcs"; import { Hex } from "../hex"; -import { HexInput } from "../../types"; +import { AnyPublicKeyVariant, HexInput } from "../../types"; /** * Represents the Secp256k1 ecdsa public key @@ -74,6 +74,16 @@ export class Secp256k1PublicKey extends PublicKey { const bytes = deserializer.deserializeBytes(); return new Secp256k1PublicKey(bytes); } + + serializeForSingleKey(serializer: Serializer): void { + serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Secp256k1); + this.serialize(serializer); + } + + static load(deserializer: Deserializer): Secp256k1PublicKey { + const bytes = deserializer.deserializeBytes(); + return new Secp256k1PublicKey(bytes); + } } /** @@ -224,4 +234,14 @@ export class Secp256k1Signature extends Signature { const hex = deserializer.deserializeBytes(); return new Secp256k1Signature(hex); } + + serializeForSingleKey(serializer: Serializer): void { + serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Secp256k1); + this.serialize(serializer); + } + + static load(deserializer: Deserializer): Secp256k1Signature { + const bytes = deserializer.deserializeBytes(); + return new Secp256k1Signature(bytes); + } } diff --git a/src/internal/transactionSubmission.ts b/src/internal/transactionSubmission.ts index 9a183e26e..0572a53a4 100644 --- a/src/internal/transactionSubmission.ts +++ b/src/internal/transactionSubmission.ts @@ -8,6 +8,7 @@ import { AptosConfig } from "../api/aptosConfig"; import { MoveVector } from "../bcs"; import { postAptosFullNode } from "../client"; +import { Hex } from "../core"; import { Account } from "../core/account"; import { AccountAuthenticator } from "../transactions/authenticator/account"; import { diff --git a/src/transactions/authenticator/account.ts b/src/transactions/authenticator/account.ts index 26774ded1..e6e7d599a 100644 --- a/src/transactions/authenticator/account.ts +++ b/src/transactions/authenticator/account.ts @@ -4,9 +4,10 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { Serializer, Deserializer, Serializable } from "../../bcs"; +import { AnyPublicKey } from "../../core/crypto/anyPublicKey"; +import { AnySignature } from "../../core/crypto/anySignature"; import { Ed25519PublicKey, Ed25519Signature } from "../../core/crypto/ed25519"; import { MultiEd25519PublicKey, MultiEd25519Signature } from "../../core/crypto/multiEd25519"; -import { Secp256k1PublicKey, Secp256k1Signature } from "../../core/crypto/secp256k1"; import { AccountAuthenticatorVariant } from "../../types"; export abstract class AccountAuthenticator extends Serializable { @@ -19,8 +20,8 @@ export abstract class AccountAuthenticator extends Serializable { return AccountAuthenticatorEd25519.load(deserializer); case AccountAuthenticatorVariant.MultiEd25519: return AccountAuthenticatorMultiEd25519.load(deserializer); - case AccountAuthenticatorVariant.Secp256k1: - return AccountAuthenticatorSecp256k1.load(deserializer); + case AccountAuthenticatorVariant.SingleKey: + return SingleKeyAuthenticator.load(deserializer); default: throw new Error(`Unknown variant index for AccountAuthenticator: ${index}`); } @@ -90,32 +91,32 @@ export class AccountAuthenticatorMultiEd25519 extends AccountAuthenticator { } /** - * A Secp256k1 AccountAuthenticator for a single signer + * SingleKeyAuthenticator for a single signer * - * @param public_key A Secp256k1 public key - * @param signature A Secp256k1 signature + * @param public_key AnyPublicKey + * @param signature AnySignature * */ -export class AccountAuthenticatorSecp256k1 extends AccountAuthenticator { - public readonly public_key: Secp256k1PublicKey; +export class SingleKeyAuthenticator extends AccountAuthenticator { + public readonly public_key: AnyPublicKey; - public readonly signature: Secp256k1Signature; + public readonly signature: AnySignature; - constructor(public_key: Secp256k1PublicKey, signature: Secp256k1Signature) { + constructor(public_key: AnyPublicKey, signature: AnySignature) { super(); this.public_key = public_key; this.signature = signature; } serialize(serializer: Serializer): void { - serializer.serializeU32AsUleb128(AccountAuthenticatorVariant.Secp256k1); + serializer.serializeU32AsUleb128(AccountAuthenticatorVariant.SingleKey); this.public_key.serialize(serializer); this.signature.serialize(serializer); } - static load(deserializer: Deserializer): AccountAuthenticatorSecp256k1 { - const public_key = Secp256k1PublicKey.deserialize(deserializer); - const signature = Secp256k1Signature.deserialize(deserializer); - return new AccountAuthenticatorSecp256k1(public_key, signature); + static load(deserializer: Deserializer): SingleKeyAuthenticator { + const public_key = AnyPublicKey.deserialize(deserializer); + const signature = AnySignature.deserialize(deserializer); + return new SingleKeyAuthenticator(public_key, signature); } } diff --git a/src/transactions/authenticator/transaction.ts b/src/transactions/authenticator/transaction.ts index 3991c1c2e..b4854de43 100644 --- a/src/transactions/authenticator/transaction.ts +++ b/src/transactions/authenticator/transaction.ts @@ -4,14 +4,13 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { AccountAuthenticator } from "./account"; -import { Deserializer, Serializer } from "../../bcs"; +import { Deserializer, Serializable, Serializer } from "../../bcs"; import { AccountAddress } from "../../core"; import { Ed25519PublicKey, Ed25519Signature } from "../../core/crypto/ed25519"; import { MultiEd25519PublicKey, MultiEd25519Signature } from "../../core/crypto/multiEd25519"; -import { Secp256k1PublicKey, Secp256k1Signature } from "../../core/crypto/secp256k1"; import { TransactionAuthenticatorVariant } from "../../types"; -export abstract class TransactionAuthenticator { +export abstract class TransactionAuthenticator extends Serializable { abstract serialize(serializer: Serializer): void; static deserialize(deserializer: Deserializer): TransactionAuthenticator { @@ -25,8 +24,8 @@ export abstract class TransactionAuthenticator { return TransactionAuthenticatorMultiAgent.load(deserializer); case TransactionAuthenticatorVariant.FeePayer: return TransactionAuthenticatorFeePayer.load(deserializer); - case TransactionAuthenticatorVariant.Secp256k1Ecdsa: - return TransactionAuthenticatorSecp256k1.load(deserializer); + case TransactionAuthenticatorVariant.SingleSender: + return SingleSender.load(deserializer); default: throw new Error(`Unknown variant index for TransactionAuthenticator: ${index}`); } @@ -192,31 +191,25 @@ export class TransactionAuthenticatorFeePayer extends TransactionAuthenticator { } /** - * Transaction authenticator Secp256k1 for a single signer transaction + * Single Sender authenticator for a single signer transaction * - * @param public_key Client's public key - * @param signature Secp256k1 signature of a `RawTransaction` + * @param sender AccountAuthenticator */ -export class TransactionAuthenticatorSecp256k1 extends TransactionAuthenticator { - public readonly public_key: Secp256k1PublicKey; - - public readonly signature: Secp256k1Signature; +export class SingleSender extends TransactionAuthenticator { + public readonly sender: AccountAuthenticator; - constructor(public_key: Secp256k1PublicKey, signature: Secp256k1Signature) { + constructor(sender: AccountAuthenticator) { super(); - this.public_key = public_key; - this.signature = signature; + this.sender = sender; } serialize(serializer: Serializer): void { - serializer.serializeU32AsUleb128(TransactionAuthenticatorVariant.Secp256k1Ecdsa); - this.public_key.serialize(serializer); - this.signature.serialize(serializer); + serializer.serializeU32AsUleb128(TransactionAuthenticatorVariant.SingleSender); + this.sender.serialize(serializer); } - static load(deserializer: Deserializer): TransactionAuthenticatorSecp256k1 { - const public_key = Secp256k1PublicKey.deserialize(deserializer); - const signature = Secp256k1Signature.deserialize(deserializer); - return new TransactionAuthenticatorSecp256k1(public_key, signature); + static load(deserializer: Deserializer): SingleSender { + const sender = AccountAuthenticator.deserialize(deserializer); + return new SingleSender(sender); } } diff --git a/src/transactions/transaction_builder/transaction_builder.ts b/src/transactions/transaction_builder/transaction_builder.ts index 0e6f01505..7e0eecd7a 100644 --- a/src/transactions/transaction_builder/transaction_builder.ts +++ b/src/transactions/transaction_builder/transaction_builder.ts @@ -11,6 +11,8 @@ import { AptosConfig } from "../../api/aptosConfig"; import { Deserializer } from "../../bcs/deserializer"; import { AccountAddress, Hex, PublicKey } from "../../core"; import { Account } from "../../core/account"; +import { AnyPublicKey } from "../../core/crypto/anyPublicKey"; +import { AnySignature } from "../../core/crypto/anySignature"; import { Ed25519PublicKey, Ed25519Signature } from "../../core/crypto/ed25519"; import { Secp256k1PublicKey, Secp256k1Signature } from "../../core/crypto/secp256k1"; import { getInfo } from "../../internal/account"; @@ -24,16 +26,12 @@ import { RAW_TRANSACTION_SALT, RAW_TRANSACTION_WITH_DATA_SALT, } from "../../utils/const"; -import { - AccountAuthenticator, - AccountAuthenticatorEd25519, - AccountAuthenticatorSecp256k1, -} from "../authenticator/account"; +import { AccountAuthenticator, AccountAuthenticatorEd25519, SingleKeyAuthenticator } from "../authenticator/account"; import { TransactionAuthenticatorEd25519, TransactionAuthenticatorFeePayer, TransactionAuthenticatorMultiAgent, - TransactionAuthenticatorSecp256k1, + SingleSender, } from "../authenticator/transaction"; import { ChainId, @@ -319,11 +317,8 @@ export function generateSignedTransactionForSimulation(args: SimulateTransaction accountAuthenticator.public_key, accountAuthenticator.signature, ); - } else if (accountAuthenticator instanceof AccountAuthenticatorSecp256k1) { - transactionAuthenticator = new TransactionAuthenticatorSecp256k1( - accountAuthenticator.public_key, - accountAuthenticator.signature, - ); + } else if (accountAuthenticator instanceof SingleKeyAuthenticator) { + transactionAuthenticator = new SingleSender(accountAuthenticator); } else { throw new Error("Invalid public key"); } @@ -331,15 +326,21 @@ export function generateSignedTransactionForSimulation(args: SimulateTransaction } export function getAuthenticatorForSimulation(publicKey: PublicKey) { - if (publicKey instanceof Ed25519PublicKey) { - return new AccountAuthenticatorEd25519( - new Ed25519PublicKey(publicKey.toUint8Array()), - new Ed25519Signature(new Uint8Array(64)), - ); + // TODO add support for AnyMultiKey + if (publicKey instanceof AnyPublicKey) { + if (publicKey.publicKey instanceof Ed25519PublicKey) { + return new SingleKeyAuthenticator(publicKey, new AnySignature(new Ed25519Signature(new Uint8Array(64)))); + } + if (publicKey.publicKey instanceof Secp256k1PublicKey) { + return new SingleKeyAuthenticator(publicKey, new AnySignature(new Secp256k1Signature(new Uint8Array(64)))); + } } - return new AccountAuthenticatorSecp256k1( - new Secp256k1PublicKey(publicKey.toUint8Array()), - new Secp256k1Signature(new Uint8Array(64)), + + // legacy code + // TODO add support to legacy multied25519 + return new AccountAuthenticatorEd25519( + new Ed25519PublicKey(publicKey.toUint8Array()), + new Ed25519Signature(new Uint8Array(64)), ); } @@ -369,11 +370,8 @@ export function sign(args: { signer: Account; transaction: AnyRawTransaction }): new Ed25519PublicKey(signer.publicKey.toUint8Array()), new Ed25519Signature(signerSignature.toUint8Array()), ); - case SigningScheme.Secp256k1Ecdsa: - return new AccountAuthenticatorSecp256k1( - new Secp256k1PublicKey(signer.publicKey.toUint8Array()), - new Secp256k1Signature(signerSignature.toUint8Array()), - ); + case SigningScheme.SingleKey: + return new SingleKeyAuthenticator(new AnyPublicKey(signer.publicKey), new AnySignature(signerSignature)); // TODO support MultiEd25519 default: throw new Error(`Cannot sign transaction, signing scheme ${signer.signingScheme} not supported`); @@ -424,11 +422,8 @@ export function generateSignedTransaction(args: { return new SignedTransaction(transactionToSubmit as RawTransaction, transactionAuthenticator).bcsToBytes(); } - if (accountAuthenticator instanceof AccountAuthenticatorSecp256k1) { - const transactionAuthenticator = new TransactionAuthenticatorSecp256k1( - accountAuthenticator.public_key, - accountAuthenticator.signature, - ); + if (accountAuthenticator instanceof SingleKeyAuthenticator) { + const transactionAuthenticator = new SingleSender(accountAuthenticator); // return signed transaction return new SignedTransaction(transactionToSubmit as RawTransaction, transactionAuthenticator).bcsToBytes(); } diff --git a/src/transactions/types.ts b/src/transactions/types.ts index 7a45b69c1..6a460bf21 100644 --- a/src/transactions/types.ts +++ b/src/transactions/types.ts @@ -17,6 +17,7 @@ import { } from "./instances"; import { AnyNumber, HexInput, MoveStructType } from "../types"; import { TypeTag } from "./typeTag/typeTag"; +import { AnyPublicKey } from "../core/crypto/anyPublicKey"; export type EntryFunctionArgumentTypes = | Bool diff --git a/src/types/index.ts b/src/types/index.ts index a87e56aa1..03b41d7ee 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -87,7 +87,7 @@ export enum TransactionAuthenticatorVariant { MultiEd25519 = 1, MultiAgent = 2, FeePayer = 3, - Secp256k1Ecdsa = 4, + SingleSender = 4, } /** @@ -97,7 +97,18 @@ export enum TransactionAuthenticatorVariant { export enum AccountAuthenticatorVariant { Ed25519 = 0, MultiEd25519 = 1, - Secp256k1 = 2, + SingleKey = 2, + MultiKey = 3, +} + +export enum AnyPublicKeyVariant { + Ed25519 = 0, + Secp256k1 = 1, +} + +export enum AnySignatureVariant { + Ed25519 = 0, + Secp256k1 = 1, } /** @@ -923,11 +934,6 @@ export type TableItemRequest = { */ export type AuthenticationKeyScheme = SigningScheme | DeriveScheme; -/** - * A list of signing schemes that are supported by Aptos. - * - * https://github.com/aptos-labs/aptos-core/blob/main/types/src/transaction/authenticator.rs#L375-L378 - */ export enum SigningScheme { /** * For Ed25519PublicKey @@ -938,7 +944,22 @@ export enum SigningScheme { */ MultiEd25519 = 1, /** - * For Secp256k1 ecdsa + * For SingleKey ecdsa + */ + SingleKey = 2, +} + +export enum SigningSchemeInput { + /** + * For Ed25519PublicKey + */ + Ed25519 = 0, + /** + * For MultiEd25519PublicKey + */ + MultiEd25519 = 1, + /** + * For Secp256k1Ecdsa */ Secp256k1Ecdsa = 2, } diff --git a/tests/e2e/transaction/transaction_submission.test.ts b/tests/e2e/transaction/transaction_submission.test.ts new file mode 100644 index 000000000..c6f10ba2e --- /dev/null +++ b/tests/e2e/transaction/transaction_submission.test.ts @@ -0,0 +1,129 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +import { + Account, + AptosConfig, + Network, + Aptos, + U64, + SigningScheme, + SigningSchemeInput, + AuthenticationKey, + Deserializer, + Secp256k1PrivateKey, + AccountAddress, + Hex, +} from "../../../src"; +import { waitForTransaction } from "../../../src/internal/transaction"; +import { AccountAuthenticator, SingleKeyAuthenticator } from "../../../src/transactions/authenticator/account"; +import { + ChainId, + RawTransaction, + Script, + TransactionPayload, + TransactionPayloadScript, +} from "../../../src/transactions/instances"; +import { FUND_AMOUNT, longTestTimeout, secp256k1TestObject } from "../../unit/helper"; + +describe("transaction submission", () => { + describe("submitTransaction", () => { + test( + "it submits a script transaction", + async () => { + const config = new AptosConfig({ network: Network.DEVNET }); + const aptos = new Aptos(config); + const alice = Account.generate(); + await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); + const bob = Account.generate(); + await aptos.fundAccount({ accountAddress: bob.accountAddress.toString(), amount: FUND_AMOUNT }); + const rawTxn = await aptos.generateTransaction({ + sender: alice.accountAddress.toString(), + secondarySignerAddresses: [bob.accountAddress.toString()], + data: { + bytecode: + // eslint-disable-next-line max-len + "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", + arguments: [ + new U64(BigInt(100)), + new U64(BigInt(200)), + bob.accountAddress, + alice.accountAddress, + new U64(BigInt(50)), + ], + }, + }); + const authenticator = aptos.signTransaction({ + signer: alice, + transaction: rawTxn, + }); + const bobauthenticator = aptos.signTransaction({ + signer: bob, + transaction: rawTxn, + }); + const response = await aptos.submitTransaction({ + transaction: rawTxn, + senderAuthenticator: authenticator, + secondarySignerAuthenticators: { + additionalSignersAuthenticators: [bobauthenticator], + }, + }); + await waitForTransaction({ aptosConfig: config, transactionHash: response.hash }); + }, + longTestTimeout, + ); + + test("it submits an entry function transaction", async () => { + const config = new AptosConfig({ network: Network.DEVNET }); + const aptos = new Aptos(config); + const alice = Account.generate(); + await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); + const bob = Account.generate(); + const rawTxn = await aptos.generateTransaction({ + sender: alice.accountAddress.toString(), + data: { + function: "0x1::aptos_account::transfer", + arguments: [bob.accountAddress, new U64(1)], + }, + }); + const authenticator = aptos.signTransaction({ + signer: alice, + transaction: rawTxn, + }); + const response = await aptos.submitTransaction({ + transaction: rawTxn, + senderAuthenticator: authenticator, + }); + await waitForTransaction({ aptosConfig: config, transactionHash: response.hash }); + }); + test.skip("it submits an entry function transaction Secp256k1", async () => { + const config = new AptosConfig({ network: Network.DEVNET }); + const aptos = new Aptos(config); + const alice = Account.fromPrivateKey(new Secp256k1PrivateKey(secp256k1TestObject.privateKey)); + //await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); + const bob = Account.generate(); + const rawTxn = await aptos.generateTransaction({ + sender: alice.accountAddress.toString(), + data: { + function: "0x1::aptos_account::transfer", + arguments: [bob.accountAddress, new U64(1)], + }, + }); + + const authenticator = aptos.signTransaction({ + signer: alice, + transaction: rawTxn, + }) as SingleKeyAuthenticator; + const response = await aptos.submitTransaction({ + transaction: rawTxn, + senderAuthenticator: authenticator, + }); + console.log(response.hash); + // await waitForTransaction({ + // aptosConfig: config, + // transactionHash: response.hash, + // options: { indexerVersionCheck: false }, + // }); + }); + }); +}); diff --git a/tests/unit/helper.ts b/tests/unit/helper.ts index 7605e460c..a9c0248b2 100644 --- a/tests/unit/helper.ts +++ b/tests/unit/helper.ts @@ -49,8 +49,8 @@ export const secp256k1TestObject = { privateKey: "0xd107155adf816a0a94c6db3c9489c13ad8a1eda7ada2e558ba3bfa47c020347e", publicKey: "0x04acdd16651b839c24665b7e2033b55225f384554949fef46c397b5275f37f6ee95554d70fb5d9f93c5831ebf695c7206e7477ce708f03ae9bb2862dc6c9e033ea", - address: "0x44b9b90a0bd6a691a20cb06148f10ec9c21da63bb5df345ae38507e0c3c2f897", - authKey: "0x44b9b90a0bd6a691a20cb06148f10ec9c21da63bb5df345ae38507e0c3c2f897", + address: "0x5792c985bc96f436270bd2a3c692210b09c7febb8889345ceefdbae4bacfe498", + authKey: "0x5792c985bc96f436270bd2a3c692210b09c7febb8889345ceefdbae4bacfe498", messageEncoded: "68656c6c6f20776f726c64", // "hello world" signatureHex: "0xd0d634e843b61339473b028105930ace022980708b2855954b977da09df84a770c0b68c29c8ca1b5409a5085b0ec263be80e433c83fcf6debb82f3447e71edca", From 4b1fd50a6f243990835cfe2c40451978df8b9cb5 Mon Sep 17 00:00:00 2001 From: maayan Date: Tue, 17 Oct 2023 22:00:34 -0700 Subject: [PATCH 02/10] generate account --- src/core/account.ts | 4 ++-- src/core/authenticationKey.ts | 10 ++++++-- src/core/crypto/anySignature.ts | 12 +++++----- tests/e2e/transaction/signTransaction.test.ts | 23 +++++++++++++------ .../transaction/transactionSimulation.test.ts | 4 ++-- .../transaction_submission.test.ts | 4 ++-- 6 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/core/account.ts b/src/core/account.ts index cd461cbb2..360d1d44b 100644 --- a/src/core/account.ts +++ b/src/core/account.ts @@ -86,14 +86,14 @@ export class Account { case SigningSchemeInput.Secp256k1Ecdsa: privateKey = Secp256k1PrivateKey.generate(); break; - // TODO: Add support for MultiEd25519 + // TODO: Add support for MultiEd25519 as AnyMultiKey default: privateKey = Ed25519PrivateKey.generate(); } const address = new AccountAddress({ data: Account.authKey({ - publicKey: privateKey.publicKey(), + publicKey: new AnyPublicKey(privateKey.publicKey()), // TODO support AnyMultiKey }).toUint8Array(), }); return new Account({ privateKey, address }); diff --git a/src/core/authenticationKey.ts b/src/core/authenticationKey.ts index 755f8d36e..2bdf278b6 100644 --- a/src/core/authenticationKey.ts +++ b/src/core/authenticationKey.ts @@ -55,11 +55,16 @@ export class AuthenticationKey { */ private static fromBytesAndScheme(args: { publicKey: PublicKey; scheme: AuthenticationKeyScheme }) { const { publicKey, scheme } = args; - const bytes = publicKey.toUint8Array(); // TODO - check for single key or multi key if (scheme === SigningScheme.SingleKey) { - let newBytes = publicKey.bcsToBytes(); + let newBytes: Uint8Array = new Uint8Array(); + if (publicKey instanceof AnyPublicKey) { + newBytes = publicKey.bcsToBytes(); + } else { + newBytes = new AnyPublicKey(publicKey).bcsToBytes(); + } + const authKeyBytes = new Uint8Array([...newBytes, scheme]); console.log("authKeyBytes", authKeyBytes); const hash = sha3Hash.create(); @@ -67,6 +72,7 @@ export class AuthenticationKey { const hashDigest = hash.digest(); return new AuthenticationKey({ data: hashDigest }); } + const bytes = publicKey.toUint8Array(); const inputBytes = Hex.fromHexInput(bytes).toUint8Array(); const authKeyBytes = new Uint8Array(inputBytes.length + 1); authKeyBytes.set(inputBytes); diff --git a/src/core/crypto/anySignature.ts b/src/core/crypto/anySignature.ts index 6a62d375a..301c33665 100644 --- a/src/core/crypto/anySignature.ts +++ b/src/core/crypto/anySignature.ts @@ -1,8 +1,8 @@ import { Serializer, Deserializer } from "../../bcs"; import { AnySignatureVariant } from "../../types"; import { Signature } from "./asymmetric_crypto"; -import { Ed25519PublicKey } from "./ed25519"; -import { Secp256k1PublicKey } from "./secp256k1"; +import { Ed25519Signature } from "./ed25519"; +import { Secp256k1Signature } from "./secp256k1"; export class AnySignature extends Signature { public readonly signature: Signature; @@ -31,10 +31,10 @@ export class AnySignature extends Signature { } serialize(serializer: Serializer): void { - if (this.signature instanceof Ed25519PublicKey) { + if (this.signature instanceof Ed25519Signature) { serializer.serializeU32AsUleb128(AnySignatureVariant.Ed25519); this.signature.serialize(serializer); - } else if (this.signature instanceof Secp256k1PublicKey) { + } else if (this.signature instanceof Secp256k1Signature) { serializer.serializeU32AsUleb128(AnySignatureVariant.Secp256k1); this.signature.serialize(serializer); } else { @@ -46,9 +46,9 @@ export class AnySignature extends Signature { const index = deserializer.deserializeUleb128AsU32(); switch (index) { case AnySignatureVariant.Ed25519: - return new AnySignature(Ed25519PublicKey.load(deserializer)); + return new AnySignature(Ed25519Signature.load(deserializer)); case AnySignatureVariant.Secp256k1: - return new AnySignature(Secp256k1PublicKey.load(deserializer)); + return new AnySignature(Secp256k1Signature.load(deserializer)); default: throw new Error(`Unknown variant index for AnyPublicKey: ${index}`); } diff --git a/tests/e2e/transaction/signTransaction.test.ts b/tests/e2e/transaction/signTransaction.test.ts index aa0c46c90..592338cc3 100644 --- a/tests/e2e/transaction/signTransaction.test.ts +++ b/tests/e2e/transaction/signTransaction.test.ts @@ -1,8 +1,17 @@ -import { AptosConfig, Network, Aptos, Account, Deserializer, U64, SigningScheme } from "../../../src"; +import { + AptosConfig, + Network, + Aptos, + Account, + Deserializer, + U64, + SigningScheme, + SigningSchemeInput, +} from "../../../src"; import { AccountAuthenticator, AccountAuthenticatorEd25519, - AccountAuthenticatorSecp256k1, + SingleKeyAuthenticator, } from "../../../src/transactions/authenticator/account"; import { longTestTimeout } from "../../unit/helper"; import { fundAccounts, publishTransferPackage, singleSignerScriptBytecode } from "./helper"; @@ -12,7 +21,7 @@ describe("sign transaction", () => { const aptos = new Aptos(config); const senderAccount = Account.generate(); const recieverAccounts = [Account.generate(), Account.generate()]; - const senderSecp256k1Account = Account.generate(SigningScheme.Secp256k1Ecdsa); + const senderSecp256k1Account = Account.generate(SigningSchemeInput.Secp256k1Ecdsa); const secondarySignerAccount = Account.generate(); const feePayerAccount = Account.generate(); beforeAll(async () => { @@ -28,7 +37,7 @@ describe("sign transaction", () => { describe("it returns the current account authenticator", () => { describe("ED25519", () => { - test("it signs a script transaction", async () => { + test.only("it signs a script transaction", async () => { const rawTxn = await aptos.generateTransaction({ sender: senderAccount.accountAddress.toString(), data: { @@ -98,7 +107,7 @@ describe("sign transaction", () => { expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); const authenticator = AccountAuthenticator.deserialize(deserializer); - expect(authenticator instanceof AccountAuthenticatorSecp256k1).toBeTruthy(); + expect(authenticator instanceof SingleKeyAuthenticator).toBeTruthy(); }); test("it signs an entry function transaction", async () => { const rawTxn = await aptos.generateTransaction({ @@ -115,7 +124,7 @@ describe("sign transaction", () => { expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); const authenticator = AccountAuthenticator.deserialize(deserializer); - expect(authenticator instanceof AccountAuthenticatorSecp256k1).toBeTruthy(); + expect(authenticator instanceof SingleKeyAuthenticator).toBeTruthy(); }); test("it signs a multi sig transaction", async () => { const rawTxn = await aptos.generateTransaction({ @@ -133,7 +142,7 @@ describe("sign transaction", () => { expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); const authenticator = AccountAuthenticator.deserialize(deserializer); - expect(authenticator instanceof AccountAuthenticatorSecp256k1).toBeTruthy(); + expect(authenticator instanceof SingleKeyAuthenticator).toBeTruthy(); }); }); }); diff --git a/tests/e2e/transaction/transactionSimulation.test.ts b/tests/e2e/transaction/transactionSimulation.test.ts index 9820758c2..c464e662f 100644 --- a/tests/e2e/transaction/transactionSimulation.test.ts +++ b/tests/e2e/transaction/transactionSimulation.test.ts @@ -1,4 +1,4 @@ -import { AptosConfig, Network, Aptos, Account, U64, SigningScheme } from "../../../src"; +import { AptosConfig, Network, Aptos, Account, U64, SigningScheme, SigningSchemeInput } from "../../../src"; import { longTestTimeout } from "../../unit/helper"; import { fundAccounts, multiSignerScriptBytecode, publishTransferPackage, singleSignerScriptBytecode } from "./helper"; @@ -7,7 +7,7 @@ describe("transaction simulation", () => { const aptos = new Aptos(config); const senderAccount = Account.generate(); const recieverAccounts = [Account.generate(), Account.generate()]; - const senderSecp256k1Account = Account.generate(SigningScheme.Secp256k1Ecdsa); + const senderSecp256k1Account = Account.generate(SigningSchemeInput.Secp256k1Ecdsa); const secondarySignerAccount = Account.generate(); const feePayerAccount = Account.generate(); beforeAll(async () => { diff --git a/tests/e2e/transaction/transaction_submission.test.ts b/tests/e2e/transaction/transaction_submission.test.ts index c6f10ba2e..87d1422d2 100644 --- a/tests/e2e/transaction/transaction_submission.test.ts +++ b/tests/e2e/transaction/transaction_submission.test.ts @@ -96,10 +96,10 @@ describe("transaction submission", () => { }); await waitForTransaction({ aptosConfig: config, transactionHash: response.hash }); }); - test.skip("it submits an entry function transaction Secp256k1", async () => { + test.only("it submits an entry function transaction Secp256k1", async () => { const config = new AptosConfig({ network: Network.DEVNET }); const aptos = new Aptos(config); - const alice = Account.fromPrivateKey(new Secp256k1PrivateKey(secp256k1TestObject.privateKey)); + const alice = Account.fromPrivateKey(new Secp256k1PrivateKey(secp256k1TestObject.privateKey), false); //await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const bob = Account.generate(); const rawTxn = await aptos.generateTransaction({ From b664ffc1e26eb1113caae185ed5af5d97f3d8dad Mon Sep 17 00:00:00 2001 From: maayan Date: Wed, 18 Oct 2023 00:39:56 -0700 Subject: [PATCH 03/10] ed25519 auth key doesnt work --- src/core/authenticationKey.ts | 8 +------- src/internal/faucet.ts | 6 +++++- tests/e2e/transaction/transaction_submission.test.ts | 7 ++++--- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/core/authenticationKey.ts b/src/core/authenticationKey.ts index 2bdf278b6..8606ce689 100644 --- a/src/core/authenticationKey.ts +++ b/src/core/authenticationKey.ts @@ -58,13 +58,7 @@ export class AuthenticationKey { // TODO - check for single key or multi key if (scheme === SigningScheme.SingleKey) { - let newBytes: Uint8Array = new Uint8Array(); - if (publicKey instanceof AnyPublicKey) { - newBytes = publicKey.bcsToBytes(); - } else { - newBytes = new AnyPublicKey(publicKey).bcsToBytes(); - } - + let newBytes = publicKey.bcsToBytes(); const authKeyBytes = new Uint8Array([...newBytes, scheme]); console.log("authKeyBytes", authKeyBytes); const hash = sha3Hash.create(); diff --git a/src/internal/faucet.ts b/src/internal/faucet.ts index b7ee92f5f..de8747eff 100644 --- a/src/internal/faucet.ts +++ b/src/internal/faucet.ts @@ -35,7 +35,11 @@ export async function fundAccount(args: { const txnHash = data.txn_hashes[0]; - await waitForTransaction({ aptosConfig, transactionHash: txnHash, options: { timeoutSecs } }); + await waitForTransaction({ + aptosConfig, + transactionHash: txnHash, + options: { timeoutSecs, indexerVersionCheck: false }, + }); return txnHash; } diff --git a/tests/e2e/transaction/transaction_submission.test.ts b/tests/e2e/transaction/transaction_submission.test.ts index 87d1422d2..dd311bb66 100644 --- a/tests/e2e/transaction/transaction_submission.test.ts +++ b/tests/e2e/transaction/transaction_submission.test.ts @@ -97,10 +97,10 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash }); }); test.only("it submits an entry function transaction Secp256k1", async () => { - const config = new AptosConfig({ network: Network.DEVNET }); + const config = new AptosConfig({ network: Network.LOCAL }); const aptos = new Aptos(config); - const alice = Account.fromPrivateKey(new Secp256k1PrivateKey(secp256k1TestObject.privateKey), false); - //await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); + const alice = Account.generate(); + await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const bob = Account.generate(); const rawTxn = await aptos.generateTransaction({ sender: alice.accountAddress.toString(), @@ -119,6 +119,7 @@ describe("transaction submission", () => { senderAuthenticator: authenticator, }); console.log(response.hash); + // console.log(response.hash); // await waitForTransaction({ // aptosConfig: config, // transactionHash: response.hash, From 70a9cdc02f44903ec36db7bf2a60f4ce0578d92e Mon Sep 17 00:00:00 2001 From: maayan Date: Thu, 19 Oct 2023 16:43:44 -0700 Subject: [PATCH 04/10] improvement --- src/core/account.ts | 50 +- src/core/authenticationKey.ts | 2 - src/core/crypto/anyPublicKey.ts | 2 +- src/core/crypto/anySignature.ts | 6 +- src/core/crypto/secp256k1.ts | 2 +- .../transaction_builder.ts | 2 +- tests/e2e/transaction/helper.ts | 5 +- .../transaction/transactionSubmission.test.ts | 713 ++++++++++++++++-- .../transaction_submission.test.ts | 130 ---- tests/unit/account.test.ts | 32 +- tests/unit/helper.ts | 8 + 11 files changed, 718 insertions(+), 234 deletions(-) delete mode 100644 tests/e2e/transaction/transaction_submission.test.ts diff --git a/src/core/account.ts b/src/core/account.ts index 360d1d44b..fc9f2bc78 100644 --- a/src/core/account.ts +++ b/src/core/account.ts @@ -50,7 +50,7 @@ export class Account { * This method is private because it should only be called by the factory static methods. * @returns Account */ - private constructor(args: { privateKey: PrivateKey; address: AccountAddress }) { + private constructor(args: { privateKey: PrivateKey; address: AccountAddress }, legacy: boolean = false) { const { privateKey, address } = args; // Derive the public key from the private key @@ -58,10 +58,16 @@ export class Account { // Derive the signing scheme from the public key if (this.publicKey instanceof Ed25519PublicKey) { - this.signingScheme = SigningScheme.Ed25519; + if (legacy) { + this.signingScheme = SigningScheme.Ed25519; + } else { + this.publicKey = new AnyPublicKey(this.publicKey); + this.signingScheme = SigningScheme.SingleKey; + } } else if (this.publicKey instanceof MultiEd25519PublicKey) { this.signingScheme = SigningScheme.MultiEd25519; } else if (this.publicKey instanceof Secp256k1PublicKey) { + this.publicKey = new AnyPublicKey(this.publicKey); this.signingScheme = SigningScheme.SingleKey; } else { throw new Error("Can not create new Account, unsupported public key type"); @@ -102,19 +108,37 @@ export class Account { /** * Derives an account with provided private key * + * NOTE: This function support the new Single Signer and + * should be used only with private key that was created + * as a Single Signer + * * @param privateKey Hex - private key of the account * @returns Account */ - static fromPrivateKey(privateKey: PrivateKey, legacy: boolean = true): Account { - let publicKey = privateKey.publicKey(); - if (!legacy) { - publicKey = new AnyPublicKey(publicKey); - } + static fromPrivateKey(privateKey: PrivateKey): Account { + const publicKey = new AnyPublicKey(privateKey.publicKey()); const authKey = Account.authKey({ publicKey }); const address = new AccountAddress({ data: authKey.toUint8Array() }); return Account.fromPrivateKeyAndAddress({ privateKey, address }); } + /** + * Derives an account with provided legacy private key + * + * NOTE: This function support the legacy keys and + * should be used only with private key that was created + * as + * + * @param privateKey Hex - private key of the account + * @returns Account + */ + static fromLegacyPrivateKey(privateKey: PrivateKey) { + const publicKey = privateKey.publicKey(); + const authKey = Account.authKey({ publicKey }); + const address = new AccountAddress({ data: authKey.toUint8Array() }); + return Account.fromLegacyPrivateKeyAndAddress({ privateKey, address }); + } + /** * Derives an account with provided private key and address * This is intended to be used for account that has it's key rotated @@ -127,6 +151,10 @@ export class Account { return new Account(args); } + static fromLegacyPrivateKeyAndAddress(args: { privateKey: PrivateKey; address: AccountAddress }): Account { + return new Account(args, true); + } + /** * Derives an account with bip44 path and mnemonics, * @@ -143,6 +171,14 @@ export class Account { return Account.fromPrivateKey(privateKey); } + static fromLegacyDerivationPath(args: { path: string; mnemonic: string }): Account { + const { path, mnemonic } = args; + + const { key } = derivePrivateKeyFromMnemonic(KeyType.ED25519, path, mnemonic); + const privateKey = new Ed25519PrivateKey(key); + return Account.fromLegacyPrivateKey(privateKey); + } + /** * This key enables account owners to rotate their private key(s) * associated with the account without changing the address that hosts their account. diff --git a/src/core/authenticationKey.ts b/src/core/authenticationKey.ts index 8606ce689..51c9566d0 100644 --- a/src/core/authenticationKey.ts +++ b/src/core/authenticationKey.ts @@ -60,7 +60,6 @@ export class AuthenticationKey { if (scheme === SigningScheme.SingleKey) { let newBytes = publicKey.bcsToBytes(); const authKeyBytes = new Uint8Array([...newBytes, scheme]); - console.log("authKeyBytes", authKeyBytes); const hash = sha3Hash.create(); hash.update(authKeyBytes); const hashDigest = hash.digest(); @@ -71,7 +70,6 @@ export class AuthenticationKey { const authKeyBytes = new Uint8Array(inputBytes.length + 1); authKeyBytes.set(inputBytes); authKeyBytes.set([scheme], inputBytes.length); - const hash = sha3Hash.create(); hash.update(authKeyBytes); const hashDigest = hash.digest(); diff --git a/src/core/crypto/anyPublicKey.ts b/src/core/crypto/anyPublicKey.ts index 50cb05b4b..54f57892f 100644 --- a/src/core/crypto/anyPublicKey.ts +++ b/src/core/crypto/anyPublicKey.ts @@ -1,7 +1,7 @@ import { Serializer, Deserializer } from "../../bcs"; import { AnyPublicKeyVariant, HexInput } from "../../types"; import { AnySignature } from "./anySignature"; -import { PublicKey } from "./asymmetric_crypto"; +import { PublicKey } from "./asymmetricCrypto"; import { Ed25519PublicKey, Ed25519Signature } from "./ed25519"; import { Secp256k1PublicKey, Secp256k1Signature } from "./secp256k1"; diff --git a/src/core/crypto/anySignature.ts b/src/core/crypto/anySignature.ts index 301c33665..f8f99acf3 100644 --- a/src/core/crypto/anySignature.ts +++ b/src/core/crypto/anySignature.ts @@ -1,6 +1,6 @@ import { Serializer, Deserializer } from "../../bcs"; import { AnySignatureVariant } from "../../types"; -import { Signature } from "./asymmetric_crypto"; +import { Signature } from "./asymmetricCrypto"; import { Ed25519Signature } from "./ed25519"; import { Secp256k1Signature } from "./secp256k1"; @@ -38,7 +38,7 @@ export class AnySignature extends Signature { serializer.serializeU32AsUleb128(AnySignatureVariant.Secp256k1); this.signature.serialize(serializer); } else { - throw new Error("Unknown public key type"); + throw new Error("Unknown signature type"); } } @@ -50,7 +50,7 @@ export class AnySignature extends Signature { case AnySignatureVariant.Secp256k1: return new AnySignature(Secp256k1Signature.load(deserializer)); default: - throw new Error(`Unknown variant index for AnyPublicKey: ${index}`); + throw new Error(`Unknown variant index for AnySignature: ${index}`); } } } diff --git a/src/core/crypto/secp256k1.ts b/src/core/crypto/secp256k1.ts index 47d888027..410a6e978 100644 --- a/src/core/crypto/secp256k1.ts +++ b/src/core/crypto/secp256k1.ts @@ -203,7 +203,7 @@ export class Secp256k1Signature extends Signature { const hex = Hex.fromHexInput(hexInput); if (hex.toUint8Array().length !== Secp256k1Signature.LENGTH) { - throw new Error(`Signature length should be ${Secp256k1Signature.LENGTH}`); + throw new Error(`Signature length should be ${Secp256k1Signature.LENGTH}, recieved ${hex.toUint8Array().length}`); } this.data = hex; } diff --git a/src/transactions/transaction_builder/transaction_builder.ts b/src/transactions/transaction_builder/transaction_builder.ts index 7e0eecd7a..6a8b1a0c6 100644 --- a/src/transactions/transaction_builder/transaction_builder.ts +++ b/src/transactions/transaction_builder/transaction_builder.ts @@ -371,7 +371,7 @@ export function sign(args: { signer: Account; transaction: AnyRawTransaction }): new Ed25519Signature(signerSignature.toUint8Array()), ); case SigningScheme.SingleKey: - return new SingleKeyAuthenticator(new AnyPublicKey(signer.publicKey), new AnySignature(signerSignature)); + return new SingleKeyAuthenticator(signer.publicKey as AnyPublicKey, new AnySignature(signerSignature)); // TODO support MultiEd25519 default: throw new Error(`Cannot sign transaction, signing scheme ${signer.signingScheme} not supported`); diff --git a/tests/e2e/transaction/helper.ts b/tests/e2e/transaction/helper.ts index 9659a34d6..82129f656 100644 --- a/tests/e2e/transaction/helper.ts +++ b/tests/e2e/transaction/helper.ts @@ -36,7 +36,10 @@ export async function publishModule( transaction: rawTransaction, senderAuthenticator: signedTxn, }); - return (await aptos.waitForTransaction({ transactionHash: txnHash.hash })) as UserTransactionResponse; + return (await aptos.waitForTransaction({ + transactionHash: txnHash.hash, + options: { indexerVersionCheck: false }, + })) as UserTransactionResponse; } export async function fundAccounts(aptos: Aptos, accounts: Array) { diff --git a/tests/e2e/transaction/transactionSubmission.test.ts b/tests/e2e/transaction/transactionSubmission.test.ts index bc510372d..7a8cdb64e 100644 --- a/tests/e2e/transaction/transactionSubmission.test.ts +++ b/tests/e2e/transaction/transactionSubmission.test.ts @@ -1,106 +1,666 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import { Account, AptosConfig, Network, Aptos, U64, SigningScheme, Deserializer } from "../../../src"; +import { + Account, + AptosConfig, + Network, + Aptos, + U64, + SigningScheme, + Deserializer, + SigningSchemeInput, + Ed25519PrivateKey, +} from "../../../src"; import { waitForTransaction } from "../../../src/internal/transaction"; import { RawTransaction, TransactionPayloadEntryFunction } from "../../../src/transactions/instances"; -import { FUND_AMOUNT, longTestTimeout } from "../../unit/helper"; +import { ed25519, FUND_AMOUNT, longTestTimeout } from "../../unit/helper"; +import { fundAccounts, multiSignerScriptBytecode, publishTransferPackage, singleSignerScriptBytecode } from "./helper"; + +/** + * generate() // default to ed25519 - should be single_signer + * generate(secpk) // should be single_signer + * fromPrivateKey(ed25519) // should be single_signer + * fromPrivateKey(secpk) // should be single_signer + * fromLegacyPrivateKey(ed25519 | multied) // should be ed25519 + * fromPrivateKeyAndAddress(ed25519) // should be single_signer + * fromPrivateKeyAndAddress(secpk) // should be single_signer + * + */ describe("transaction submission", () => { - describe("submitTransaction", () => { - test( - "it submits a script transaction", - async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); - const alice = Account.generate(); - await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); - const bob = Account.generate(); - await aptos.fundAccount({ accountAddress: bob.accountAddress.toString(), amount: FUND_AMOUNT }); - const rawTxn = await aptos.generateTransaction({ - sender: alice.accountAddress.toString(), - secondarySignerAddresses: [bob.accountAddress.toString()], - data: { - bytecode: - // eslint-disable-next-line max-len - "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", + const config = new AptosConfig({ network: Network.LOCAL }); + const aptos = new Aptos(config); + const contractPublisherAccount = Account.generate(); + const singleSignerED25519SenderAccount = Account.generate(); + const legacyED25519SenderAccount = Account.fromLegacyPrivateKey(new Ed25519PrivateKey(ed25519.privateKey)); + const receiverAccounts = [Account.generate(), Account.generate()]; + const singleSignerSecp256k1Account = Account.generate(SigningSchemeInput.Secp256k1Ecdsa); + const secondarySignerAccount = Account.generate(); + const feePayerAccount = Account.generate(); + beforeAll(async () => { + await fundAccounts(aptos, [ + contractPublisherAccount, + singleSignerED25519SenderAccount, + singleSignerSecp256k1Account, + legacyED25519SenderAccount, + ...receiverAccounts, + secondarySignerAccount, + feePayerAccount, + ]); + await publishTransferPackage(aptos, contractPublisherAccount); + }, longTestTimeout); + describe("Single Sender ED25519", () => { + describe("single sender", () => { + test("with script payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: singleSignerED25519SenderAccount.accountAddress.toString(), + data: { + bytecode: singleSignerScriptBytecode, + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], + }, + }); + const response = await aptos.signAndSubmitTransaction({ + signer: singleSignerED25519SenderAccount, + transaction, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + + expect(response.signature?.type).toBe("single_sender"); + }); + test("with entry function payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: singleSignerED25519SenderAccount.accountAddress.toString(), + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], + }, + }); + const response = await aptos.signAndSubmitTransaction({ + signer: singleSignerED25519SenderAccount, + transaction, + }); + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("single_sender"); + }); + }); + describe("multi agent", () => { + test("with script payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: singleSignerED25519SenderAccount.accountAddress.toString(), + secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], + data: { + bytecode: multiSignerScriptBytecode, functionArguments: [ new U64(BigInt(100)), new U64(BigInt(200)), - bob.accountAddress, - alice.accountAddress, + receiverAccounts[0].accountAddress, + receiverAccounts[1].accountAddress, new U64(BigInt(50)), ], }, }); - const authenticator = aptos.signTransaction({ - signer: alice, - transaction: rawTxn, + + const senderAuthenticator = aptos.signTransaction({ signer: singleSignerED25519SenderAccount, transaction }); + const secondarySignerAuthenticator = aptos.signTransaction({ signer: secondarySignerAccount, transaction }); + + const response = await aptos.submitTransaction({ + transaction, + senderAuthenticator, + secondarySignerAuthenticators: { additionalSignersAuthenticators: [secondarySignerAuthenticator] }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("multi_agent_signature"); + }); + + test( + "with entry function payload", + async () => { + const transaction = await aptos.generateTransaction({ + sender: singleSignerED25519SenderAccount.accountAddress.toString(), + secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::two_by_two`, + functionArguments: [ + new U64(100), + new U64(200), + receiverAccounts[0].accountAddress, + receiverAccounts[1].accountAddress, + new U64(50), + ], + }, + }); + + const senderAuthenticator = aptos.signTransaction({ signer: singleSignerED25519SenderAccount, transaction }); + const secondarySignerAuthenticator = aptos.signTransaction({ signer: secondarySignerAccount, transaction }); + + const response = await aptos.submitTransaction({ + transaction, + senderAuthenticator, + secondarySignerAuthenticators: { additionalSignersAuthenticators: [secondarySignerAuthenticator] }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("multi_agent_signature"); + }, + longTestTimeout, + ); + }); + describe("fee payer", () => { + test("with script payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: singleSignerED25519SenderAccount.accountAddress.toString(), + feePayerAddress: feePayerAccount.accountAddress.toString(), + data: { + bytecode: singleSignerScriptBytecode, + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], + }, + }); + + const senderAuthenticator = aptos.signTransaction({ signer: singleSignerED25519SenderAccount, transaction }); + const feePayerSignerAuthenticator = aptos.signTransaction({ signer: feePayerAccount, transaction }); + + const response = await aptos.submitTransaction({ + transaction, + senderAuthenticator, + secondarySignerAuthenticators: { feePayerAuthenticator: feePayerSignerAuthenticator }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("fee_payer_signature"); + }); + test("with entry function payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: singleSignerED25519SenderAccount.accountAddress.toString(), + feePayerAddress: feePayerAccount.accountAddress.toString(), + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], + }, + }); + const senderAuthenticator = aptos.signTransaction({ signer: singleSignerED25519SenderAccount, transaction }); + const feePayerSignerAuthenticator = aptos.signTransaction({ signer: feePayerAccount, transaction }); + + const response = await aptos.submitTransaction({ + transaction, + senderAuthenticator, + secondarySignerAuthenticators: { feePayerAuthenticator: feePayerSignerAuthenticator }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, }); - const bobAuthenticator = aptos.signTransaction({ - signer: bob, - transaction: rawTxn, + expect(response.signature?.type).toBe("fee_payer_signature"); + }); + test("with multi agent transaction", async () => { + const transaction = await aptos.generateTransaction({ + sender: singleSignerED25519SenderAccount.accountAddress.toString(), + secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], + feePayerAddress: feePayerAccount.accountAddress.toString(), + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::two_by_two`, + functionArguments: [ + new U64(100), + new U64(200), + receiverAccounts[0].accountAddress, + receiverAccounts[1].accountAddress, + new U64(50), + ], + }, }); + + const senderAuthenticator = aptos.signTransaction({ signer: singleSignerED25519SenderAccount, transaction }); + const secondarySignerAuthenticator = aptos.signTransaction({ signer: secondarySignerAccount, transaction }); + const feePayerSignerAuthenticator = aptos.signTransaction({ signer: feePayerAccount, transaction }); + const response = await aptos.submitTransaction({ - transaction: rawTxn, - senderAuthenticator: authenticator, + transaction, + senderAuthenticator, secondarySignerAuthenticators: { - additionalSignersAuthenticators: [bobAuthenticator], - }, - }); - await waitForTransaction({ aptosConfig: config, transactionHash: response.hash }); - }, - longTestTimeout, - ); - - test("it submits an entry function transaction", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); - const alice = Account.generate(); - await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); - const bob = Account.generate(); - const rawTxn = await aptos.generateTransaction({ - sender: alice.accountAddress.toString(), - data: { - function: "0x1::aptos_account::transfer", - functionArguments: [bob.accountAddress, new U64(1)], - }, + additionalSignersAuthenticators: [secondarySignerAuthenticator], + feePayerAuthenticator: feePayerSignerAuthenticator, + }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("fee_payer_signature"); }); - const authenticator = aptos.signTransaction({ - signer: alice, - transaction: rawTxn, + }); + }); + describe("Single Signer Secp256k1", () => { + describe("single signer", () => { + test("with script payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + data: { + bytecode: singleSignerScriptBytecode, + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], + }, + }); + const response = await aptos.signAndSubmitTransaction({ + signer: singleSignerSecp256k1Account, + transaction, + }); + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("single_sender"); }); - const response = await aptos.submitTransaction({ - transaction: rawTxn, - senderAuthenticator: authenticator, + test("with entry function payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], + }, + }); + const response = await aptos.signAndSubmitTransaction({ + signer: singleSignerSecp256k1Account, + transaction, + }); + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("single_sender"); }); - await waitForTransaction({ aptosConfig: config, transactionHash: response.hash }); }); - test.skip("it submits an entry function transaction with Secp256k1Ecdsa", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); - const alice = Account.generate(SigningScheme.Secp256k1Ecdsa); - await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); - const bob = Account.generate(); - const rawTxn = await aptos.generateTransaction({ - sender: alice.accountAddress.toString(), - data: { - function: "0x1::aptos_account::transfer", - functionArguments: [bob.accountAddress, new U64(1)], + describe("multi agent", () => { + test("with script payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], + data: { + bytecode: multiSignerScriptBytecode, + functionArguments: [ + new U64(BigInt(100)), + new U64(BigInt(200)), + receiverAccounts[0].accountAddress, + receiverAccounts[1].accountAddress, + new U64(BigInt(50)), + ], + }, + }); + + const senderAuthenticator = aptos.signTransaction({ signer: singleSignerSecp256k1Account, transaction }); + const secondarySignerAuthenticator = aptos.signTransaction({ signer: secondarySignerAccount, transaction }); + + const response = await aptos.submitTransaction({ + transaction, + senderAuthenticator, + secondarySignerAuthenticators: { additionalSignersAuthenticators: [secondarySignerAuthenticator] }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("multi_agent_signature"); + }); + + test( + "with entry function payload", + async () => { + const transaction = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::two_by_two`, + functionArguments: [ + new U64(100), + new U64(200), + receiverAccounts[0].accountAddress, + receiverAccounts[1].accountAddress, + new U64(50), + ], + }, + }); + + const senderAuthenticator = aptos.signTransaction({ signer: singleSignerSecp256k1Account, transaction }); + const secondarySignerAuthenticator = aptos.signTransaction({ signer: secondarySignerAccount, transaction }); + + const response = await aptos.submitTransaction({ + transaction, + senderAuthenticator, + secondarySignerAuthenticators: { additionalSignersAuthenticators: [secondarySignerAuthenticator] }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("multi_agent_signature"); }, + longTestTimeout, + ); + }); + describe("fee payer", () => { + test("with script payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + feePayerAddress: feePayerAccount.accountAddress.toString(), + data: { + bytecode: singleSignerScriptBytecode, + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], + }, + }); + + const senderAuthenticator = aptos.signTransaction({ signer: singleSignerSecp256k1Account, transaction }); + const feePayerSignerAuthenticator = aptos.signTransaction({ signer: feePayerAccount, transaction }); + + const response = await aptos.submitTransaction({ + transaction, + senderAuthenticator, + secondarySignerAuthenticators: { feePayerAuthenticator: feePayerSignerAuthenticator }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("fee_payer_signature"); }); - const authenticator = aptos.signTransaction({ - signer: alice, - transaction: rawTxn, + test("with entry function payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + feePayerAddress: feePayerAccount.accountAddress.toString(), + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], + }, + }); + const senderAuthenticator = aptos.signTransaction({ signer: singleSignerSecp256k1Account, transaction }); + const feePayerSignerAuthenticator = aptos.signTransaction({ signer: feePayerAccount, transaction }); + + const response = await aptos.submitTransaction({ + transaction, + senderAuthenticator, + secondarySignerAuthenticators: { feePayerAuthenticator: feePayerSignerAuthenticator }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("fee_payer_signature"); }); - const response = await aptos.submitTransaction({ - transaction: rawTxn, - senderAuthenticator: authenticator, + test("with multi agent transaction", async () => { + const transaction = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], + feePayerAddress: feePayerAccount.accountAddress.toString(), + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::two_by_two`, + functionArguments: [ + new U64(100), + new U64(200), + receiverAccounts[0].accountAddress, + receiverAccounts[1].accountAddress, + new U64(50), + ], + }, + }); + + const senderAuthenticator = aptos.signTransaction({ signer: singleSignerSecp256k1Account, transaction }); + const secondarySignerAuthenticator = aptos.signTransaction({ signer: secondarySignerAccount, transaction }); + const feePayerSignerAuthenticator = aptos.signTransaction({ signer: feePayerAccount, transaction }); + + const response = await aptos.submitTransaction({ + transaction, + senderAuthenticator, + secondarySignerAuthenticators: { + additionalSignersAuthenticators: [secondarySignerAuthenticator], + feePayerAuthenticator: feePayerSignerAuthenticator, + }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("fee_payer_signature"); }); - await waitForTransaction({ aptosConfig: config, transactionHash: response.hash }); }); }); + describe("Legacy ED25519", () => { + describe("single signer", () => { + test("with script payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: legacyED25519SenderAccount.accountAddress.toString(), + data: { + bytecode: singleSignerScriptBytecode, + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], + }, + }); + const response = await aptos.signAndSubmitTransaction({ + signer: legacyED25519SenderAccount, + transaction, + }); + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("ed25519_signature"); + }); + test("with entry function payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: legacyED25519SenderAccount.accountAddress.toString(), + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], + }, + }); + const response = await aptos.signAndSubmitTransaction({ + signer: legacyED25519SenderAccount, + transaction, + }); + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("ed25519_signature"); + }); + }); + describe("multi agent", () => { + test("with script payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: legacyED25519SenderAccount.accountAddress.toString(), + secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], + data: { + bytecode: multiSignerScriptBytecode, + functionArguments: [ + new U64(BigInt(100)), + new U64(BigInt(200)), + receiverAccounts[0].accountAddress, + receiverAccounts[1].accountAddress, + new U64(BigInt(50)), + ], + }, + }); + const senderAuthenticator = aptos.signTransaction({ signer: legacyED25519SenderAccount, transaction }); + const secondarySignerAuthenticator = aptos.signTransaction({ signer: secondarySignerAccount, transaction }); + + const response = await aptos.submitTransaction({ + transaction, + senderAuthenticator, + secondarySignerAuthenticators: { additionalSignersAuthenticators: [secondarySignerAuthenticator] }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("multi_agent_signature"); + }); + + test( + "with entry function payload", + async () => { + const transaction = await aptos.generateTransaction({ + sender: legacyED25519SenderAccount.accountAddress.toString(), + secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::two_by_two`, + functionArguments: [ + new U64(100), + new U64(200), + receiverAccounts[0].accountAddress, + receiverAccounts[1].accountAddress, + new U64(50), + ], + }, + }); + + const senderAuthenticator = aptos.signTransaction({ signer: legacyED25519SenderAccount, transaction }); + const secondarySignerAuthenticator = aptos.signTransaction({ signer: secondarySignerAccount, transaction }); + + const response = await aptos.submitTransaction({ + transaction, + senderAuthenticator, + secondarySignerAuthenticators: { additionalSignersAuthenticators: [secondarySignerAuthenticator] }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("multi_agent_signature"); + }, + longTestTimeout, + ); + }); + describe("fee payer", () => { + test("with script payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: legacyED25519SenderAccount.accountAddress.toString(), + feePayerAddress: feePayerAccount.accountAddress.toString(), + data: { + bytecode: singleSignerScriptBytecode, + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], + }, + }); + + const senderAuthenticator = aptos.signTransaction({ signer: legacyED25519SenderAccount, transaction }); + const feePayerSignerAuthenticator = aptos.signTransaction({ signer: feePayerAccount, transaction }); + + const response = await aptos.submitTransaction({ + transaction, + senderAuthenticator, + secondarySignerAuthenticators: { feePayerAuthenticator: feePayerSignerAuthenticator }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("fee_payer_signature"); + }); + test("with entry function payload", async () => { + const transaction = await aptos.generateTransaction({ + sender: legacyED25519SenderAccount.accountAddress.toString(), + feePayerAddress: feePayerAccount.accountAddress.toString(), + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], + }, + }); + const senderAuthenticator = aptos.signTransaction({ signer: legacyED25519SenderAccount, transaction }); + const feePayerSignerAuthenticator = aptos.signTransaction({ signer: feePayerAccount, transaction }); + + const response = await aptos.submitTransaction({ + transaction, + senderAuthenticator, + secondarySignerAuthenticators: { feePayerAuthenticator: feePayerSignerAuthenticator }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("fee_payer_signature"); + }); + test("with multi agent transaction", async () => { + const transaction = await aptos.generateTransaction({ + sender: legacyED25519SenderAccount.accountAddress.toString(), + secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], + feePayerAddress: feePayerAccount.accountAddress.toString(), + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::two_by_two`, + functionArguments: [ + new U64(100), + new U64(200), + receiverAccounts[0].accountAddress, + receiverAccounts[1].accountAddress, + new U64(50), + ], + }, + }); + + const senderAuthenticator = aptos.signTransaction({ signer: legacyED25519SenderAccount, transaction }); + const secondarySignerAuthenticator = aptos.signTransaction({ signer: secondarySignerAccount, transaction }); + const feePayerSignerAuthenticator = aptos.signTransaction({ signer: feePayerAccount, transaction }); + + const response = await aptos.submitTransaction({ + transaction, + senderAuthenticator, + secondarySignerAuthenticators: { + additionalSignersAuthenticators: [secondarySignerAuthenticator], + feePayerAuthenticator: feePayerSignerAuthenticator, + }, + }); + + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + options: { indexerVersionCheck: false }, + }); + expect(response.signature?.type).toBe("fee_payer_signature"); + }); + }); + }); describe("publish move module", () => { const config = new AptosConfig({ network: Network.LOCAL }); const aptos = new Aptos(config); @@ -137,7 +697,10 @@ describe("transaction submission", () => { metadataBytes, byteCode, }); - const response = await aptos.signAndSubmitTransaction({ signer: account, transaction }); + const response = await aptos.signAndSubmitTransaction({ + signer: account, + transaction, + }); await waitForTransaction({ aptosConfig: config, transactionHash: response.hash }); const accountModules = await aptos.getAccountModules({ accountAddress: account.accountAddress.toString() }); expect(accountModules[0].bytecode).toEqual(`0x${byteCode}`); diff --git a/tests/e2e/transaction/transaction_submission.test.ts b/tests/e2e/transaction/transaction_submission.test.ts deleted file mode 100644 index dd311bb66..000000000 --- a/tests/e2e/transaction/transaction_submission.test.ts +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -import { - Account, - AptosConfig, - Network, - Aptos, - U64, - SigningScheme, - SigningSchemeInput, - AuthenticationKey, - Deserializer, - Secp256k1PrivateKey, - AccountAddress, - Hex, -} from "../../../src"; -import { waitForTransaction } from "../../../src/internal/transaction"; -import { AccountAuthenticator, SingleKeyAuthenticator } from "../../../src/transactions/authenticator/account"; -import { - ChainId, - RawTransaction, - Script, - TransactionPayload, - TransactionPayloadScript, -} from "../../../src/transactions/instances"; -import { FUND_AMOUNT, longTestTimeout, secp256k1TestObject } from "../../unit/helper"; - -describe("transaction submission", () => { - describe("submitTransaction", () => { - test( - "it submits a script transaction", - async () => { - const config = new AptosConfig({ network: Network.DEVNET }); - const aptos = new Aptos(config); - const alice = Account.generate(); - await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); - const bob = Account.generate(); - await aptos.fundAccount({ accountAddress: bob.accountAddress.toString(), amount: FUND_AMOUNT }); - const rawTxn = await aptos.generateTransaction({ - sender: alice.accountAddress.toString(), - secondarySignerAddresses: [bob.accountAddress.toString()], - data: { - bytecode: - // eslint-disable-next-line max-len - "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", - arguments: [ - new U64(BigInt(100)), - new U64(BigInt(200)), - bob.accountAddress, - alice.accountAddress, - new U64(BigInt(50)), - ], - }, - }); - const authenticator = aptos.signTransaction({ - signer: alice, - transaction: rawTxn, - }); - const bobauthenticator = aptos.signTransaction({ - signer: bob, - transaction: rawTxn, - }); - const response = await aptos.submitTransaction({ - transaction: rawTxn, - senderAuthenticator: authenticator, - secondarySignerAuthenticators: { - additionalSignersAuthenticators: [bobauthenticator], - }, - }); - await waitForTransaction({ aptosConfig: config, transactionHash: response.hash }); - }, - longTestTimeout, - ); - - test("it submits an entry function transaction", async () => { - const config = new AptosConfig({ network: Network.DEVNET }); - const aptos = new Aptos(config); - const alice = Account.generate(); - await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); - const bob = Account.generate(); - const rawTxn = await aptos.generateTransaction({ - sender: alice.accountAddress.toString(), - data: { - function: "0x1::aptos_account::transfer", - arguments: [bob.accountAddress, new U64(1)], - }, - }); - const authenticator = aptos.signTransaction({ - signer: alice, - transaction: rawTxn, - }); - const response = await aptos.submitTransaction({ - transaction: rawTxn, - senderAuthenticator: authenticator, - }); - await waitForTransaction({ aptosConfig: config, transactionHash: response.hash }); - }); - test.only("it submits an entry function transaction Secp256k1", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); - const alice = Account.generate(); - await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); - const bob = Account.generate(); - const rawTxn = await aptos.generateTransaction({ - sender: alice.accountAddress.toString(), - data: { - function: "0x1::aptos_account::transfer", - arguments: [bob.accountAddress, new U64(1)], - }, - }); - - const authenticator = aptos.signTransaction({ - signer: alice, - transaction: rawTxn, - }) as SingleKeyAuthenticator; - const response = await aptos.submitTransaction({ - transaction: rawTxn, - senderAuthenticator: authenticator, - }); - console.log(response.hash); - // console.log(response.hash); - // await waitForTransaction({ - // aptosConfig: config, - // transactionHash: response.hash, - // options: { indexerVersionCheck: false }, - // }); - }); - }); -}); diff --git a/tests/unit/account.test.ts b/tests/unit/account.test.ts index c3db7eb38..f8896f191 100644 --- a/tests/unit/account.test.ts +++ b/tests/unit/account.test.ts @@ -12,28 +12,33 @@ import { Secp256k1PublicKey, Secp256k1Signature, SigningScheme, + SigningSchemeInput, } from "../../src"; +import { AnyPublicKey } from "../../src/core/crypto/anyPublicKey"; +import { AnySignature } from "../../src/core/crypto/anySignature"; import { ed25519, secp256k1TestObject, wallet } from "./helper"; describe("Ed25519 Account", () => { it("should create an instance of Account correctly without error when scheme is not specified", () => { - // Account with Ed25519 scheme + // Account with Ed25519 SingleKey scheme const edAccount = Account.generate(); expect(edAccount).toBeInstanceOf(Account); - expect(edAccount.signingScheme).toEqual(SigningScheme.Ed25519); + expect(edAccount.publicKey).toBeInstanceOf(AnyPublicKey); + expect(edAccount.signingScheme).toEqual(SigningScheme.SingleKey); }); it("should create an instance of Account correctly without error when scheme is specified", () => { - // Account with Ed25519 scheme - const edAccount = Account.generate(SigningScheme.Ed25519); + // Account with Ed25519 SingleKey scheme + const edAccount = Account.generate(SigningSchemeInput.Secp256k1Ecdsa); expect(edAccount).toBeInstanceOf(Account); - expect(edAccount.signingScheme).toEqual(SigningScheme.Ed25519); + expect(edAccount.publicKey).toBeInstanceOf(AnyPublicKey); + expect(edAccount.signingScheme).toEqual(SigningScheme.SingleKey); }); - it("should create a new account from a provided private key", () => { + it("should create a new account from a provided legacy private key", () => { const { privateKey: privateKeyBytes, publicKey, address } = ed25519; const privateKey = new Ed25519PrivateKey(privateKeyBytes); - const newAccount = Account.fromPrivateKey(privateKey); + const newAccount = Account.fromLegacyPrivateKey(privateKey); expect(newAccount).toBeInstanceOf(Account); expect((newAccount.privateKey as Ed25519PrivateKey).toString()).toEqual(privateKey.toString()); expect((newAccount.publicKey as Ed25519PublicKey).toString()).toEqual(new Ed25519PublicKey(publicKey).toString()); @@ -48,21 +53,22 @@ describe("Ed25519 Account", () => { address: AccountAddress.fromString(address), }); expect(newAccount).toBeInstanceOf(Account); + expect(newAccount.publicKey).toBeInstanceOf(AnyPublicKey); expect((newAccount.privateKey as Ed25519PrivateKey).toString()).toEqual(privateKey.toString()); expect((newAccount.publicKey as Ed25519PublicKey).toString()).toEqual(new Ed25519PublicKey(publicKey).toString()); expect(newAccount.accountAddress.toString()).toEqual(address); }); - it("should create a new account from a bip44 path and mnemonics", () => { + it("should create a new account from legacy bip44 path and mnemonics", () => { const { mnemonic, address, path } = wallet; - const newAccount = Account.fromDerivationPath({ path, mnemonic }); + const newAccount = Account.fromLegacyDerivationPath({ path, mnemonic }); expect(newAccount.accountAddress.toString()).toEqual(address); }); it("should prevent an invalid bip44 path ", () => { const { mnemonic } = wallet; const path = "1234"; - expect(() => Account.fromDerivationPath({ path, mnemonic })).toThrow("Invalid derivation path"); + expect(() => Account.fromLegacyDerivationPath({ path, mnemonic })).toThrow("Invalid derivation path"); }); it("should return the authentication key for a public key", () => { @@ -83,7 +89,7 @@ describe("Ed25519 Account", () => { expect(account.sign(message).toString()).toEqual(signedMessage); // Verify the signature - const signature = new Ed25519Signature(signedMessage); + const signature = new AnySignature(new Ed25519Signature(signedMessage)); expect(account.verifySignature({ message, signature })).toBe(true); }); }); @@ -91,9 +97,9 @@ describe("Ed25519 Account", () => { describe.skip("Secp256k1 Account", () => { it("should create an instance of Account correctly without error", () => { // Account with Secp256k1 scheme - const secp256k1Account = Account.generate(SigningScheme.Secp256k1Ecdsa); + const secp256k1Account = Account.generate(SigningSchemeInput.Secp256k1Ecdsa); expect(secp256k1Account).toBeInstanceOf(Account); - expect(secp256k1Account.signingScheme).toEqual(SigningScheme.Secp256k1Ecdsa); + expect(secp256k1Account.signingScheme).toEqual(SigningSchemeInput.Secp256k1Ecdsa); }); it("should create a new account from a provided private key", () => { diff --git a/tests/unit/helper.ts b/tests/unit/helper.ts index a9c0248b2..054c80702 100644 --- a/tests/unit/helper.ts +++ b/tests/unit/helper.ts @@ -56,6 +56,14 @@ export const secp256k1TestObject = { "0xd0d634e843b61339473b028105930ace022980708b2855954b977da09df84a770c0b68c29c8ca1b5409a5085b0ec263be80e433c83fcf6debb82f3447e71edca", }; +export const singleSignerED25519 = { + publicKey: "0xe425451a5dc888ac871976c3c724dec6118910e7d11d344b4b07a22cd94e8c2e", + privateKey: "0xf508cbef4e0fe463204aab724a90791c9a9dbe60a53b4978bbddbc712b55f2fd", + address: "0x5bdf77d5bf826c8c04273d4e7323f7bc4a85ee7ee34b37bd7458b7aed3639dd3", + authKey: "0x5bdf77d5bf826c8c04273d4e7323f7bc4a85ee7ee34b37bd7458b7aed3639dd3", + messageEncoded: "68656c6c6f20776f726c64", // "hello world" +}; + export const longTestTimeout = 120 * 1000; export async function customClient(requestOptions: ClientRequest): Promise> { From 920fea971bae7c9fb6a9aac14405404e3dc55902 Mon Sep 17 00:00:00 2001 From: maayan Date: Thu, 19 Oct 2023 21:29:20 -0700 Subject: [PATCH 05/10] derive account signing key from private key --- src/client/core.ts | 2 +- src/core/account.ts | 115 +++++----- src/core/authenticationKey.ts | 2 +- tests/e2e/api/account.test.ts | 2 +- .../transaction/transactionSubmission.test.ts | 9 +- tests/unit/account.test.ts | 212 +++++++----------- tests/unit/authenticationKey.test.ts | 2 +- 7 files changed, 146 insertions(+), 198 deletions(-) diff --git a/src/client/core.ts b/src/client/core.ts index ae9d5184e..e7dd35b9e 100644 --- a/src/client/core.ts +++ b/src/client/core.ts @@ -95,7 +95,7 @@ export async function aptosRequest( } const errorMessage = errors[result.status]; - console.log(result); + throw new AptosApiError( options, result, diff --git a/src/core/account.ts b/src/core/account.ts index fc9f2bc78..295454b08 100644 --- a/src/core/account.ts +++ b/src/core/account.ts @@ -11,6 +11,8 @@ import { Hex } from "./hex"; import { HexInput, SigningScheme, SigningSchemeInput } from "../types"; import { derivePrivateKeyFromMnemonic, KeyType } from "../utils/hdKey"; import { AnyPublicKey } from "./crypto/anyPublicKey"; +import { getInfo, lookupOriginalAccountAddress } from "../internal/account"; +import { AptosConfig } from "../api"; /** * Class for creating and managing account on Aptos network @@ -50,8 +52,8 @@ export class Account { * This method is private because it should only be called by the factory static methods. * @returns Account */ - private constructor(args: { privateKey: PrivateKey; address: AccountAddress }, legacy: boolean = false) { - const { privateKey, address } = args; + private constructor(args: { privateKey: PrivateKey; address: AccountAddress; legacy?: boolean }) { + const { privateKey, address, legacy } = args; // Derive the public key from the private key this.publicKey = privateKey.publicKey(); @@ -85,10 +87,10 @@ export class Account { * * @returns Account with the given signing scheme */ - static generate(scheme?: SigningSchemeInput): Account { + static generate(args?: { scheme?: SigningSchemeInput; legacy?: boolean }): Account { let privateKey: PrivateKey; - switch (scheme) { + switch (args?.scheme) { case SigningSchemeInput.Secp256k1Ecdsa: privateKey = Secp256k1PrivateKey.generate(); break; @@ -97,62 +99,77 @@ export class Account { privateKey = Ed25519PrivateKey.generate(); } + let publicKey = privateKey.publicKey(); + if (!args?.legacy) { + publicKey = new AnyPublicKey(privateKey.publicKey()); + } + const address = new AccountAddress({ data: Account.authKey({ - publicKey: new AnyPublicKey(privateKey.publicKey()), // TODO support AnyMultiKey + publicKey, // TODO support AnyMultiKey }).toUint8Array(), }); - return new Account({ privateKey, address }); + return new Account({ privateKey, address, legacy: args?.legacy }); } /** * Derives an account with provided private key * - * NOTE: This function support the new Single Signer and - * should be used only with private key that was created - * as a Single Signer + * NOTE: This function derives the public and auth keys + * from the provided private key and then creates an Account + * based on the Account configured signing scheme - + * ED25519 or Single Sender * * @param privateKey Hex - private key of the account * @returns Account */ - static fromPrivateKey(privateKey: PrivateKey): Account { + static async fromPrivateKey(privateKey: PrivateKey, config: AptosConfig): Promise { const publicKey = new AnyPublicKey(privateKey.publicKey()); - const authKey = Account.authKey({ publicKey }); - const address = new AccountAddress({ data: authKey.toUint8Array() }); - return Account.fromPrivateKeyAndAddress({ privateKey, address }); - } - /** - * Derives an account with provided legacy private key - * - * NOTE: This function support the legacy keys and - * should be used only with private key that was created - * as - * - * @param privateKey Hex - private key of the account - * @returns Account - */ - static fromLegacyPrivateKey(privateKey: PrivateKey) { - const publicKey = privateKey.publicKey(); - const authKey = Account.authKey({ publicKey }); - const address = new AccountAddress({ data: authKey.toUint8Array() }); - return Account.fromLegacyPrivateKeyAndAddress({ privateKey, address }); - } + if (privateKey instanceof Secp256k1PrivateKey) { + // private key is secp256k1, therefore we know it for sure uses a single signer key + const authKey = AuthenticationKey.fromBytesAndScheme({ publicKey, scheme: 2 }); + const address = new AccountAddress({ data: authKey.toUint8Array() }); + return new Account({ privateKey, address }); + } - /** - * Derives an account with provided private key and address - * This is intended to be used for account that has it's key rotated - * - * @param args.privateKey Hex - private key of the account - * @param args.address AccountAddress - address of the account - * @returns Account - */ - static fromPrivateKeyAndAddress(args: { privateKey: PrivateKey; address: AccountAddress }): Account { - return new Account(args); + if (privateKey instanceof Ed25519PrivateKey) { + // lookup single sender ed25519 + const singleSenderAuthKey = AuthenticationKey.fromBytesAndScheme({ publicKey, scheme: SigningScheme.SingleKey }); + const isSingleSender = await Account.lookupAddress(singleSenderAuthKey, config); + if (isSingleSender) { + const address = new AccountAddress({ data: singleSenderAuthKey.toUint8Array() }); + return new Account({ privateKey, address }); + } + // lookup legacy ed25519 + const legacyAuthKey = AuthenticationKey.fromBytesAndScheme({ publicKey, scheme: SigningScheme.Ed25519 }); + const isLegacyEd25519 = await Account.lookupAddress(legacyAuthKey, config); + if (isLegacyEd25519) { + const address = new AccountAddress({ data: legacyAuthKey.toUint8Array() }); + return new Account({ privateKey, address, legacy: true }); + } + } + + // if we are here, it means we couldn't find an address with an + //auth key that matches the provided private key + throw new Error(`Can't derive account from private key ${privateKey}`); } - static fromLegacyPrivateKeyAndAddress(args: { privateKey: PrivateKey; address: AccountAddress }): Account { - return new Account(args, true); + private static async lookupAddress(authKey: AuthenticationKey, config: AptosConfig): Promise { + const potentialAddress1 = await lookupOriginalAccountAddress({ + aptosConfig: config, + authenticationKey: authKey.toString(), + }); + + try { + await getInfo({ + aptosConfig: config, + accountAddress: potentialAddress1.toString(), + }); + return true; + } catch (error: any) { + return false; + } } /** @@ -165,18 +182,12 @@ export class Account { */ static fromDerivationPath(args: { path: string; mnemonic: string }): Account { const { path, mnemonic } = args; - const { key } = derivePrivateKeyFromMnemonic(KeyType.ED25519, path, mnemonic); const privateKey = new Ed25519PrivateKey(key); - return Account.fromPrivateKey(privateKey); - } - - static fromLegacyDerivationPath(args: { path: string; mnemonic: string }): Account { - const { path, mnemonic } = args; - - const { key } = derivePrivateKeyFromMnemonic(KeyType.ED25519, path, mnemonic); - const privateKey = new Ed25519PrivateKey(key); - return Account.fromLegacyPrivateKey(privateKey); + const publicKey = privateKey.publicKey(); + const authKey = Account.authKey({ publicKey }); + const address = new AccountAddress({ data: authKey.toUint8Array() }); + return new Account({ privateKey, address, legacy: true }); } /** diff --git a/src/core/authenticationKey.ts b/src/core/authenticationKey.ts index 51c9566d0..2a97e008b 100644 --- a/src/core/authenticationKey.ts +++ b/src/core/authenticationKey.ts @@ -53,7 +53,7 @@ export class AuthenticationKey { * This allows for the creation of AuthenticationKeys that are not derived from Public Keys directly * @param args */ - private static fromBytesAndScheme(args: { publicKey: PublicKey; scheme: AuthenticationKeyScheme }) { + public static fromBytesAndScheme(args: { publicKey: PublicKey; scheme: AuthenticationKeyScheme }) { const { publicKey, scheme } = args; // TODO - check for single key or multi key diff --git a/tests/e2e/api/account.test.ts b/tests/e2e/api/account.test.ts index 21c4515ae..6735b3b1d 100644 --- a/tests/e2e/api/account.test.ts +++ b/tests/e2e/api/account.test.ts @@ -198,7 +198,7 @@ describe("account api", () => { expect(accountCoinsCount).toBe(1); }); - test("lookupOriginalAccountAddress - Look up account address before key rotation", async () => { + test.only("lookupOriginalAccountAddress - Look up account address before key rotation", async () => { const config = new AptosConfig({ network: Network.LOCAL }); const aptos = new Aptos(config); const account = Account.generate(); diff --git a/tests/e2e/transaction/transactionSubmission.test.ts b/tests/e2e/transaction/transactionSubmission.test.ts index 7a8cdb64e..8588e2a05 100644 --- a/tests/e2e/transaction/transactionSubmission.test.ts +++ b/tests/e2e/transaction/transactionSubmission.test.ts @@ -27,15 +27,14 @@ import { fundAccounts, multiSignerScriptBytecode, publishTransferPackage, single * fromPrivateKeyAndAddress(secpk) // should be single_signer * */ - +const config = new AptosConfig({ network: Network.LOCAL }); +const aptos = new Aptos(config); describe("transaction submission", () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const contractPublisherAccount = Account.generate(); const singleSignerED25519SenderAccount = Account.generate(); - const legacyED25519SenderAccount = Account.fromLegacyPrivateKey(new Ed25519PrivateKey(ed25519.privateKey)); + const legacyED25519SenderAccount = Account.fromPrivateKey(new Ed25519PrivateKey(ed25519.privateKey), config); const receiverAccounts = [Account.generate(), Account.generate()]; - const singleSignerSecp256k1Account = Account.generate(SigningSchemeInput.Secp256k1Ecdsa); + const singleSignerSecp256k1Account = Account.generate({ scheme: SigningSchemeInput.Secp256k1Ecdsa }); const secondarySignerAccount = Account.generate(); const feePayerAccount = Account.generate(); beforeAll(async () => { diff --git a/tests/unit/account.test.ts b/tests/unit/account.test.ts index f8896f191..91e67ba8c 100644 --- a/tests/unit/account.test.ts +++ b/tests/unit/account.test.ts @@ -3,166 +3,104 @@ import { Account, - AccountAddress, + AptosConfig, Ed25519PrivateKey, Ed25519PublicKey, - Ed25519Signature, Hex, + Network, Secp256k1PrivateKey, Secp256k1PublicKey, - Secp256k1Signature, SigningScheme, SigningSchemeInput, } from "../../src"; import { AnyPublicKey } from "../../src/core/crypto/anyPublicKey"; -import { AnySignature } from "../../src/core/crypto/anySignature"; -import { ed25519, secp256k1TestObject, wallet } from "./helper"; -describe("Ed25519 Account", () => { - it("should create an instance of Account correctly without error when scheme is not specified", () => { - // Account with Ed25519 SingleKey scheme - const edAccount = Account.generate(); - expect(edAccount).toBeInstanceOf(Account); - expect(edAccount.publicKey).toBeInstanceOf(AnyPublicKey); - expect(edAccount.signingScheme).toEqual(SigningScheme.SingleKey); - }); - - it("should create an instance of Account correctly without error when scheme is specified", () => { - // Account with Ed25519 SingleKey scheme - const edAccount = Account.generate(SigningSchemeInput.Secp256k1Ecdsa); - expect(edAccount).toBeInstanceOf(Account); - expect(edAccount.publicKey).toBeInstanceOf(AnyPublicKey); - expect(edAccount.signingScheme).toEqual(SigningScheme.SingleKey); - }); - - it("should create a new account from a provided legacy private key", () => { - const { privateKey: privateKeyBytes, publicKey, address } = ed25519; - const privateKey = new Ed25519PrivateKey(privateKeyBytes); - const newAccount = Account.fromLegacyPrivateKey(privateKey); - expect(newAccount).toBeInstanceOf(Account); - expect((newAccount.privateKey as Ed25519PrivateKey).toString()).toEqual(privateKey.toString()); - expect((newAccount.publicKey as Ed25519PublicKey).toString()).toEqual(new Ed25519PublicKey(publicKey).toString()); - expect(newAccount.accountAddress.toString()).toEqual(address); - }); - - it("should create a new account from a provided private key and address", () => { - const { privateKey: privateKeyBytes, publicKey, address } = ed25519; - const privateKey = new Ed25519PrivateKey(privateKeyBytes); - const newAccount = Account.fromPrivateKeyAndAddress({ - privateKey, - address: AccountAddress.fromString(address), +import { ed25519, secp256k1TestObject, singleSignerED25519, wallet } from "./helper"; + +describe("Account", () => { + const config = new AptosConfig({ network: Network.LOCAL }); + describe("generate", () => { + it("should create an instance of Account when Secp256k1 scheme is specified", () => { + // Account with Ed25519 SingleKey scheme + const secpAccount = Account.generate({ scheme: SigningSchemeInput.Secp256k1Ecdsa }); + expect(secpAccount).toBeInstanceOf(Account); + expect(secpAccount.publicKey).toBeInstanceOf(AnyPublicKey); + expect(secpAccount.signingScheme).toEqual(SigningScheme.SingleKey); }); - expect(newAccount).toBeInstanceOf(Account); - expect(newAccount.publicKey).toBeInstanceOf(AnyPublicKey); - expect((newAccount.privateKey as Ed25519PrivateKey).toString()).toEqual(privateKey.toString()); - expect((newAccount.publicKey as Ed25519PublicKey).toString()).toEqual(new Ed25519PublicKey(publicKey).toString()); - expect(newAccount.accountAddress.toString()).toEqual(address); - }); - - it("should create a new account from legacy bip44 path and mnemonics", () => { - const { mnemonic, address, path } = wallet; - const newAccount = Account.fromLegacyDerivationPath({ path, mnemonic }); - expect(newAccount.accountAddress.toString()).toEqual(address); - }); - - it("should prevent an invalid bip44 path ", () => { - const { mnemonic } = wallet; - const path = "1234"; - expect(() => Account.fromLegacyDerivationPath({ path, mnemonic })).toThrow("Invalid derivation path"); - }); - - it("should return the authentication key for a public key", () => { - const { publicKey: publicKeyBytes, address } = ed25519; - const publicKey = new Ed25519PublicKey(publicKeyBytes); - const authKey = Account.authKey({ publicKey }); - expect(authKey).toBeInstanceOf(Hex); - expect(authKey.toString()).toEqual(address); - }); - - it("should sign data, return a Hex signature, and verify", () => { - const { privateKey: privateKeyBytes, address, message, signedMessage } = ed25519; - const privateKey = new Ed25519PrivateKey(privateKeyBytes); - const account = Account.fromPrivateKeyAndAddress({ - privateKey, - address: AccountAddress.fromString(address), + it("should create an instance of Account with a legacy ED25519 scheme is not specified and legacy set to true", () => { + // Account with Ed25519 SingleKey scheme + const edAccount = Account.generate({ legacy: true }); + expect(edAccount).toBeInstanceOf(Account); + expect(edAccount.publicKey).toBeInstanceOf(Ed25519PublicKey); + expect(edAccount.signingScheme).toEqual(SigningScheme.Ed25519); + }); + it("should create an instance of Account with a Single Sender ED25519 when scheme and legacy not specified", () => { + // Account with Ed25519 SingleKey scheme + const edAccount = Account.generate(); + expect(edAccount).toBeInstanceOf(Account); + expect(edAccount.publicKey).toBeInstanceOf(AnyPublicKey); + expect(edAccount.signingScheme).toEqual(SigningScheme.SingleKey); + }); + it("should create an instance of Account with a legacy ED25519 when scheme and legacy specified", () => { + // Account with Ed25519 SingleKey scheme + const edAccount = Account.generate({ scheme: SigningSchemeInput.Ed25519, legacy: true }); + expect(edAccount).toBeInstanceOf(Account); + expect(edAccount.publicKey).toBeInstanceOf(Ed25519PublicKey); + expect(edAccount.signingScheme).toEqual(SigningScheme.Ed25519); }); - expect(account.sign(message).toString()).toEqual(signedMessage); - - // Verify the signature - const signature = new AnySignature(new Ed25519Signature(signedMessage)); - expect(account.verifySignature({ message, signature })).toBe(true); - }); -}); - -describe.skip("Secp256k1 Account", () => { - it("should create an instance of Account correctly without error", () => { - // Account with Secp256k1 scheme - const secp256k1Account = Account.generate(SigningSchemeInput.Secp256k1Ecdsa); - expect(secp256k1Account).toBeInstanceOf(Account); - expect(secp256k1Account.signingScheme).toEqual(SigningSchemeInput.Secp256k1Ecdsa); - }); - - it("should create a new account from a provided private key", () => { - const { privateKey: privateKeyBytes, publicKey, address } = secp256k1TestObject; - const privateKey = new Secp256k1PrivateKey(privateKeyBytes); - const newAccount = Account.fromPrivateKey(privateKey); - expect(newAccount).toBeInstanceOf(Account); - expect((newAccount.privateKey as Secp256k1PrivateKey).toString()).toEqual(privateKey.toString()); - expect((newAccount.publicKey as Secp256k1PublicKey).toString()).toEqual( - new Secp256k1PublicKey(publicKey).toString(), - ); - expect(newAccount.accountAddress.toString()).toEqual(address); }); + describe("fromPrivateKey", () => { + it("derives the correct account from a legacy ed25519 private key", async () => { + const { privateKey: privateKeyBytes, publicKey, address } = ed25519; + const privateKey = new Ed25519PrivateKey(privateKeyBytes); + const newAccount = await Account.fromPrivateKey(privateKey, config); + expect(newAccount).toBeInstanceOf(Account); + expect((newAccount.privateKey as Ed25519PrivateKey).toString()).toEqual(privateKey.toString()); + expect((newAccount.publicKey as Ed25519PublicKey).toString()).toEqual(new Ed25519PublicKey(publicKey).toString()); + expect(newAccount.accountAddress.toString()).toEqual(address); + }); - it("should create a new account from a provided private key and address", () => { - const { privateKey: privateKeyBytes, publicKey, address } = secp256k1TestObject; - const privateKey = new Secp256k1PrivateKey(privateKeyBytes); - const newAccount = Account.fromPrivateKeyAndAddress({ - privateKey, - address: AccountAddress.fromString(address), + it("derives the correct account from a single signer ed25519 private key", async () => { + const { privateKey: privateKeyBytes, publicKey, address } = singleSignerED25519; + const privateKey = new Ed25519PrivateKey(privateKeyBytes); + const newAccount = await Account.fromPrivateKey(privateKey, config); + expect(newAccount).toBeInstanceOf(Account); + expect((newAccount.privateKey as Ed25519PrivateKey).toString()).toEqual(privateKey.toString()); + expect((newAccount.publicKey as Ed25519PublicKey).toString()).toEqual(new Ed25519PublicKey(publicKey).toString()); + expect(newAccount.accountAddress.toString()).toEqual(address); }); - expect(newAccount).toBeInstanceOf(Account); - expect((newAccount.privateKey as Secp256k1PrivateKey).toString()).toEqual(privateKey.toString()); - expect((newAccount.publicKey as Secp256k1PublicKey).toString()).toEqual( - new Secp256k1PublicKey(publicKey).toString(), - ); - expect(newAccount.accountAddress.toString()).toEqual(address); - }); - it("should create a new account from a bip44 path and mnemonics", () => { - const { mnemonic, address, path } = wallet; - const newAccount = Account.fromDerivationPath({ path, mnemonic }); - expect(newAccount.accountAddress.toString()).toEqual(address); + it("derives the correct account from a single signer secp256k1 private key", async () => { + const { privateKey: privateKeyBytes, publicKey, address } = secp256k1TestObject; + const privateKey = new Secp256k1PrivateKey(privateKeyBytes); + const newAccount = await Account.fromPrivateKey(privateKey, config); + expect(newAccount).toBeInstanceOf(Account); + expect((newAccount.privateKey as Secp256k1PrivateKey).toString()).toEqual(privateKey.toString()); + expect((newAccount.publicKey as Secp256k1PublicKey).toString()).toEqual( + new Secp256k1PublicKey(publicKey).toString(), + ); + expect(newAccount.accountAddress.toString()).toEqual(address); + }); }); + describe("fromDerivationPath", () => { + it("should create a new account from legacy bip44 path and mnemonics", async () => { + const { mnemonic, address, path } = wallet; + const newAccount = await Account.fromDerivationPath({ path, mnemonic }); + expect(newAccount.accountAddress.toString()).toEqual(address); + }); - it("should prevent an invalid bip44 path ", () => { - const { mnemonic } = wallet; - const path = "1234"; - expect(() => Account.fromDerivationPath({ path, mnemonic })).toThrow("Invalid derivation path"); + it("should prevent an invalid bip44 path ", () => { + const { mnemonic } = wallet; + const path = "1234"; + expect(() => Account.fromDerivationPath({ path, mnemonic })).toThrow("Invalid derivation path"); + }); }); it("should return the authentication key for a public key", () => { - const { publicKey: publicKeyBytes, address } = secp256k1TestObject; - const publicKey = new Secp256k1PublicKey(publicKeyBytes); + const { publicKey: publicKeyBytes, address } = ed25519; + const publicKey = new Ed25519PublicKey(publicKeyBytes); const authKey = Account.authKey({ publicKey }); expect(authKey).toBeInstanceOf(Hex); expect(authKey.toString()).toEqual(address); }); - - it("should sign data, return a Hex signature, and verify", () => { - const { privateKey: privateKeyBytes, address, signatureHex, messageEncoded } = secp256k1TestObject; - - // Sign the message - const secp256k1PrivateKey = new Secp256k1PrivateKey(privateKeyBytes); - const account = Account.fromPrivateKeyAndAddress({ - privateKey: secp256k1PrivateKey, - address: AccountAddress.fromString(address), - }); - const signedMessage = account.sign(messageEncoded); - expect(signedMessage.toString()).toEqual(signatureHex); - - // Verify the signature - const signature = new Secp256k1Signature(signatureHex); - expect(account.verifySignature({ message: messageEncoded, signature })).toBe(true); - }); }); diff --git a/tests/unit/authenticationKey.test.ts b/tests/unit/authenticationKey.test.ts index 47b6f9b1e..5ba719aca 100644 --- a/tests/unit/authenticationKey.test.ts +++ b/tests/unit/authenticationKey.test.ts @@ -27,7 +27,7 @@ describe("AuthenticationKey", () => { ); }); - it("should create AuthenticationKey from Ed25519PublicKey", () => { + it.only("should create AuthenticationKey from Ed25519PublicKey", () => { const publicKey = new Ed25519PublicKey(ed25519.publicKey); const authKey = AuthenticationKey.fromPublicKey({ publicKey }); expect(authKey).toBeInstanceOf(AuthenticationKey); From 8dfadd4fa32f848d992592c8ce644e64ad0df9a8 Mon Sep 17 00:00:00 2001 From: maayan Date: Fri, 20 Oct 2023 09:00:10 -0700 Subject: [PATCH 06/10] cleanup --- src/core/account.ts | 52 ++-- src/core/authenticationKey.ts | 4 +- src/core/crypto/anyPublicKey.ts | 1 + src/core/crypto/ed25519.ts | 89 +----- src/core/crypto/index.ts | 1 + src/core/crypto/secp256k1.ts | 12 +- src/core/crypto/singleKey.ts | 78 +++++ src/internal/account.ts | 3 +- src/internal/faucet.ts | 2 +- src/internal/transactionSubmission.ts | 1 - src/transactions/authenticator/account.ts | 10 +- src/transactions/authenticator/transaction.ts | 12 +- .../transaction_builder.ts | 22 +- src/transactions/types.ts | 1 - src/types/index.ts | 2 +- tests/e2e/api/account.test.ts | 2 +- tests/e2e/api/coin.test.ts | 46 +-- tests/e2e/api/token.test.ts | 4 +- tests/e2e/transaction/helper.ts | 5 +- tests/e2e/transaction/signTransaction.test.ts | 137 +++++--- .../transaction/transactionBuilder.test.ts | 126 +++----- .../transaction/transactionSimulation.test.ts | 293 ++++++++++++++---- .../transaction/transactionSubmission.test.ts | 58 +--- tests/unit/authenticationKey.test.ts | 2 +- 24 files changed, 562 insertions(+), 401 deletions(-) create mode 100644 src/core/crypto/singleKey.ts diff --git a/src/core/account.ts b/src/core/account.ts index 295454b08..ebc8f034b 100644 --- a/src/core/account.ts +++ b/src/core/account.ts @@ -12,7 +12,7 @@ import { HexInput, SigningScheme, SigningSchemeInput } from "../types"; import { derivePrivateKeyFromMnemonic, KeyType } from "../utils/hdKey"; import { AnyPublicKey } from "./crypto/anyPublicKey"; import { getInfo, lookupOriginalAccountAddress } from "../internal/account"; -import { AptosConfig } from "../api"; +import { AptosConfig } from "../api/aptosConfig"; /** * Class for creating and managing account on Aptos network @@ -48,6 +48,8 @@ export class Account { * * @param args.privateKey PrivateKey - private key of the account * @param args.address AccountAddress - address of the account + * @param args.legacy optional. If set to true, would create a legacy Ed25519 signing keys. Default + * is Single Sender signing key * * This method is private because it should only be called by the factory static methods. * @returns Account @@ -82,8 +84,10 @@ export class Account { /** * Derives an account with random private key and address * - * @param scheme optional SigningScheme - type of SigningScheme to use. Default to Ed25519 + * @param args.scheme optional. SigningScheme - type of SigningScheme to use. Default to Ed25519 * Currently only Ed25519 and Secp256k1 are supported + * @param args.legacy optional. If set to true, would create a legacy Ed25519 signing keys. Default + * is Single Sender signing key * * @returns Account with the given signing scheme */ @@ -118,12 +122,14 @@ export class Account { * NOTE: This function derives the public and auth keys * from the provided private key and then creates an Account * based on the Account configured signing scheme - - * ED25519 or Single Sender + * Legacy ED25519 or Single Sender * - * @param privateKey Hex - private key of the account - * @returns Account + * @param privateKey PrivateKey - private key of the account + * @param aptosConfig AptosConfig type + * + * @returns Promise */ - static async fromPrivateKey(privateKey: PrivateKey, config: AptosConfig): Promise { + static async fromPrivateKey(privateKey: PrivateKey, aptosConfig: AptosConfig): Promise { const publicKey = new AnyPublicKey(privateKey.publicKey()); if (privateKey instanceof Secp256k1PrivateKey) { @@ -135,15 +141,21 @@ export class Account { if (privateKey instanceof Ed25519PrivateKey) { // lookup single sender ed25519 - const singleSenderAuthKey = AuthenticationKey.fromBytesAndScheme({ publicKey, scheme: SigningScheme.SingleKey }); - const isSingleSender = await Account.lookupAddress(singleSenderAuthKey, config); - if (isSingleSender) { - const address = new AccountAddress({ data: singleSenderAuthKey.toUint8Array() }); + const SingleSenderTransactionAuthenticatorAuthKey = AuthenticationKey.fromBytesAndScheme({ + publicKey, + scheme: SigningScheme.SingleKey, + }); + const isSingleSenderTransactionAuthenticator = await Account.lookupAddress( + SingleSenderTransactionAuthenticatorAuthKey, + aptosConfig, + ); + if (isSingleSenderTransactionAuthenticator) { + const address = new AccountAddress({ data: SingleSenderTransactionAuthenticatorAuthKey.toUint8Array() }); return new Account({ privateKey, address }); } // lookup legacy ed25519 const legacyAuthKey = AuthenticationKey.fromBytesAndScheme({ publicKey, scheme: SigningScheme.Ed25519 }); - const isLegacyEd25519 = await Account.lookupAddress(legacyAuthKey, config); + const isLegacyEd25519 = await Account.lookupAddress(legacyAuthKey, aptosConfig); if (isLegacyEd25519) { const address = new AccountAddress({ data: legacyAuthKey.toUint8Array() }); return new Account({ privateKey, address, legacy: true }); @@ -151,24 +163,28 @@ export class Account { } // if we are here, it means we couldn't find an address with an - //auth key that matches the provided private key + // auth key that matches the provided private key throw new Error(`Can't derive account from private key ${privateKey}`); } - private static async lookupAddress(authKey: AuthenticationKey, config: AptosConfig): Promise { - const potentialAddress1 = await lookupOriginalAccountAddress({ - aptosConfig: config, + private static async lookupAddress(authKey: AuthenticationKey, aptosConfig: AptosConfig): Promise { + const accountAddress = await lookupOriginalAccountAddress({ + aptosConfig, authenticationKey: authKey.toString(), }); try { await getInfo({ - aptosConfig: config, - accountAddress: potentialAddress1.toString(), + aptosConfig, + accountAddress: accountAddress.toString(), }); return true; } catch (error: any) { - return false; + // account not found + if (error.code === 404) { + return false; + } + throw new Error(`Error while looking for an account info ${accountAddress.toString()} `); } } diff --git a/src/core/authenticationKey.ts b/src/core/authenticationKey.ts index 2a97e008b..23e3450a8 100644 --- a/src/core/authenticationKey.ts +++ b/src/core/authenticationKey.ts @@ -58,7 +58,7 @@ export class AuthenticationKey { // TODO - check for single key or multi key if (scheme === SigningScheme.SingleKey) { - let newBytes = publicKey.bcsToBytes(); + const newBytes = publicKey.bcsToBytes(); const authKeyBytes = new Uint8Array([...newBytes, scheme]); const hash = sha3Hash.create(); hash.update(authKeyBytes); @@ -99,7 +99,7 @@ export class AuthenticationKey { throw new Error("No supported authentication scheme for public key"); } - return AuthenticationKey.fromBytesAndScheme({ publicKey: publicKey, scheme }); + return AuthenticationKey.fromBytesAndScheme({ publicKey, scheme }); } /** diff --git a/src/core/crypto/anyPublicKey.ts b/src/core/crypto/anyPublicKey.ts index 54f57892f..38240acfd 100644 --- a/src/core/crypto/anyPublicKey.ts +++ b/src/core/crypto/anyPublicKey.ts @@ -42,6 +42,7 @@ export class AnyPublicKey extends PublicKey { const { message, signature } = args; if (this.publicKey instanceof Ed25519PublicKey && signature.signature instanceof Ed25519Signature) { return this.publicKey.verifySignature({ message, signature: signature.signature }); + // eslint-disable-next-line no-else-return } else if (this.publicKey instanceof Secp256k1PublicKey && signature.signature instanceof Secp256k1Signature) { return this.publicKey.verifySignature({ message, signature: signature.signature }); } else { diff --git a/src/core/crypto/ed25519.ts b/src/core/crypto/ed25519.ts index c1009b0d7..ca828e4c4 100644 --- a/src/core/crypto/ed25519.ts +++ b/src/core/crypto/ed25519.ts @@ -6,7 +6,7 @@ import { PublicKey, PrivateKey, Signature } from "./asymmetricCrypto"; import { Deserializer } from "../../bcs/deserializer"; import { Serializer } from "../../bcs/serializer"; import { Hex } from "../hex"; -import { AnyPublicKeyVariant, HexInput } from "../../types"; +import { HexInput } from "../../types"; /** * Represents the public key of an Ed25519 key pair. @@ -77,88 +77,6 @@ export class Ed25519PublicKey extends PublicKey { return new Ed25519PublicKey(bytes); } - serializeForSingleKey(serializer: Serializer): void { - serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Ed25519); - this.serialize(serializer); - } - - static load(deserializer: Deserializer): Ed25519PublicKey { - const bytes = deserializer.deserializeBytes(); - return new Ed25519PublicKey(bytes); - } -} - -export class SingleKey extends PublicKey { - /** - * Length of an Ed25519 public key - */ - static readonly LENGTH: number = 65; - - /** - * Bytes of the public key - * @private - */ - private readonly key: Hex; - - /** - * Create a new PublicKey instance from a Uint8Array or String. - * - * @param hexInput A HexInput (string or Uint8Array) - */ - constructor(hexInput: HexInput) { - super(); - - const hex = Hex.fromHexInput(hexInput); - if (hex.toUint8Array().length !== SingleKey.LENGTH) { - throw new Error(`PublicKey length should be ${SingleKey.LENGTH}`); - } - this.key = hex; - } - - /** - * Get the public key in bytes (Uint8Array). - * - * @returns Uint8Array representation of the public key - */ - toUint8Array(): Uint8Array { - return this.key.toUint8Array(); - } - - /** - * Get the public key as a hex string with the 0x prefix. - * - * @returns string representation of the public key - */ - toString(): string { - return this.key.toString(); - } - - /** - * Verifies a signed data with a public key - * @param args.message a signed message - * @param args.signature the signature of the message - */ - verifySignature(args: { message: HexInput; signature: Ed25519Signature }): boolean { - const { message, signature } = args; - const rawMessage = Hex.fromHexInput(message).toUint8Array(); - const rawSignature = Hex.fromHexInput(signature.toUint8Array()).toUint8Array(); - return nacl.sign.detached.verify(rawMessage, rawSignature, this.key.toUint8Array()); - } - - serialize(serializer: Serializer): void { - serializer.serializeBytes(this.key.toUint8Array()); - } - - static deserialize(deserializer: Deserializer): Ed25519PublicKey { - const bytes = deserializer.deserializeBytes(); - return new Ed25519PublicKey(bytes); - } - - serializeForSingleKey(serializer: Serializer): void { - serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Ed25519); - this.serialize(serializer); - } - static load(deserializer: Deserializer): Ed25519PublicKey { const bytes = deserializer.deserializeBytes(); return new Ed25519PublicKey(bytes); @@ -309,11 +227,6 @@ export class Ed25519Signature extends Signature { return new Ed25519Signature(bytes); } - serializeForSingleKey(serializer: Serializer): void { - serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Ed25519); - this.serialize(serializer); - } - static load(deserializer: Deserializer): Ed25519Signature { const bytes = deserializer.deserializeBytes(); return new Ed25519Signature(bytes); diff --git a/src/core/crypto/index.ts b/src/core/crypto/index.ts index 364b66815..819681567 100644 --- a/src/core/crypto/index.ts +++ b/src/core/crypto/index.ts @@ -5,3 +5,4 @@ export * from "./asymmetricCrypto"; export * from "./ed25519"; export * from "./multiEd25519"; export * from "./secp256k1"; +export * from "./singleKey"; diff --git a/src/core/crypto/secp256k1.ts b/src/core/crypto/secp256k1.ts index 410a6e978..81bcbc79e 100644 --- a/src/core/crypto/secp256k1.ts +++ b/src/core/crypto/secp256k1.ts @@ -6,7 +6,7 @@ import { secp256k1 } from "@noble/curves/secp256k1"; import { PrivateKey, PublicKey, Signature } from "./asymmetricCrypto"; import { Deserializer, Serializer } from "../../bcs"; import { Hex } from "../hex"; -import { AnyPublicKeyVariant, HexInput } from "../../types"; +import { HexInput } from "../../types"; /** * Represents the Secp256k1 ecdsa public key @@ -75,11 +75,6 @@ export class Secp256k1PublicKey extends PublicKey { return new Secp256k1PublicKey(bytes); } - serializeForSingleKey(serializer: Serializer): void { - serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Secp256k1); - this.serialize(serializer); - } - static load(deserializer: Deserializer): Secp256k1PublicKey { const bytes = deserializer.deserializeBytes(); return new Secp256k1PublicKey(bytes); @@ -235,11 +230,6 @@ export class Secp256k1Signature extends Signature { return new Secp256k1Signature(hex); } - serializeForSingleKey(serializer: Serializer): void { - serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Secp256k1); - this.serialize(serializer); - } - static load(deserializer: Deserializer): Secp256k1Signature { const bytes = deserializer.deserializeBytes(); return new Secp256k1Signature(bytes); diff --git a/src/core/crypto/singleKey.ts b/src/core/crypto/singleKey.ts new file mode 100644 index 000000000..ae58c5bc1 --- /dev/null +++ b/src/core/crypto/singleKey.ts @@ -0,0 +1,78 @@ +import nacl from "tweetnacl"; +import { Serializer, Deserializer } from "../../bcs"; +import { HexInput } from "../../types"; +import { Hex } from "../hex"; +import { PublicKey } from "./asymmetricCrypto"; +import { Ed25519Signature, Ed25519PublicKey } from "./ed25519"; + +export class SingleKey extends PublicKey { + /** + * Length of an Ed25519 public key + */ + static readonly LENGTH: number = 65; + + /** + * Bytes of the public key + * @private + */ + private readonly key: Hex; + + /** + * Create a new PublicKey instance from a Uint8Array or String. + * + * @param hexInput A HexInput (string or Uint8Array) + */ + constructor(hexInput: HexInput) { + super(); + + const hex = Hex.fromHexInput(hexInput); + if (hex.toUint8Array().length !== SingleKey.LENGTH) { + throw new Error(`PublicKey length should be ${SingleKey.LENGTH}`); + } + this.key = hex; + } + + /** + * Get the public key in bytes (Uint8Array). + * + * @returns Uint8Array representation of the public key + */ + toUint8Array(): Uint8Array { + return this.key.toUint8Array(); + } + + /** + * Get the public key as a hex string with the 0x prefix. + * + * @returns string representation of the public key + */ + toString(): string { + return this.key.toString(); + } + + /** + * Verifies a signed data with a public key + * @param args.message a signed message + * @param args.signature the signature of the message + */ + verifySignature(args: { message: HexInput; signature: Ed25519Signature }): boolean { + const { message, signature } = args; + const rawMessage = Hex.fromHexInput(message).toUint8Array(); + const rawSignature = Hex.fromHexInput(signature.toUint8Array()).toUint8Array(); + return nacl.sign.detached.verify(rawMessage, rawSignature, this.key.toUint8Array()); + } + + serialize(serializer: Serializer): void { + serializer.serializeBytes(this.key.toUint8Array()); + } + + static deserialize(deserializer: Deserializer): Ed25519PublicKey { + const bytes = deserializer.deserializeBytes(); + return new Ed25519PublicKey(bytes); + } + + static load(deserializer: Deserializer): Ed25519PublicKey { + const bytes = deserializer.deserializeBytes(); + return new Ed25519PublicKey(bytes); + } +} diff --git a/src/internal/account.ts b/src/internal/account.ts index c2b9befae..7bb65f428 100644 --- a/src/internal/account.ts +++ b/src/internal/account.ts @@ -10,7 +10,8 @@ import { AptosConfig } from "../api/aptosConfig"; import { AptosApiError, getAptosFullNode, paginateWithCursor } from "../client"; -import { AccountAddress, Hex } from "../core"; +import { AccountAddress } from "../core/accountAddress"; +import { Hex } from "../core/hex"; import { getTableItem, queryIndexer } from "./general"; import { AccountData, diff --git a/src/internal/faucet.ts b/src/internal/faucet.ts index de8747eff..e8d65921a 100644 --- a/src/internal/faucet.ts +++ b/src/internal/faucet.ts @@ -38,7 +38,7 @@ export async function fundAccount(args: { await waitForTransaction({ aptosConfig, transactionHash: txnHash, - options: { timeoutSecs, indexerVersionCheck: false }, + options: { timeoutSecs }, }); return txnHash; diff --git a/src/internal/transactionSubmission.ts b/src/internal/transactionSubmission.ts index 0572a53a4..9a183e26e 100644 --- a/src/internal/transactionSubmission.ts +++ b/src/internal/transactionSubmission.ts @@ -8,7 +8,6 @@ import { AptosConfig } from "../api/aptosConfig"; import { MoveVector } from "../bcs"; import { postAptosFullNode } from "../client"; -import { Hex } from "../core"; import { Account } from "../core/account"; import { AccountAuthenticator } from "../transactions/authenticator/account"; import { diff --git a/src/transactions/authenticator/account.ts b/src/transactions/authenticator/account.ts index e6e7d599a..2cda5facb 100644 --- a/src/transactions/authenticator/account.ts +++ b/src/transactions/authenticator/account.ts @@ -21,7 +21,7 @@ export abstract class AccountAuthenticator extends Serializable { case AccountAuthenticatorVariant.MultiEd25519: return AccountAuthenticatorMultiEd25519.load(deserializer); case AccountAuthenticatorVariant.SingleKey: - return SingleKeyAuthenticator.load(deserializer); + return AccountAuthenticatorSingleKey.load(deserializer); default: throw new Error(`Unknown variant index for AccountAuthenticator: ${index}`); } @@ -91,13 +91,13 @@ export class AccountAuthenticatorMultiEd25519 extends AccountAuthenticator { } /** - * SingleKeyAuthenticator for a single signer + * AccountAuthenticatorSingleKey for a single signer * * @param public_key AnyPublicKey * @param signature AnySignature * */ -export class SingleKeyAuthenticator extends AccountAuthenticator { +export class AccountAuthenticatorSingleKey extends AccountAuthenticator { public readonly public_key: AnyPublicKey; public readonly signature: AnySignature; @@ -114,9 +114,9 @@ export class SingleKeyAuthenticator extends AccountAuthenticator { this.signature.serialize(serializer); } - static load(deserializer: Deserializer): SingleKeyAuthenticator { + static load(deserializer: Deserializer): AccountAuthenticatorSingleKey { const public_key = AnyPublicKey.deserialize(deserializer); const signature = AnySignature.deserialize(deserializer); - return new SingleKeyAuthenticator(public_key, signature); + return new AccountAuthenticatorSingleKey(public_key, signature); } } diff --git a/src/transactions/authenticator/transaction.ts b/src/transactions/authenticator/transaction.ts index b4854de43..0532a3548 100644 --- a/src/transactions/authenticator/transaction.ts +++ b/src/transactions/authenticator/transaction.ts @@ -24,8 +24,8 @@ export abstract class TransactionAuthenticator extends Serializable { return TransactionAuthenticatorMultiAgent.load(deserializer); case TransactionAuthenticatorVariant.FeePayer: return TransactionAuthenticatorFeePayer.load(deserializer); - case TransactionAuthenticatorVariant.SingleSender: - return SingleSender.load(deserializer); + case TransactionAuthenticatorVariant.SingleSenderTransactionAuthenticator: + return SingleSenderTransactionAuthenticator.load(deserializer); default: throw new Error(`Unknown variant index for TransactionAuthenticator: ${index}`); } @@ -195,7 +195,7 @@ export class TransactionAuthenticatorFeePayer extends TransactionAuthenticator { * * @param sender AccountAuthenticator */ -export class SingleSender extends TransactionAuthenticator { +export class SingleSenderTransactionAuthenticator extends TransactionAuthenticator { public readonly sender: AccountAuthenticator; constructor(sender: AccountAuthenticator) { @@ -204,12 +204,12 @@ export class SingleSender extends TransactionAuthenticator { } serialize(serializer: Serializer): void { - serializer.serializeU32AsUleb128(TransactionAuthenticatorVariant.SingleSender); + serializer.serializeU32AsUleb128(TransactionAuthenticatorVariant.SingleSenderTransactionAuthenticator); this.sender.serialize(serializer); } - static load(deserializer: Deserializer): SingleSender { + static load(deserializer: Deserializer): SingleSenderTransactionAuthenticator { const sender = AccountAuthenticator.deserialize(deserializer); - return new SingleSender(sender); + return new SingleSenderTransactionAuthenticator(sender); } } diff --git a/src/transactions/transaction_builder/transaction_builder.ts b/src/transactions/transaction_builder/transaction_builder.ts index 6a8b1a0c6..9f976267e 100644 --- a/src/transactions/transaction_builder/transaction_builder.ts +++ b/src/transactions/transaction_builder/transaction_builder.ts @@ -26,12 +26,16 @@ import { RAW_TRANSACTION_SALT, RAW_TRANSACTION_WITH_DATA_SALT, } from "../../utils/const"; -import { AccountAuthenticator, AccountAuthenticatorEd25519, SingleKeyAuthenticator } from "../authenticator/account"; +import { + AccountAuthenticator, + AccountAuthenticatorEd25519, + AccountAuthenticatorSingleKey, +} from "../authenticator/account"; import { TransactionAuthenticatorEd25519, TransactionAuthenticatorFeePayer, TransactionAuthenticatorMultiAgent, - SingleSender, + SingleSenderTransactionAuthenticator, } from "../authenticator/transaction"; import { ChainId, @@ -317,8 +321,8 @@ export function generateSignedTransactionForSimulation(args: SimulateTransaction accountAuthenticator.public_key, accountAuthenticator.signature, ); - } else if (accountAuthenticator instanceof SingleKeyAuthenticator) { - transactionAuthenticator = new SingleSender(accountAuthenticator); + } else if (accountAuthenticator instanceof AccountAuthenticatorSingleKey) { + transactionAuthenticator = new SingleSenderTransactionAuthenticator(accountAuthenticator); } else { throw new Error("Invalid public key"); } @@ -329,10 +333,10 @@ export function getAuthenticatorForSimulation(publicKey: PublicKey) { // TODO add support for AnyMultiKey if (publicKey instanceof AnyPublicKey) { if (publicKey.publicKey instanceof Ed25519PublicKey) { - return new SingleKeyAuthenticator(publicKey, new AnySignature(new Ed25519Signature(new Uint8Array(64)))); + return new AccountAuthenticatorSingleKey(publicKey, new AnySignature(new Ed25519Signature(new Uint8Array(64)))); } if (publicKey.publicKey instanceof Secp256k1PublicKey) { - return new SingleKeyAuthenticator(publicKey, new AnySignature(new Secp256k1Signature(new Uint8Array(64)))); + return new AccountAuthenticatorSingleKey(publicKey, new AnySignature(new Secp256k1Signature(new Uint8Array(64)))); } } @@ -371,7 +375,7 @@ export function sign(args: { signer: Account; transaction: AnyRawTransaction }): new Ed25519Signature(signerSignature.toUint8Array()), ); case SigningScheme.SingleKey: - return new SingleKeyAuthenticator(signer.publicKey as AnyPublicKey, new AnySignature(signerSignature)); + return new AccountAuthenticatorSingleKey(signer.publicKey as AnyPublicKey, new AnySignature(signerSignature)); // TODO support MultiEd25519 default: throw new Error(`Cannot sign transaction, signing scheme ${signer.signingScheme} not supported`); @@ -422,8 +426,8 @@ export function generateSignedTransaction(args: { return new SignedTransaction(transactionToSubmit as RawTransaction, transactionAuthenticator).bcsToBytes(); } - if (accountAuthenticator instanceof SingleKeyAuthenticator) { - const transactionAuthenticator = new SingleSender(accountAuthenticator); + if (accountAuthenticator instanceof AccountAuthenticatorSingleKey) { + const transactionAuthenticator = new SingleSenderTransactionAuthenticator(accountAuthenticator); // return signed transaction return new SignedTransaction(transactionToSubmit as RawTransaction, transactionAuthenticator).bcsToBytes(); } diff --git a/src/transactions/types.ts b/src/transactions/types.ts index 6a460bf21..7a45b69c1 100644 --- a/src/transactions/types.ts +++ b/src/transactions/types.ts @@ -17,7 +17,6 @@ import { } from "./instances"; import { AnyNumber, HexInput, MoveStructType } from "../types"; import { TypeTag } from "./typeTag/typeTag"; -import { AnyPublicKey } from "../core/crypto/anyPublicKey"; export type EntryFunctionArgumentTypes = | Bool diff --git a/src/types/index.ts b/src/types/index.ts index 03b41d7ee..d5180fc17 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -87,7 +87,7 @@ export enum TransactionAuthenticatorVariant { MultiEd25519 = 1, MultiAgent = 2, FeePayer = 3, - SingleSender = 4, + SingleSenderTransactionAuthenticator = 4, } /** diff --git a/tests/e2e/api/account.test.ts b/tests/e2e/api/account.test.ts index 6735b3b1d..21c4515ae 100644 --- a/tests/e2e/api/account.test.ts +++ b/tests/e2e/api/account.test.ts @@ -198,7 +198,7 @@ describe("account api", () => { expect(accountCoinsCount).toBe(1); }); - test.only("lookupOriginalAccountAddress - Look up account address before key rotation", async () => { + test("lookupOriginalAccountAddress - Look up account address before key rotation", async () => { const config = new AptosConfig({ network: Network.LOCAL }); const aptos = new Aptos(config); const account = Account.generate(); diff --git a/tests/e2e/api/coin.test.ts b/tests/e2e/api/coin.test.ts index b5cb801ac..feff6645e 100644 --- a/tests/e2e/api/coin.test.ts +++ b/tests/e2e/api/coin.test.ts @@ -1,7 +1,7 @@ import { AptosConfig, Network, Aptos, Account, Deserializer, TypeTagStruct } from "../../../src"; import { waitForTransaction } from "../../../src/internal/transaction"; import { RawTransaction, TransactionPayloadEntryFunction } from "../../../src/transactions/instances"; -import { FUND_AMOUNT } from "../../unit/helper"; +import { FUND_AMOUNT, longTestTimeout } from "../../unit/helper"; describe("coin", () => { test("it generates a transfer coin transaction with AptosCoin coin type", async () => { @@ -47,28 +47,32 @@ describe("coin", () => { expect((typeArgs[0] as TypeTagStruct).value.name.identifier).toBe("type"); }); - test("it transfers APT coin amount from sender to recipient", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); - const sender = Account.generate(); - const recipient = Account.generate(); + test( + "it transfers APT coin amount from sender to recipient", + async () => { + const config = new AptosConfig({ network: Network.LOCAL }); + const aptos = new Aptos(config); + const sender = Account.generate(); + const recipient = Account.generate(); - await aptos.fundAccount({ accountAddress: sender.accountAddress.toString(), amount: FUND_AMOUNT }); - const senderCoinsBefore = await aptos.getAccountCoinsData({ accountAddress: sender.accountAddress.toString() }); + await aptos.fundAccount({ accountAddress: sender.accountAddress.toString(), amount: FUND_AMOUNT }); + const senderCoinsBefore = await aptos.getAccountCoinsData({ accountAddress: sender.accountAddress.toString() }); - const transaction = await aptos.transferCoinTransaction({ - sender, - recipient: recipient.accountAddress.toString(), - amount: 10, - }); - const response = await aptos.signAndSubmitTransaction({ signer: sender, transaction }); + const transaction = await aptos.transferCoinTransaction({ + sender, + recipient: recipient.accountAddress.toString(), + amount: 10, + }); + const response = await aptos.signAndSubmitTransaction({ signer: sender, transaction }); - await waitForTransaction({ aptosConfig: config, transactionHash: response.hash }); - const recipientCoins = await aptos.getAccountCoinsData({ accountAddress: recipient.accountAddress.toString() }); - const senderCoinsAfter = await aptos.getAccountCoinsData({ accountAddress: sender.accountAddress.toString() }); + await waitForTransaction({ aptosConfig: config, transactionHash: response.hash }); + const recipientCoins = await aptos.getAccountCoinsData({ accountAddress: recipient.accountAddress.toString() }); + const senderCoinsAfter = await aptos.getAccountCoinsData({ accountAddress: sender.accountAddress.toString() }); - expect(recipientCoins[0].amount).toBe(10); - expect(recipientCoins[0].asset_type).toBe("0x1::aptos_coin::AptosCoin"); - expect(senderCoinsAfter[0].amount).toBeLessThan(senderCoinsBefore[0].amount); - }); + expect(recipientCoins[0].amount).toBe(10); + expect(recipientCoins[0].asset_type).toBe("0x1::aptos_coin::AptosCoin"); + expect(senderCoinsAfter[0].amount).toBeLessThan(senderCoinsBefore[0].amount); + }, + longTestTimeout, + ); }); diff --git a/tests/e2e/api/token.test.ts b/tests/e2e/api/token.test.ts index 30abe2ba3..787d536ac 100644 --- a/tests/e2e/api/token.test.ts +++ b/tests/e2e/api/token.test.ts @@ -4,7 +4,7 @@ import { AptosConfig, Aptos, Account } from "../../../src"; import { waitForTransaction } from "../../../src/internal/transaction"; import { Network } from "../../../src/utils/apiEndpoints"; -import { FUND_AMOUNT } from "../../unit/helper"; +import { FUND_AMOUNT, longTestTimeout } from "../../unit/helper"; const config = new AptosConfig({ network: Network.LOCAL }); const aptos = new Aptos(config); @@ -55,7 +55,7 @@ describe("token api", () => { beforeAll(async () => { await setupCollection(); tokenAddress = await setupToken(); - }); + }, longTestTimeout); test("it gets token data for a token's address", async () => { const tokenData = await aptos.getTokenData({ tokenAddress }); diff --git a/tests/e2e/transaction/helper.ts b/tests/e2e/transaction/helper.ts index 82129f656..bc6b82f6d 100644 --- a/tests/e2e/transaction/helper.ts +++ b/tests/e2e/transaction/helper.ts @@ -38,7 +38,6 @@ export async function publishModule( }); return (await aptos.waitForTransaction({ transactionHash: txnHash.hash, - options: { indexerVersionCheck: false }, })) as UserTransactionResponse; } @@ -152,7 +151,9 @@ export const rawTransactionMultiAgentHelper = async ( }, }); - const response = await aptos.waitForTransaction({ transactionHash: transactionResponse.hash }); + const response = await aptos.waitForTransaction({ + transactionHash: transactionResponse.hash, + }); return response as UserTransactionResponse; }; diff --git a/tests/e2e/transaction/signTransaction.test.ts b/tests/e2e/transaction/signTransaction.test.ts index 592338cc3..d5f1cec76 100644 --- a/tests/e2e/transaction/signTransaction.test.ts +++ b/tests/e2e/transaction/signTransaction.test.ts @@ -1,148 +1,197 @@ -import { - AptosConfig, - Network, - Aptos, - Account, - Deserializer, - U64, - SigningScheme, - SigningSchemeInput, -} from "../../../src"; +import { AptosConfig, Network, Aptos, Account, Deserializer, U64, SigningSchemeInput } from "../../../src"; import { AccountAuthenticator, AccountAuthenticatorEd25519, - SingleKeyAuthenticator, + AccountAuthenticatorSingleKey, } from "../../../src/transactions/authenticator/account"; import { longTestTimeout } from "../../unit/helper"; import { fundAccounts, publishTransferPackage, singleSignerScriptBytecode } from "./helper"; +const config = new AptosConfig({ network: Network.LOCAL }); +const aptos = new Aptos(config); + describe("sign transaction", () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); - const senderAccount = Account.generate(); + const contractPublisherAccount = Account.generate(); + const singleSignerED25519SenderAccount = Account.generate(); + const legacyED25519SenderAccount = Account.generate({ legacy: true }); const recieverAccounts = [Account.generate(), Account.generate()]; - const senderSecp256k1Account = Account.generate(SigningSchemeInput.Secp256k1Ecdsa); + const singleSignerSecp256k1Account = Account.generate({ scheme: SigningSchemeInput.Secp256k1Ecdsa }); const secondarySignerAccount = Account.generate(); const feePayerAccount = Account.generate(); beforeAll(async () => { await fundAccounts(aptos, [ - senderAccount, - senderSecp256k1Account, + contractPublisherAccount, + singleSignerED25519SenderAccount, + legacyED25519SenderAccount, + singleSignerSecp256k1Account, ...recieverAccounts, secondarySignerAccount, feePayerAccount, ]); - await publishTransferPackage(aptos, senderAccount); + await publishTransferPackage(aptos, contractPublisherAccount); }, longTestTimeout); describe("it returns the current account authenticator", () => { - describe("ED25519", () => { - test.only("it signs a script transaction", async () => { + describe("Single Sender ED25519", () => { + test("it signs a script transaction", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderAccount.accountAddress.toString(), + sender: singleSignerED25519SenderAccount.accountAddress.toString(), data: { bytecode: singleSignerScriptBytecode, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ - signer: senderAccount, + signer: singleSignerED25519SenderAccount, transaction: rawTxn, }); expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); const authenticator = AccountAuthenticator.deserialize(deserializer); - expect(authenticator instanceof AccountAuthenticatorEd25519).toBeTruthy(); + expect(authenticator instanceof AccountAuthenticatorSingleKey).toBeTruthy(); }); test("it signs an entry function transaction", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderAccount.accountAddress.toString(), + sender: singleSignerED25519SenderAccount.accountAddress.toString(), data: { - function: `${senderAccount.accountAddress.toString()}::transfer::transfer`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ - signer: senderAccount, + signer: singleSignerED25519SenderAccount, transaction: rawTxn, }); expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); const authenticator = AccountAuthenticator.deserialize(deserializer); - expect(authenticator instanceof AccountAuthenticatorEd25519).toBeTruthy(); + expect(authenticator instanceof AccountAuthenticatorSingleKey).toBeTruthy(); }); test("it signs a multi sig transaction", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderAccount.accountAddress.toString(), + sender: singleSignerED25519SenderAccount.accountAddress.toString(), data: { multisigAddress: secondarySignerAccount.accountAddress, - function: `${senderAccount.accountAddress.toString()}::transfer::transfer`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ - signer: senderAccount, + signer: singleSignerED25519SenderAccount, transaction: rawTxn, }); expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); const authenticator = AccountAuthenticator.deserialize(deserializer); - expect(authenticator instanceof AccountAuthenticatorEd25519).toBeTruthy(); + expect(authenticator instanceof AccountAuthenticatorSingleKey).toBeTruthy(); }); }); - - describe.skip("Secp256k1", () => { + describe("Single Sender Secp256k1", () => { + test("it signs a script transaction", async () => { + const rawTxn = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + data: { + bytecode: singleSignerScriptBytecode, + functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + }, + }); + const accountAuthenticator = aptos.signTransaction({ + signer: singleSignerSecp256k1Account, + transaction: rawTxn, + }); + expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); + const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); + const authenticator = AccountAuthenticator.deserialize(deserializer); + expect(authenticator instanceof AccountAuthenticatorSingleKey).toBeTruthy(); + }); + test("it signs an entry function transaction", async () => { + const rawTxn = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + }, + }); + const accountAuthenticator = aptos.signTransaction({ + signer: singleSignerSecp256k1Account, + transaction: rawTxn, + }); + expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); + const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); + const authenticator = AccountAuthenticator.deserialize(deserializer); + expect(authenticator instanceof AccountAuthenticatorSingleKey).toBeTruthy(); + }); + test("it signs a multi sig transaction", async () => { + const rawTxn = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + data: { + multisigAddress: secondarySignerAccount.accountAddress, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + }, + }); + const accountAuthenticator = aptos.signTransaction({ + signer: singleSignerED25519SenderAccount, + transaction: rawTxn, + }); + expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); + const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); + const authenticator = AccountAuthenticator.deserialize(deserializer); + expect(authenticator instanceof AccountAuthenticatorSingleKey).toBeTruthy(); + }); + }); + describe("Legacy ED25519", () => { test("it signs a script transaction", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderSecp256k1Account.accountAddress.toString(), + sender: legacyED25519SenderAccount.accountAddress.toString(), data: { bytecode: singleSignerScriptBytecode, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ - signer: senderSecp256k1Account, + signer: legacyED25519SenderAccount, transaction: rawTxn, }); expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); const authenticator = AccountAuthenticator.deserialize(deserializer); - expect(authenticator instanceof SingleKeyAuthenticator).toBeTruthy(); + expect(authenticator instanceof AccountAuthenticatorEd25519).toBeTruthy(); }); test("it signs an entry function transaction", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderSecp256k1Account.accountAddress.toString(), + sender: legacyED25519SenderAccount.accountAddress.toString(), data: { - function: `${senderAccount.accountAddress.toString()}::transfer::transfer`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ - signer: senderSecp256k1Account, + signer: legacyED25519SenderAccount, transaction: rawTxn, }); expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); const authenticator = AccountAuthenticator.deserialize(deserializer); - expect(authenticator instanceof SingleKeyAuthenticator).toBeTruthy(); + expect(authenticator instanceof AccountAuthenticatorEd25519).toBeTruthy(); }); test("it signs a multi sig transaction", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderSecp256k1Account.accountAddress.toString(), + sender: legacyED25519SenderAccount.accountAddress.toString(), data: { multisigAddress: secondarySignerAccount.accountAddress, - function: `${senderAccount.accountAddress.toString()}::transfer::transfer`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ - signer: senderSecp256k1Account, + signer: legacyED25519SenderAccount, transaction: rawTxn, }); expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); const authenticator = AccountAuthenticator.deserialize(deserializer); - expect(authenticator instanceof SingleKeyAuthenticator).toBeTruthy(); + expect(authenticator instanceof AccountAuthenticatorEd25519).toBeTruthy(); }); }); }); diff --git a/tests/e2e/transaction/transactionBuilder.test.ts b/tests/e2e/transaction/transactionBuilder.test.ts index 61990d4cb..ad40ac22a 100644 --- a/tests/e2e/transaction/transactionBuilder.test.ts +++ b/tests/e2e/transaction/transactionBuilder.test.ts @@ -1,8 +1,8 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import { Account, Aptos, AptosConfig, Deserializer, Ed25519PrivateKey, Network, U64 } from "../../../src"; -import { AccountAuthenticator, AccountAuthenticatorEd25519 } from "../../../src/transactions/authenticator/account"; +import { Account, Aptos, AptosConfig, Deserializer, Network, U64 } from "../../../src"; +import { AccountAuthenticator, AccountAuthenticatorSingleKey } from "../../../src/transactions/authenticator/account"; import { FeePayerRawTransaction, MultiAgentRawTransaction, @@ -23,14 +23,21 @@ import { import { SignedTransaction } from "../../../src/transactions/instances/signedTransaction"; import { MoveObject } from "../../../src/bcs/serializable/moveStructs"; import { FUND_AMOUNT, longTestTimeout } from "../../unit/helper"; +import { fundAccounts, multiSignerScriptBytecode, publishTransferPackage } from "./helper"; +const config = new AptosConfig({ network: Network.LOCAL }); +const aptos = new Aptos(config); /* eslint-disable max-len */ describe("transaction builder", () => { + const contractPublisherAccount = Account.generate(); + beforeAll(async () => { + await fundAccounts(aptos, [contractPublisherAccount]); + await publishTransferPackage(aptos, contractPublisherAccount); + }, longTestTimeout); describe("generate transaction payload", () => { test("it generates a script transaction payload", async () => { const payload = generateTransactionPayload({ - bytecode: - "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", + bytecode: multiSignerScriptBytecode, functionArguments: [ new U64(100), new U64(200), @@ -44,14 +51,14 @@ describe("transaction builder", () => { test("it generates a multi sig transaction payload", async () => { const payload = generateTransactionPayload({ multisigAddress: Account.generate().accountAddress, - function: "0x1::aptos_account::transfer", + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, functionArguments: [], }); expect(payload instanceof TransactionPayloadMultisig).toBeTruthy(); }); test("it generates an entry function transaction payload", async () => { const payload = generateTransactionPayload({ - function: "0x1::aptos_account::transfer", + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, functionArguments: [], }); expect(payload instanceof TransactionPayloadEntryFunction).toBeTruthy(); @@ -59,13 +66,10 @@ describe("transaction builder", () => { }); describe("generate raw transaction", () => { test("it generates a raw transaction with script payload", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const payload = generateTransactionPayload({ - bytecode: - "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", + bytecode: multiSignerScriptBytecode, functionArguments: [ new U64(100), new U64(200), @@ -84,15 +88,13 @@ describe("transaction builder", () => { }); test("it generates a raw transaction with a multi sig payload", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const bob = Account.generate(); const payload = generateTransactionPayload({ multisigAddress: bob.accountAddress, - function: "0x1::aptos_account::transfer", - functionArguments: [bob.accountAddress, new U64(1)], + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new MoveObject(bob.accountAddress), new U64(1)], }); const rawTxn = await generateRawTransaction({ aptosConfig: config, @@ -104,14 +106,12 @@ describe("transaction builder", () => { }); test("it generates a raw transaction with an entry function payload", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const bob = Account.generate(); const payload = generateTransactionPayload({ - function: "0x1::aptos_account::transfer", - functionArguments: [bob.accountAddress, new U64(1)], + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new MoveObject(bob.accountAddress), new U64(1)], }); const rawTxn = await generateRawTransaction({ aptosConfig: config, @@ -124,13 +124,10 @@ describe("transaction builder", () => { }); describe("generate transaction", () => { test("it returns a serialized raw transaction", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const payload = generateTransactionPayload({ - bytecode: - "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", + bytecode: multiSignerScriptBytecode, functionArguments: [ new U64(100), new U64(200), @@ -150,14 +147,12 @@ describe("transaction builder", () => { }); test("it returns a serialized raw transaction and secondary signers addresses", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const bob = Account.generate(); const payload = generateTransactionPayload({ - function: "0x1::aptos_account::transfer", - functionArguments: [bob.accountAddress, new U64(1)], + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new MoveObject(bob.accountAddress), new U64(1)], }); const secondarySignerAddress = Account.generate(); const transaction = await buildTransaction({ @@ -176,14 +171,12 @@ describe("transaction builder", () => { }); test("it returns a serialized raw transaction and a fee payer address", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const bob = Account.generate(); const payload = generateTransactionPayload({ - function: "0x1::aptos_account::transfer", - functionArguments: [bob.accountAddress, new U64(1)], + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new MoveObject(bob.accountAddress), new U64(1)], }); const feePayer = Account.generate(); const transaction = await buildTransaction({ @@ -201,15 +194,12 @@ describe("transaction builder", () => { test( "it returns a serialized raw transaction, secondary signers addresses and a fee payer address", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const bob = Account.generate(); const payload = generateTransactionPayload({ - function: "0x1::aptos_account::transfer", - typeArguments: [], - functionArguments: [bob.accountAddress, new U64(1)], + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new MoveObject(bob.accountAddress), new U64(1)], }); const feePayer = Account.generate(); const secondarySignerAddress = Account.generate(); @@ -234,13 +224,10 @@ describe("transaction builder", () => { }); describe("generateSignedTransactionForSimulation", () => { test("it generates a signed raw transaction for simulation", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const payload = generateTransactionPayload({ - bytecode: - "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", + bytecode: multiSignerScriptBytecode, functionArguments: [ new U64(100), new U64(200), @@ -267,13 +254,10 @@ describe("transaction builder", () => { }); describe("sign", () => { test("it signs a raw transaction", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const payload = generateTransactionPayload({ - bytecode: - "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", + bytecode: multiSignerScriptBytecode, functionArguments: [ new U64(100), new U64(200), @@ -294,19 +278,17 @@ describe("transaction builder", () => { expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); const authenticator = AccountAuthenticator.deserialize(deserializer); - expect(authenticator instanceof AccountAuthenticatorEd25519).toBeTruthy(); + expect(authenticator instanceof AccountAuthenticatorSingleKey).toBeTruthy(); }); test("it signs a fee payer transaction", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const bob = Account.generate(); const payload = generateTransactionPayload({ multisigAddress: bob.accountAddress, - function: "0x1::aptos_account::transfer", - functionArguments: [bob.accountAddress, new U64(1)], + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new MoveObject(bob.accountAddress), new U64(1)], }); const transaction = await buildTransaction({ aptosConfig: config, @@ -321,18 +303,15 @@ describe("transaction builder", () => { expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); const authenticator = AccountAuthenticator.deserialize(deserializer); - expect(authenticator instanceof AccountAuthenticatorEd25519).toBeTruthy(); + expect(authenticator instanceof AccountAuthenticatorSingleKey).toBeTruthy(); }); test("it signs a multi agent transaction", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const bob = Account.generate(); const payload = generateTransactionPayload({ - bytecode: - "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", + bytecode: multiSignerScriptBytecode, functionArguments: [ new U64(100), new U64(200), @@ -354,18 +333,15 @@ describe("transaction builder", () => { expect(accountAuthenticator instanceof AccountAuthenticator).toBeTruthy(); const deserializer = new Deserializer(accountAuthenticator.bcsToBytes()); const authenticator = AccountAuthenticator.deserialize(deserializer); - expect(authenticator instanceof AccountAuthenticatorEd25519).toBeTruthy(); + expect(authenticator instanceof AccountAuthenticatorSingleKey).toBeTruthy(); }); }); describe("generateSignedTransaction", () => { test("it generates a single signer signed transaction", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const payload = generateTransactionPayload({ - bytecode: - "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", + bytecode: multiSignerScriptBytecode, functionArguments: [ new U64(100), new U64(200), @@ -391,16 +367,12 @@ describe("transaction builder", () => { }); test("it generates a multi agent signed transaction", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); - const bob = Account.fromPrivateKey( - new Ed25519PrivateKey("0x5aba8dab1c523be32bd4dafe2cc612f7f8050ce42a3322b60216ef67dc97768c"), - ); + const bob = await Account.generate(); const payload = generateTransactionPayload({ - function: "0x1::aptos_account::transfer", - functionArguments: [bob.accountAddress, new U64(1)], + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new MoveObject(bob.accountAddress), new U64(1)], }); const transaction = await buildTransaction({ aptosConfig: config, @@ -427,14 +399,12 @@ describe("transaction builder", () => { }); test("it generates a fee payer signed transaction", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const bob = Account.generate(); const payload = generateTransactionPayload({ - function: "0x1::aptos_account::transfer", - functionArguments: [bob.accountAddress, new U64(1)], + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new MoveObject(bob.accountAddress), new U64(1)], }); const transaction = await buildTransaction({ aptosConfig: config, @@ -462,14 +432,12 @@ describe("transaction builder", () => { }); describe("deriveTransactionType", () => { test("it derives the transaction type as a RawTransaction", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const bob = Account.generate(); const payload = generateTransactionPayload({ - function: "0x1::aptos_account::transfer", - functionArguments: [bob.accountAddress, new U64(1)], + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new MoveObject(bob.accountAddress), new U64(1)], }); const transaction = await buildTransaction({ aptosConfig: config, @@ -481,14 +449,12 @@ describe("transaction builder", () => { }); test("it derives the transaction type as a FeePayerRawTransaction", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const bob = Account.generate(); const payload = generateTransactionPayload({ - function: "0x1::aptos_account::transfer", - functionArguments: [bob.accountAddress, new U64(1)], + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new MoveObject(bob.accountAddress), new U64(1)], }); const transaction = await buildTransaction({ aptosConfig: config, @@ -502,14 +468,12 @@ describe("transaction builder", () => { }); test("it derives the transaction type as a MultiAgentRawTransaction", async () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const alice = Account.generate(); await aptos.fundAccount({ accountAddress: alice.accountAddress.toString(), amount: FUND_AMOUNT }); const bob = Account.generate(); const payload = generateTransactionPayload({ - function: "0x1::aptos_account::transfer", - functionArguments: [bob.accountAddress, new U64(1)], + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new MoveObject(bob.accountAddress), new U64(1)], }); const transaction = await buildTransaction({ aptosConfig: config, diff --git a/tests/e2e/transaction/transactionSimulation.test.ts b/tests/e2e/transaction/transactionSimulation.test.ts index c464e662f..3a2662ec4 100644 --- a/tests/e2e/transaction/transactionSimulation.test.ts +++ b/tests/e2e/transaction/transactionSimulation.test.ts @@ -1,66 +1,70 @@ -import { AptosConfig, Network, Aptos, Account, U64, SigningScheme, SigningSchemeInput } from "../../../src"; +import { AptosConfig, Network, Aptos, Account, U64, SigningSchemeInput } from "../../../src"; import { longTestTimeout } from "../../unit/helper"; import { fundAccounts, multiSignerScriptBytecode, publishTransferPackage, singleSignerScriptBytecode } from "./helper"; describe("transaction simulation", () => { const config = new AptosConfig({ network: Network.LOCAL }); const aptos = new Aptos(config); - const senderAccount = Account.generate(); + const contractPublisherAccount = Account.generate(); + const singleSignerED25519SenderAccount = Account.generate(); + const legacyED25519SenderAccount = Account.generate({ legacy: true }); + const singleSignerSecp256k1Account = Account.generate({ scheme: SigningSchemeInput.Secp256k1Ecdsa }); const recieverAccounts = [Account.generate(), Account.generate()]; - const senderSecp256k1Account = Account.generate(SigningSchemeInput.Secp256k1Ecdsa); const secondarySignerAccount = Account.generate(); const feePayerAccount = Account.generate(); beforeAll(async () => { await fundAccounts(aptos, [ - senderAccount, - senderSecp256k1Account, + contractPublisherAccount, + singleSignerED25519SenderAccount, + singleSignerSecp256k1Account, + legacyED25519SenderAccount, ...recieverAccounts, secondarySignerAccount, feePayerAccount, ]); - await publishTransferPackage(aptos, senderAccount); + await publishTransferPackage(aptos, contractPublisherAccount); }, longTestTimeout); - describe("ED25519", () => { + describe("Single Sender ED25519", () => { describe("single signer", () => { test("with script payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderAccount.accountAddress.toString(), + sender: singleSignerED25519SenderAccount.accountAddress.toString(), data: { bytecode: singleSignerScriptBytecode, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderAccount.publicKey, + signerPublicKey: singleSignerED25519SenderAccount.publicKey, transaction: rawTxn, }); expect(response.success).toBeTruthy(); }); test("with entry function payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderAccount.accountAddress.toString(), + sender: singleSignerED25519SenderAccount.accountAddress.toString(), data: { - function: `${senderAccount.accountAddress.toString()}::transfer::transfer`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderAccount.publicKey, + signerPublicKey: singleSignerED25519SenderAccount.publicKey, transaction: rawTxn, }); expect(response.success).toBeTruthy(); }); test("with multisig payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderAccount.accountAddress.toString(), + sender: singleSignerED25519SenderAccount.accountAddress.toString(), data: { multisigAddress: secondarySignerAccount.accountAddress, - function: `${senderAccount.accountAddress.toString()}::transfer::transfer`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderAccount.publicKey, + signerPublicKey: singleSignerED25519SenderAccount.publicKey, transaction: rawTxn, }); expect(response.success).toBeTruthy(); @@ -69,7 +73,7 @@ describe("transaction simulation", () => { describe("multi agent", () => { test("with script payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderAccount.accountAddress.toString(), + sender: singleSignerED25519SenderAccount.accountAddress.toString(), secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], data: { bytecode: multiSignerScriptBytecode, @@ -84,7 +88,7 @@ describe("transaction simulation", () => { }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderAccount.publicKey, + signerPublicKey: singleSignerED25519SenderAccount.publicKey, transaction: rawTxn, secondarySignersPublicKeys: [secondarySignerAccount.publicKey], }); @@ -95,10 +99,10 @@ describe("transaction simulation", () => { "with entry function payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderAccount.accountAddress.toString(), + sender: singleSignerED25519SenderAccount.accountAddress.toString(), secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], data: { - function: `${senderAccount.accountAddress.toString()}::transfer::two_by_two`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::two_by_two`, functionArguments: [ new U64(100), new U64(200), @@ -110,7 +114,7 @@ describe("transaction simulation", () => { }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderAccount.publicKey, + signerPublicKey: singleSignerED25519SenderAccount.publicKey, transaction: rawTxn, secondarySignersPublicKeys: [secondarySignerAccount.publicKey], }); @@ -122,7 +126,7 @@ describe("transaction simulation", () => { describe("fee payer", () => { test("with script payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderAccount.accountAddress.toString(), + sender: singleSignerED25519SenderAccount.accountAddress.toString(), feePayerAddress: feePayerAccount.accountAddress.toString(), data: { bytecode: singleSignerScriptBytecode, @@ -131,7 +135,7 @@ describe("transaction simulation", () => { }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderAccount.publicKey, + signerPublicKey: singleSignerED25519SenderAccount.publicKey, transaction: rawTxn, feePayerPublicKey: feePayerAccount.publicKey, }); @@ -139,15 +143,15 @@ describe("transaction simulation", () => { }); test("with entry function payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderAccount.accountAddress.toString(), + sender: singleSignerED25519SenderAccount.accountAddress.toString(), feePayerAddress: feePayerAccount.accountAddress.toString(), data: { - function: `${senderAccount.accountAddress.toString()}::transfer::transfer`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderAccount.publicKey, + signerPublicKey: singleSignerED25519SenderAccount.publicKey, transaction: rawTxn, feePayerPublicKey: feePayerAccount.publicKey, }); @@ -155,17 +159,17 @@ describe("transaction simulation", () => { }); test("with multisig payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderAccount.accountAddress.toString(), + sender: singleSignerED25519SenderAccount.accountAddress.toString(), feePayerAddress: feePayerAccount.accountAddress.toString(), data: { multisigAddress: secondarySignerAccount.accountAddress, - function: `${senderAccount.accountAddress.toString()}::transfer::transfer`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderAccount.publicKey, + signerPublicKey: singleSignerED25519SenderAccount.publicKey, transaction: rawTxn, feePayerPublicKey: feePayerAccount.publicKey, }); @@ -173,11 +177,11 @@ describe("transaction simulation", () => { }); test("with multi agent transaction", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderAccount.accountAddress.toString(), + sender: singleSignerED25519SenderAccount.accountAddress.toString(), secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], feePayerAddress: feePayerAccount.accountAddress.toString(), data: { - function: `${senderAccount.accountAddress.toString()}::transfer::two_by_two`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::two_by_two`, functionArguments: [ new U64(100), new U64(200), @@ -189,7 +193,7 @@ describe("transaction simulation", () => { }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderAccount.publicKey, + signerPublicKey: singleSignerED25519SenderAccount.publicKey, transaction: rawTxn, secondarySignersPublicKeys: [secondarySignerAccount.publicKey], feePayerPublicKey: feePayerAccount.publicKey, @@ -198,48 +202,225 @@ describe("transaction simulation", () => { }); }); }); + describe("Single Sender Secp256k1", () => { + describe("single signer", () => { + test("with script payload", async () => { + const rawTxn = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + data: { + bytecode: singleSignerScriptBytecode, + functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + }, + }); + const [response] = await aptos.simulateTransaction({ + signerPublicKey: singleSignerSecp256k1Account.publicKey, + transaction: rawTxn, + }); + expect(response.success).toBeTruthy(); + }); + test("with entry function payload", async () => { + const rawTxn = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + }, + }); + const [response] = await aptos.simulateTransaction({ + signerPublicKey: singleSignerSecp256k1Account.publicKey, + transaction: rawTxn, + }); + expect(response.success).toBeTruthy(); + }); + test("with multisig payload", async () => { + const rawTxn = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + data: { + multisigAddress: secondarySignerAccount.accountAddress, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + }, + }); + const [response] = await aptos.simulateTransaction({ + signerPublicKey: singleSignerSecp256k1Account.publicKey, + transaction: rawTxn, + }); + expect(response.success).toBeTruthy(); + }); + }); + describe("multi agent", () => { + test("with script payload", async () => { + const rawTxn = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], + data: { + bytecode: multiSignerScriptBytecode, + functionArguments: [ + new U64(BigInt(100)), + new U64(BigInt(200)), + recieverAccounts[0].accountAddress, + recieverAccounts[1].accountAddress, + new U64(BigInt(50)), + ], + }, + }); + + const [response] = await aptos.simulateTransaction({ + signerPublicKey: singleSignerSecp256k1Account.publicKey, + transaction: rawTxn, + secondarySignersPublicKeys: [secondarySignerAccount.publicKey], + }); + expect(response.success).toBeTruthy(); + }); + + test( + "with entry function payload", + async () => { + const rawTxn = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::two_by_two`, + functionArguments: [ + new U64(100), + new U64(200), + recieverAccounts[0].accountAddress, + recieverAccounts[1].accountAddress, + new U64(50), + ], + }, + }); + + const [response] = await aptos.simulateTransaction({ + signerPublicKey: singleSignerSecp256k1Account.publicKey, + transaction: rawTxn, + secondarySignersPublicKeys: [secondarySignerAccount.publicKey], + }); + expect(response.success).toBeTruthy(); + }, + longTestTimeout, + ); + }); + describe("fee payer", () => { + test("with script payload", async () => { + const rawTxn = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + feePayerAddress: feePayerAccount.accountAddress.toString(), + data: { + bytecode: singleSignerScriptBytecode, + functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + }, + }); + + const [response] = await aptos.simulateTransaction({ + signerPublicKey: singleSignerSecp256k1Account.publicKey, + transaction: rawTxn, + feePayerPublicKey: feePayerAccount.publicKey, + }); + expect(response.success).toBeTruthy(); + }); + test("with entry function payload", async () => { + const rawTxn = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + feePayerAddress: feePayerAccount.accountAddress.toString(), + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + }, + }); + const [response] = await aptos.simulateTransaction({ + signerPublicKey: singleSignerSecp256k1Account.publicKey, + transaction: rawTxn, + feePayerPublicKey: feePayerAccount.publicKey, + }); + expect(response.success).toBeTruthy(); + }); + test("with multisig payload", async () => { + const rawTxn = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + feePayerAddress: feePayerAccount.accountAddress.toString(), + data: { + multisigAddress: secondarySignerAccount.accountAddress, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, + functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + }, + }); + + const [response] = await aptos.simulateTransaction({ + signerPublicKey: singleSignerSecp256k1Account.publicKey, + transaction: rawTxn, + feePayerPublicKey: feePayerAccount.publicKey, + }); + expect(response.success).toBeTruthy(); + }); + test("with multi agent transaction", async () => { + const rawTxn = await aptos.generateTransaction({ + sender: singleSignerSecp256k1Account.accountAddress.toString(), + secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], + feePayerAddress: feePayerAccount.accountAddress.toString(), + data: { + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::two_by_two`, + functionArguments: [ + new U64(100), + new U64(200), + recieverAccounts[0].accountAddress, + recieverAccounts[1].accountAddress, + new U64(50), + ], + }, + }); - describe.skip("Secp256k1", () => { + const [response] = await aptos.simulateTransaction({ + signerPublicKey: singleSignerSecp256k1Account.publicKey, + transaction: rawTxn, + secondarySignersPublicKeys: [secondarySignerAccount.publicKey], + feePayerPublicKey: feePayerAccount.publicKey, + }); + expect(response.success).toBeTruthy(); + }); + }); + }); + describe("Legacy ED25519", () => { describe("single signer", () => { test("with script payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderSecp256k1Account.accountAddress.toString(), + sender: legacyED25519SenderAccount.accountAddress.toString(), data: { bytecode: singleSignerScriptBytecode, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderSecp256k1Account.publicKey, + signerPublicKey: legacyED25519SenderAccount.publicKey, transaction: rawTxn, }); expect(response.success).toBeTruthy(); }); test("with entry function payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderSecp256k1Account.accountAddress.toString(), + sender: legacyED25519SenderAccount.accountAddress.toString(), data: { - function: `${senderAccount.accountAddress.toString()}::transfer::transfer`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderSecp256k1Account.publicKey, + signerPublicKey: legacyED25519SenderAccount.publicKey, transaction: rawTxn, }); expect(response.success).toBeTruthy(); }); test("with multisig payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderSecp256k1Account.accountAddress.toString(), + sender: legacyED25519SenderAccount.accountAddress.toString(), data: { multisigAddress: secondarySignerAccount.accountAddress, - function: `${senderAccount.accountAddress.toString()}::transfer::transfer`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderSecp256k1Account.publicKey, + signerPublicKey: legacyED25519SenderAccount.publicKey, transaction: rawTxn, }); expect(response.success).toBeTruthy(); @@ -248,7 +429,7 @@ describe("transaction simulation", () => { describe("multi agent", () => { test("with script payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderSecp256k1Account.accountAddress.toString(), + sender: legacyED25519SenderAccount.accountAddress.toString(), secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], data: { bytecode: multiSignerScriptBytecode, @@ -263,7 +444,7 @@ describe("transaction simulation", () => { }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderSecp256k1Account.publicKey, + signerPublicKey: legacyED25519SenderAccount.publicKey, transaction: rawTxn, secondarySignersPublicKeys: [secondarySignerAccount.publicKey], }); @@ -274,10 +455,10 @@ describe("transaction simulation", () => { "with entry function payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderSecp256k1Account.accountAddress.toString(), + sender: legacyED25519SenderAccount.accountAddress.toString(), secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], data: { - function: `${senderAccount.accountAddress.toString()}::transfer::two_by_two`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::two_by_two`, functionArguments: [ new U64(100), new U64(200), @@ -289,7 +470,7 @@ describe("transaction simulation", () => { }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderSecp256k1Account.publicKey, + signerPublicKey: legacyED25519SenderAccount.publicKey, transaction: rawTxn, secondarySignersPublicKeys: [secondarySignerAccount.publicKey], }); @@ -301,7 +482,7 @@ describe("transaction simulation", () => { describe("fee payer", () => { test("with script payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderSecp256k1Account.accountAddress.toString(), + sender: legacyED25519SenderAccount.accountAddress.toString(), feePayerAddress: feePayerAccount.accountAddress.toString(), data: { bytecode: singleSignerScriptBytecode, @@ -310,7 +491,7 @@ describe("transaction simulation", () => { }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderSecp256k1Account.publicKey, + signerPublicKey: legacyED25519SenderAccount.publicKey, transaction: rawTxn, feePayerPublicKey: feePayerAccount.publicKey, }); @@ -318,15 +499,15 @@ describe("transaction simulation", () => { }); test("with entry function payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderSecp256k1Account.accountAddress.toString(), + sender: legacyED25519SenderAccount.accountAddress.toString(), feePayerAddress: feePayerAccount.accountAddress.toString(), data: { - function: `${senderAccount.accountAddress.toString()}::transfer::transfer`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderSecp256k1Account.publicKey, + signerPublicKey: legacyED25519SenderAccount.publicKey, transaction: rawTxn, feePayerPublicKey: feePayerAccount.publicKey, }); @@ -334,17 +515,17 @@ describe("transaction simulation", () => { }); test("with multisig payload", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderSecp256k1Account.accountAddress.toString(), + sender: legacyED25519SenderAccount.accountAddress.toString(), feePayerAddress: feePayerAccount.accountAddress.toString(), data: { multisigAddress: secondarySignerAccount.accountAddress, - function: `${senderAccount.accountAddress.toString()}::transfer::transfer`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, functionArguments: [new U64(1), recieverAccounts[0].accountAddress], }, }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderSecp256k1Account.publicKey, + signerPublicKey: legacyED25519SenderAccount.publicKey, transaction: rawTxn, feePayerPublicKey: feePayerAccount.publicKey, }); @@ -352,11 +533,11 @@ describe("transaction simulation", () => { }); test("with multi agent transaction", async () => { const rawTxn = await aptos.generateTransaction({ - sender: senderSecp256k1Account.accountAddress.toString(), + sender: legacyED25519SenderAccount.accountAddress.toString(), secondarySignerAddresses: [secondarySignerAccount.accountAddress.toString()], feePayerAddress: feePayerAccount.accountAddress.toString(), data: { - function: `${senderAccount.accountAddress.toString()}::transfer::two_by_two`, + function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::two_by_two`, functionArguments: [ new U64(100), new U64(200), @@ -368,7 +549,7 @@ describe("transaction simulation", () => { }); const [response] = await aptos.simulateTransaction({ - signerPublicKey: senderSecp256k1Account.publicKey, + signerPublicKey: legacyED25519SenderAccount.publicKey, transaction: rawTxn, secondarySignersPublicKeys: [secondarySignerAccount.publicKey], feePayerPublicKey: feePayerAccount.publicKey, diff --git a/tests/e2e/transaction/transactionSubmission.test.ts b/tests/e2e/transaction/transactionSubmission.test.ts index 8588e2a05..41820d140 100644 --- a/tests/e2e/transaction/transactionSubmission.test.ts +++ b/tests/e2e/transaction/transactionSubmission.test.ts @@ -1,38 +1,18 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import { - Account, - AptosConfig, - Network, - Aptos, - U64, - SigningScheme, - Deserializer, - SigningSchemeInput, - Ed25519PrivateKey, -} from "../../../src"; +import { Account, AptosConfig, Network, Aptos, U64, Deserializer, SigningSchemeInput } from "../../../src"; import { waitForTransaction } from "../../../src/internal/transaction"; import { RawTransaction, TransactionPayloadEntryFunction } from "../../../src/transactions/instances"; -import { ed25519, FUND_AMOUNT, longTestTimeout } from "../../unit/helper"; +import { longTestTimeout } from "../../unit/helper"; import { fundAccounts, multiSignerScriptBytecode, publishTransferPackage, singleSignerScriptBytecode } from "./helper"; -/** - * generate() // default to ed25519 - should be single_signer - * generate(secpk) // should be single_signer - * fromPrivateKey(ed25519) // should be single_signer - * fromPrivateKey(secpk) // should be single_signer - * fromLegacyPrivateKey(ed25519 | multied) // should be ed25519 - * fromPrivateKeyAndAddress(ed25519) // should be single_signer - * fromPrivateKeyAndAddress(secpk) // should be single_signer - * - */ const config = new AptosConfig({ network: Network.LOCAL }); const aptos = new Aptos(config); describe("transaction submission", () => { const contractPublisherAccount = Account.generate(); const singleSignerED25519SenderAccount = Account.generate(); - const legacyED25519SenderAccount = Account.fromPrivateKey(new Ed25519PrivateKey(ed25519.privateKey), config); + const legacyED25519SenderAccount = Account.generate({ legacy: true }); const receiverAccounts = [Account.generate(), Account.generate()]; const singleSignerSecp256k1Account = Account.generate({ scheme: SigningSchemeInput.Secp256k1Ecdsa }); const secondarySignerAccount = Account.generate(); @@ -50,7 +30,7 @@ describe("transaction submission", () => { await publishTransferPackage(aptos, contractPublisherAccount); }, longTestTimeout); describe("Single Sender ED25519", () => { - describe("single sender", () => { + describe("single signer", () => { test("with script payload", async () => { const transaction = await aptos.generateTransaction({ sender: singleSignerED25519SenderAccount.accountAddress.toString(), @@ -67,7 +47,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("single_sender"); @@ -87,7 +66,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("single_sender"); }); @@ -121,7 +99,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("multi_agent_signature"); }); @@ -156,7 +133,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("multi_agent_signature"); }, @@ -186,7 +162,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("fee_payer_signature"); }); @@ -211,7 +186,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("fee_payer_signature"); }); @@ -248,13 +222,12 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("fee_payer_signature"); }); }); }); - describe("Single Signer Secp256k1", () => { + describe("Single Sender Secp256k1", () => { describe("single signer", () => { test("with script payload", async () => { const transaction = await aptos.generateTransaction({ @@ -271,7 +244,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("single_sender"); }); @@ -290,7 +262,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("single_sender"); }); @@ -324,7 +295,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("multi_agent_signature"); }); @@ -359,7 +329,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("multi_agent_signature"); }, @@ -389,7 +358,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("fee_payer_signature"); }); @@ -414,7 +382,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("fee_payer_signature"); }); @@ -451,7 +418,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("fee_payer_signature"); }); @@ -474,7 +440,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("ed25519_signature"); }); @@ -493,7 +458,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("ed25519_signature"); }); @@ -527,7 +491,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("multi_agent_signature"); }); @@ -562,7 +525,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("multi_agent_signature"); }, @@ -592,7 +554,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("fee_payer_signature"); }); @@ -617,7 +578,6 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("fee_payer_signature"); }); @@ -654,15 +614,12 @@ describe("transaction submission", () => { await waitForTransaction({ aptosConfig: config, transactionHash: response.hash, - options: { indexerVersionCheck: false }, }); expect(response.signature?.type).toBe("fee_payer_signature"); }); }); }); describe("publish move module", () => { - const config = new AptosConfig({ network: Network.LOCAL }); - const aptos = new Aptos(config); const account = Account.generate(); const metadataBytes = // eslint-disable-next-line max-len @@ -700,7 +657,10 @@ describe("transaction submission", () => { signer: account, transaction, }); - await waitForTransaction({ aptosConfig: config, transactionHash: response.hash }); + await waitForTransaction({ + aptosConfig: config, + transactionHash: response.hash, + }); const accountModules = await aptos.getAccountModules({ accountAddress: account.accountAddress.toString() }); expect(accountModules[0].bytecode).toEqual(`0x${byteCode}`); }); diff --git a/tests/unit/authenticationKey.test.ts b/tests/unit/authenticationKey.test.ts index 5ba719aca..47b6f9b1e 100644 --- a/tests/unit/authenticationKey.test.ts +++ b/tests/unit/authenticationKey.test.ts @@ -27,7 +27,7 @@ describe("AuthenticationKey", () => { ); }); - it.only("should create AuthenticationKey from Ed25519PublicKey", () => { + it("should create AuthenticationKey from Ed25519PublicKey", () => { const publicKey = new Ed25519PublicKey(ed25519.publicKey); const authKey = AuthenticationKey.fromPublicKey({ publicKey }); expect(authKey).toBeInstanceOf(AuthenticationKey); From 75d582ef248c9303f8dfa9b9a9293103061a8e8a Mon Sep 17 00:00:00 2001 From: maayan Date: Mon, 23 Oct 2023 18:20:47 -0700 Subject: [PATCH 07/10] address feedback --- src/api/account.ts | 21 +++++- src/core/account.ts | 124 +++++++++++++------------------- src/core/authenticationKey.ts | 36 +++++----- src/core/crypto/anyPublicKey.ts | 12 +++- src/core/crypto/index.ts | 1 - src/core/crypto/singleKey.ts | 78 -------------------- src/internal/account.ts | 68 ++++++++++++++++++ src/internal/transaction.ts | 2 +- src/types/index.ts | 25 +++++++ tests/e2e/api/account.test.ts | 31 +++++++- tests/unit/account.test.ts | 19 ++--- 11 files changed, 233 insertions(+), 184 deletions(-) delete mode 100644 src/core/crypto/singleKey.ts diff --git a/src/api/account.ts b/src/api/account.ts index 6c3427699..e680a621c 100644 --- a/src/api/account.ts +++ b/src/api/account.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { AptosConfig } from "./aptosConfig"; -import { AccountAddress } from "../core"; +import { AccountAddress, PrivateKey, Account as AccountModule } from "../core"; import { AccountData, GetAccountCoinsDataResponse, @@ -21,6 +21,7 @@ import { TransactionResponse, } from "../types"; import { + deriveAccountFromPrivateKey, getAccountCoinsCount, getAccountCoinsData, getAccountCollectionsWithOwnedTokens, @@ -358,4 +359,22 @@ export class Account { ...args, }); } + + /** + * Derives an account by providing a private key. + * This functions resolves the provided private key type and derives the public key from it. + * + * If the privateKey is a Secp256k1 type, it derives the account using the derived public key and + * auth key using the SingleKey scheme locally. + * + * If the privateKey is a ED25519 type, it looksup the auth key on chain, and uses it to resolve + * whether it is a Legacy ED25519 key or a Unified ED25519 key. It then derives the account based + * on that. + * + * @param args.privateKey An account private key + * @returns Account type + */ + async deriveAccountFromPrivateKey(args: { privateKey: PrivateKey }): Promise { + return deriveAccountFromPrivateKey({ aptosConfig: this.config, ...args }); + } } diff --git a/src/core/account.ts b/src/core/account.ts index ebc8f034b..9cbf3f2ec 100644 --- a/src/core/account.ts +++ b/src/core/account.ts @@ -8,17 +8,31 @@ import { Ed25519PrivateKey, Ed25519PublicKey } from "./crypto/ed25519"; import { MultiEd25519PublicKey } from "./crypto/multiEd25519"; import { Secp256k1PrivateKey, Secp256k1PublicKey } from "./crypto/secp256k1"; import { Hex } from "./hex"; -import { HexInput, SigningScheme, SigningSchemeInput } from "../types"; +import { GenerateAccount, HexInput, SigningScheme, SigningSchemeInput } from "../types"; import { derivePrivateKeyFromMnemonic, KeyType } from "../utils/hdKey"; import { AnyPublicKey } from "./crypto/anyPublicKey"; -import { getInfo, lookupOriginalAccountAddress } from "../internal/account"; -import { AptosConfig } from "../api/aptosConfig"; /** * Class for creating and managing account on Aptos network * * Use this class to create accounts, sign transactions, and more. * Note: Creating an account instance does not create the account on-chain. + * + * Since [AIP-55](https://github.com/aptos-foundation/AIPs/pull/263) Aptos supports + * `Legacy` and `Unified` authentications. + * + * @Legacy includes `ED25519` and `MultiED25519` + * @Unified includes `SingleSender` and `MultiSender`, where currently + * `SingleSender` supports `ED25519` and `Secp256k1`, and `MultiSender` supports + * `MultiED25519`. + * + * In TypeScript SDK, we support all of these options + * @generate default to generate Unified keys, with an optional `legacy` boolean argument + * that lets you generate new keys conforming to the Legacy authentication. + * @fromPrivateKey derives an account by a provided private key and address, with an optional + * `legacy` boolean argument that lets you generate new keys conforming to the Legacy authentication. + * @fromDerivationPath derives an account with bip44 path and mnemonics, + * */ export class Account { /** @@ -48,8 +62,8 @@ export class Account { * * @param args.privateKey PrivateKey - private key of the account * @param args.address AccountAddress - address of the account - * @param args.legacy optional. If set to true, would create a legacy Ed25519 signing keys. Default - * is Single Sender signing key + * @param args.legacy optional. If set to true, would create a Legacy signing keys. Default + * is Unified signing key * * This method is private because it should only be called by the factory static methods. * @returns Account @@ -82,16 +96,33 @@ export class Account { } /** - * Derives an account with random private key and address + * Derives an account with random private key and address. + * Default generation is using the Unified flow with ED25519 key + * + * @param args optional. Unify GenerateAccount type for Legacy and Unified keys + * + * Account input type to generate an account using Legacy + * Ed25519 or MultiEd25519 keys or without a specified `scheme`. + * ``` + * GenerateAccountWithLegacyKey = { + * scheme?: SigningSchemeInput.Ed25519 | SigningSchemeInput.MultiEd25519; + * legacy: true; + * }; + * ``` * - * @param args.scheme optional. SigningScheme - type of SigningScheme to use. Default to Ed25519 - * Currently only Ed25519 and Secp256k1 are supported - * @param args.legacy optional. If set to true, would create a legacy Ed25519 signing keys. Default - * is Single Sender signing key + * Account input type to generate an account using Unified + * Secp256k1Ecdsa key + * In this case `legacy` is always false + * ``` + * GenerateAccountWithUnifiedKey = { + * scheme: SigningSchemeInput.Secp256k1Ecdsa; + * legacy?: false; + * }; + * ``` * * @returns Account with the given signing scheme */ - static generate(args?: { scheme?: SigningSchemeInput; legacy?: boolean }): Account { + static generate(args?: GenerateAccount): Account { let privateKey: PrivateKey; switch (args?.scheme) { @@ -119,73 +150,16 @@ export class Account { /** * Derives an account with provided private key * - * NOTE: This function derives the public and auth keys - * from the provided private key and then creates an Account - * based on the Account configured signing scheme - - * Legacy ED25519 or Single Sender - * * @param privateKey PrivateKey - private key of the account - * @param aptosConfig AptosConfig type + * @param address The account address + * @param legacy optional. If set to true, would create a Legacy signing keys. Default + * is Unified signing key * - * @returns Promise + * @returns Account */ - static async fromPrivateKey(privateKey: PrivateKey, aptosConfig: AptosConfig): Promise { - const publicKey = new AnyPublicKey(privateKey.publicKey()); - - if (privateKey instanceof Secp256k1PrivateKey) { - // private key is secp256k1, therefore we know it for sure uses a single signer key - const authKey = AuthenticationKey.fromBytesAndScheme({ publicKey, scheme: 2 }); - const address = new AccountAddress({ data: authKey.toUint8Array() }); - return new Account({ privateKey, address }); - } - - if (privateKey instanceof Ed25519PrivateKey) { - // lookup single sender ed25519 - const SingleSenderTransactionAuthenticatorAuthKey = AuthenticationKey.fromBytesAndScheme({ - publicKey, - scheme: SigningScheme.SingleKey, - }); - const isSingleSenderTransactionAuthenticator = await Account.lookupAddress( - SingleSenderTransactionAuthenticatorAuthKey, - aptosConfig, - ); - if (isSingleSenderTransactionAuthenticator) { - const address = new AccountAddress({ data: SingleSenderTransactionAuthenticatorAuthKey.toUint8Array() }); - return new Account({ privateKey, address }); - } - // lookup legacy ed25519 - const legacyAuthKey = AuthenticationKey.fromBytesAndScheme({ publicKey, scheme: SigningScheme.Ed25519 }); - const isLegacyEd25519 = await Account.lookupAddress(legacyAuthKey, aptosConfig); - if (isLegacyEd25519) { - const address = new AccountAddress({ data: legacyAuthKey.toUint8Array() }); - return new Account({ privateKey, address, legacy: true }); - } - } - - // if we are here, it means we couldn't find an address with an - // auth key that matches the provided private key - throw new Error(`Can't derive account from private key ${privateKey}`); - } - - private static async lookupAddress(authKey: AuthenticationKey, aptosConfig: AptosConfig): Promise { - const accountAddress = await lookupOriginalAccountAddress({ - aptosConfig, - authenticationKey: authKey.toString(), - }); - - try { - await getInfo({ - aptosConfig, - accountAddress: accountAddress.toString(), - }); - return true; - } catch (error: any) { - // account not found - if (error.code === 404) { - return false; - } - throw new Error(`Error while looking for an account info ${accountAddress.toString()} `); - } + static fromPrivateKey(args: { privateKey: PrivateKey; address: AccountAddress; legacy?: boolean }): Account { + const { privateKey, address, legacy } = args; + return new Account({ privateKey, address, legacy }); } /** diff --git a/src/core/authenticationKey.ts b/src/core/authenticationKey.ts index 23e3450a8..e2f2667f8 100644 --- a/src/core/authenticationKey.ts +++ b/src/core/authenticationKey.ts @@ -53,27 +53,31 @@ export class AuthenticationKey { * This allows for the creation of AuthenticationKeys that are not derived from Public Keys directly * @param args */ - public static fromBytesAndScheme(args: { publicKey: PublicKey; scheme: AuthenticationKeyScheme }) { + public static fromPublicKeyAndScheme(args: { publicKey: PublicKey; scheme: AuthenticationKeyScheme }) { const { publicKey, scheme } = args; + let authKeyBytes: Uint8Array; - // TODO - check for single key or multi key - if (scheme === SigningScheme.SingleKey) { - const newBytes = publicKey.bcsToBytes(); - const authKeyBytes = new Uint8Array([...newBytes, scheme]); - const hash = sha3Hash.create(); - hash.update(authKeyBytes); - const hashDigest = hash.digest(); - return new AuthenticationKey({ data: hashDigest }); + // TODO - support multied25519 key and MultiKey + switch (scheme) { + case SigningScheme.SingleKey: { + const singleKeyBytes = publicKey.bcsToBytes(); + authKeyBytes = new Uint8Array([...singleKeyBytes, scheme]); + break; + } + case SigningScheme.Ed25519: + case SigningScheme.MultiEd25519: { + const ed25519PublicKeyBytes = publicKey.toUint8Array(); + const inputBytes = Hex.fromHexInput(ed25519PublicKeyBytes).toUint8Array(); + authKeyBytes = new Uint8Array([...inputBytes, scheme]); + break; + } + default: + throw new Error(`Scheme ${scheme} is not supported`); } - const bytes = publicKey.toUint8Array(); - const inputBytes = Hex.fromHexInput(bytes).toUint8Array(); - const authKeyBytes = new Uint8Array(inputBytes.length + 1); - authKeyBytes.set(inputBytes); - authKeyBytes.set([scheme], inputBytes.length); + const hash = sha3Hash.create(); hash.update(authKeyBytes); const hashDigest = hash.digest(); - return new AuthenticationKey({ data: hashDigest }); } @@ -99,7 +103,7 @@ export class AuthenticationKey { throw new Error("No supported authentication scheme for public key"); } - return AuthenticationKey.fromBytesAndScheme({ publicKey, scheme }); + return AuthenticationKey.fromPublicKeyAndScheme({ publicKey, scheme }); } /** diff --git a/src/core/crypto/anyPublicKey.ts b/src/core/crypto/anyPublicKey.ts index 38240acfd..b21c1e296 100644 --- a/src/core/crypto/anyPublicKey.ts +++ b/src/core/crypto/anyPublicKey.ts @@ -40,16 +40,24 @@ export class AnyPublicKey extends PublicKey { */ verifySignature(args: { message: HexInput; signature: AnySignature }): boolean { const { message, signature } = args; - if (this.publicKey instanceof Ed25519PublicKey && signature.signature instanceof Ed25519Signature) { + if (this.isED25519Signature(signature)) { return this.publicKey.verifySignature({ message, signature: signature.signature }); // eslint-disable-next-line no-else-return - } else if (this.publicKey instanceof Secp256k1PublicKey && signature.signature instanceof Secp256k1Signature) { + } else if (this.isSecp256k1Signature(signature)) { return this.publicKey.verifySignature({ message, signature: signature.signature }); } else { throw new Error("Unknown public key type"); } } + isED25519Signature(signature: AnySignature): boolean { + return this.publicKey instanceof Ed25519PublicKey && signature.signature instanceof Ed25519Signature; + } + + isSecp256k1Signature(signature: AnySignature): boolean { + return this.publicKey instanceof Secp256k1PublicKey && signature.signature instanceof Secp256k1Signature; + } + serialize(serializer: Serializer): void { if (this.publicKey instanceof Ed25519PublicKey) { serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Ed25519); diff --git a/src/core/crypto/index.ts b/src/core/crypto/index.ts index 819681567..364b66815 100644 --- a/src/core/crypto/index.ts +++ b/src/core/crypto/index.ts @@ -5,4 +5,3 @@ export * from "./asymmetricCrypto"; export * from "./ed25519"; export * from "./multiEd25519"; export * from "./secp256k1"; -export * from "./singleKey"; diff --git a/src/core/crypto/singleKey.ts b/src/core/crypto/singleKey.ts deleted file mode 100644 index ae58c5bc1..000000000 --- a/src/core/crypto/singleKey.ts +++ /dev/null @@ -1,78 +0,0 @@ -import nacl from "tweetnacl"; -import { Serializer, Deserializer } from "../../bcs"; -import { HexInput } from "../../types"; -import { Hex } from "../hex"; -import { PublicKey } from "./asymmetricCrypto"; -import { Ed25519Signature, Ed25519PublicKey } from "./ed25519"; - -export class SingleKey extends PublicKey { - /** - * Length of an Ed25519 public key - */ - static readonly LENGTH: number = 65; - - /** - * Bytes of the public key - * @private - */ - private readonly key: Hex; - - /** - * Create a new PublicKey instance from a Uint8Array or String. - * - * @param hexInput A HexInput (string or Uint8Array) - */ - constructor(hexInput: HexInput) { - super(); - - const hex = Hex.fromHexInput(hexInput); - if (hex.toUint8Array().length !== SingleKey.LENGTH) { - throw new Error(`PublicKey length should be ${SingleKey.LENGTH}`); - } - this.key = hex; - } - - /** - * Get the public key in bytes (Uint8Array). - * - * @returns Uint8Array representation of the public key - */ - toUint8Array(): Uint8Array { - return this.key.toUint8Array(); - } - - /** - * Get the public key as a hex string with the 0x prefix. - * - * @returns string representation of the public key - */ - toString(): string { - return this.key.toString(); - } - - /** - * Verifies a signed data with a public key - * @param args.message a signed message - * @param args.signature the signature of the message - */ - verifySignature(args: { message: HexInput; signature: Ed25519Signature }): boolean { - const { message, signature } = args; - const rawMessage = Hex.fromHexInput(message).toUint8Array(); - const rawSignature = Hex.fromHexInput(signature.toUint8Array()).toUint8Array(); - return nacl.sign.detached.verify(rawMessage, rawSignature, this.key.toUint8Array()); - } - - serialize(serializer: Serializer): void { - serializer.serializeBytes(this.key.toUint8Array()); - } - - static deserialize(deserializer: Deserializer): Ed25519PublicKey { - const bytes = deserializer.deserializeBytes(); - return new Ed25519PublicKey(bytes); - } - - static load(deserializer: Deserializer): Ed25519PublicKey { - const bytes = deserializer.deserializeBytes(); - return new Ed25519PublicKey(bytes); - } -} diff --git a/src/internal/account.ts b/src/internal/account.ts index 7bb65f428..168dd11d0 100644 --- a/src/internal/account.ts +++ b/src/internal/account.ts @@ -11,6 +11,8 @@ import { AptosConfig } from "../api/aptosConfig"; import { AptosApiError, getAptosFullNode, paginateWithCursor } from "../client"; import { AccountAddress } from "../core/accountAddress"; +import { Account } from "../core/account"; +import { PrivateKey } from "../core/crypto/asymmetricCrypto"; import { Hex } from "../core/hex"; import { getTableItem, queryIndexer } from "./general"; import { @@ -27,6 +29,7 @@ import { MoveResourceType, OrderBy, PaginationArgs, + SigningScheme, TokenStandard, TransactionResponse, } from "../types"; @@ -51,6 +54,8 @@ import { GetAccountTransactionsCount, } from "../types/generated/queries"; import { memoizeAsync } from "../utils/memoize"; +import { Secp256k1PrivateKey, AuthenticationKey, Ed25519PrivateKey } from "../core"; +import { AnyPublicKey } from "../core/crypto/anyPublicKey"; export async function getInfo(args: { aptosConfig: AptosConfig; accountAddress: HexInput }): Promise { const { aptosConfig, accountAddress } = args; @@ -486,3 +491,66 @@ export async function getAccountOwnedObjects(args: { return data.current_objects; } + +export async function deriveAccountFromPrivateKey(args: { + aptosConfig: AptosConfig; + privateKey: PrivateKey; +}): Promise { + const { aptosConfig, privateKey } = args; + const publicKey = new AnyPublicKey(privateKey.publicKey()); + + if (privateKey instanceof Secp256k1PrivateKey) { + // private key is secp256k1, therefore we know it for sure uses a single signer key + const authKey = AuthenticationKey.fromPublicKeyAndScheme({ publicKey, scheme: SigningScheme.SingleKey }); + const address = new AccountAddress({ data: authKey.toUint8Array() }); + return Account.fromPrivateKey({ privateKey, address }); + } + + if (privateKey instanceof Ed25519PrivateKey) { + // lookup single sender ed25519 + const SingleSenderTransactionAuthenticatorAuthKey = AuthenticationKey.fromPublicKeyAndScheme({ + publicKey, + scheme: SigningScheme.SingleKey, + }); + const isSingleSenderTransactionAuthenticator = await isAccountExist({ + authKey: SingleSenderTransactionAuthenticatorAuthKey, + aptosConfig, + }); + if (isSingleSenderTransactionAuthenticator) { + const address = new AccountAddress({ data: SingleSenderTransactionAuthenticatorAuthKey.toUint8Array() }); + return Account.fromPrivateKey({ privateKey, address }); + } + // lookup legacy ed25519 + const legacyAuthKey = AuthenticationKey.fromPublicKeyAndScheme({ publicKey, scheme: SigningScheme.Ed25519 }); + const isLegacyEd25519 = await isAccountExist({ authKey: legacyAuthKey, aptosConfig }); + if (isLegacyEd25519) { + const address = new AccountAddress({ data: legacyAuthKey.toUint8Array() }); + return Account.fromPrivateKey({ privateKey, address, legacy: true }); + } + } + // if we are here, it means we couldn't find an address with an + // auth key that matches the provided private key + throw new Error(`Can't derive account from private key ${privateKey}`); +} + +export async function isAccountExist(args: { aptosConfig: AptosConfig; authKey: AuthenticationKey }): Promise { + const { aptosConfig, authKey } = args; + const accountAddress = await lookupOriginalAccountAddress({ + aptosConfig, + authenticationKey: authKey.toString(), + }); + + try { + await getInfo({ + aptosConfig, + accountAddress: accountAddress.toString(), + }); + return true; + } catch (error: any) { + // account not found + if (error.status === 404) { + return false; + } + throw new Error(`Error while looking for an account info ${accountAddress.toString()}`); + } +} diff --git a/src/internal/transaction.ts b/src/internal/transaction.ts index 17aa70769..6df384920 100644 --- a/src/internal/transaction.ts +++ b/src/internal/transaction.ts @@ -104,7 +104,7 @@ export async function waitForTransaction(args: { const { aptosConfig, transactionHash, options } = args; const timeoutSecs = options?.timeoutSecs ?? DEFAULT_TXN_TIMEOUT_SEC; const checkSuccess = options?.checkSuccess ?? true; - const indexerVersionCheck = options?.indexerVersionCheck ?? true; + const indexerVersionCheck = options?.indexerVersionCheck ?? false; let isPending = true; let timeElapsed = 0; diff --git a/src/types/index.ts b/src/types/index.ts index d5180fc17..9cef582d0 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -995,3 +995,28 @@ export type WaitForTransactionOptions = { checkSuccess?: boolean; indexerVersionCheck?: boolean; }; +/** + * Account input type to generate an account using Legacy + * Ed25519 or MultiEd25519 keys or without a specified `scheme`. + * If `scheme` is not specified, we default to ED25519 + * In this case `legacy` is always true + */ +export type GenerateAccountWithLegacyKey = { + scheme?: SigningSchemeInput.Ed25519 | SigningSchemeInput.MultiEd25519; + legacy: true; +}; + +/** + * Account input type to generate an account using Unified + * Secp256k1Ecdsa key + * In this case `legacy` is always false + */ +export type GenerateAccountWithUnifiedKey = { + scheme: SigningSchemeInput.Secp256k1Ecdsa; + legacy?: false; +}; + +/** + * Unify GenerateAccount type for Legacy and Unified keys + */ +export type GenerateAccount = GenerateAccountWithLegacyKey | GenerateAccountWithUnifiedKey; diff --git a/tests/e2e/api/account.test.ts b/tests/e2e/api/account.test.ts index 21c4515ae..b4c696c32 100644 --- a/tests/e2e/api/account.test.ts +++ b/tests/e2e/api/account.test.ts @@ -1,7 +1,7 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import { Account, Aptos, AptosConfig, Network, U64 } from "../../../src"; +import { Account, Aptos, AptosConfig, Network, SigningSchemeInput, U64 } from "../../../src"; describe("account api", () => { const FUND_AMOUNT = 100_000_000; @@ -211,6 +211,35 @@ describe("account api", () => { }); expect(lookupAccount.toString()).toBe(account.accountAddress.toString()); }); + + describe("it derives an account from a private key", () => { + test("single sender ed25519", async () => { + const config = new AptosConfig({ network: Network.LOCAL }); + const aptos = new Aptos(config); + const account = Account.generate(); + await aptos.fundAccount({ accountAddress: account.accountAddress.toString(), amount: 100 }); + + const derivedAccount = await aptos.deriveAccountFromPrivateKey({ privateKey: account.privateKey }); + expect(derivedAccount).toStrictEqual(account); + }); + test("single sender secp256k1", async () => { + const config = new AptosConfig({ network: Network.LOCAL }); + const aptos = new Aptos(config); + const account = Account.generate({ scheme: SigningSchemeInput.Secp256k1Ecdsa }); + + const derivedAccount = await aptos.deriveAccountFromPrivateKey({ privateKey: account.privateKey }); + expect(derivedAccount).toStrictEqual(account); + }); + test("legacy ed25519", async () => { + const config = new AptosConfig({ network: Network.LOCAL }); + const aptos = new Aptos(config); + const account = Account.generate({ legacy: true }); + await aptos.fundAccount({ accountAddress: account.accountAddress.toString(), amount: 100 }); + + const derivedAccount = await aptos.deriveAccountFromPrivateKey({ privateKey: account.privateKey }); + expect(derivedAccount).toStrictEqual(account); + }); + }); }); describe("fetch data with account address as Uint8Array", () => { diff --git a/tests/unit/account.test.ts b/tests/unit/account.test.ts index 91e67ba8c..d1b3e8fc2 100644 --- a/tests/unit/account.test.ts +++ b/tests/unit/account.test.ts @@ -3,11 +3,10 @@ import { Account, - AptosConfig, + AccountAddress, Ed25519PrivateKey, Ed25519PublicKey, Hex, - Network, Secp256k1PrivateKey, Secp256k1PublicKey, SigningScheme, @@ -18,7 +17,6 @@ import { AnyPublicKey } from "../../src/core/crypto/anyPublicKey"; import { ed25519, secp256k1TestObject, singleSignerED25519, wallet } from "./helper"; describe("Account", () => { - const config = new AptosConfig({ network: Network.LOCAL }); describe("generate", () => { it("should create an instance of Account when Secp256k1 scheme is specified", () => { // Account with Ed25519 SingleKey scheme @@ -50,30 +48,33 @@ describe("Account", () => { }); }); describe("fromPrivateKey", () => { - it("derives the correct account from a legacy ed25519 private key", async () => { + it("derives the correct account from a legacy ed25519 private key", () => { const { privateKey: privateKeyBytes, publicKey, address } = ed25519; const privateKey = new Ed25519PrivateKey(privateKeyBytes); - const newAccount = await Account.fromPrivateKey(privateKey, config); + const accountAddress = AccountAddress.fromHexInput(address); + const newAccount = Account.fromPrivateKey({ privateKey, address: accountAddress, legacy: true }); expect(newAccount).toBeInstanceOf(Account); expect((newAccount.privateKey as Ed25519PrivateKey).toString()).toEqual(privateKey.toString()); expect((newAccount.publicKey as Ed25519PublicKey).toString()).toEqual(new Ed25519PublicKey(publicKey).toString()); expect(newAccount.accountAddress.toString()).toEqual(address); }); - it("derives the correct account from a single signer ed25519 private key", async () => { + it("derives the correct account from a single signer ed25519 private key", () => { const { privateKey: privateKeyBytes, publicKey, address } = singleSignerED25519; const privateKey = new Ed25519PrivateKey(privateKeyBytes); - const newAccount = await Account.fromPrivateKey(privateKey, config); + const accountAddress = AccountAddress.fromHexInput(address); + const newAccount = Account.fromPrivateKey({ privateKey, address: accountAddress }); expect(newAccount).toBeInstanceOf(Account); expect((newAccount.privateKey as Ed25519PrivateKey).toString()).toEqual(privateKey.toString()); expect((newAccount.publicKey as Ed25519PublicKey).toString()).toEqual(new Ed25519PublicKey(publicKey).toString()); expect(newAccount.accountAddress.toString()).toEqual(address); }); - it("derives the correct account from a single signer secp256k1 private key", async () => { + it("derives the correct account from a single signer secp256k1 private key", () => { const { privateKey: privateKeyBytes, publicKey, address } = secp256k1TestObject; const privateKey = new Secp256k1PrivateKey(privateKeyBytes); - const newAccount = await Account.fromPrivateKey(privateKey, config); + const accountAddress = AccountAddress.fromHexInput(address); + const newAccount = Account.fromPrivateKey({ privateKey, address: accountAddress }); expect(newAccount).toBeInstanceOf(Account); expect((newAccount.privateKey as Secp256k1PrivateKey).toString()).toEqual(privateKey.toString()); expect((newAccount.publicKey as Secp256k1PublicKey).toString()).toEqual( From 98d68d051c84968be7e59d95352a8e83e91c3b4b Mon Sep 17 00:00:00 2001 From: maayan Date: Mon, 23 Oct 2023 20:11:10 -0700 Subject: [PATCH 08/10] feedback --- src/internal/transaction.ts | 2 +- src/types/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/internal/transaction.ts b/src/internal/transaction.ts index 6df384920..17aa70769 100644 --- a/src/internal/transaction.ts +++ b/src/internal/transaction.ts @@ -104,7 +104,7 @@ export async function waitForTransaction(args: { const { aptosConfig, transactionHash, options } = args; const timeoutSecs = options?.timeoutSecs ?? DEFAULT_TXN_TIMEOUT_SEC; const checkSuccess = options?.checkSuccess ?? true; - const indexerVersionCheck = options?.indexerVersionCheck ?? false; + const indexerVersionCheck = options?.indexerVersionCheck ?? true; let isPending = true; let timeElapsed = 0; diff --git a/src/types/index.ts b/src/types/index.ts index 9cef582d0..7b15d5e08 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1012,7 +1012,7 @@ export type GenerateAccountWithLegacyKey = { * In this case `legacy` is always false */ export type GenerateAccountWithUnifiedKey = { - scheme: SigningSchemeInput.Secp256k1Ecdsa; + scheme: SigningSchemeInput.Secp256k1Ecdsa | SigningSchemeInput.Ed25519; legacy?: false; }; From 136c5702df71cf2f0d5d832e15e193e984bfd7ce Mon Sep 17 00:00:00 2001 From: maayan Date: Wed, 25 Oct 2023 01:32:56 -0700 Subject: [PATCH 09/10] address feedback --- src/api/account.ts | 2 +- src/core/account.ts | 8 +++---- tests/e2e/transaction/signTransaction.test.ts | 22 +++++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/api/account.ts b/src/api/account.ts index e680a621c..0927727fd 100644 --- a/src/api/account.ts +++ b/src/api/account.ts @@ -367,7 +367,7 @@ export class Account { * If the privateKey is a Secp256k1 type, it derives the account using the derived public key and * auth key using the SingleKey scheme locally. * - * If the privateKey is a ED25519 type, it looksup the auth key on chain, and uses it to resolve + * If the privateKey is a ED25519 type, it looks up the authentication key on chain, and uses it to resolve * whether it is a Legacy ED25519 key or a Unified ED25519 key. It then derives the account based * on that. * diff --git a/src/core/account.ts b/src/core/account.ts index 9cbf3f2ec..1b1228869 100644 --- a/src/core/account.ts +++ b/src/core/account.ts @@ -62,8 +62,8 @@ export class Account { * * @param args.privateKey PrivateKey - private key of the account * @param args.address AccountAddress - address of the account - * @param args.legacy optional. If set to true, would create a Legacy signing keys. Default - * is Unified signing key + * @param args.legacy optional. If set to true, the keypair generated is a Legacy keypair. Defaults + * to generating a Unified keypair * * This method is private because it should only be called by the factory static methods. * @returns Account @@ -152,8 +152,8 @@ export class Account { * * @param privateKey PrivateKey - private key of the account * @param address The account address - * @param legacy optional. If set to true, would create a Legacy signing keys. Default - * is Unified signing key + * @param args.legacy optional. If set to true, the keypair generated is a Legacy keypair. Defaults + * to generating a Unified keypair * * @returns Account */ diff --git a/tests/e2e/transaction/signTransaction.test.ts b/tests/e2e/transaction/signTransaction.test.ts index d5f1cec76..be96ab6a4 100644 --- a/tests/e2e/transaction/signTransaction.test.ts +++ b/tests/e2e/transaction/signTransaction.test.ts @@ -14,7 +14,7 @@ describe("sign transaction", () => { const contractPublisherAccount = Account.generate(); const singleSignerED25519SenderAccount = Account.generate(); const legacyED25519SenderAccount = Account.generate({ legacy: true }); - const recieverAccounts = [Account.generate(), Account.generate()]; + const receiverAccounts = [Account.generate(), Account.generate()]; const singleSignerSecp256k1Account = Account.generate({ scheme: SigningSchemeInput.Secp256k1Ecdsa }); const secondarySignerAccount = Account.generate(); const feePayerAccount = Account.generate(); @@ -24,7 +24,7 @@ describe("sign transaction", () => { singleSignerED25519SenderAccount, legacyED25519SenderAccount, singleSignerSecp256k1Account, - ...recieverAccounts, + ...receiverAccounts, secondarySignerAccount, feePayerAccount, ]); @@ -38,7 +38,7 @@ describe("sign transaction", () => { sender: singleSignerED25519SenderAccount.accountAddress.toString(), data: { bytecode: singleSignerScriptBytecode, - functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ @@ -55,7 +55,7 @@ describe("sign transaction", () => { sender: singleSignerED25519SenderAccount.accountAddress.toString(), data: { function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, - functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ @@ -73,7 +73,7 @@ describe("sign transaction", () => { data: { multisigAddress: secondarySignerAccount.accountAddress, function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, - functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ @@ -92,7 +92,7 @@ describe("sign transaction", () => { sender: singleSignerSecp256k1Account.accountAddress.toString(), data: { bytecode: singleSignerScriptBytecode, - functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ @@ -109,7 +109,7 @@ describe("sign transaction", () => { sender: singleSignerSecp256k1Account.accountAddress.toString(), data: { function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, - functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ @@ -127,7 +127,7 @@ describe("sign transaction", () => { data: { multisigAddress: secondarySignerAccount.accountAddress, function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, - functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ @@ -146,7 +146,7 @@ describe("sign transaction", () => { sender: legacyED25519SenderAccount.accountAddress.toString(), data: { bytecode: singleSignerScriptBytecode, - functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ @@ -163,7 +163,7 @@ describe("sign transaction", () => { sender: legacyED25519SenderAccount.accountAddress.toString(), data: { function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, - functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ @@ -181,7 +181,7 @@ describe("sign transaction", () => { data: { multisigAddress: secondarySignerAccount.accountAddress, function: `0x${contractPublisherAccount.accountAddress.toStringWithoutPrefix()}::transfer::transfer`, - functionArguments: [new U64(1), recieverAccounts[0].accountAddress], + functionArguments: [new U64(1), receiverAccounts[0].accountAddress], }, }); const accountAuthenticator = aptos.signTransaction({ From 0b32eab1ee0e301cbf2ca5e449e224fe0e2336b1 Mon Sep 17 00:00:00 2001 From: maayan Date: Wed, 25 Oct 2023 11:41:58 -0700 Subject: [PATCH 10/10] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8bf65e91..87e8c8fe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to the Aptos TypeScript SDK will be captured in this file. T - Support to config a custom client instance - Changed all Regex based inputs requiring a `0x` to be optional. This is to allow for easier copy/pasting of addresses and keys. - Change GetAccountResource to take in a generic output type that matches the struct +- Add support for Single Sender ## 0.0.0 (2023-10-18)