Skip to content

Commit 633f889

Browse files
author
Hack.bg R&D
committed
wip: test: liquidtestnet redeem procedure
1 parent 40be31b commit 633f889

2 files changed

Lines changed: 44 additions & 22 deletions

File tree

src/sdk.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ export function Spend (): Spend {
9696
if (!asset) {
9797
throw new Error('use .asset(id) first to assert asset id')
9898
}
99+
if (typeof y === 'string' && isNaN(Number(y))) {
100+
throw new Error(`.output(address, amount <- must be numeric, got ${y}`)
101+
}
102+
if ((typeof y === 'bigint' && y < 0n) || Number(y) <= 0) {
103+
throw new Error('.output(address, amount <- must be >0')
104+
}
99105
address = x;
100106
amount = y;
101107
return spend;

src/test.ts

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,19 @@ export function TestOnLocalnet () {
5353
// Empty program, always passes:
5454
TestProgram("unit program", 'fn main () {}', {
5555
p2tr: 'ert1p9jcvyzkdwdqtf49kta4xpc5g35xkfcexwfsl8v70w2gwttelncyspjlnrz',
56-
cmr: 'c40a10263f7436b4160acbef1c36fba4be4d95df181a968afeab5eac247adff7',
57-
fee: 2.7e-7 }),
56+
fee: 2.7e-7
57+
}),
5858
// Correct assertion, always passes:
5959
TestProgram("assert true", 'fn main () { assert!(true) }', {
6060
p2tr: 'ert1per0vg2wvc4ua2rsndm8j6062r7z7ys7q6wcvwumepgz8t5m6hfhsrd8d8q',
61-
cmr: '633f62f67589423aafcd3ce0a4dc41f6192403c4aeb61997f438dbd7b96c5cf7',
62-
fee: 2.7e-7 }),
61+
fee: 2.7e-7
62+
}),
6363
// Incorrect assertion, always fails:
6464
TestProgram("assert false fails", 'fn main () { assert!(false) }', {
6565
shouldFail: true,
6666
p2tr: 'ert1p7p4rgaw5dmhxt6qutf2v3rtuy6afghfgktmmedkpju5uamxdz5js3hdug9',
67-
cmr: 'd3c6b9ecfc2876ec72f0099c6f454b7b34645d08c1f220c05ae80e77eed4bdf3',
68-
fee: 2.7e-7 }),
67+
fee: 2.7e-7
68+
}),
6969
// Test basic language features. Guards against general failure of all jets
7070
// (Symptom of mislinked WASM, see other mentions in README and/or comments.)
7171
TestProgram("basic jets work", `fn main () {
@@ -75,14 +75,12 @@ export function TestOnLocalnet () {
7575
assert!(jet::eq_8(ab, 0b10111101));
7676
}`, {
7777
p2tr: 'ert1pmy9edmq0yfrc477jvcc835umyajlgjsnyujplt8nppr45zrwl7qs02gj3x',
78-
cmr: 'b8b3509f12177723609e3995101ff589e504361ce32ec4d417bba3b37bbb7fac',
7978
fee: 2.7e-7, }),
8079
// Witness signing:
8180
TestProgram("pay to pubkey", `fn main () {
8281
jet::bip_0340_verify((param::PK, jet::sig_all_hash()), witness::SIG)
8382
}`, {
8483
p2tr: 'ert1pa69jdawgz5wu5uc8ce2cv7lcqf64kadyl4wsrddparl25erfj2vq9824m9',
85-
cmr: 'b1b4447ce3082324635798876f1ae6c9aec9a228eb6e21e3cb991f8970986965',
8684
argTypes: { PK: "u256" },
8785
witTypes: { SIG: "[u8; 64]" },
8886
provideArgs: () => ({
@@ -106,8 +104,7 @@ export function TestOnTestnet () {
106104
TestSend(), // Test the basic transaction primitive
107105
Test('Programs',
108106
TestProgram("unit program", 'fn main () {}', {
109-
p2tr: 'ert1p9jcvyzkdwdqtf49kta4xpc5g35xkfcexwfsl8v70w2gwttelncyspjlnrz',
110-
cmr: 'c40a10263f7436b4160acbef1c36fba4be4d95df181a968afeab5eac247adff7',
107+
p2tr: 'tex1p9jcvyzkdwdqtf49kta4xpc5g35xkfcexwfsl8v70w2gwttelncyshxjk56',
111108
fee: 2.7e-7 })),
112109
);
113110
}
@@ -122,7 +119,9 @@ function TestProgram (name: string, src: string, {
122119
cmr = null as null|string,
123120
/** Expected pay-to-taproot address of program. */
124121
p2tr = null as null|string,
122+
/** Expected compile-time signature of program. */
125123
argTypes = {} as Record<string, string>,
124+
/** Expected runtime signature of program. */
126125
witTypes = {} as Record<string, string>,
127126
/** Function that provides parameter data. */
128127
provideArgs = null as null|Fn.Returns<Async<SimplicityHL.Args>>,
@@ -136,7 +135,7 @@ function TestProgram (name: string, src: string, {
136135

137136
// Test the SimplicityHL program specified above on the given chain.
138137
async function testProgram (chain: Btc) {
139-
138+
const debug = (chain.debug ?? console.debug) || (()=>{});
140139
// Compile this program with these arguments for this chain.
141140
const program = await SimplicityHL.Program(src, {
142141
// Expected program address, optional. Makes it safer.
@@ -155,35 +154,52 @@ function TestProgram (name: string, src: string, {
155154
// Check against expected program address, if provided.
156155
if (p2tr) equal(program.p2tr, p2tr);
157156

158-
// Fund program from deployer:
159-
const commitSource = await chain.getUtxo(chain.P2WPKH(keypair1.publicKey()).address);
160-
const commitAmount = BigInt(Math.floor(commitSource.amount / 100) * 1e8) - BigInt(fee * 1e8);
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+
};
161167

162-
const commitTxid = await SimplicityHL.Spend() // TODO wrap as program.commit() ?
168+
// 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;
172+
const commitTxid = await SimplicityHL.Spend() // TODO wrap as program.commit() ?
163173
.asset(commitSource.asset)
164174
.input(commitSource, keypair1)
165175
.output(program.p2tr, commitAmount)
166-
.fee(fee)
176+
.fee(commitFee)
167177
.broadcast(chain);
178+
debug('Commit TX:', commitTxid);
168179

169180
// Note current recipient balance:
170181
const recipient = chain.P2WPKH(keypair1.publicKey()).address;
171182
const recipientBalance = async (asset = 'bitcoin') =>
172-
BigInt(Math.round((await chain.getBalance(recipient, 0))[asset] * 1e8));
183+
toSat((await chain.getBalance(recipient, 0))[asset] ?? 0);
173184
const balance = await recipientBalance();
174185

175186
// Find commit (deploy) output = redeem (spend) input:
187+
// This is a tad different across RPC vs Esplora, TODO move to lib too:
176188
const asset = commitSource.asset;
177189
const prev = await chain.getTxInfo(commitTxid);
178190
const txid = prev.txid;
179-
const vout = prev.vout.filter(x=>x.scriptPubKey.address === p2tr)[0];
191+
debug('Redeem from:', prev);
192+
const toSPKA = (x: Btc.Utxo) => x.scriptpubkey_address || x.scriptPubKey?.address;
193+
const finder = (x: Btc.Utxo) => toSPKA(x) === p2tr;
194+
const vout = prev.vout.find(finder);
180195
if (!vout) throw new Error('no corresponding vout found');
181-
const utxos = [{ txid, asset, vout: vout.n, address: vout.scriptPubKey.address, amount: vout.value }];
196+
const redeemSource = toUtxo({ txid, asset, vout: vout.n, address: toSPKA(vout), amount: vout.value });
197+
debug('Redeem UTXO:', redeemSource);
182198

183199
// To get SIGHASH_ALL for signing, first the rest of the transaction must be specified:
184-
const redeemFee = 1e-4;
185-
const redeemAmount = commitAmount - BigInt(redeemFee * 1e8);
186-
const sighashOpts = { asset, utxos, recipient, amount: redeemAmount, fee: redeemFee };
200+
const redeemFee = 10000n;
201+
const redeemAmount = commitAmount - 10000n;
202+
const sighashOpts = { asset, utxos: [redeemSource], recipient, amount: redeemAmount, fee: redeemFee };
187203
const sighash = program.redeemSighash(sighashOpts);
188204
ok(sighash instanceof Uint8Array, 'sighash expected to be returned from WASM as Uint8Array')
189205
ok(Base16.encode(sighash), 'sighash expected to be base16-encodable');

0 commit comments

Comments
 (0)