Skip to content

Commit

Permalink
deserilize arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
0xmaayan committed May 5, 2024
1 parent 9bd4b03 commit dafe89f
Show file tree
Hide file tree
Showing 9 changed files with 632 additions and 35 deletions.
75 changes: 75 additions & 0 deletions examples/typescript/proofChallenge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* eslint-disable no-console */
/* eslint-disable max-len */

import { Account, Aptos, AptosConfig, Network, NetworkToNetworkName, MoveVector, U8 } from "@aptos-labs/ts-sdk";

/**
* This example demonstrate the end-to-end flow of creating, signing and submitting
* a proog challenge to the Aptos chain
*/

// Setup the client
const APTOS_NETWORK: Network = NetworkToNetworkName[process.env.APTOS_NETWORK ?? Network.LOCAL];
const config = new AptosConfig({ network: APTOS_NETWORK });
const aptos = new Aptos(config);

async function main() {
// Create accounts
const fromAccount = Account.generate();
const newAccount = Account.generate();

// Fund and create the accounts on chain
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,
});

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

// Display the challenge in a human readable format. This step is for
// any service who needs/wants to show the challenge to the account before
// they sign it.
const deserializedChallenge = await aptos.getProofChallenge({
struct: "0x1::account::RotationProofChallenge",
data: challenge.bcsToBytes(),
});

console.log("rotation proof challenge to sign on", deserializedChallenge);

// 1st account signs the challenge
const proofSignedByCurrentPrivateKey = aptos.signProofChallenge({ challenge, signer: fromAccount });
// 2nd account signs the challenge
const proofSignedByNewPrivateKey = aptos.signProofChallenge({ challenge, signer: newAccount });

// Submit challenge to chain
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 });
await aptos.waitForTransaction({ transactionHash: response.hash });
}

main();
16 changes: 15 additions & 1 deletion src/api/proofChallenge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import { Account, Signature } from "../core";
import { createProofChallenge, signProofChallenge } from "../internal/proofChallenge";
import { createProofChallenge, getProofChallenge, signProofChallenge } from "../internal/proofChallenge";
import { MoveFunctionId } from "../types";
import { AptosConfig } from "./aptosConfig";
import { ProofChallenge as ProofChallengeInstance } from "../transactions/instances/proofChallenge";
Expand Down Expand Up @@ -36,6 +36,20 @@ export class ProofChallenge {
});
}

/**
* Get the proog challenge in a human readable format
*
* @param args.struct The struct name
* @param args.data The serialized challenge
* @returns
*/
async getProofChallenge(args: { struct: MoveFunctionId; data: Uint8Array }) {
return getProofChallenge({
config: this.config,
...args,
});
}

/**
* Signs a generic proof challenge
*
Expand Down
12 changes: 11 additions & 1 deletion src/internal/proofChallenge.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { AptosConfig } from "../api/aptosConfig";
import { Account, Signature } from "../core";
import { EntryFunctionArgumentTypes, SimpleEntryFunctionArgumentTypes, generateProofChallenge } from "../transactions";
import {
EntryFunctionArgumentTypes,
SimpleEntryFunctionArgumentTypes,
deserializeProofChallenge,
generateProofChallenge,
} from "../transactions";
import { ProofChallenge } from "../transactions/instances/proofChallenge";
import { MoveFunctionId } from "../types";

Expand All @@ -13,6 +18,11 @@ export async function createProofChallenge(args: {
return challenge;
}

export async function getProofChallenge(args: { config: AptosConfig; struct: MoveFunctionId; data: Uint8Array }) {
const challenge = deserializeProofChallenge({ ...args });
return challenge;
}

export function signProofChallenge(args: { challenge: ProofChallenge; signer: Account }): Signature {
const { challenge, signer } = args;
const challengeHex = challenge.bcsToBytes();
Expand Down
63 changes: 63 additions & 0 deletions src/transactions/transactionBuilder/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ import {
import { Bool, FixedBytes, MoveOption, MoveString, MoveVector, U128, U16, U256, U32, U64, U8 } from "../../bcs";
import { AccountAddress } from "../../core";
import { MoveFunction, MoveFunctionId } from "../../types";
import {
TypeTag,
TypeTagAddress,
TypeTagBool,
TypeTagGeneric,
TypeTagSigner,
TypeTagStruct,
TypeTagU128,
TypeTagU16,
TypeTagU256,
TypeTagU32,
TypeTagU64,
TypeTagU8,
TypeTagVector,
} from "../typeTag";

export function isBool(arg: SimpleEntryFunctionArgumentTypes): arg is boolean {
return typeof arg === "boolean";
Expand Down Expand Up @@ -128,3 +143,51 @@ export function getFunctionParts(functionArg: MoveFunctionId) {
const functionName = funcNameParts[2];
return { moduleAddress, moduleName, functionName };
}

export function isTypeTagBool(param: TypeTag): boolean {
return param instanceof TypeTagBool;
}

export function isTypeTagAddress(param: TypeTag): boolean {
return param instanceof TypeTagAddress;
}

export function isTypeTagGeneric(param: TypeTag): boolean {
return param instanceof TypeTagGeneric;
}

export function isTypeTagSigner(param: TypeTag): boolean {
return param instanceof TypeTagSigner;
}

export function isTypeTagVector(param: TypeTag): boolean {
return param instanceof TypeTagVector;
}

export function isTypeTagStruct(param: TypeTag): boolean {
return param instanceof TypeTagStruct;
}

export function isTypeTagU8(param: TypeTag): boolean {
return param instanceof TypeTagU8;
}

export function isTypeTagU16(param: TypeTag): boolean {
return param instanceof TypeTagU16;
}

export function isTypeTagU32(param: TypeTag): boolean {
return param instanceof TypeTagU32;
}

export function isTypeTagU64(param: TypeTag): boolean {
return param instanceof TypeTagU64;
}

export function isTypeTagU128(param: TypeTag): boolean {
return param instanceof TypeTagU128;
}

export function isTypeTagU256(param: TypeTag): boolean {
return param instanceof TypeTagU256;
}
123 changes: 118 additions & 5 deletions src/transactions/transactionBuilder/remoteAbi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {
ViewFunctionABI,
FunctionABI,
} from "../types";
import { Bool, MoveOption, MoveString, MoveVector, U128, U16, U256, U32, U64, U8 } from "../../bcs";
import { AccountAddress } from "../../core";
import { Bool, Deserializer, MoveOption, MoveString, MoveVector, U128, U16, U256, U32, U64, U8 } from "../../bcs";
import { AccountAddress, Ed25519PublicKey } from "../../core";
import { getModule } from "../../internal/account";
import {
findFirstNonSignerArg,
Expand All @@ -31,9 +31,18 @@ import {
isNull,
isNumber,
isString,
isTypeTagAddress,
isTypeTagBool,
isTypeTagGeneric,
isTypeTagU128,
isTypeTagU16,
isTypeTagU256,
isTypeTagU32,
isTypeTagU64,
isTypeTagU8,
throwTypeMismatch,
} from "./helpers";
import { MoveFunction } from "../../types";
import { MoveFunction, MoveStruct } from "../../types";

const TEXT_ENCODER = new TextEncoder();

Expand Down Expand Up @@ -80,7 +89,7 @@ export async function fetchStructAbi(
moduleName: string,
structName: string,
aptosConfig: AptosConfig,
) {
): Promise<MoveStruct | undefined> {
// This fetch from the API is currently cached
const module = await getModule({ aptosConfig, accountAddress: moduleAddress, moduleName });

Expand Down Expand Up @@ -181,7 +190,7 @@ export async function fetchStructFieldsAbi(
moduleName: string,
structName: string,
aptosConfig: AptosConfig,
) {
): Promise<FunctionABI> {
const structAbi = await fetchStructAbi(moduleAddress, moduleName, structName, aptosConfig);

// If there's no ABI, then the function is invalid
Expand Down Expand Up @@ -467,3 +476,107 @@ function checkType(param: TypeTag, arg: EntryFunctionArgumentTypes, position: nu

throw new Error(`Type mismatch for argument ${position}, expected '${param.toString()}'`);
}

export function deserializeArgument(
params: Array<TypeTag>,
deserializer: Deserializer,
): Array<SimpleEntryFunctionArgumentTypes> {
return params.map((param) => deserializeArg(deserializer, param));
}

export function deserializeArg(deserializer: Deserializer, param: TypeTag): SimpleEntryFunctionArgumentTypes {
if (isTypeTagBool(param)) {
return Bool.deserialize(deserializer).value;
}
if (isTypeTagAddress(param)) {
return AccountAddress.deserialize(deserializer).toString();
}
if (isTypeTagU8(param)) {
return U8.deserialize(deserializer).value;
}
if (isTypeTagU16(param)) {
return U16.deserialize(deserializer).value;
}
if (isTypeTagU32(param)) {
return U32.deserialize(deserializer).value;
}
if (isTypeTagU64(param)) {
return U64.deserialize(deserializer).value;
}
if (isTypeTagU128(param)) {
return U128.deserialize(deserializer).value;
}
if (isTypeTagU256(param)) {
return U256.deserialize(deserializer).value;
}
if (isTypeTagGeneric(param)) {
// // Currently, TS SDK `deserialize` can only handle a single class, not a class with generics
throw new Error("Generic type deserialization is not implemented");
}

if (param.isVector()) {
if (isTypeTagU8(param.value)) {
// TODO handle Secp256k1PublicKey
const { values } = MoveVector.deserialize(deserializer, U8);
const numbers = values.map((value) => value.value);
try {
return new Ed25519PublicKey(new Uint8Array(numbers)).toString();
} catch (e: any) {
return numbers;
}
}
if (isTypeTagU16(param.value)) {
const { values } = MoveVector.deserialize(deserializer, U16);
return values.map((value) => value.value);
}
if (isTypeTagU32(param.value)) {
const { values } = MoveVector.deserialize(deserializer, U32);
return values.map((value) => value.value);
}
if (isTypeTagU64(param.value)) {
const { values } = MoveVector.deserialize(deserializer, U64);
return values.map((value) => value.value);
}
if (isTypeTagU128(param.value)) {
const { values } = MoveVector.deserialize(deserializer, U128);
return values.map((value) => value.value);
}
if (isTypeTagU256(param.value)) {
const { values } = MoveVector.deserialize(deserializer, U256);
return values.map((value) => value.value);
}
if (isTypeTagBool(param.value)) {
const { values } = MoveVector.deserialize(deserializer, Bool);
return values.map((value) => value.value);
}
if (isTypeTagAddress(param.value)) {
const { values } = MoveVector.deserialize(deserializer, AccountAddress);
return values.map((value) => value.toString());
}
if (param.value.isStruct()) {
if (param.value.isObject()) {
const { values } = MoveVector.deserialize(deserializer, AccountAddress);
return values.map((value) => value.toString());
}
if (param.value.isOption()) {
// Currently, TS SDK `deserialize` can only handle a single class, not a class with generics
throw new Error("Option type deserialization is not implemented");
}

const { values } = MoveVector.deserialize(deserializer, MoveString);
return values.map((value) => value.value);
}
}
if (param.isStruct()) {
if (param.isObject()) {
return AccountAddress.deserialize(deserializer).toString();
}
if (param.isOption()) {
// Currently, TS SDK `deserialize` can only handle a single class, not a class with generics
throw new Error("Option type deserialization is not implemented");
}
return MoveString.deserialize(deserializer).value;
}

throw new Error(`Could not deserialize type '${param.toString()}'`);
}
Loading

0 comments on commit dafe89f

Please sign in to comment.