Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support generic challenge signing. Introduce ABI deserialization. #361

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
6 changes: 6 additions & 0 deletions src/api/aptos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { General } from "./general";
import { ANS } from "./ans";
import { Staking } from "./staking";
import { Transaction } from "./transaction";
import { ProofChallenge } from "./proofChallenge";

/**
* This class is the main entry point into Aptos's
Expand Down Expand Up @@ -47,6 +48,8 @@ export class Aptos {

readonly transaction: Transaction;

readonly proofChallenge: ProofChallenge;

constructor(settings?: AptosConfig) {
this.config = new AptosConfig(settings);
this.account = new Account(this.config);
Expand All @@ -59,6 +62,7 @@ export class Aptos {
this.general = new General(this.config);
this.staking = new Staking(this.config);
this.transaction = new Transaction(this.config);
this.proofChallenge = new ProofChallenge(this.config);
}
}

Expand All @@ -74,6 +78,7 @@ export interface Aptos
FungibleAsset,
General,
Staking,
ProofChallenge,
Omit<Transaction, "build" | "simulate" | "submit" | "batch"> {}

/**
Expand Down Expand Up @@ -107,3 +112,4 @@ applyMixin(Aptos, FungibleAsset, "fungibleAsset");
applyMixin(Aptos, General, "general");
applyMixin(Aptos, Staking, "staking");
applyMixin(Aptos, Transaction, "transaction");
applyMixin(Aptos, ProofChallenge, "proofChallenge");
67 changes: 67 additions & 0 deletions src/api/proofChallenge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

import { Account, Signature } from "../core";
import { createProofChallenge, getProofChallenge, signProofChallenge } from "../internal/proofChallenge";
import { MoveFunctionId } from "../types";
import { AptosConfig } from "./aptosConfig";
import { ProofChallenge as ProofChallengeInstance } from "../transactions/instances/proofChallenge";
import { EntryFunctionArgumentTypes, SimpleEntryFunctionArgumentTypes } from "../transactions/types";

/**
* A class for all `ProofChallenge` Aptos related operations
*/
export class ProofChallenge {
readonly config: AptosConfig;

constructor(config: AptosConfig) {
this.config = config;
}

/**
* Creates a generic proof challenge
*
* @param args.struct The struct address of the challenge
* @param args.data The struct arguments
*
* @returns ProofChallenge
*/
async createProofChallenge(args: {
struct: MoveFunctionId;
data: Array<EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes>;
}): Promise<ProofChallengeInstance> {
return createProofChallenge({
config: this.config,
...args,
});
}

/**
* 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
*
* @param args.challenge The generated challenge
* @param args.signer The signer account
*
* @returns Signature
*/
// eslint-disable-next-line class-methods-use-this
signProofChallenge(args: { challenge: ProofChallengeInstance; signer: Account }): Signature {
return signProofChallenge({
...args,
});
}
}
31 changes: 31 additions & 0 deletions src/internal/proofChallenge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { AptosConfig } from "../api/aptosConfig";
import { Account, Signature } from "../core";
import {
EntryFunctionArgumentTypes,
SimpleEntryFunctionArgumentTypes,
deserializeProofChallenge,
generateProofChallenge,
} from "../transactions";
import { ProofChallenge } from "../transactions/instances/proofChallenge";
import { MoveFunctionId } from "../types";

export async function createProofChallenge(args: {
config: AptosConfig;
struct: MoveFunctionId;
data: Array<EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes>;
}): Promise<ProofChallenge> {
const challenge = generateProofChallenge({ ...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();
const signature = signer.sign(challengeHex);
return signature;
}
16 changes: 16 additions & 0 deletions src/transactions/instances/proofChallenge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Serializable, Serializer } 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);
});
}
}
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;
}
Loading
Loading