@@ -28,21 +28,6 @@ export function TestWasm () {
2828 } ) ) ;
2929}
3030
31- // Test the spend helper.
32- function TestSend ( amount = 3000n , fee = 12000n ) {
33- return Fn . Name ( `Spend ${ amount } for ${ fee } ` , testSend ) ;
34- async function testSend ( chain : Btc ) {
35- const from = chain . P2WPKH ( keypair1 . publicKey ( ) ) . address ;
36- const to = chain . P2WPKH ( keypair2 . publicKey ( ) ) . address ;
37- const utxo = await chain . getUtxo ( from ) ;
38- return Object . assign ( chain , await SimplicityHL . Spend ( )
39- . asset ( utxo . asset )
40- . input ( utxo , keypair1 )
41- . output ( to , amount )
42- . fee ( fee ) . broadcast ( chain ) ) ;
43- }
44- }
45-
4631// Self-explanatory.
4732export function TestOnLocalnet ( ) {
4833 // Tests that run on temporary localnet:
@@ -109,6 +94,21 @@ export function TestOnTestnet () {
10994 ) ;
11095}
11196
97+ // Test the spend helper.
98+ function TestSend ( amount = 3000n , fee = 12000n ) {
99+ return Fn . Name ( `Spend ${ amount } for ${ fee } ` , testSend ) ;
100+ async function testSend ( chain : Btc ) {
101+ const from = chain . P2WPKH ( keypair1 . publicKey ( ) ) . address ;
102+ const to = chain . P2WPKH ( keypair2 . publicKey ( ) ) . address ;
103+ const utxo = await chain . getUtxo ( from ) ;
104+ return Object . assign ( chain , await SimplicityHL . Spend ( )
105+ . asset ( utxo . asset )
106+ . input ( utxo , keypair1 )
107+ . output ( to , amount )
108+ . fee ( fee ) . broadcast ( chain ) ) ;
109+ }
110+ }
111+
112112/** Define example program. */
113113function TestProgram ( name : string , src : string , {
114114 /** Program runs that should fail. */
@@ -154,52 +154,58 @@ function TestProgram (name: string, src: string, {
154154 // Check against expected program address, if provided.
155155 if ( p2tr ) equal ( program . p2tr , p2tr ) ;
156156
157- // UTXO helpers. TODO move to Bitcoin lib.
158- const toUtxo = ( x : Btc . Utxo ) : Btc . Utxo & { amount : bigint } => Object . assign ( x , {
159- amount : toSat ( x . amount )
160- } ) ;
161-
162- const toSat = ( x : unknown ) : bigint => {
163- if ( typeof x === 'bigint' ) return x ;
164- if ( typeof x === 'number' ) return BigInt ( Math . round ( x * 1e8 ) ) ;
165- throw new Error ( `expected BigInt(100000001)sat or Number(1.00000001)btc, got: ${ x } ` ) ;
166- } ;
167-
168157 // Fund program from deployer:
169- const commitSource = toUtxo ( await chain . getUtxo ( chain . P2WPKH ( keypair1 . publicKey ( ) ) . address ) ) ;
170- const commitFee = toSat ( fee ?? 1e-4 ) ;
171- const commitAmount = ( toSat ( commitSource . amount ) / 10n ) - commitFee ;
158+ const commitSource = Btc . Utxo ( await chain . getUtxo ( chain . P2WPKH ( keypair1 . publicKey ( ) ) . address ) ) ;
159+ const commitFee = Btc . toSat ( fee ?? 1e-4 ) ;
160+ const commitAmount = ( Btc . toSat ( commitSource . amount ) / 10n ) - commitFee ;
172161 const commitTxid = await SimplicityHL . Spend ( ) // TODO wrap as program.commit() ?
173- . asset ( commitSource . asset )
174- . input ( commitSource , keypair1 )
175- . output ( program . p2tr , commitAmount )
176- . fee ( commitFee )
177- . broadcast ( chain ) ;
162+ . asset ( commitSource . asset ) . input ( commitSource , keypair1 )
163+ . output ( program . p2tr , commitAmount ) . fee ( commitFee ) . broadcast ( chain ) ;
178164 debug ( 'Commit TX:' , commitTxid ) ;
179165
180166 // Note current recipient balance:
181167 const recipient = chain . P2WPKH ( keypair1 . publicKey ( ) ) . address ;
182168 const recipientBalance = async ( asset = 'bitcoin' ) =>
183- toSat ( ( await chain . getBalance ( recipient , 0 ) ) [ asset ] ?? 0 ) ;
169+ Btc . toSat ( ( await chain . getBalance ( recipient , 0 ) ) [ asset ] ?? 0 ) ;
184170 const balance = await recipientBalance ( ) ;
185171
186172 // Find commit (deploy) output = redeem (spend) input:
187173 // This is a tad different across RPC vs Esplora, TODO move to lib too:
188174 const asset = commitSource . asset ;
189- const prev = await chain . getTxInfo ( commitTxid ) ;
175+
176+ // Nasty wait-for-TX loop because of potential race condition between Esplora endpoints
177+ let prev ;
178+ let retries = 30 ;
179+ while ( retries > 0 ) try {
180+ prev = await chain . getTxInfo ( commitTxid ) ;
181+ break ;
182+ } catch ( e ) {
183+ retries -- ;
184+ debug ( e ) ;
185+ debug ( 'Waiting for tx' , commitTxid ) ;
186+ await sleep ( 1000 ) ;
187+ }
190188 const txid = prev . txid ;
189+
191190 debug ( 'Redeem from:' , prev ) ;
192191 const toSPKA = ( x : Btc . Utxo ) => x . scriptpubkey_address || x . scriptPubKey ?. address ;
193- const finder = ( x : Btc . Utxo ) => toSPKA ( x ) === p2tr ;
192+ let index = null ;
193+ const finder = ( x : Btc . Utxo , i : number ) => {
194+ if ( toSPKA ( x ) === p2tr ) {
195+ index = i ;
196+ return true ;
197+ }
198+ } ;
194199 const vout = prev . vout . find ( finder ) ;
195200 if ( ! vout ) throw new Error ( 'no corresponding vout found' ) ;
196- const redeemSource = toUtxo ( { txid, asset, vout : vout . n , address : toSPKA ( vout ) , amount : vout . value } ) ;
201+ const redeemSource = Btc . Utxo ( { txid, asset, vout : index , address : toSPKA ( vout ) , amount : vout . value } ) ;
197202 debug ( 'Redeem UTXO:' , redeemSource ) ;
198203
199204 // To get SIGHASH_ALL for signing, first the rest of the transaction must be specified:
200- const redeemFee = 10000n ;
201- const redeemAmount = commitAmount - 10000n ;
205+ const redeemFee = 1000n ;
206+ const redeemAmount = commitAmount - redeemFee ;
202207 const sighashOpts = { asset, utxos : [ redeemSource ] , recipient, amount : redeemAmount , fee : redeemFee } ;
208+ debug ( 'Redeem opts:' , sighashOpts ) ;
203209 const sighash = program . redeemSighash ( sighashOpts ) ;
204210 ok ( sighash instanceof Uint8Array , 'sighash expected to be returned from WASM as Uint8Array' )
205211 ok ( Base16 . encode ( sighash ) , 'sighash expected to be base16-encodable' ) ;
@@ -213,9 +219,10 @@ function TestProgram (name: string, src: string, {
213219 }
214220
215221 // Spend from program:
216- const redeemTx = program . redeemTx ( { ...sighashOpts , witness } ) ;
222+ const { hex, ...redeemTx } = program . redeemTx ( { ...sighashOpts , witness } ) ;
223+ debug ( 'Redeeming:' , redeemTx ) ;
217224 // TX is expected to pass
218- await chain . broadcast ( redeemTx . hex ) ;
225+ await chain . broadcast ( hex ) ;
219226 // Balance is expected to increase
220227 equal ( await recipientBalance ( ) , balance + redeemAmount ) ;
221228 }
0 commit comments