Skip to content

Commit 531fc3d

Browse files
OttoAllmendingerllm-git
andcommitted
feat(utxo-staking): add babylon unstaking transaction functionality
Add ability to create and sign babylon unstaking transactions. Implement test fixtures for both testnet and mock environments to validate behavior. Issue: BTC-1966 Co-authored-by: llm-git <[email protected]>
1 parent d46fecf commit 531fc3d

File tree

3 files changed

+140
-4
lines changed

3 files changed

+140
-4
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"transaction": {
3+
"version": 2,
4+
"locktime": 0,
5+
"ins": [
6+
{
7+
"hash": "64da14050ffd0e6186c68a59f371a0adc95cc84b0502fe0915be7eba3f771bb3",
8+
"index": 0,
9+
"script": "",
10+
"sequence": 10000,
11+
"witness": [
12+
"34a9fb0ebbcd13f30cb8ca398bdedb23073c19cabb17b6de6ba4f45afea360bd9798b4fa5d2d886af2fd1e161f37787316fa3482e45ef4a6ca5692a619550ac0",
13+
"207b6a08504c46336f985a5787a5423eb18c10b149fef29cd3316ad86f565cd2b9ad021027b2",
14+
"c050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac01e1d2e84c4fc6bdddf3b8018065cf5ca8405988579188585f382c51c088e882696962cb69242e00941e0a6197b7ecdb83ab8bb0490dbfd23fa4b8d26b75989eb"
15+
]
16+
}
17+
],
18+
"outs": [
19+
{
20+
"value": "54555",
21+
"script": "0014896f1ba65deaeb045bb3121e20e5744e66ca0e48"
22+
}
23+
],
24+
"network": {
25+
"messagePrefix": "\u0018Bitcoin Signed Message:\n",
26+
"bech32": "bc",
27+
"bip32": {
28+
"public": 76067358,
29+
"private": 76066276
30+
},
31+
"pubKeyHash": 0,
32+
"scriptHash": 5,
33+
"wif": 128,
34+
"coin": "btc"
35+
}
36+
}
37+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"transaction": {
3+
"version": 2,
4+
"locktime": 0,
5+
"ins": [
6+
{
7+
"hash": "a999d7bc6af1a17cd4e70d0fd875a1734152dfb55f9166240f35965c79fa36c2",
8+
"index": 0,
9+
"script": "",
10+
"sequence": 10000,
11+
"witness": [
12+
"3856a8f99911738be2687076e7aa977499f9db8187ed85d574e7cbaa10520252b8d954011b35d18afb875cfac5019f937a2dcbc2e4143cf12c2c120f8250a8ab",
13+
"207b6a08504c46336f985a5787a5423eb18c10b149fef29cd3316ad86f565cd2b9ad021027b2",
14+
"c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac068cc9790c0627bc8f28afd009b31974a2fce5ea4fe2ed4dd8b65c8feda196875c1d262cfe1f3b746337f6766e31854333d370f733bd845727aaed2baebc7a552"
15+
]
16+
}
17+
],
18+
"outs": [
19+
{
20+
"value": "54555",
21+
"script": "0014896f1ba65deaeb045bb3121e20e5744e66ca0e48"
22+
}
23+
],
24+
"network": {
25+
"messagePrefix": "\u0018Bitcoin Signed Message:\n",
26+
"bech32": "bc",
27+
"bip32": {
28+
"public": 76067358,
29+
"private": 76066276
30+
},
31+
"pubKeyHash": 0,
32+
"scriptHash": 5,
33+
"wif": 128,
34+
"coin": "btc"
35+
}
36+
}
37+
}

modules/utxo-staking/test/unit/babylon/transactions.ts

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import * as bitcoinjslib from 'bitcoinjs-lib';
55
import * as utxolib from '@bitgo/utxo-lib';
66
import { ECPairInterface } from '@bitgo/utxo-lib';
77
import { ast, Descriptor, Miniscript } from '@bitgo/wasm-miniscript';
8-
import { createAddressFromDescriptor } from '@bitgo/utxo-core/descriptor';
8+
import {
9+
createAddressFromDescriptor,
10+
createPsbt,
11+
getNewSignatureCount,
12+
signWithKey,
13+
toUtxoPsbt,
14+
toWrappedPsbt,
15+
} from '@bitgo/utxo-core/descriptor';
916
import { getFixture, toPlainObject } from '@bitgo/utxo-core/testutil';
1017
import { getBabylonParamByVersion } from '@bitgo/babylonlabs-io-btc-staking-ts';
1118

@@ -89,6 +96,40 @@ function getStakingTransactionTreeVendor(
8996
};
9097
}
9198

99+
function createUnstakingTransaction(
100+
stakingTx: vendor.TransactionResult,
101+
stakingDescriptor: Descriptor,
102+
changeAddress: string,
103+
{ sequence }: { sequence: number }
104+
): utxolib.Psbt {
105+
const network = utxolib.networks.bitcoin;
106+
const witnessUtxoNumber = stakingTx.transaction.outs[0];
107+
const witnessUtxo = {
108+
script: witnessUtxoNumber.script,
109+
value: BigInt(witnessUtxoNumber.value),
110+
};
111+
return createPsbt(
112+
{
113+
network,
114+
},
115+
[
116+
{
117+
hash: stakingTx.transaction.getId(),
118+
index: 0,
119+
witnessUtxo,
120+
descriptor: stakingDescriptor,
121+
sequence,
122+
},
123+
],
124+
[
125+
{
126+
script: utxolib.address.toOutputScript(changeAddress, network),
127+
value: BigInt(witnessUtxoNumber.value) - 1000n,
128+
},
129+
]
130+
);
131+
}
132+
92133
function getTestnetStakingParamsWithCovenant(
93134
params: vendor.StakingParams,
94135
covenantKeys: ECPairInterface[]
@@ -198,7 +239,7 @@ function describeWithKeys(
198239
stakingParams: vendor.StakingParams,
199240
{ signIntermediateTxs = false } = {}
200241
) {
201-
const stakerKey = getECKey('staker');
242+
const stakerKey = getECKey('staker') as ECPairInterface & { privateKey: Buffer };
202243
const covenantThreshold = stakingParams.covenantQuorum;
203244
const stakingTimelock = stakingParams.minStakingTimeBlocks;
204245
const unbondingTimelock = stakingParams.unbondingTime;
@@ -256,15 +297,20 @@ function describeWithKeys(
256297
const feeRateSatB = 2;
257298
const utxo = mockUtxo(mainWallet);
258299

259-
it('has expected transactions', async function () {
260-
const stakingTx = vendor.stakingTransaction(
300+
let stakingTx: vendor.TransactionResult;
301+
302+
before('setup stakingTx', function () {
303+
stakingTx = vendor.stakingTransaction(
261304
vendorBuilder.buildScripts(),
262305
amount,
263306
changeAddress,
264307
[mockUtxo(mainWallet)],
265308
bitcoinjslib.networks.bitcoin,
266309
feeRateSatB
267310
);
311+
});
312+
313+
it('has expected transactions', async function () {
268314
await assertTransactionEqualsFixture(`test/fixtures/babylon/stakingTransaction.${tag}.json`, stakingTx);
269315

270316
// simply one staking output and one change output
@@ -337,6 +383,22 @@ function describeWithKeys(
337383
);
338384
}
339385
});
386+
387+
it('creates unstaking transaction', async function () {
388+
const unstaking = createUnstakingTransaction(
389+
stakingTx,
390+
descriptorBuilder.getStakingDescriptor(),
391+
changeAddress,
392+
{ sequence: stakingParams.minStakingTimeBlocks }
393+
);
394+
const wrappedPsbt = toWrappedPsbt(unstaking);
395+
assert(getNewSignatureCount(signWithKey(wrappedPsbt, stakerKey)) > 0);
396+
wrappedPsbt.finalize();
397+
const tx = toUtxoPsbt(wrappedPsbt, utxolib.networks.bitcoin).extractTransaction();
398+
await assertTransactionEqualsFixture(`test/fixtures/babylon/unstakingTransaction.${tag}.json`, {
399+
transaction: tx,
400+
});
401+
});
340402
});
341403
});
342404
}

0 commit comments

Comments
 (0)