Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More integration tests #302

Merged
merged 13 commits into from Nov 25, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
75 changes: 14 additions & 61 deletions README.md
Expand Up @@ -28,6 +28,7 @@ A continued implementation of the original `0.1.3` version used by over a millio


## Should I use this in production?

If you are thinking of using the master branch of this library in production, *stop*.
Master is not stable; it is our development branch, and only tagged releases may be classified as stable.

Expand Down Expand Up @@ -64,73 +65,25 @@ From NPM:
After loading this file in your browser, you will be able to use the global `bitcoin` object.


## Usage

These examples assume you are running bitcoinjs-lib in the browser.


### Generating a Bitcoin address

```javascript

key = bitcoin.ECKey.makeRandom()

// Print your private key (in WIF format)
console.log(key.toWIF())
// => Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct

// Print your public key (toString defaults to a Bitcoin address)
console.log(key.pub.getAddress().toString())
// => 14bZ7YWde4KdRb5YN7GYkToz3EHVCvRxkF
```


### Creating a Transaction

```javascript
tx = new bitcoin.Transaction()

// Add the input (who is paying) of the form [previous transaction hash, index of the output to use]
tx.addInput("aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31", 0)
## Examples

// Add the output (who to pay to) of the form [payee's address, amount in satoshis]
tx.addOutput("1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK", 15000)
The below examples are implemented as integration tests, but should be very easy to follow. Pull requests welcome.

// Initialize a private key using WIF
key = bitcoin.ECKey.fromWIF("L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy")

// Sign the first input with the new key
tx.sign(0, key)

// Print transaction serialized as hex
console.log(tx.toHex())
// => 0100000001313eb630b128102b60241ca895f1d0ffca2170d5a0990e094f2182c102ab94aa000000008a47304402200169f1f844936dc60df54e812345f5dd3e6681fea52e33c25154ad9cc23a330402204381ed8e73d74a95b15f312f33d5a0072c7a12dd6c3294df6e8efbe4aff27426014104e75628573696aed32d7656fb35e9c71ea08eb6492837e13d2662b9a36821d0fff992692fd14d74fdec20fae29128ba12653249cbeef521fc5eba84dde0689f27ffffffff01983a0000000000001976a914ad618cf4333b3b248f9744e8e81db2964d0ae39788ac00000000

// You could now push the transaction onto the Bitcoin network manually (see https://blockchain.info/pushtx)
```


### Creating a P2SH Multisig Address

``` javascript
var bitcoin = require('bitcoinjs-lib')

var privKeys = [bitcoin.ECKey.makeRandom(), bitcoin.ECKey.makeRandom(), bitcoin.ECKey.makeRandom()]
var pubKeys = privKeys.map(function(x) { return x.pub })

var redeemScript = bitcoin.scripts.multisigOutput(2, pubKeys) // 2 of 3
var scriptPubKey = bitcoin.scripts.scriptHashOutput(redeemScript.getHash())

var multisigAddress = bitcoin.Address.fromOutputScript(scriptPubKey).toString()

console.log("multisigP2SH:", multisigAddress)
// => multisigP2SH: 35k9EWv2F1X5JKXHSF1DhTm7Ybdiwx4RkD
```
- [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L8)
- [Generate a address from a SHA256 hash](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L20)
- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L29)
- [Create a Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L36)
- [Sign a Bitcoin message](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/advanced.js#L9)
- [Verify a Bitcoin message](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/advanced.js#L17)
- [Generate a single-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/advanced.js#L25)
- [Generate a dual-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/advanced.js#L58)
- [Create an OP RETURN transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/advanced.js#L60)
- [Create a 2-of-3 multisig P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/multisig.js#L8)
- [Spend from a 2-of-2 multisig P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/multisig.js#L22)


## Projects utilizing BitcoinJS


- [BitAddress](https://www.bitaddress.org)
- [Blockchain.info](https://blockchain.info/wallet)
- [Brainwallet](https://brainwallet.github.io)
Expand Down
108 changes: 108 additions & 0 deletions test/integration/advanced.js
@@ -0,0 +1,108 @@
var assert = require('assert')
var bigi = require('bigi')
var bitcoin = require('../../')
var helloblock = require('helloblock-js')({
network: 'testnet'
})

describe('bitcoinjs-lib (advanced)', function() {
it('can sign a Bitcoin message', function() {
var key = bitcoin.ECKey.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss')
var message = 'This is an example of a signed message.'

var signature = bitcoin.Message.sign(key, message)
assert.equal(signature.toString('base64'), 'G9L5yLFjti0QTHhPyFrZCT1V/MMnBtXKmoiKDZ78NDBjERki6ZTQZdSMCtkgoNmp17By9ItJr8o7ChX0XxY91nk=')
})

it('can verify a Bitcoin message', function() {
var address = '1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN'
var signature = 'HJLQlDWLyb1Ef8bQKEISzFbDAKctIlaqOpGbrk3YVtRsjmC61lpE5ErkPRUFtDKtx98vHFGUWlFhsh3DiW6N0rE'
var message = 'This is an example of a signed message.'

assert(bitcoin.Message.verify(address, signature, message))
})

it('can generate a single-key stealth address', function() {
var receiver = bitcoin.ECKey.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss')

// XXX: ephemeral, must be random (and secret to sender) to preserve privacy
var sender = bitcoin.ECKey.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct')

var G = bitcoin.ECKey.curve.G
var d = receiver.d // secret (receiver only)
var Q = receiver.pub.Q // shared

var e = sender.d // secret (sender only)
var P = sender.pub.Q // shared

// derived shared secret
var eQ = Q.multiply(e) // sender
var dP = P.multiply(d) // receiver
assert.deepEqual(eQ.getEncoded(), dP.getEncoded())

var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded()))
var cG = G.multiply(c)

// derived public key
var QprimeS = Q.add(cG)
var QprimeR = G.multiply(d.add(c))
assert.deepEqual(QprimeR.getEncoded(), QprimeS.getEncoded())

// derived shared-secret address
var address = new bitcoin.ECPubKey(QprimeS).getAddress().toString()

assert.equal(address, '1EwCNJNZM5q58YPPTnjR1H5BvYRNeyZi47')
})

// TODO
it.skip('can generate a dual-key stealth address', function() {})

it('can create an OP_RETURN transaction', function(done) {
this.timeout(20000)

var key = bitcoin.ECKey.fromWIF("L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy")
var address = key.pub.getAddress(bitcoin.networks.testnet).toString()

helloblock.faucet.withdraw(address, 2e4, function(err) {
if (err) return done(err)

helloblock.addresses.getUnspents(address, function(err, _, unspents) {
if (err) return done(err)

// filter small unspents
unspents = unspents.filter(function(unspent) { return unspent.value > 1e4 })

// use the oldest unspent
var unspent = unspents.pop()

var tx = new bitcoin.TransactionBuilder()

var data = new Buffer('cafedeadbeef', 'hex')
var dataScript = bitcoin.scripts.dataOutput(data)

tx.addInput(unspent.txHash, unspent.index)
tx.addOutput(dataScript, 1000)
tx.sign(0, key)

helloblock.transactions.propagate(tx.build().toHex(), function(err) {
if (err) return done(err)

// check that the message was propagated
helloblock.addresses.getTransactions(address, function(err, res, transactions) {
if (err) return done(err)

var transaction = transactions[0]
var output = transaction.outputs[0]
var dataScript2 = bitcoin.Script.fromHex(output.scriptPubKey)
var data2 = dataScript2.chunks[1]

assert.deepEqual(dataScript, dataScript2)
assert.deepEqual(data, data2)

done()
})
})
})
})
})
})
46 changes: 46 additions & 0 deletions test/integration/basic.js
@@ -0,0 +1,46 @@
var assert = require('assert')
var bigi = require('bigi')
var bitcoin = require('../../')
var crypto = require('crypto')
var sinon = require('sinon')

describe('bitcoinjs-lib (basic)', function() {
it('can generate a random bitcoin address', sinon.test(function() {
// for testing only
this.mock(crypto).expects('randomBytes')
.onCall(0).returns(new Buffer('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'))

// generate random key
var key = bitcoin.ECKey.makeRandom()
var address = key.pub.getAddress().toString()

assert.equal(address, '1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64')
}))

it('can generate an address from a SHA256 hash', function() {
var hash = bitcoin.crypto.sha256('correct horse battery staple')
var d = bigi.fromBuffer(hash)

var key = new bitcoin.ECKey(d)

assert.equal(key.pub.getAddress().toString(), '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8')
})

it('can import an address via WIF', function() {
var key = bitcoin.ECKey.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct')
var address = key.pub.getAddress().toString()

assert.equal(address, '19AAjaTUbRjQCMuVczepkoPswiZRhjtg31')
})

it('can create a Transaction', function() {
var key = bitcoin.ECKey.fromWIF("L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy")
var tx = new bitcoin.TransactionBuilder()

tx.addInput("aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31", 0)
tx.addOutput("1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK", 15000)
tx.sign(0, key)

assert.equal(tx.build().toHex(), '0100000001313eb630b128102b60241ca895f1d0ffca2170d5a0990e094f2182c102ab94aa000000006b483045022100aefbcf847900b01dd3e3debe054d3b6d03d715d50aea8525f5ea3396f168a1fb022013d181d05b15b90111808b22ef4f9ebe701caf2ab48db269691fdf4e9048f4f60121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59fffffffff01983a0000000000001976a914ad618cf4333b3b248f9744e8e81db2964d0ae39788ac00000000')
})
})
76 changes: 76 additions & 0 deletions test/integration/multisig.js
@@ -0,0 +1,76 @@
var assert = require('assert')
var bitcoin = require('../../')
var helloblock = require('helloblock-js')({
network: 'testnet'
})

describe('bitcoinjs-lib (multisig)', function() {
it('can create a 2-of-3 multisig P2SH address', function() {
var pubKeys = [
'026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01',
'02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9',
'03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9'
].map(bitcoin.ECPubKey.fromHex)

var redeemScript = bitcoin.scripts.multisigOutput(2, pubKeys) // 2 of 3
var scriptPubKey = bitcoin.scripts.scriptHashOutput(redeemScript.getHash())
var address = bitcoin.Address.fromOutputScript(scriptPubKey).toString()

assert.equal(address, '36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7')
})

it('can spend from a 2-of-2 multsig P2SH address', function(done) {
this.timeout(20000)

var privKeys = [
'91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx',
'91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT'
].map(bitcoin.ECKey.fromWIF)
var pubKeys = privKeys.map(function(x) { return x.pub })

var redeemScript = bitcoin.scripts.multisigOutput(2, pubKeys) // 2 of 2
var scriptPubKey = bitcoin.scripts.scriptHashOutput(redeemScript.getHash())
var address = bitcoin.Address.fromOutputScript(scriptPubKey, bitcoin.networks.testnet).toString()

// Attempt to send funds to the source address
helloblock.faucet.withdraw(address, 2e4, function(err) {
if (err) return done(err)

// get latest unspents from the address
helloblock.addresses.getUnspents(address, function(err, _, unspents) {
if (err) return done(err)

// filter small unspents
unspents = unspents.filter(function(unspent) { return unspent.value > 1e4 })

// use the oldest unspent
var unspent = unspents.pop()

// make a random destination address
var targetAddress = bitcoin.ECKey.makeRandom().pub.getAddress(bitcoin.networks.testnet).toString()

var txb = new bitcoin.TransactionBuilder()
txb.addInput(unspent.txHash, unspent.index)
txb.addOutput(targetAddress, 1e4)

// sign w/ each private key
privKeys.forEach(function(privKey) {
txb.sign(0, privKey, redeemScript)
})

// broadcast our transaction
helloblock.transactions.propagate(txb.build().toHex(), function(err) {
if (err) return done(err)

// check that the funds (1e4 Satoshis) indeed arrived at the intended address
helloblock.addresses.get(targetAddress, function(err, res, addrInfo) {
if (err) return done(err)

assert.equal(addrInfo.balance, 1e4)
done()
})
})
})
})
})
})