Skip to content

Commit 2e3b045

Browse files
author
Hack.bg R&D
committed
wip: test(sdk): need more utxo selector
1 parent 633f889 commit 2e3b045

2 files changed

Lines changed: 55 additions & 48 deletions

File tree

src/sdk.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,19 +110,21 @@ export function Spend (): Spend {
110110
fee = x;
111111
return spend;
112112
},
113-
async broadcast (chain: Btc) {
113+
async broadcast (chain: Btc): Promise<string> {
114114
const debug = (chain.debug ?? console.debug) || (() => {});
115115
if (!utxo) throw new Error('no input specified')
116116
if (!address || !amount) throw new Error('no output specified')
117117
if (!fee) throw new Error('no fee specified')
118118
const sender = chain.P2WPKH(signer.publicKey()).address;
119119
const options = { recipient: address, sender, utxos: [utxo], asset: utxo.asset, amount, fee };
120-
debug('Broadcasting:', options);
120+
debug('Constructing and signing:', options);
121121
const { sendSigned } = await Wasm();
122-
const { hex } = sendSigned(signer, options);
122+
const { hex, ...signed } = sendSigned(signer, options);
123+
debug('Broadcasting:', signed);
123124
const tx = await chain.broadcast(hex);
124-
125+
return tx;
125126
// TODO: Post-broadcast validation?
127+
// This was previously a sanity check in the test suite:
126128
//function assertTxOuts (
127129
//tx: { hex: unknown, vout: unknown[] },
128130
//p2tr: string,
@@ -147,8 +149,6 @@ export function Spend (): Spend {
147149
//return x.value === cost;
148150
//}
149151
//}
150-
151-
return tx;
152152
}
153153
};
154154
return spend;

src/test.ts

Lines changed: 49 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
4732
export 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. */
113113
function 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

Comments
 (0)