Skip to content

Commit fea5e18

Browse files
committed
support general proof challenege
1 parent 32d4360 commit fea5e18

File tree

7 files changed

+167
-4
lines changed

7 files changed

+167
-4
lines changed

src/api/general.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import { AptosConfig } from "./aptosConfig";
55
import {
6+
createProofChallenge,
67
getBlockByHeight,
78
getBlockByVersion,
89
getChainTopUserTransactions,
@@ -21,11 +22,12 @@ import {
2122
GraphqlQuery,
2223
LedgerInfo,
2324
LedgerVersionArg,
25+
MoveFunctionId,
2426
MoveValue,
2527
TableItemRequest,
2628
} from "../types";
2729
import { ProcessorType } from "../utils/const";
28-
import { InputViewFunctionData } from "../transactions";
30+
import { InputViewFunctionData, ProofChallenge } from "../transactions";
2931

3032
/**
3133
* A class to query all `General` Aptos related queries
@@ -224,4 +226,11 @@ export class General {
224226
async getProcessorStatus(processorType: ProcessorType): Promise<GetProcessorStatusResponse[0]> {
225227
return getProcessorStatus({ aptosConfig: this.config, processorType });
226228
}
229+
230+
async createProofChallenge(args: { struct: MoveFunctionId; data: Array<any> }): Promise<ProofChallenge> {
231+
return createProofChallenge({
232+
config: this.config,
233+
...args,
234+
});
235+
}
227236
}

src/api/transaction.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,17 @@ import {
2525
publicPackageTransaction,
2626
rotateAuthKey,
2727
signAndSubmitTransaction,
28+
signProofChallenge,
2829
signTransaction,
2930
} from "../internal/transactionSubmission";
3031
import {
3132
AccountAuthenticator,
3233
AnyRawTransaction,
3334
InputGenerateTransactionOptions,
3435
InputGenerateTransactionPayloadData,
36+
ProofChallenge,
3537
} from "../transactions";
36-
import { AccountAddressInput, Account, PrivateKey } from "../core";
38+
import { AccountAddressInput, Account, PrivateKey, Signature } from "../core";
3739
import { Build } from "./transactionSubmission/build";
3840
import { Simulate } from "./transactionSubmission/simulate";
3941
import { Submit } from "./transactionSubmission/submit";
@@ -306,6 +308,13 @@ export class Transaction {
306308
});
307309
}
308310

311+
// eslint-disable-next-line class-methods-use-this
312+
signProofChallenge(args: { challenge: ProofChallenge; signer: Account }): Signature {
313+
return signProofChallenge({
314+
...args,
315+
});
316+
}
317+
309318
// TRANSACTION SUBMISSION //
310319

311320
/**

src/internal/general.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@
99
*/
1010

1111
import { AptosConfig } from "../api/aptosConfig";
12+
import { MoveString } from "../bcs";
1213
import { getAptosFullNode, postAptosFullNode, postAptosIndexer } from "../client";
14+
import { AccountAddress } from "../core";
15+
import { getFunctionParts } from "../transactions/transactionBuilder/helpers";
16+
import { fetchStructFieldsAbi, convertArgument } from "../transactions/transactionBuilder/remoteAbi";
17+
import { ProofChallenge } from "../transactions/instances";
18+
import { EntryFunctionArgumentTypes } from "../transactions/types";
1319
import {
1420
AnyNumber,
1521
Block,
@@ -18,6 +24,7 @@ import {
1824
GraphqlQuery,
1925
LedgerInfo,
2026
LedgerVersionArg,
27+
MoveFunctionId,
2128
TableItemRequest,
2229
} from "../types";
2330
import { GetChainTopUserTransactionsQuery, GetProcessorStatusQuery } from "../types/generated/operations";
@@ -162,3 +169,33 @@ export async function getProcessorStatus(args: {
162169

163170
return data.processor_status[0];
164171
}
172+
173+
export async function createProofChallenge(args: {
174+
config: AptosConfig;
175+
struct: MoveFunctionId;
176+
data: Array<any>;
177+
}): Promise<ProofChallenge> {
178+
const { config, struct, data } = args;
179+
const { moduleAddress, moduleName, functionName } = getFunctionParts(struct);
180+
const structFieldsAbi = await fetchStructFieldsAbi(moduleAddress, moduleName, functionName, config);
181+
182+
// Check all BCS types, and convert any non-BCS types
183+
const functionArguments: Array<EntryFunctionArgumentTypes> =
184+
data.map((arg, i) => convertArgument(functionName, structFieldsAbi, arg, i, structFieldsAbi.parameters)) ?? [];
185+
186+
// Check that all arguments are accounted for
187+
if (functionArguments.length !== structFieldsAbi.parameters.length) {
188+
throw new Error(
189+
// eslint-disable-next-line max-len
190+
`Too few arguments for '${moduleAddress}::${moduleName}::${functionName}', expected ${structFieldsAbi.parameters.length} but got ${functionArguments.length}`,
191+
);
192+
}
193+
194+
const challenge = new ProofChallenge([
195+
AccountAddress.from(moduleAddress),
196+
new MoveString(moduleName),
197+
new MoveString(functionName),
198+
...functionArguments,
199+
]);
200+
return challenge;
201+
}

src/internal/transactionSubmission.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import { MoveVector, U8 } from "../bcs";
1010
import { postAptosFullNode } from "../client";
1111
import { Account } from "../core/account";
1212
import { AccountAddress, AccountAddressInput } from "../core/accountAddress";
13-
import { PrivateKey } from "../core/crypto";
13+
import { PrivateKey, Signature } from "../core/crypto";
1414
import { AccountAuthenticator } from "../transactions/authenticator/account";
15-
import { RotationProofChallenge } from "../transactions/instances/rotationProofChallenge";
15+
import { ProofChallenge, RotationProofChallenge } from "../transactions/instances/rotationProofChallenge";
1616
import {
1717
buildTransaction,
1818
generateTransactionPayload,
@@ -208,6 +208,13 @@ export function signTransaction(args: { signer: Account; transaction: AnyRawTran
208208
return accountAuthenticator;
209209
}
210210

211+
export function signProofChallenge(args: { challenge: ProofChallenge; signer: Account }): Signature {
212+
const { challenge, signer } = args;
213+
const challengeHex = challenge.bcsToBytes();
214+
const signature = signer.sign(challengeHex);
215+
return signature;
216+
}
217+
211218
/**
212219
* Simulates a transaction before singing it.
213220
*
@@ -350,6 +357,7 @@ export async function rotateAuthKey(args: {
350357

351358
// Sign the challenge
352359
const challengeHex = challenge.bcsToBytes();
360+
353361
const proofSignedByCurrentPrivateKey = fromAccount.sign(challengeHex);
354362
const proofSignedByNewPrivateKey = newAccount.sign(challengeHex);
355363

src/transactions/instances/rotationProofChallenge.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@ import { AnyNumber } from "../../types";
77
import { PublicKey } from "../../core/crypto";
88
import { MoveString, MoveVector, U64, U8 } from "../../bcs";
99

10+
export class ProofChallenge extends Serializable {
11+
public readonly data: Serializable[];
12+
13+
constructor(data: Serializable[]) {
14+
super();
15+
this.data = data;
16+
}
17+
18+
serialize(serializer: Serializer): void {
19+
this.data.forEach((data) => {
20+
serializer.serialize(data);
21+
});
22+
}
23+
}
24+
1025
/**
1126
* Representation of the challenge which is needed to sign by owner of the account
1227
* to rotate the authentication key.

src/transactions/transactionBuilder/remoteAbi.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,22 @@ export async function fetchFunctionAbi(
7575
return undefined;
7676
}
7777

78+
export async function fetchStructAbi(
79+
moduleAddress: string,
80+
moduleName: string,
81+
structName: string,
82+
aptosConfig: AptosConfig,
83+
) {
84+
// This fetch from the API is currently cached
85+
const module = await getModule({ aptosConfig, accountAddress: moduleAddress, moduleName });
86+
87+
if (module.abi) {
88+
return module.abi.structs.find((struct) => struct.name === structName);
89+
}
90+
91+
return undefined;
92+
}
93+
7894
/**
7995
* Fetches the ABI for an entry function from the module
8096
*
@@ -160,6 +176,30 @@ export async function fetchViewFunctionAbi(
160176
};
161177
}
162178

179+
export async function fetchStructFieldsAbi(
180+
moduleAddress: string,
181+
moduleName: string,
182+
structName: string,
183+
aptosConfig: AptosConfig,
184+
) {
185+
const structAbi = await fetchStructAbi(moduleAddress, moduleName, structName, aptosConfig);
186+
187+
// If there's no ABI, then the function is invalid
188+
if (!structAbi) {
189+
throw new Error(`Could not find Struct ABI for '${moduleAddress}::${moduleName}::${structName}'`);
190+
}
191+
192+
const params: TypeTag[] = [];
193+
for (let i = 0; i < structAbi.fields.length; i += 1) {
194+
params.push(parseTypeTag(structAbi.fields[i].type, { allowGenerics: true }));
195+
}
196+
197+
return {
198+
typeParameters: structAbi.generic_type_params,
199+
parameters: params,
200+
};
201+
}
202+
163203
/**
164204
* Converts a non-BCS encoded argument into BCS encoded, if necessary
165205
* @param functionName

tests/e2e/transaction/signTransaction.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
AccountAuthenticator,
77
AccountAuthenticatorEd25519,
88
AccountAuthenticatorSingleKey,
9+
MoveVector,
10+
U8,
911
} from "../../../src";
1012
import { longTestTimeout } from "../../unit/helper";
1113
import { getAptosClient } from "../helper";
@@ -216,3 +218,46 @@ describe("sign transaction", () => {
216218
});
217219
});
218220
});
221+
222+
test.only("test", async () => {
223+
const fromAccount = Account.generate();
224+
const newAccount = Account.generate();
225+
226+
await aptos.fundAccount({ accountAddress: fromAccount.accountAddress, amount: 1_000_000_000 });
227+
await aptos.fundAccount({ accountAddress: newAccount.accountAddress, amount: 1_000_000_000 });
228+
229+
const accountInfo = await aptos.getAccountInfo({
230+
accountAddress: fromAccount.accountAddress,
231+
});
232+
233+
const challenge = await aptos.createProofChallenge({
234+
struct: "0x1::account::RotationProofChallenge",
235+
data: [
236+
BigInt(accountInfo.sequence_number),
237+
fromAccount.accountAddress,
238+
accountInfo.authentication_key,
239+
newAccount.publicKey.toUint8Array(),
240+
],
241+
});
242+
243+
const proofSignedByCurrentPrivateKey = aptos.signProofChallenge({ challenge, signer: fromAccount });
244+
const proofSignedByNewPrivateKey = aptos.signProofChallenge({ challenge, signer: newAccount });
245+
246+
const transaction = await aptos.transaction.build.simple({
247+
sender: fromAccount.accountAddress,
248+
data: {
249+
function: "0x1::account::rotate_authentication_key",
250+
functionArguments: [
251+
new U8(fromAccount.signingScheme), // from scheme
252+
MoveVector.U8(fromAccount.publicKey.toUint8Array()),
253+
new U8(newAccount.signingScheme), // to scheme
254+
MoveVector.U8(newAccount.publicKey.toUint8Array()),
255+
MoveVector.U8(proofSignedByCurrentPrivateKey.toUint8Array()),
256+
MoveVector.U8(proofSignedByNewPrivateKey.toUint8Array()),
257+
],
258+
},
259+
});
260+
261+
const response = await aptos.signAndSubmitTransaction({ signer: fromAccount, transaction });
262+
console.log("response", response);
263+
});

0 commit comments

Comments
 (0)