@@ -15,11 +15,13 @@ import {
15
15
TransactionInstruction ,
16
16
} from "@solana/web3.js" ;
17
17
import Squads from "@sqds/mesh" ;
18
- import { getIxAuthorityPDA } from "@sqds/mesh" ;
18
+ import { getIxAuthorityPDA , getIxPDA } from "@sqds/mesh" ;
19
+ import { InstructionAccount } from "@sqds/mesh/lib/types" ;
19
20
import bs58 from "bs58" ;
20
21
import { program } from "commander" ;
21
22
import * as fs from "fs" ;
22
23
import { LedgerNodeWallet } from "./wallet" ;
24
+ import loadash from "lodash" ;
23
25
24
26
setDefaultWasm ( "node" ) ;
25
27
@@ -86,6 +88,44 @@ program
86
88
) ;
87
89
} ) ;
88
90
91
+ program
92
+ . command ( "verify" )
93
+ . description ( "Verify given wormhole transaction has the given payload" )
94
+ . option ( "-c, --cluster <network>" , "solana cluster to use" , "devnet" )
95
+ . option ( "-l, --ledger" , "use ledger" )
96
+ . option (
97
+ "-lda, --ledger-derivation-account <number>" ,
98
+ "ledger derivation account to use"
99
+ )
100
+ . option (
101
+ "-ldc, --ledger-derivation-change <number>" ,
102
+ "ledger derivation change to use"
103
+ )
104
+ . option (
105
+ "-w, --wallet <filepath>" ,
106
+ "multisig wallet secret key filepath" ,
107
+ "keys/key.json"
108
+ )
109
+ . requiredOption ( "-p, --payload <hex-string>" , "expected payload" )
110
+ . requiredOption ( "-t, --tx-pda <address>" , "transaction PDA" )
111
+ . action ( async ( options ) => {
112
+ const cluster : Cluster = options . cluster ;
113
+ const squad = await getSquadsClient (
114
+ cluster ,
115
+ options . ledger ,
116
+ options . ledgerDerivationAccount ,
117
+ options . ledgerDerivationChange ,
118
+ options . wallet
119
+ ) ;
120
+ await verifyWormholePayload (
121
+ options . cluster ,
122
+ squad ,
123
+ CONFIG [ cluster ] . vault ,
124
+ new PublicKey ( options . txPda ) ,
125
+ options . payload ,
126
+ ) ;
127
+ } ) ;
128
+
89
129
program
90
130
. command ( "set-is-active" )
91
131
. description (
@@ -392,8 +432,7 @@ async function addInstructionsToTx(
392
432
await squad . approveTransaction ( txKey ) ;
393
433
console . log ( "Transaction approved." ) ;
394
434
console . log (
395
- `Tx URL: https://mesh${
396
- cluster === "devnet" ? "-devnet" : ""
435
+ `Tx URL: https://mesh${ cluster === "devnet" ? "-devnet" : ""
397
436
} .squads.so/transactions/${ vault . toBase58 ( ) } /tx/${ txKey . toBase58 ( ) } `
398
437
) ;
399
438
}
@@ -531,6 +570,79 @@ async function createWormholeMsgMultisigTx(
531
570
) ;
532
571
}
533
572
573
+ async function verifyWormholePayload (
574
+ cluster : Cluster ,
575
+ squad : Squads ,
576
+ vault : PublicKey ,
577
+ txPubkey : PublicKey ,
578
+ payload : string ,
579
+ ) {
580
+ const msAccount = await squad . getMultisig ( vault ) ;
581
+ const emitter = squad . getAuthorityPDA (
582
+ msAccount . publicKey ,
583
+ msAccount . authorityIndex
584
+ ) ;
585
+ console . log ( `Emitter Address: ${ emitter . toBase58 ( ) } ` ) ;
586
+
587
+ const tx = await squad . getTransaction ( txPubkey ) ;
588
+
589
+ if ( tx . instructionIndex !== 2 ) {
590
+ throw new Error ( `Expected 2 instructions in the transaction, found ${ tx . instructionIndex + 1 } ` )
591
+ }
592
+
593
+ const [ ix1PubKey , ] = getIxPDA ( txPubkey , new anchor . BN ( 1 ) , squad . multisigProgramId ) ;
594
+ const [ ix2PubKey , ] = getIxPDA ( txPubkey , new anchor . BN ( 2 ) , squad . multisigProgramId ) ;
595
+
596
+ const onChainInstructions = await squad . getInstructions ( [ ix1PubKey , ix2PubKey ] ) ;
597
+
598
+ console . log ( onChainInstructions [ 0 ] ) ;
599
+ console . log ( onChainInstructions [ 1 ] ) ;
600
+
601
+ const [ messagePDA , ] = getIxAuthorityPDA (
602
+ txPubkey ,
603
+ new anchor . BN ( 1 ) ,
604
+ squad . multisigProgramId
605
+ ) ;
606
+
607
+ const wormholeIxs = await getWormholeMessageIx (
608
+ cluster ,
609
+ emitter ,
610
+ emitter ,
611
+ messagePDA ,
612
+ squad . connection ,
613
+ payload
614
+ ) ;
615
+
616
+ console . log ( "Checking equality of the 1st instruction..." ) ;
617
+ verifyOnChainInstruction ( wormholeIxs [ 0 ] , onChainInstructions [ 0 ] as InstructionAccount ) ;
618
+
619
+ console . log ( "Checking equality of the 2nd instruction..." ) ;
620
+ verifyOnChainInstruction ( wormholeIxs [ 1 ] , onChainInstructions [ 1 ] as InstructionAccount ) ;
621
+
622
+ console . log ( "✅ The transaction is verified to be created with the given payload." ) ;
623
+ }
624
+
625
+ function verifyOnChainInstruction ( instruction : TransactionInstruction , onChainInstruction : InstructionAccount ) {
626
+ if ( ! instruction . programId . equals ( onChainInstruction . programId ) ) {
627
+ throw new Error (
628
+ `Program id mismatch: Expected ${ instruction . programId . toBase58 ( ) } , found ${ onChainInstruction . programId . toBase58 ( ) } `
629
+ ) ;
630
+ }
631
+
632
+ if ( ! loadash . isEqual ( instruction . keys , onChainInstruction . keys ) ) {
633
+ throw new Error (
634
+ `Instruction accounts mismatch. Expected ${ instruction . keys } , found ${ onChainInstruction . keys } `
635
+ ) ;
636
+ }
637
+
638
+ const onChainData = onChainInstruction . data as Buffer ;
639
+ if ( ! instruction . data . equals ( onChainData ) ) {
640
+ throw new Error (
641
+ `Instruction data mismatch. Expected ${ instruction . data . toString ( 'hex' ) } , Found ${ onChainData . toString ( 'hex' ) } `
642
+ )
643
+ }
644
+ }
645
+
534
646
async function executeMultisigTx (
535
647
cluster : string ,
536
648
squad : Squads ,
@@ -584,8 +696,7 @@ async function executeMultisigTx(
584
696
const signature = await provider . sendAndConfirm ( executeTx ) ;
585
697
586
698
console . log (
587
- `Executed tx: https://explorer.solana.com/tx/${ signature } ${
588
- cluster === "devnet" ? "?cluster=devnet" : ""
699
+ `Executed tx: https://explorer.solana.com/tx/${ signature } ${ cluster === "devnet" ? "?cluster=devnet" : ""
589
700
} `
590
701
) ;
591
702
0 commit comments