@@ -6,68 +6,99 @@ import { regtestUtils } from './_regtest';
6
6
import * as bitcoin from '../..' ;
7
7
import { toXOnly } from '../../src/psbt/bip371' ;
8
8
9
+ import { tweakSigner } from './taproot.utils' ;
10
+
9
11
const rng = require ( 'randombytes' ) ;
10
12
const regtest = regtestUtils . network ;
11
13
bitcoin . initEccLib ( ecc ) ;
12
14
const bip32 = BIP32Factory ( ecc ) ;
13
15
14
16
describe ( 'bitcoinjs-lib (silent payments)' , ( ) => {
15
17
it ( 'can create (and broadcast via 3PBP) a simple silent payment' , async ( ) => {
18
+ // for simplicity the transactions in this test have only one input and one output
19
+
16
20
const { senderKeyPair, receiverKeyPair, sharedSecret } = initParticipants ( ) ;
17
- // this is what the sender sees/scans
21
+
22
+ // this is what the sender sees/scans (from twitter bio, public forum, truck door)
18
23
const silentPublicKey = toXOnly ( receiverKeyPair . publicKey ) ;
19
24
20
- // the input being spent
21
- const { output : p2wpkhOutput } = bitcoin . payments . p2wpkh ( {
22
- pubkey : senderKeyPair . publicKey ,
25
+ const senderUtxo = await fundP2pkhUtxo ( senderKeyPair . publicKey ) ;
26
+
27
+ // amount to pay the silent address
28
+ const payAmount = senderUtxo . value - 1e4 ;
29
+ const {
30
+ psbt : payPsbt ,
31
+ address : tweakedSilentAddress ,
32
+ } = buildPayToSilentAddress (
33
+ senderUtxo . txId ,
34
+ senderUtxo ,
35
+ silentPublicKey ,
36
+ payAmount ,
37
+ sharedSecret ,
38
+ ) ;
39
+ payPsbt . signInput ( 0 , senderKeyPair ) . finalizeAllInputs ( ) ;
40
+
41
+ // the transaction paying to the silent address
42
+ const payTx = payPsbt . extractTransaction ( ) ;
43
+ await broadcastAndVerifyTx ( payTx , tweakedSilentAddress ! , payAmount ) ;
44
+
45
+ // the amount the receiver will spend
46
+ const sendAmount = payAmount - 1e4 ;
47
+ // the utxo with the tweaked silent address
48
+ const receiverUtxo = { value : payAmount , script : payTx . outs [ 0 ] . script } ;
49
+ const { psbt : spendPsbt , address } = buildSpendFromSilentAddress (
50
+ payTx . getId ( ) ,
51
+ receiverUtxo ,
52
+ silentPublicKey ,
53
+ sendAmount ,
54
+ sharedSecret ,
55
+ ) ;
56
+
57
+ const tweakedSigner = tweakSigner ( receiverKeyPair ! , {
58
+ tweakHash : sharedSecret ,
23
59
network : regtest ,
24
60
} ) ;
61
+ spendPsbt . signInput ( 0 , tweakedSigner ) . finalizeAllInputs ( ) ;
25
62
26
- // amount from faucet
27
- const amount = 42e4 ;
28
- // amount to send
29
- const sendAmount = amount - 1e4 ;
30
- // get faucet
31
- const unspent = await regtestUtils . faucetComplex ( p2wpkhOutput ! , amount ) ;
32
-
33
- const psbt = new bitcoin . Psbt ( { network : regtest } ) ;
34
- psbt . addInput ( {
35
- hash : unspent . txId ,
36
- index : 0 ,
37
- witnessUtxo : { value : amount , script : p2wpkhOutput ! } ,
38
- } ) ;
39
-
40
- // destination
41
- const { address } = bitcoin . payments . p2tr ( {
42
- internalPubkey : silentPublicKey ,
43
- hash : sharedSecret ,
44
- network : regtest ,
45
- } ) ;
46
- psbt . addOutput ( { value : sendAmount , address : address ! } ) ;
63
+ // the transaction spending from the silent address
64
+ const spendTx = spendPsbt . extractTransaction ( ) ;
65
+ await broadcastAndVerifyTx ( spendTx , address ! , sendAmount ) ;
66
+ } ) ;
67
+ } ) ;
47
68
48
- psbt . signInput ( 0 , senderKeyPair ) ;
69
+ async function fundP2pkhUtxo ( senderPubKey : Buffer ) {
70
+ // the input being spent
71
+ const { output : p2wpkhOutput } = bitcoin . payments . p2wpkh ( {
72
+ pubkey : senderPubKey ,
73
+ network : regtest ,
74
+ } ) ;
49
75
50
- psbt . finalizeAllInputs ( ) ;
51
- const tx = psbt . extractTransaction ( ) ;
52
- const rawTx = tx . toBuffer ( ) ;
76
+ // amount from faucet
77
+ const amount = 42e4 ;
78
+ // get faucet
79
+ const unspent = await regtestUtils . faucetComplex ( p2wpkhOutput ! , amount ) ;
53
80
54
- const hex = rawTx . toString ( 'hex' ) ;
81
+ return { value : amount , script : p2wpkhOutput ! , txId : unspent . txId } ;
82
+ }
55
83
56
- await regtestUtils . broadcast ( hex ) ;
57
- await regtestUtils . verify ( {
58
- txId : tx . getId ( ) ,
59
- address : address ! ,
60
- vout : 0 ,
61
- value : sendAmount ,
62
- } ) ;
84
+ async function broadcastAndVerifyTx (
85
+ tx : bitcoin . Transaction ,
86
+ address : string ,
87
+ value : number ,
88
+ ) {
89
+ await regtestUtils . broadcast ( tx . toBuffer ( ) . toString ( 'hex' ) ) ;
90
+ await regtestUtils . verify ( {
91
+ txId : tx . getId ( ) ,
92
+ address : address ! ,
93
+ vout : 0 ,
94
+ value,
63
95
} ) ;
64
- } ) ;
96
+ }
65
97
66
98
function initParticipants ( ) {
67
99
const receiverKeyPair = bip32 . fromSeed ( rng ( 64 ) , regtest ) ;
68
100
const senderKeyPair = bip32 . fromSeed ( rng ( 64 ) , regtest ) ;
69
101
70
-
71
102
const senderSharedSecret = ecc . pointMultiply (
72
103
receiverKeyPair . publicKey ,
73
104
senderKeyPair . privateKey ! ,
@@ -88,4 +119,55 @@ function initParticipants() {
88
119
} ;
89
120
}
90
121
122
+ function buildPayToSilentAddress (
123
+ prevOutTxId : string ,
124
+ witnessUtxo : { value : number ; script : Buffer } ,
125
+ silentPublicKey : Buffer ,
126
+ sendAmount : number ,
127
+ sharedSecret : Buffer ,
128
+ ) {
129
+ const psbt = new bitcoin . Psbt ( { network : regtest } ) ;
130
+ psbt . addInput ( {
131
+ hash : prevOutTxId ,
132
+ index : 0 ,
133
+ witnessUtxo,
134
+ } ) ;
135
+
136
+ // destination
137
+ const { address } = bitcoin . payments . p2tr ( {
138
+ internalPubkey : silentPublicKey ,
139
+ hash : sharedSecret ,
140
+ network : regtest ,
141
+ } ) ;
142
+ psbt . addOutput ( { value : sendAmount , address : address ! } ) ;
143
+
144
+ return { psbt, address } ;
145
+ }
146
+
147
+ function buildSpendFromSilentAddress (
148
+ prevOutTxId : string ,
149
+ witnessUtxo : { value : number ; script : Buffer } ,
150
+ silentPublicKey : Buffer ,
151
+ sendAmount : number ,
152
+ sharedSecret : Buffer ,
153
+ ) {
154
+ const psbt = new bitcoin . Psbt ( { network : regtest } ) ;
155
+ psbt . addInput ( {
156
+ hash : prevOutTxId ,
157
+ index : 0 ,
158
+ witnessUtxo,
159
+ tapInternalKey : silentPublicKey ,
160
+ tapMerkleRoot : sharedSecret ,
161
+ } ) ;
162
+
163
+ // random address value, not important
164
+ const address =
165
+ 'bcrt1pqknex3jwpsaatu5e5dcjw70nac3fr5k5y3hcxr4hgg6rljzp59nqs6a0vh' ;
166
+ psbt . addOutput ( {
167
+ value : sendAmount ,
168
+ address,
169
+ } ) ;
170
+
171
+ return { psbt, address } ;
172
+ }
91
173
const toBuffer = ( a : Uint8Array ) => Buffer . from ( a ) ;
0 commit comments