Skip to content

Commit

Permalink
move vaa submission to core
Browse files Browse the repository at this point in the history
  • Loading branch information
barnjamin committed Dec 29, 2023
1 parent 12bdbfc commit d0fd509
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 306 deletions.
33 changes: 24 additions & 9 deletions platforms/algorand/protocols/core/src/core.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import {
AccountAddress,
ChainId,
ChainsConfig,
Contracts,
Network,
PayloadLiteral,
UniversalAddress,
UnsignedTransaction,
VAA,
WormholeCore,
WormholeMessageId,
encoding,
toChainId,
} from "@wormhole-foundation/connect-sdk";
import {
AlgorandAddress,
AlgorandChains,
AlgorandPlatform,
AlgorandPlatformType,
AlgorandUnsignedTransaction,
AnyAlgorandAddress,
TransactionSignerPair,
} from "@wormhole-foundation/connect-sdk-algorand";
import { Algodv2, bytesToBigInt, decodeAddress, getApplicationAddress, modelsv2 } from "algosdk";
import { submitVAAHeader } from "./vaa";

export class AlgorandWormholeCore<N extends Network, C extends AlgorandChains>
implements WormholeCore<N, AlgorandPlatformType, C>
Expand All @@ -31,7 +31,7 @@ export class AlgorandWormholeCore<N extends Network, C extends AlgorandChains>
readonly tokenBridgeAppId: bigint;
readonly tokenBridgeAppAddress: string;

private constructor(
constructor(
readonly network: N,
readonly chain: C,
readonly connection: Algodv2,
Expand All @@ -54,11 +54,13 @@ export class AlgorandWormholeCore<N extends Network, C extends AlgorandChains>
this.tokenBridgeAppAddress = getApplicationAddress(tokenBridge);
}

verifyMessage(
sender: AccountAddress<C>,
vaa: VAA<PayloadLiteral>,
): AsyncGenerator<UnsignedTransaction<N, C>, any, unknown> {
throw new Error("Method not implemented.");
async *verifyMessage(sender: AnyAlgorandAddress, vaa: VAA) {
const appId = 0n;
const address = new AlgorandAddress(sender).toString();
const txset = await submitVAAHeader(this.connection, this.coreAppId, appId, vaa, address);
for (const tx of txset.txs) {
yield this.createUnsignedTx(tx, "Core.verifyMessage");
}
}

static async fromRpc<N extends Network>(
Expand Down Expand Up @@ -115,4 +117,17 @@ export class AlgorandWormholeCore<N extends Network, C extends AlgorandChains>
}
return sequence;
}
private createUnsignedTx(
txReq: TransactionSignerPair,
description: string,
parallelizable: boolean = true, // Default true for Algorand atomic transaction grouping
): AlgorandUnsignedTransaction<N, C> {
return new AlgorandUnsignedTransaction(
txReq,
this.network,
this.chain,
description,
parallelizable,
);
}
}
2 changes: 2 additions & 0 deletions platforms/algorand/protocols/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ declare global {
registerProtocol(_platform, "WormholeCore", AlgorandWormholeCore);

export * from "./core";
export * from "./vaa";
export * from "./storage";
94 changes: 94 additions & 0 deletions platforms/algorand/protocols/core/src/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {
Algodv2,
LogicSigAccount,
SuggestedParams,
Transaction,
getApplicationAddress,
makeApplicationOptInTxnFromObject,
makePaymentTxnWithSuggestedParamsFromObject,
signLogicSigTransaction,
} from "algosdk";
import {
SEED_AMT,
TransactionSet,
TransactionSignerPair,
safeBigIntToNumber,
} from "@wormhole-foundation/connect-sdk-algorand";

/**
* Checks to see if the account exists for the application
* @param client An Algodv2 client
* @param appId Application ID
* @param acctAddr Account address to check
* @returns True, if account exists for application, False otherwise
*/
export async function storageAccountExists(
client: Algodv2,
address: string,
appId: bigint,
): Promise<boolean> {
try {
const acctAppInfo = client
.accountApplicationInformation(address, safeBigIntToNumber(appId))
.do();
return Object.keys(acctAppInfo).length > 0;
} catch (e) {}
return false;
}

/**
* Constructs opt in transactions
* @param client An Algodv2 client
* @param senderAddr Sender address
* @param appId Application ID
* @param storage StorageLogicSig
* @returns Address and array of TransactionSignerPairs
*/
export async function maybeCreateStorageTx(
client: Algodv2,
senderAddr: string,
appId: bigint,
storage: LogicSigAccount,
suggestedParams?: SuggestedParams,
): Promise<TransactionSet> {
const appAddr: string = getApplicationAddress(appId);
const storageAddress = storage.address();

const txs: TransactionSignerPair[] = [];

try {
const exists = await storageAccountExists(client, storageAddress, appId);
if (exists) return { accounts: [storageAddress], txs };
} catch {}

suggestedParams = suggestedParams ?? (await client.getTransactionParams().do());

const seedTxn = makePaymentTxnWithSuggestedParamsFromObject({
from: senderAddr,
to: storageAddress,
amount: SEED_AMT,
suggestedParams,
});
seedTxn.fee = seedTxn.fee * 2;
txs.push({ tx: seedTxn, signer: null });

const optinTxn = makeApplicationOptInTxnFromObject({
from: storageAddress,
suggestedParams,
appIndex: safeBigIntToNumber(appId),
rekeyTo: appAddr,
});
optinTxn.fee = 0;
txs.push({
tx: optinTxn,
signer: {
address: storage.address(),
signTxn: (txn: Transaction) => Promise.resolve(signLogicSigTransaction(txn, storage).blob),
},
});

return {
accounts: [storageAddress],
txs,
};
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { TokenBridge, encoding, keccak256, serialize } from "@wormhole-foundation/connect-sdk";
import { VAA, encoding, keccak256, serialize } from "@wormhole-foundation/connect-sdk";
import {
TransactionSignerPair,
ALGO_VERIFY,
ALGO_VERIFY_HASH,
MAX_SIGS_PER_TXN,
safeBigIntToNumber,
decodeLocalState,
StorageLogicSig,
TransactionSet,
TransactionSignerPair,
decodeLocalState,
safeBigIntToNumber,
} from "@wormhole-foundation/connect-sdk-algorand";
import {
Algodv2,
Expand All @@ -17,12 +18,7 @@ import {
makeApplicationCallTxnFromObject,
signLogicSigTransaction,
} from "algosdk";
import { maybeOptInTx } from "./assets";

type SubmitVAAState = {
accounts: string[];
txs: TransactionSignerPair[];
};
import { maybeCreateStorageTx } from "./storage";

/**
* Submits just the header of the VAA
Expand All @@ -37,9 +33,9 @@ export async function submitVAAHeader(
client: Algodv2,
coreId: bigint,
appid: bigint,
vaa: TokenBridge.VAA,
vaa: VAA,
senderAddr: string,
): Promise<SubmitVAAState> {
): Promise<TransactionSet> {
let txs: TransactionSignerPair[] = [];

// Get storage acct for message ID
Expand All @@ -48,22 +44,18 @@ export async function submitVAAHeader(
sequence: vaa.sequence,
emitter: vaa.emitterAddress,
});
const { address: seqAddr, txs: seqOptInTxs } = await maybeOptInTx(
client,
senderAddr,
appid,
msgStorage,
);
const {
accounts: [seqAddr],
txs: seqOptInTxs,
} = await maybeCreateStorageTx(client, senderAddr, appid, msgStorage);
txs.push(...seqOptInTxs);

// Get storage account for Guardian set
const gsStorage = StorageLogicSig.forGuardianSet(coreId, vaa.guardianSet);
const { address: guardianAddr, txs: guardianOptInTxs } = await maybeOptInTx(
client,
senderAddr,
coreId,
gsStorage,
);
const {
accounts: [guardianAddr],
txs: guardianOptInTxs,
} = await maybeCreateStorageTx(client, senderAddr, coreId, gsStorage);
txs.push(...guardianOptInTxs);

let accts: string[] = [seqAddr, guardianAddr];
Expand Down Expand Up @@ -130,7 +122,7 @@ export async function submitVAAHeader(
txs.push({
tx: appTxn,
signer: {
addr: lsa.address(),
address: lsa.address(),
signTxn: (txn: Transaction) => Promise.resolve(signLogicSigTransaction(txn, lsa).blob),
},
});
Expand Down
Loading

0 comments on commit d0fd509

Please sign in to comment.