diff --git a/Address.js b/Address.js index b9a4ed15d..c3bf0eed6 100644 --- a/Address.js +++ b/Address.js @@ -1,7 +1,9 @@ 'use strict'; var imports = require('soop').imports(); -var parent = imports.parent || require('./util/VersionedData'); -var networks= imports.networks || require('./networks'); +var coinUtil = imports.coinUtil || require('./util/util'); +var parent = imports.parent || require('./util/VersionedData'); +var networks = imports.networks || require('./networks'); +var Script = imports.Script || require('./Script'); function Address() { Address.super(this, arguments); @@ -10,6 +12,47 @@ function Address() { Address.parent = parent; parent.applyEncodingsTo(Address); +//create a pubKeyHash address +Address.fromPubKey = function(pubKey, network) { + if (!network) + network = 'livenet'; + + if (pubKey.length != 33 && pubKey.length != 65) + throw new Error('Invalid public key'); + + var version = networks[network].addressVersion; + var hash = coinUtil.sha256ripe160(pubKey); + + return new Address(version, hash); +}; + +//create a p2sh m-of-n multisig address +Address.fromPubKeys = function(mReq, pubKeys, network, opts) { + if (!network) + network = 'livenet'; + + for (var i in pubKeys) { + var pubKey = pubKeys[i]; + if (pubKey.length != 33 && pubKey.length != 65) + throw new Error('Invalid public key'); + } + + var script = Script.createMultisig(mReq, pubKeys, opts); + + return Address.fromScript(script, network); +}; + +//create a p2sh address from redeemScript +Address.fromScript = function(script, network) { + if (!network) + network = 'livenet'; + + var version = networks[network].P2SHVersion; + var buf = script.getBuffer(); + var hash = coinUtil.sha256ripe160(buf); + + return new Address(version, hash); +}; Address.prototype.validate = function() { this.doAsBinary(function() { diff --git a/Buffers.monkey.js b/Buffers.monkey.js index ef7c73d26..5cf677085 100644 --- a/Buffers.monkey.js +++ b/Buffers.monkey.js @@ -9,8 +9,7 @@ exports.patch = function(Buffers) { } var pos = this.pos(i); this.buffers = this.buffers.slice(pos.buf); - this.buffers[0].length -= pos.offset; - this.buffers[0].offset += pos.offset; + this.buffers[0] = new Buffer(this.buffers[0].slice(pos.offset)); this.length -= i; }; }; diff --git a/README.md b/README.md index 1cd568f9b..3c71e035d 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ peerman.on('connect', function() { var conn = peerman.getActiveConnection(); if (conn) { var outs = [{address:toAddress, amount:amt}]; - var opts = {remainderAddress: changeAddressString}; + var opts = {remainderOut: {address: changeAddressString}}; var Builder = bitcore.TransactionBuilder; var tx = new Builder(opts) diff --git a/Script.js b/Script.js index a4ffbd2cd..c9755388f 100644 --- a/Script.js +++ b/Script.js @@ -36,7 +36,6 @@ function Script(buffer) { this.chunks = []; this.parse(); } -this.class = Script; Script.TX_UNKNOWN = TX_UNKNOWN; Script.TX_PUBKEY = TX_PUBKEY; diff --git a/Transaction.js b/Transaction.js index fe065634b..2a4e566c4 100644 --- a/Transaction.js +++ b/Transaction.js @@ -40,6 +40,8 @@ function TransactionIn(data) { this.q = data.q ? data.q : data.sequence; } +TransactionIn.MAX_SEQUENCE = 0xffffffff; + TransactionIn.prototype.getScript = function getScript() { return new Script(this.s); }; @@ -125,7 +127,6 @@ function Transaction(data) { }) : []; if (data.buffer) this._buffer = data.buffer; }; -this.class = Transaction; Transaction.In = TransactionIn; Transaction.Out = TransactionOut; @@ -197,6 +198,22 @@ Transaction.prototype.getHash = function getHash() { return this.hash; }; + +Transaction.prototype.calcNormalizedHash = function () { + this.normalizedHash = this.hashForSignature(new Script(),0, SIGHASH_ALL); + return this.normalizedHash; +}; + + +Transaction.prototype.getNormalizedHash = function () { + if (!this.normalizedHash || !this.normalizedHash.length) { + this.normalizedHash = this.calcNormalizedHash(); + } + return this.normalizedHash; +}; + + + // convert encoded list of inputs to easy-to-use JS list-of-lists Transaction.prototype.inputs = function inputs() { var res = []; @@ -432,7 +449,8 @@ Transaction.prototype.getStandardizedObject = function getStandardizedObject() { prev_out: { hash: buffertools.reverse(new Buffer(txin.getOutpointHash())).toString('hex'), n: txin.getOutpointIndex() - } + }, + sequence: txin.q }; if (txin.isCoinBase()) { txinObj.coinbase = txin.s.toString('hex'); @@ -568,7 +586,7 @@ Transaction.prototype.calcSize = function() { return totalSize; }; -Transaction.prototype.getSize = function getHash() { +Transaction.prototype.getSize = function () { if (!this.size) { this.size = this.calcSize(); } diff --git a/TransactionBuilder.js b/TransactionBuilder.js index f389ed04b..ebb3a6f5a 100644 --- a/TransactionBuilder.js +++ b/TransactionBuilder.js @@ -110,6 +110,7 @@ function TransactionBuilder(opts) { this.tx = {}; this.inputsSigned= 0; + this.signaturesAdded= 0; return this; } @@ -482,7 +483,7 @@ TransactionBuilder.prototype._signPubKey = function(walletKeyMap, input, txSigHa var scriptSig = new Script(); scriptSig.chunks.push(sig); scriptSig.updateBuffer(); - return {isFullySigned: true, signaturesAdded: true, script: scriptSig.getBuffer()}; + return {isFullySigned: true, signaturesAdded: 1, script: scriptSig.getBuffer()}; }; TransactionBuilder.prototype._signPubKeyHash = function(walletKeyMap, input, txSigHash) { @@ -501,7 +502,7 @@ TransactionBuilder.prototype._signPubKeyHash = function(walletKeyMap, input, txS scriptSig.chunks.push(sig); scriptSig.chunks.push(wk.privKey.public); scriptSig.updateBuffer(); - return {isFullySigned: true, signaturesAdded: true, script: scriptSig.getBuffer()}; + return {isFullySigned: true, signaturesAdded: 1, script: scriptSig.getBuffer()}; }; // FOR TESTING @@ -577,7 +578,7 @@ TransactionBuilder.prototype._signMultiSig = function(walletKeyMap, input, txSig originalScriptBuf = this.tx.ins[input.i].s; var scriptSig = new Script (originalScriptBuf); - var signaturesAdded = false; + var signaturesAdded = 0; for(var j=0; j Key::New(const Arguments& args) { - if (!args.IsConstructCall()) { - return FromConstructorTemplate(s_ct, args); - } - HandleScope scope; - Key* key = new Key(); - if (key->lastError != NULL) { - return VException(key->lastError); + // this was invoked as Key() not new Key() + if (!args.IsConstructCall()) { + const int argc = 1; + Local argv[argc] = { args[0] }; + return scope.Close(s_ct->GetFunction()->NewInstance(argc, argv)); } + else{ + Key* key = new Key(); + if (key->lastError != NULL) { + return VException(key->lastError); + } - key->Wrap(args.Holder()); + key->Wrap(args.Holder()); - return scope.Close(args.This()); + return scope.Close(args.This()); + } } Handle diff --git a/test/index-testling.html b/test/index-testling.html new file mode 100644 index 000000000..e6a9447f0 --- /dev/null +++ b/test/index-testling.html @@ -0,0 +1,52 @@ + + + + Mocha + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/test.Address.js b/test/test.Address.js index 19825534a..bffc08a2e 100644 --- a/test/test.Address.js +++ b/test/test.Address.js @@ -82,5 +82,60 @@ describe('Address', function() { // script testnet new Address('2NBSBcf2KfjPEEqVusmrWdmUeNHRiUTS3Li').isScript().should.equal(true); }); + + describe('#fromPubKey', function() { + it('should make this pubkeyhash address from uncompressed this public key', function() { + var pubkey = new Buffer('04fa05ce8b25010cb6e17a30e0b66668bf083c40687547748ec330ee77adf53a42abd3d26148cbacfcf79c907ddefeb2c37f8bebc0a695ba79d634449d871de218', 'hex'); + var hash = bitcore.util.sha256ripe160(pubkey); + var addr = new Address(0, hash); + addr.toString().should.equal(Address.fromPubKey(pubkey).toString()); + }); + }); + + describe('#fromPubKeys', function() { + it('should make this p2sh multisig address from these pubkeys', function() { + var pubkey1 = new Buffer('03e0973263b4e0d5f5f56d25d430e777ab3838ff644db972c0bf32c31da5686c27', 'hex'); + var pubkey2 = new Buffer('0371f94c57cc013507101e30794161f4e6b9efd58a9ea68838daf429b7feac8cb2', 'hex'); + var pubkey3 = new Buffer('032c0d2e394541e2efdc7ac3500e16e7e69df541f38670402e95aa477202fa06bb', 'hex'); + var sortedPubKeys = [pubkey3, pubkey2, pubkey1]; + var mReq = 2; + var script = bitcore.Script.createMultisig(mReq, sortedPubKeys, {noSorting: true}); + var hash = bitcore.util.sha256ripe160(script.getBuffer()); + var version = bitcore.networks['livenet'].P2SHVersion; + var addr = new Address(version, hash); + var addr2 = Address.fromPubKeys(mReq, sortedPubKeys); + addr.toString().should.equal(addr2.toString()); + }); + }); + + describe('#fromScript', function() { + it('should make this p2sh multisig address from these pubkeys', function() { + var pubkey1 = new Buffer('03e0973263b4e0d5f5f56d25d430e777ab3838ff644db972c0bf32c31da5686c27', 'hex'); + var pubkey2 = new Buffer('0371f94c57cc013507101e30794161f4e6b9efd58a9ea68838daf429b7feac8cb2', 'hex'); + var pubkey3 = new Buffer('032c0d2e394541e2efdc7ac3500e16e7e69df541f38670402e95aa477202fa06bb', 'hex'); + var pubKeys = [pubkey1, pubkey2, pubkey3]; + var mReq = 2; + var script = bitcore.Script.createMultisig(mReq, pubKeys); + var addr = Address.fromScript(script); + var addr2 = Address.fromPubKeys(mReq, pubKeys); + addr.toString().should.equal(addr2.toString()); + }); + + it('it should make this hand-crafted address', function() { + var pubkey1 = new Buffer('03e0973263b4e0d5f5f56d25d430e777ab3838ff644db972c0bf32c31da5686c27', 'hex'); + var pubkey2 = new Buffer('0371f94c57cc013507101e30794161f4e6b9efd58a9ea68838daf429b7feac8cb2', 'hex'); + var pubkey3 = new Buffer('032c0d2e394541e2efdc7ac3500e16e7e69df541f38670402e95aa477202fa06bb', 'hex'); + var pubKeys = [pubkey1, pubkey2, pubkey3]; + var mReq = 2; + var script = bitcore.Script.createMultisig(mReq, pubKeys); + var addr = Address.fromScript(script); + + var hash = bitcore.util.sha256ripe160(script.getBuffer()); + var version = bitcore.networks['livenet'].P2SHVersion; + var addr2 = new Address(version, hash); + + addr.toString().should.equal(addr2.toString()); + }); + }); }); diff --git a/test/test.Connection.js b/test/test.Connection.js index f070993d8..cbf98ce46 100644 --- a/test/test.Connection.js +++ b/test/test.Connection.js @@ -24,6 +24,7 @@ describe('Connection', function() { should.exist(c); }); it('should create a proxied socket if instructed', function() { + var mPeer; var c = new Connection(null, mPeer, { proxy: { host: 'localhost', port: 9050 } }); diff --git a/test/test.Key.js b/test/test.Key.js index aa412560e..9eb988239 100644 --- a/test/test.Key.js +++ b/test/test.Key.js @@ -16,6 +16,21 @@ describe('Key', function() { var k = new Key(); should.exist(k); }); + it('should not fail when called as Key() without "new"', function() { + var key = Key(); + should.exist(key); + }); + it('should not fail when called as Key() without "new" with some args', function() { + var key = Key(1, 2, 3, 4, 5); + should.exist(key); + }); + it('should have correct properties when called with Key() without "new"', function() { + var key = Key(); + key.compressed.should.equal(true); + should.not.exist(key.public); + should.not.exist(key.private); + should.exist(key); + }); it('should be able to generateSync instance', function() { var k = Key.generateSync(); should.exist(k); diff --git a/test/test.PeerManager.js b/test/test.PeerManager.js index 5f3aa7f30..ab6565d4c 100644 --- a/test/test.PeerManager.js +++ b/test/test.PeerManager.js @@ -5,7 +5,7 @@ var bitcore = bitcore || require('../bitcore'); var should = chai.should(); -var PeerManagerModule = bitcore.PeerManager || require('./PeerManager'); +var PeerManagerModule = bitcore.PeerManager; var PeerManager; diff --git a/test/test.PrivateKey.js b/test/test.PrivateKey.js index 6374bdcd3..1b72dc59b 100644 --- a/test/test.PrivateKey.js +++ b/test/test.PrivateKey.js @@ -48,5 +48,12 @@ describe('PrivateKey', function() { var a = new PrivateKey('5JZsbYcnYN8Dz2YeSLZr6aswrVevedMUSFWxpie6SPpYRb2E4Gi'); a.network().name.should.equal('livenet'); }); + + describe('#isValid', function() { + it('should detect this private key as valid', function() { + var privKey = new PrivateKey('5JQkUX6RNQC91xv4Www6Cgbxb6Eri6WBJjtPGqwfXVKBzT37cAf'); + privKey.isValid().should.equal(true); + }); + }); }); diff --git a/test/test.Transaction.js b/test/test.Transaction.js index 70d93ddc5..df68753f1 100644 --- a/test/test.Transaction.js +++ b/test/test.Transaction.js @@ -1,7 +1,7 @@ 'use strict'; var chai = chai || require('chai'); -chai.Assertion.includeStack = true; +chai.config.includeStack = true; var bitcore = bitcore || require('../bitcore'); var should = chai.should(); @@ -121,4 +121,20 @@ describe('Transaction', function() { coreTest(testdata.dataTxValid, true); coreTest(testdata.dataTxInvalid, false); + it('#normalized hash', function() { + // string output generated from: bitcoind createrawtransaction '[{"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1","vout":1},{"txid":"2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2","vout":0} ]' '{"mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE":0.08}' + // + var Parser = bitcore.BinaryParser; + var tx = new Transaction(); + tx.parse(new Buffer('0100000002c1cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a0100000000ffffffffc2cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a0000000000ffffffff0100127a00000000001976a914774e603bafb717bd3f070e68bbcccfd907c77d1388ac00000000','hex')); + + tx.getNormalizedHash().toString('hex').should.equal('1f7d2666e2d0d663e098abb76db6ba392da972d21c14b6ea6f4336171d29966b'); + + var tx2 = new Transaction(); + tx2.parse(new Buffer('0100000001c1cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a010000004b00473044022059085ff1b8ad03033e60969b1c770aa29ba5d74c28a9992c514b100d860792f1022057a307f77f91f4563651eefc0a959aa916d275c58525320309b6aeeff43d0d8a010000ffffffff0215cd5b07000000001976a91434f8e0c5be216025a52addf18a987543cad23f7a88acdbd53e340000000017a9147a769913c0721b1e0aa6bf8a93f4ef810c60587a8700000000','hex')); + + tx2.getNormalizedHash().toString('hex').should.equal('e298bbf3734898581b8e342f2064236abf0acca6ac7e9a3009a16ef7b64d4983'); + }); + + }); diff --git a/test/test.TransactionBuilder.js b/test/test.TransactionBuilder.js index dc32198ce..667a24926 100644 --- a/test/test.TransactionBuilder.js +++ b/test/test.TransactionBuilder.js @@ -1,7 +1,7 @@ 'use strict'; var chai = chai || require('chai'); -chai.Assertion.includeStack = true; +chai.config.includeStack = true; var bitcore = bitcore || require('../bitcore'); var should = chai.should(); @@ -563,7 +563,7 @@ describe('TransactionBuilder', function() { }; var data = getInfoForP2sh(); // multisig p2sh - var p2shOpts = {nreq:3, pubkeys:data.pubkeys, amount:0.05}; + var p2shOpts = {nreq:3, pubkeys:data.pubkeys}; var info = TransactionBuilder.infoForP2sh(p2shOpts, network); var outs = outs || [{ @@ -605,36 +605,43 @@ describe('TransactionBuilder', function() { var k2 = testdata.dataUnspentSign.keyStringsP2sh.slice(1,2); var k5 = testdata.dataUnspentSign.keyStringsP2sh.slice(4,5); b.isFullySigned().should.equal(false); + b.signaturesAdded.should.equal(0); b.sign(k1); b.isFullySigned().should.equal(false); + b.signaturesAdded.should.equal(1); var tx = b.build(); tx.ins.length.should.equal(1); tx.outs.length.should.equal(2); tx.isComplete().should.equal(false); + b.signaturesAdded.should.equal(1); // Sign with the same b.sign(k1); b.isFullySigned().should.equal(false); tx.isComplete().should.equal(false); + b.signaturesAdded.should.equal(1); // Sign with k5 b.sign(k5); /// b.isFullySigned().should.equal(false); tx.isComplete().should.equal(false); + b.signaturesAdded.should.equal(2); // Sign with same b.sign(k5); b.isFullySigned().should.equal(false); tx.isComplete().should.equal(false); + b.signaturesAdded.should.equal(2); // Sign k2 b.sign(k2); b.isFullySigned().should.equal(true); tx.isComplete().should.equal(true); + b.signaturesAdded.should.equal(3); }); it('should sign in steps a p2sh/p2pubkeyhash tx', function() { @@ -644,7 +651,7 @@ describe('TransactionBuilder', function() { remainderOut: {address: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd'}, }; // p2hash/ p2sh - var p2shOpts = {address:'mgwqzy6pF5BSc72vxHBFSnnhNEBcV4TJzV', amount:0.05}; + var p2shOpts = {address:'mgwqzy6pF5BSc72vxHBFSnnhNEBcV4TJzV'}; var info = TransactionBuilder.infoForP2sh(p2shOpts, network); //addr: 2NAwCQ1jPYPrSsyBQvfP6AJ6d6SSxnHsZ4e diff --git a/test/test.misc.js b/test/test.misc.js index b3ecda63b..f3da0b852 100644 --- a/test/test.misc.js +++ b/test/test.misc.js @@ -3,6 +3,7 @@ var chai = chai || require('chai'); var bitcore = bitcore || require('../bitcore'); var buffertools = require('buffertools'); +buffertools.extend(); var should = chai.should(); @@ -15,6 +16,9 @@ var base58Check = base58.base58Check; var Address = bitcore.Address; var networks = bitcore.networks; var WalletKey = bitcore.WalletKey; +var Buffers = require('buffers'); +var m = bitcore['Buffers.monkey'] || require('../Buffers.monkey'); +m.patch(Buffers); describe('Miscelaneous stuff', function() { it('should initialze the config object', function() { @@ -26,7 +30,7 @@ describe('Miscelaneous stuff', function() { it('should initialze the network object', function() { should.exist(networks); var nets = [networks.livenet, networks.testnet]; - for (var i=0; i<2; i++) { + for (var i = 0; i < 2; i++) { var net = nets[i]; should.exist(net.addressVersion); should.exist(net.privKeyVersion); @@ -42,8 +46,39 @@ describe('Miscelaneous stuff', function() { should.exist(bitcore.Deserialize); should.exist(bitcore.Deserialize.intFromCompact); }); + it('should initialze the Buffer class', function() { + should.exist(bitcore.Buffer); + }); + describe('Buffers monkey patch', function() { + var bufs; + beforeEach(function() { + bufs = new Buffers(); + bufs.push(new Buffer('aaaaaa', 'hex')); + bufs.push(new Buffer('bbbb', 'hex')); + bufs.push(new Buffer('cc', 'hex')); + }); + it('should monkey patch the Buffers class', function() { + should.exist(bufs.skip); + }); + it('should work for 0', function() { + bufs.skip(0); + bufs.toBuffer().toHex().should.equal('aaaaaabbbbcc'); + }); + it('should work for length', function() { + bufs.skip(bufs.length); + bufs.toBuffer().toHex().should.equal(''); + }); + it('should work for middle values', function() { + bufs.skip(4); + bufs.toBuffer().toHex().should.equal('bbcc'); + bufs.skip(1); + bufs.toBuffer().toHex().should.equal('cc'); + bufs.skip(1); + bufs.toBuffer().toHex().should.equal(''); + }); + }); // bignum it('should initialze the bignum object', function() { should.exist(bitcore.bignum);