/
transactions.js
157 lines (126 loc) · 7.17 KB
/
transactions.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/* global describe, it */
var assert = require('assert')
var bitcoin = require('../../')
var dhttp = require('dhttp/200')
var testnet = bitcoin.networks.testnet
var testnetUtils = require('./_testnet')
function rng () {
return Buffer.from('YT8dAtK4d16A3P1z+TpwB2jJ4aFH3g9M1EioIBkLEV4=', 'base64')
}
describe('bitcoinjs-lib (transactions)', function () {
it('can create a 1-to-1 Transaction', function () {
var alice = bitcoin.ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy')
var tx = new bitcoin.TransactionBuilder()
tx.addInput('61d520ccb74288c96bc1a2b20ea1c0d5a704776dd0164a396efec3ea7040349d', 0) // Alice's previous transaction output, has 15000 satoshis
tx.addOutput('1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', 12000)
// (in)15000 - (out)12000 = (fee)3000, this is the miner fee
tx.sign(0, alice)
// build, prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below
assert.strictEqual(tx.build().toHex(), '01000000019d344070eac3fe6e394a16d06d7704a7d5c0a10eb2a2c16bc98842b7cc20d561000000006b48304502210088828c0bdfcdca68d8ae0caeb6ec62cd3fd5f9b2191848edae33feb533df35d302202e0beadd35e17e7f83a733f5277028a9b453d525553e3f5d2d7a7aa8010a81d60121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59fffffffff01e02e0000000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac00000000')
})
it('can create a 2-to-2 Transaction', function () {
var alice = bitcoin.ECPair.fromWIF('L1Knwj9W3qK3qMKdTvmg3VfzUs3ij2LETTFhxza9LfD5dngnoLG1')
var bob = bitcoin.ECPair.fromWIF('KwcN2pT3wnRAurhy7qMczzbkpY5nXMW2ubh696UBc1bcwctTx26z')
var tx = new bitcoin.TransactionBuilder()
tx.addInput('b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c', 6) // Alice's previous transaction output, has 200000 satoshis
tx.addInput('7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730', 0) // Bob's previous transaction output, has 300000 satoshis
tx.addOutput('1CUNEBjYrCn2y1SdiUMohaKUi4wpP326Lb', 180000)
tx.addOutput('1JtK9CQw1syfWj1WtFMWomrYdV3W2tWBF9', 170000)
// (in)(200000 + 300000) - (out)(180000 + 150000) = (fee)170000, this is the miner fee
tx.sign(1, bob) // Bob signs his input, which was the second input (1th)
tx.sign(0, alice) // Alice signs her input, which was the first input (0th)
// build, prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below
assert.strictEqual(tx.build().toHex(), '01000000024c94e48a870b85f41228d33cf25213dfcc8dd796e7211ed6b1f9a014809dbbb5060000006b483045022100b514ec74a7dc22679554607034d669ea9d1e5c44464eee480af0bfdb16fb2f5002201cffdb6915729a71709cf2e43bfa921fe1ccaaab8accf57f0980f4b5fccdd350012103e05ce435e462ec503143305feb6c00e06a3ad52fbf939e85c65f3a765bb7baacffffffff3077d9de049574c3af9bc9c09a7c9db80f2d94caaf63988c9166249b955e867d000000006a4730440220528a814fe1a9a0b13afa4629e5dfc7632373a17c230ba15095b4237715b2745a02200b74a4d371cf22d301b34a8609420c8bd770f9a4ddbc2069330f25b214753693012103df7940ee7cddd2f97763f67e1fb13488da3fbdd7f9c68ec5ef0864074745a289ffffffff02605af405000000001976a9147dd65592d0ab2fe0d0257d571abf032cd9db93dc88ac00e1f505000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac00000000')
})
it('can create (and broadcast via 3PBP) a typical Transaction', function (done) {
this.timeout(30000)
var alice1 = bitcoin.ECPair.makeRandom({ network: testnet })
var alice2 = bitcoin.ECPair.makeRandom({ network: testnet })
var aliceChange = bitcoin.ECPair.makeRandom({ rng: rng, network: testnet })
// "simulate" on testnet that Alice has 2 unspent outputs
testnetUtils.faucetMany([
{
address: alice1.getAddress(),
value: 4e4
},
{
address: alice2.getAddress(),
value: 2e4
}
], function (err, unspents) {
if (err) return done(err)
var tx = new bitcoin.TransactionBuilder(testnet)
tx.addInput(unspents[0].txId, unspents[0].vout) // alice1 unspent
tx.addInput(unspents[1].txId, unspents[1].vout) // alice2 unspent
tx.addOutput('mvGVHWi6gbkBZZPaqBVRcxvKVPYd9r3fp7', 1e4) // the actual "spend"
tx.addOutput(aliceChange.getAddress(), 3e4) // Alice's change
// (in)(4e4 + 2e4) - (out)(1e4 + 3e4) = (fee)2e4 = 20000, this is the miner fee
// Alice signs each input with the respective private keys
tx.sign(0, alice1)
tx.sign(1, alice2)
// build and broadcast to the Bitcoin Testnet network
dhttp({
method: 'POST',
url: 'https://api.ei8ht.com.au:9443/3/pushtx',
// url: 'http://tbtc.blockr.io/api/v1/tx/push',
body: tx.build().toHex()
}, done)
// to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839
})
})
it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', function (done) {
this.timeout(30000)
var keyPair = bitcoin.ECPair.makeRandom({ network: testnet })
var address = keyPair.getAddress()
testnetUtils.faucet(address, 5e4, function (err, unspent) {
if (err) return done(err)
var tx = new bitcoin.TransactionBuilder(testnet)
var data = Buffer.from('bitcoinjs-lib', 'utf8')
var dataScript = bitcoin.script.nullData.output.encode(data)
tx.addInput(unspent.txId, unspent.vout)
tx.addOutput(dataScript, 1000)
tx.addOutput(testnetUtils.RETURN_ADDRESS, 4e4)
tx.sign(0, keyPair)
// build and broadcast to the Bitcoin Testnet network
dhttp({
method: 'POST',
url: 'https://api.ei8ht.com.au:9443/3/pushtx',
body: tx.build().toHex()
}, done)
})
})
it('can create (and broadcast via 3PBP) a Transaction with a 2-of-4 multisig P2SH input', function (done) {
this.timeout(30000)
var keyPairs = [
'91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx',
'91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT',
'91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe',
'91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx9rcrL7'
].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, bitcoin.networks.testnet) })
var pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() })
var redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys) // 2 of 4
var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript))
var address = bitcoin.address.fromOutputScript(scriptPubKey, bitcoin.networks.testnet)
// attempt to send funds to the source address
testnetUtils.faucet(address, 2e4, function (err, unspent) {
if (err) return done(err)
var txb = new bitcoin.TransactionBuilder(bitcoin.networks.testnet)
txb.addInput(unspent.txId, unspent.vout)
txb.addOutput(testnet.RETURN_ADDRESS, 1e4)
// sign with 1st and 3rd key
txb.sign(0, keyPairs[0], redeemScript)
txb.sign(0, keyPairs[2], redeemScript)
// broadcast our transaction
var tx = txb.build()
var txId = tx.getId()
dhttp({
method: 'POST',
url: 'https://api.ei8ht.com.au:9443/3/pushtx',
body: tx.toHex()
}, function (err) {
if (err) return done(err)
testnetUtils.verify(address, txId, 1e4, done)
})
})
})
})