Skip to content

Commit

Permalink
support general proof challenege
Browse files Browse the repository at this point in the history
  • Loading branch information
0xmaayan committed Apr 10, 2024
1 parent 94b3b3b commit ff5e305
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 4 deletions.
11 changes: 10 additions & 1 deletion src/api/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import { AptosConfig } from "./aptosConfig";
import {
createProofChallenge,
getBlockByHeight,
getBlockByVersion,
getChainTopUserTransactions,
Expand All @@ -21,11 +22,12 @@ import {
GraphqlQuery,
LedgerInfo,
LedgerVersionArg,
MoveFunctionId,
MoveValue,
TableItemRequest,
} from "../types";
import { ProcessorType } from "../utils/const";
import { InputViewFunctionData } from "../transactions";
import { InputViewFunctionData, ProofChallenge } from "../transactions";

/**
* A class to query all `General` Aptos related queries
Expand Down Expand Up @@ -201,4 +203,11 @@ export class General {
async getProcessorStatus(processorType: ProcessorType): Promise<GetProcessorStatusResponse[0]> {
return getProcessorStatus({ aptosConfig: this.config, processorType });
}

async createProofChallenge(args: { struct: MoveFunctionId; data: Array<any> }): Promise<ProofChallenge> {
return createProofChallenge({
config: this.config,
...args,
});
}
}
11 changes: 10 additions & 1 deletion src/api/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,17 @@ import {
publicPackageTransaction,
rotateAuthKey,
signAndSubmitTransaction,
signProofChallenge,
signTransaction,
} from "../internal/transactionSubmission";
import {
AccountAuthenticator,
AnyRawTransaction,
InputGenerateTransactionOptions,
InputGenerateTransactionPayloadData,
ProofChallenge,
} from "../transactions";
import { AccountAddressInput, Account, PrivateKey } from "../core";
import { AccountAddressInput, Account, PrivateKey, Signature } from "../core";
import { Build } from "./transactionSubmission/build";
import { Simulate } from "./transactionSubmission/simulate";
import { Submit } from "./transactionSubmission/submit";
Expand Down Expand Up @@ -263,6 +265,13 @@ export class Transaction {
});
}

// eslint-disable-next-line class-methods-use-this
signProofChallenge(args: { challenge: ProofChallenge; signer: Account }): Signature {
return signProofChallenge({
...args,
});
}

// TRANSACTION SUBMISSION //

/**
Expand Down
37 changes: 37 additions & 0 deletions src/internal/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@
*/

import { AptosConfig } from "../api/aptosConfig";
import { MoveString } from "../bcs";
import { getAptosFullNode, postAptosFullNode, postAptosIndexer } from "../client";
import { AccountAddress } from "../core";
import { getFunctionParts } from "../transactions/transactionBuilder/helpers";
import { fetchStructFieldsAbi, convertArgument } from "../transactions/transactionBuilder/remoteAbi";

Check failure on line 16 in src/internal/general.ts

View workflow job for this annotation

GitHub Actions / run-tests

Dependency cycle via ../../internal/account:16
import { ProofChallenge } from "../transactions/instances";
import { EntryFunctionArgumentTypes } from "../transactions/types";
import {
AnyNumber,
Block,
Expand All @@ -18,6 +24,7 @@ import {
GraphqlQuery,
LedgerInfo,
LedgerVersionArg,
MoveFunctionId,
TableItemRequest,
} from "../types";
import { GetChainTopUserTransactionsQuery, GetProcessorStatusQuery } from "../types/generated/operations";
Expand Down Expand Up @@ -162,3 +169,33 @@ export async function getProcessorStatus(args: {

return data.processor_status[0];
}

export async function createProofChallenge(args: {
config: AptosConfig;
struct: MoveFunctionId;
data: Array<any>;
}): Promise<ProofChallenge> {
const { config, struct, data } = args;
const { moduleAddress, moduleName, functionName } = getFunctionParts(struct);
const structFieldsAbi = await fetchStructFieldsAbi(moduleAddress, moduleName, functionName, config);

// Check all BCS types, and convert any non-BCS types
const functionArguments: Array<EntryFunctionArgumentTypes> =
data.map((arg, i) => convertArgument(functionName, structFieldsAbi, arg, i, structFieldsAbi.parameters)) ?? [];

// Check that all arguments are accounted for
if (functionArguments.length !== structFieldsAbi.parameters.length) {
throw new Error(
// eslint-disable-next-line max-len
`Too few arguments for '${moduleAddress}::${moduleName}::${functionName}', expected ${structFieldsAbi.parameters.length} but got ${functionArguments.length}`,
);
}

const challenge = new ProofChallenge([
AccountAddress.from(moduleAddress),
new MoveString(moduleName),
new MoveString(functionName),
...functionArguments,
]);
return challenge;
}
12 changes: 10 additions & 2 deletions src/internal/transactionSubmission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import { MoveVector, U8 } from "../bcs";
import { postAptosFullNode } from "../client";
import { Account } from "../core/account";
import { AccountAddress, AccountAddressInput } from "../core/accountAddress";
import { PrivateKey } from "../core/crypto";
import { PrivateKey, Signature } from "../core/crypto";
import { AccountAuthenticator } from "../transactions/authenticator/account";
import { RotationProofChallenge } from "../transactions/instances/rotationProofChallenge";
import { ProofChallenge, RotationProofChallenge } from "../transactions/instances/rotationProofChallenge";
import {
buildTransaction,
generateTransactionPayload,
Expand Down Expand Up @@ -206,6 +206,13 @@ export function signTransaction(args: { signer: Account; transaction: AnyRawTran
return accountAuthenticator;
}

export function signProofChallenge(args: { challenge: ProofChallenge; signer: Account }): Signature {
const { challenge, signer } = args;
const challengeHex = challenge.bcsToBytes();
const signature = signer.sign(challengeHex);
return signature;
}

/**
* Simulates a transaction before singing it.
*
Expand Down Expand Up @@ -348,6 +355,7 @@ export async function rotateAuthKey(args: {

// Sign the challenge
const challengeHex = challenge.bcsToBytes();

const proofSignedByCurrentPrivateKey = fromAccount.sign(challengeHex);
const proofSignedByNewPrivateKey = newAccount.sign(challengeHex);

Expand Down
15 changes: 15 additions & 0 deletions src/transactions/instances/rotationProofChallenge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@ import { AnyNumber } from "../../types";
import { PublicKey } from "../../core/crypto";
import { MoveString, MoveVector, U64, U8 } from "../../bcs";

export class ProofChallenge extends Serializable {
public readonly data: Serializable[];

constructor(data: Serializable[]) {
super();
this.data = data;
}

serialize(serializer: Serializer): void {
this.data.forEach((data) => {
serializer.serialize(data);
});
}
}

/**
* Representation of the challenge which is needed to sign by owner of the account
* to rotate the authentication key.
Expand Down
40 changes: 40 additions & 0 deletions src/transactions/transactionBuilder/remoteAbi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,22 @@ export async function fetchFunctionAbi(
return undefined;
}

export async function fetchStructAbi(
moduleAddress: string,
moduleName: string,
structName: string,
aptosConfig: AptosConfig,
) {
// This fetch from the API is currently cached
const module = await getModule({ aptosConfig, accountAddress: moduleAddress, moduleName });

if (module.abi) {
return module.abi.structs.find((struct) => struct.name === structName);
}

return undefined;
}

/**
* Fetches the ABI for an entry function from the module
*
Expand Down Expand Up @@ -160,6 +176,30 @@ export async function fetchViewFunctionAbi(
};
}

export async function fetchStructFieldsAbi(
moduleAddress: string,
moduleName: string,
structName: string,
aptosConfig: AptosConfig,
) {
const structAbi = await fetchStructAbi(moduleAddress, moduleName, structName, aptosConfig);

// If there's no ABI, then the function is invalid
if (!structAbi) {
throw new Error(`Could not find Struct ABI for '${moduleAddress}::${moduleName}::${structName}'`);
}

const params: TypeTag[] = [];
for (let i = 0; i < structAbi.fields.length; i += 1) {
params.push(parseTypeTag(structAbi.fields[i].type, { allowGenerics: true }));
}

return {
typeParameters: structAbi.generic_type_params,
parameters: params,
};
}

/**
* Converts a non-BCS encoded argument into BCS encoded, if necessary
* @param functionName
Expand Down
45 changes: 45 additions & 0 deletions tests/e2e/transaction/signTransaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
AccountAuthenticator,
AccountAuthenticatorEd25519,
AccountAuthenticatorSingleKey,
MoveVector,
U8,
} from "../../../src";
import { longTestTimeout } from "../../unit/helper";
import { getAptosClient } from "../helper";
Expand Down Expand Up @@ -216,3 +218,46 @@ describe("sign transaction", () => {
});
});
});

test.only("test", async () => {
const fromAccount = Account.generate();
const newAccount = Account.generate();

await aptos.fundAccount({ accountAddress: fromAccount.accountAddress, amount: 1_000_000_000 });
await aptos.fundAccount({ accountAddress: newAccount.accountAddress, amount: 1_000_000_000 });

const accountInfo = await aptos.getAccountInfo({
accountAddress: fromAccount.accountAddress,
});

const challenge = await aptos.createProofChallenge({
struct: "0x1::account::RotationProofChallenge",
data: [
BigInt(accountInfo.sequence_number),
fromAccount.accountAddress,
accountInfo.authentication_key,
newAccount.publicKey.toUint8Array(),
],
});

const proofSignedByCurrentPrivateKey = aptos.signProofChallenge({ challenge, signer: fromAccount });
const proofSignedByNewPrivateKey = aptos.signProofChallenge({ challenge, signer: newAccount });

const transaction = await aptos.transaction.build.simple({
sender: fromAccount.accountAddress,
data: {
function: "0x1::account::rotate_authentication_key",
functionArguments: [
new U8(fromAccount.signingScheme), // from scheme
MoveVector.U8(fromAccount.publicKey.toUint8Array()),
new U8(newAccount.signingScheme), // to scheme
MoveVector.U8(newAccount.publicKey.toUint8Array()),
MoveVector.U8(proofSignedByCurrentPrivateKey.toUint8Array()),
MoveVector.U8(proofSignedByNewPrivateKey.toUint8Array()),
],
},
});

const response = await aptos.signAndSubmitTransaction({ signer: fromAccount, transaction });
console.log("response", response);

Check warning on line 262 in tests/e2e/transaction/signTransaction.test.ts

View workflow job for this annotation

GitHub Actions / run-tests

Unexpected console statement
});

0 comments on commit ff5e305

Please sign in to comment.