diff --git a/src/base58check.js b/src/base58check.js deleted file mode 100644 index 040dd99..0000000 --- a/src/base58check.js +++ /dev/null @@ -1,18 +0,0 @@ -var bs58check = require('bs58check') - -function decode () { - console.warn('bs58check will be removed in 2.0.0. require("bs58check") instead.') - - return bs58check.decode.apply(undefined, arguments) -} - -function encode () { - console.warn('bs58check will be removed in 2.0.0. require("bs58check") instead.') - - return bs58check.encode.apply(undefined, arguments) -} - -module.exports = { - decode: decode, - encode: encode -} diff --git a/src/bufferutils.js b/src/bufferutils.js index 02c9594..afe03e6 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -165,6 +165,16 @@ function varIntBuffer (i) { return buffer } +function equal (a, b) { + if (a.length !== b.length) return false + + for (var i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) return false + } + + return true +} + function reverse (buffer) { var buffer2 = new Buffer(buffer) Array.prototype.reverse.call(buffer2) @@ -172,6 +182,7 @@ function reverse (buffer) { } module.exports = { + equal: equal, pushDataSize: pushDataSize, readPushDataInt: readPushDataInt, readUInt64LE: readUInt64LE, diff --git a/src/crypto.js b/src/crypto.js index 6d81bb7..dec8a79 100644 --- a/src/crypto.js +++ b/src/crypto.js @@ -20,23 +20,10 @@ function sha256 (buffer) { return crypto.createHash('sha256').update(buffer).digest() } -// FIXME: Name not consistent with others -function HmacSHA256 (buffer, secret) { - console.warn('Hmac* functions are deprecated for removal in 2.0.0, use node crypto instead') - return crypto.createHmac('sha256', secret).update(buffer).digest() -} - -function HmacSHA512 (buffer, secret) { - console.warn('Hmac* functions are deprecated for removal in 2.0.0, use node crypto instead') - return crypto.createHmac('sha512', secret).update(buffer).digest() -} - module.exports = { - ripemd160: ripemd160, - sha1: sha1, - sha256: sha256, hash160: hash160, hash256: hash256, - HmacSHA256: HmacSHA256, - HmacSHA512: HmacSHA512 + ripemd160: ripemd160, + sha1: sha1, + sha256: sha256 } diff --git a/src/ecdsa.js b/src/ecdsa.js index 2439add..0455d19 100644 --- a/src/ecdsa.js +++ b/src/ecdsa.js @@ -12,34 +12,7 @@ var ONE = new Buffer([1]) function deterministicGenerateK (curve, hash, d, checkSig) { typeForce('Buffer', hash) typeForce('BigInteger', d) - - // FIXME: remove/uncomment for 2.0.0 - // typeForce('Function', checkSig) - - if (typeof checkSig !== 'function') { - console.warn('deterministicGenerateK requires a checkSig callback in 2.0.0, see #337 for more information') - - checkSig = function (k) { - var G = curve.G - var n = curve.n - var e = BigInteger.fromBuffer(hash) - - var Q = G.multiply(k) - - if (curve.isInfinity(Q)) - return false - - var r = Q.affineX.mod(n) - if (r.signum() === 0) - return false - - var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n) - if (s.signum() === 0) - return false - - return true - } - } + typeForce('Function', checkSig) // sanity check assert.equal(hash.length, 32, 'Hash must be 256 bit') diff --git a/src/hdnode.js b/src/hdnode.js index ea079d8..f0aa613 100644 --- a/src/hdnode.js +++ b/src/hdnode.js @@ -78,15 +78,7 @@ HDNode.fromSeedHex = function (hex, network) { } HDNode.fromBase58 = function (string, network) { - return HDNode.fromBuffer(base58check.decode(string), network, true) -} - -// FIXME: remove in 2.x.y -HDNode.fromBuffer = function (buffer, network, __ignoreDeprecation) { - if (!__ignoreDeprecation) { - console.warn('HDNode.fromBuffer() is deprecated for removal in 2.x.y, use fromBase58 instead') - } - + var buffer = base58check.decode(string) assert.strictEqual(buffer.length, HDNode.LENGTH, 'Invalid buffer length') // 4 byte: version bytes @@ -145,11 +137,6 @@ HDNode.fromBuffer = function (buffer, network, __ignoreDeprecation) { return hd } -// FIXME: remove in 2.x.y -HDNode.fromHex = function (hex, network) { - return HDNode.fromBuffer(new Buffer(hex, 'hex'), network) -} - HDNode.prototype.getIdentifier = function () { return bcrypto.hash160(this.pubKey.toBuffer()) } @@ -171,26 +158,11 @@ HDNode.prototype.neutered = function () { return neutered } -HDNode.prototype.toBase58 = function (isPrivate) { - return base58check.encode(this.toBuffer(isPrivate, true)) -} - -// FIXME: remove in 2.x.y -HDNode.prototype.toBuffer = function (isPrivate, __ignoreDeprecation) { - if (isPrivate === undefined) { - isPrivate = !!this.privKey - - // FIXME: remove in 2.x.y - } else { - console.warn('isPrivate flag is deprecated, please use the .neutered() method instead') - } - - if (!__ignoreDeprecation) { - console.warn('HDNode.toBuffer() is deprecated for removal in 2.x.y, use toBase58 instead') - } +HDNode.prototype.toBase58 = function (__isPrivate) { + assert.strictEqual(__isPrivate, undefined, 'Unsupported argument in 2.0.0') // Version - var version = isPrivate ? this.network.bip32.private : this.network.bip32.public + var version = this.privKey ? this.network.bip32.private : this.network.bip32.public var buffer = new Buffer(HDNode.LENGTH) // 4 bytes: version bytes @@ -210,25 +182,19 @@ HDNode.prototype.toBuffer = function (isPrivate, __ignoreDeprecation) { // 32 bytes: the chain code this.chainCode.copy(buffer, 13) - // 33 bytes: the public key or private key data - if (isPrivate) { - // FIXME: remove in 2.x.y - assert(this.privKey, 'Missing private key') - + // 33 bytes: the private key, or + if (this.privKey) { // 0x00 + k for private keys buffer.writeUInt8(0, 45) this.privKey.d.toBuffer(32).copy(buffer, 46) + + // 33 bytes: the public key } else { // X9.62 encoding for public keys this.pubKey.toBuffer().copy(buffer, 45) } - return buffer -} - -// FIXME: remove in 2.x.y -HDNode.prototype.toHex = function (isPrivate) { - return this.toBuffer(isPrivate).toString('hex') + return base58check.encode(buffer) } // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions diff --git a/src/index.js b/src/index.js index 5dc683f..3b5317b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,5 @@ module.exports = { Address: require('./address'), - base58check: require('./base58check'), Block: require('./block'), bufferutils: require('./bufferutils'), crypto: require('./crypto'), @@ -8,13 +7,12 @@ module.exports = { ECKey: require('./eckey'), ECPubKey: require('./ecpubkey'), ECSignature: require('./ecsignature'), - Message: require('./message'), + message: require('./message'), opcodes: require('./opcodes'), HDNode: require('./hdnode'), Script: require('./script'), scripts: require('./scripts'), Transaction: require('./transaction'), TransactionBuilder: require('./transaction_builder'), - networks: require('./networks'), - Wallet: require('./wallet') + networks: require('./networks') } diff --git a/src/scripts.js b/src/scripts.js index 79ef9eb..3876184 100644 --- a/src/scripts.js +++ b/src/scripts.js @@ -291,9 +291,5 @@ module.exports = { pubKeyHashInput: pubKeyHashInput, scriptHashInput: scriptHashInput, multisigInput: multisigInput, - dataOutput: function (data) { - console.warn('dataOutput is deprecated, use nullDataOutput by 2.0.0') - return nullDataOutput(data) - }, nullDataOutput: nullDataOutput } diff --git a/src/transaction.js b/src/transaction.js index c356bed..fa1b18a 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -3,10 +3,7 @@ var bufferutils = require('./bufferutils') var crypto = require('./crypto') var typeForce = require('typeforce') var opcodes = require('./opcodes') -var scripts = require('./scripts') -var Address = require('./address') -var ECSignature = require('./ecsignature') var Script = require('./script') function Transaction () { @@ -106,16 +103,6 @@ Transaction.isCoinbaseHash = function (buffer) { }) } -/** - * Create a new txIn. - * - * Can be called with any of: - * - * - A transaction and an index - * - A transaction hash and an index - * - * Note that this method does not sign the created input. - */ Transaction.prototype.addInput = function (hash, index, sequence, script) { if (sequence === undefined || sequence === null) { sequence = Transaction.DEFAULT_SEQUENCE @@ -123,13 +110,6 @@ Transaction.prototype.addInput = function (hash, index, sequence, script) { script = script || Script.EMPTY - if (typeof hash === 'string') { - // TxId hex is big-endian, we need little-endian - hash = bufferutils.reverse(new Buffer(hash, 'hex')) - } else if (hash instanceof Transaction) { - hash = hash.getHash() - } - typeForce('Buffer', hash) typeForce('Number', index) typeForce('Number', sequence) @@ -146,26 +126,7 @@ Transaction.prototype.addInput = function (hash, index, sequence, script) { }) - 1) } -/** - * Create a new txOut. - * - * Can be called with: - * - * - A base58 address string and a value - * - An Address object and a value - * - A scriptPubKey Script and a value - */ Transaction.prototype.addOutput = function (scriptPubKey, value) { - // Attempt to get a valid address if it's a base58 address string - if (typeof scriptPubKey === 'string') { - scriptPubKey = Address.fromBase58Check(scriptPubKey) - } - - // Attempt to get a valid script if it's an Address object - if (scriptPubKey instanceof Address) { - scriptPubKey = scriptPubKey.toOutputScript() - } - typeForce('Script', scriptPubKey) typeForce('Number', value) @@ -203,22 +164,12 @@ Transaction.prototype.clone = function () { /** * Hash transaction for signing a specific input. * - * Bitcoin uses a different hash for each signed transaction input. This - * method copies the transaction, makes the necessary changes based on the - * hashType, serializes and finally hashes the result. This hash can then be - * used to sign the transaction input in question. + * Bitcoin uses a different hash for each signed transaction input. + * This method copies the transaction, makes the necessary changes based on the + * hashType, and then hashes the result. + * This hash can then be used to sign the provided transaction input. */ Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashType) { - // FIXME: remove in 2.x.y - if (arguments[0] instanceof Script) { - console.warn('hashForSignature(prevOutScript, inIndex, ...) has been deprecated. Use hashForSignature(inIndex, prevOutScript, ...)') - - // swap the arguments (must be stored in tmp, arguments is special) - var tmp = arguments[0] - inIndex = arguments[1] - prevOutScript = tmp - } - typeForce('Number', inIndex) typeForce('Script', prevOutScript) typeForce('Number', hashType) @@ -333,37 +284,4 @@ Transaction.prototype.setInputScript = function (index, script) { this.ins[index].script = script } -// FIXME: remove in 2.x.y -Transaction.prototype.sign = function (index, privKey, hashType) { - console.warn('Transaction.prototype.sign is deprecated. Use TransactionBuilder instead.') - - var prevOutScript = privKey.pub.getAddress().toOutputScript() - var signature = this.signInput(index, prevOutScript, privKey, hashType) - - var scriptSig = scripts.pubKeyHashInput(signature, privKey.pub) - this.setInputScript(index, scriptSig) -} - -// FIXME: remove in 2.x.y -Transaction.prototype.signInput = function (index, prevOutScript, privKey, hashType) { - console.warn('Transaction.prototype.signInput is deprecated. Use TransactionBuilder instead.') - - hashType = hashType || Transaction.SIGHASH_ALL - - var hash = this.hashForSignature(index, prevOutScript, hashType) - var signature = privKey.sign(hash) - - return signature.toScriptSignature(hashType) -} - -// FIXME: remove in 2.x.y -Transaction.prototype.validateInput = function (index, prevOutScript, pubKey, buffer) { - console.warn('Transaction.prototype.validateInput is deprecated. Use TransactionBuilder instead.') - - var parsed = ECSignature.parseScriptSignature(buffer) - var hash = this.hashForSignature(index, prevOutScript, parsed.hashType) - - return pubKey.verify(hash, parsed.signature) -} - module.exports = Transaction diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 8a58e9a..924f177 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -2,6 +2,7 @@ var assert = require('assert') var ops = require('./opcodes') var scripts = require('./scripts') +var Address = require('./address') var ECPubKey = require('./ecpubkey') var ECSignature = require('./ecsignature') var Script = require('./script') @@ -120,24 +121,17 @@ TransactionBuilder.fromTransaction = function (transaction) { return txb } -TransactionBuilder.prototype.addInput = function (prevTx, index, sequence, prevOutScript) { - var prevOutHash +TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOutScript) { + // is it a txId? + if (typeof txHash === 'string') { + // a txId is big-endian hex, we want a little-endian Buffer + txHash = new Buffer(txHash, 'hex') + Array.prototype.reverse.call(txHash) - // txId - if (typeof prevTx === 'string') { - prevOutHash = new Buffer(prevTx, 'hex') - - // TxId hex is big-endian, we want little-endian hash - Array.prototype.reverse.call(prevOutHash) - - // Transaction - } else if (prevTx instanceof Transaction) { - prevOutHash = prevTx.getHash() - prevOutScript = prevTx.outs[index].script - - // txHash - } else { - prevOutHash = prevTx + // is it a Transaction? + } else if (txHash instanceof Transaction) { + prevOutScript = txHash.outs[vout].script + txHash = txHash.getHash() } var input = {} @@ -171,10 +165,10 @@ TransactionBuilder.prototype.addInput = function (prevTx, index, sequence, prevO return input2.hashType & Transaction.SIGHASH_ANYONECANPAY }), 'No, this would invalidate signatures') - var prevOut = prevOutHash.toString('hex') + ':' + index + var prevOut = txHash.toString('hex') + ':' + vout assert(!(prevOut in this.prevTxMap), 'Transaction is already an input') - var vin = this.tx.addInput(prevOutHash, index, sequence) + var vin = this.tx.addInput(txHash, vout, sequence) this.inputs[vin] = input this.prevTxMap[prevOut] = vin @@ -188,6 +182,16 @@ TransactionBuilder.prototype.addOutput = function (scriptPubKey, value) { return (input.hashType & 0x1f) === Transaction.SIGHASH_SINGLE }), 'No, this would invalidate signatures') + // Attempt to get a valid address if it's a base58 address string + if (typeof scriptPubKey === 'string') { + scriptPubKey = Address.fromBase58Check(scriptPubKey) + } + + // Attempt to get a valid script if it's an Address object + if (scriptPubKey instanceof Address) { + scriptPubKey = scriptPubKey.toOutputScript() + } + return this.tx.addOutput(scriptPubKey, value) } diff --git a/src/wallet.js b/src/wallet.js deleted file mode 100644 index 4d0f807..0000000 --- a/src/wallet.js +++ /dev/null @@ -1,371 +0,0 @@ -var assert = require('assert') -var bufferutils = require('./bufferutils') -var crypto = require('crypto') -var typeForce = require('typeforce') -var networks = require('./networks') - -var Address = require('./address') -var HDNode = require('./hdnode') -var TransactionBuilder = require('./transaction_builder') -var Script = require('./script') - -function Wallet (seed, network) { - console.warn('Wallet is deprecated and will be removed in 2.0.0, see #296') - - seed = seed || crypto.randomBytes(32) - network = network || networks.bitcoin - - // Stored in a closure to make accidental serialization less likely - var masterKey = HDNode.fromSeedBuffer(seed, network) - - // HD first-level child derivation method should be hardened - // See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254 - var accountZero = masterKey.deriveHardened(0) - var externalAccount = accountZero.derive(0) - var internalAccount = accountZero.derive(1) - - this.addresses = [] - this.changeAddresses = [] - this.network = network - this.unspents = [] - - // FIXME: remove in 2.0.0 - this.unspentMap = {} - - // FIXME: remove in 2.0.0 - var me = this - this.newMasterKey = function (seed) { - console.warn('newMasterKey is deprecated, please make a new Wallet instance instead') - - seed = seed || crypto.randomBytes(32) - masterKey = HDNode.fromSeedBuffer(seed, network) - - accountZero = masterKey.deriveHardened(0) - externalAccount = accountZero.derive(0) - internalAccount = accountZero.derive(1) - - me.addresses = [] - me.changeAddresses = [] - - me.unspents = [] - me.unspentMap = {} - } - - this.getMasterKey = function () { - return masterKey - } - this.getAccountZero = function () { - return accountZero - } - this.getExternalAccount = function () { - return externalAccount - } - this.getInternalAccount = function () { - return internalAccount - } -} - -Wallet.prototype.createTransaction = function (to, value, options) { - // FIXME: remove in 2.0.0 - if (typeof options !== 'object') { - if (options !== undefined) { - console.warn('Non options object parameters are deprecated, use options object instead') - - options = { - fixedFee: arguments[2], - changeAddress: arguments[3] - } - } - } - - options = options || {} - - assert(value > this.network.dustThreshold, value + ' must be above dust threshold (' + this.network.dustThreshold + ' Satoshis)') - - var changeAddress = options.changeAddress - var fixedFee = options.fixedFee - var minConf = options.minConf === undefined ? 0 : options.minConf // FIXME: change minConf:1 by default in 2.0.0 - - // filter by minConf, then pending and sort by descending value - var unspents = this.unspents.filter(function (unspent) { - return unspent.confirmations >= minConf - }).filter(function (unspent) { - return !unspent.pending - }).sort(function (o1, o2) { - return o2.value - o1.value - }) - - var accum = 0 - var addresses = [] - var subTotal = value - - var txb = new TransactionBuilder() - txb.addOutput(to, value) - - for (var i = 0; i < unspents.length; ++i) { - var unspent = unspents[i] - addresses.push(unspent.address) - - txb.addInput(unspent.txHash, unspent.index) - - var fee = fixedFee === undefined ? estimatePaddedFee(txb.buildIncomplete(), this.network) : fixedFee - - accum += unspent.value - subTotal = value + fee - - if (accum >= subTotal) { - var change = accum - subTotal - - if (change > this.network.dustThreshold) { - txb.addOutput(changeAddress || this.getChangeAddress(), change) - } - - break - } - } - - assert(accum >= subTotal, 'Not enough funds (incl. fee): ' + accum + ' < ' + subTotal) - - return this.signWith(txb, addresses).build() -} - -// FIXME: remove in 2.0.0 -Wallet.prototype.processPendingTx = function (tx) { - this.__processTx(tx, true) -} - -// FIXME: remove in 2.0.0 -Wallet.prototype.processConfirmedTx = function (tx) { - this.__processTx(tx, false) -} - -// FIXME: remove in 2.0.0 -Wallet.prototype.__processTx = function (tx, isPending) { - console.warn('processTransaction is considered harmful, see issue #260 for more information') - - var txId = tx.getId() - var txHash = tx.getHash() - - tx.outs.forEach(function (txOut, i) { - var address - - try { - address = Address.fromOutputScript(txOut.script, this.network).toString() - } catch (e) { - if (!(e.message.match(/has no matching Address/))) - throw e - } - - var myAddresses = this.addresses.concat(this.changeAddresses) - if (myAddresses.indexOf(address) > -1) { - var lookup = txId + ':' + i - if (lookup in this.unspentMap) return - - // its unique, add it - var unspent = { - address: address, - confirmations: 0, // no way to determine this without more information - index: i, - txHash: txHash, - txId: txId, - value: txOut.value, - pending: isPending - } - - this.unspentMap[lookup] = unspent - this.unspents.push(unspent) - } - }, this) - - tx.ins.forEach(function (txIn) { - // copy and convert to big-endian hex - var txInId = bufferutils.reverse(txIn.hash).toString('hex') - - var lookup = txInId + ':' + txIn.index - if (!(lookup in this.unspentMap)) return - - var unspent = this.unspentMap[lookup] - - if (isPending) { - unspent.pending = true - unspent.spent = true - } else { - delete this.unspentMap[lookup] - - this.unspents = this.unspents.filter(function (unspent2) { - return unspent !== unspent2 - }) - } - }, this) -} - -Wallet.prototype.generateAddress = function () { - var k = this.addresses.length - var address = this.getExternalAccount().derive(k).getAddress() - - this.addresses.push(address.toString()) - - return this.getReceiveAddress() -} - -Wallet.prototype.generateChangeAddress = function () { - var k = this.changeAddresses.length - var address = this.getInternalAccount().derive(k).getAddress() - - this.changeAddresses.push(address.toString()) - - return this.getChangeAddress() -} - -Wallet.prototype.getAddress = function () { - if (this.addresses.length === 0) { - this.generateAddress() - } - - return this.addresses[this.addresses.length - 1] -} - -Wallet.prototype.getBalance = function (minConf) { - minConf = minConf || 0 - - return this.unspents.filter(function (unspent) { - return unspent.confirmations >= minConf - - // FIXME: remove spent filter in 2.0.0 - }).filter(function (unspent) { - return !unspent.spent - }).reduce(function (accum, unspent) { - return accum + unspent.value - }, 0) -} - -Wallet.prototype.getChangeAddress = function () { - if (this.changeAddresses.length === 0) { - this.generateChangeAddress() - } - - return this.changeAddresses[this.changeAddresses.length - 1] -} - -Wallet.prototype.getInternalPrivateKey = function (index) { - return this.getInternalAccount().derive(index).privKey -} - -Wallet.prototype.getPrivateKey = function (index) { - return this.getExternalAccount().derive(index).privKey -} - -Wallet.prototype.getPrivateKeyForAddress = function (address) { - var index - - if ((index = this.addresses.indexOf(address)) > -1) { - return this.getPrivateKey(index) - } - - if ((index = this.changeAddresses.indexOf(address)) > -1) { - return this.getInternalPrivateKey(index) - } - - assert(false, 'Unknown address. Make sure the address is from the keychain and has been generated') -} - -Wallet.prototype.getUnspentOutputs = function (minConf) { - minConf = minConf || 0 - - return this.unspents.filter(function (unspent) { - return unspent.confirmations >= minConf - - // FIXME: remove spent filter in 2.0.0 - }).filter(function (unspent) { - return !unspent.spent - }).map(function (unspent) { - return { - address: unspent.address, - confirmations: unspent.confirmations, - index: unspent.index, - txId: unspent.txId, - value: unspent.value, - - // FIXME: remove in 2.0.0 - hash: unspent.txId, - pending: unspent.pending - } - }) -} - -Wallet.prototype.setUnspentOutputs = function (unspents) { - this.unspentMap = {} - this.unspents = unspents.map(function (unspent) { - // FIXME: remove unspent.hash in 2.0.0 - var txId = unspent.txId || unspent.hash - var index = unspent.index - - // FIXME: remove in 2.0.0 - if (unspent.hash !== undefined) { - console.warn('unspent.hash is deprecated, use unspent.txId instead') - } - - // FIXME: remove in 2.0.0 - if (index === undefined) { - console.warn('unspent.outputIndex is deprecated, use unspent.index instead') - index = unspent.outputIndex - } - - typeForce('String', txId) - typeForce('Number', index) - typeForce('Number', unspent.value) - - assert.equal(txId.length, 64, 'Expected valid txId, got ' + txId) - assert.doesNotThrow(function () { - Address.fromBase58Check(unspent.address) - }, 'Expected Base58 Address, got ' + unspent.address) - assert(isFinite(index), 'Expected finite index, got ' + index) - - // FIXME: remove branch in 2.0.0 - if (unspent.confirmations !== undefined) { - typeForce('Number', unspent.confirmations) - } - - var txHash = bufferutils.reverse(new Buffer(txId, 'hex')) - - unspent = { - address: unspent.address, - confirmations: unspent.confirmations || 0, - index: index, - txHash: txHash, - txId: txId, - value: unspent.value, - - // FIXME: remove in 2.0.0 - pending: unspent.pending || false - } - - // FIXME: remove in 2.0.0 - this.unspentMap[txId + ':' + index] = unspent - - return unspent - }, this) -} - -Wallet.prototype.signWith = function (tx, addresses) { - addresses.forEach(function (address, i) { - var privKey = this.getPrivateKeyForAddress(address) - - tx.sign(i, privKey) - }, this) - - return tx -} - -function estimatePaddedFee (tx, network) { - var tmpTx = tx.clone() - tmpTx.addOutput(Script.EMPTY, network.dustSoftThreshold || 0) - - return network.estimateFee(tmpTx) -} - -// FIXME: 1.0.0 shims, remove in 2.0.0 -Wallet.prototype.getReceiveAddress = Wallet.prototype.getAddress -Wallet.prototype.createTx = Wallet.prototype.createTransaction - -module.exports = Wallet diff --git a/test/base58check.js b/test/base58check.js deleted file mode 100644 index 5c0d5c4..0000000 --- a/test/base58check.js +++ /dev/null @@ -1,30 +0,0 @@ -/* global describe, it, beforeEach */ - -var assert = require('assert') -var base58check = require('../src/base58check') -var bs58check = require('bs58check') -var sinon = require('sinon') - -describe('base58check', function () { - var param - - beforeEach(function () { - param = {} - }) - - it('wraps bs58check.decode', sinon.test(function () { - var expectation = this.mock(bs58check).expects('decode') - expectation.once().calledWith(param) - expectation.onCall(0).returns('foo') - - assert.equal(base58check.decode(param), 'foo') - })) - - it('wraps bs58check.encode', sinon.test(function () { - var expectation = this.mock(bs58check).expects('encode') - expectation.once().calledWith(param) - expectation.onCall(0).returns('foo') - - assert.equal(base58check.encode(param), 'foo') - })) -}) diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index 8ad7cd1..ee7d4e8 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -2,10 +2,10 @@ var assert = require('assert') var base58 = require('bs58') +var base58check = require('bs58check') var Bitcoin = require('../') var Address = Bitcoin.Address -var base58check = Bitcoin.base58check var networks = Bitcoin.networks var ECKey = Bitcoin.ECKey var ECSignature = Bitcoin.ECSignature diff --git a/test/bufferutils.js b/test/bufferutils.js index a41e2a7..485447b 100644 --- a/test/bufferutils.js +++ b/test/bufferutils.js @@ -77,6 +77,22 @@ describe('bufferutils', function () { }) }) + describe('equal', function () { + fixtures.valid.forEach(function (f) { + describe('for ' + f.hexVI, function () { + fixtures.valid.forEach(function (f2) { + it('equates the string comparison: ' + f.hexVI + ' === ' + f2.hexVI, function () { + var a = new Buffer(f.hexVI, 'hex') + var b = new Buffer(f2.hexVI, 'hex') + var expected = f.hexVI === f2.hexVI + + assert.equal(bufferutils.equal(a, b), expected) + }) + }) + }) + }) + }) + describe('reverse', function () { fixtures.valid.forEach(function (f) { it('reverses ' + f.hex64 + ' correctly', function () { diff --git a/test/crypto.js b/test/crypto.js index e1c6306..91827ca 100644 --- a/test/crypto.js +++ b/test/crypto.js @@ -3,84 +3,21 @@ var assert = require('assert') var crypto = require('../src/crypto') -var fixtures = require('./fixtures/crypto.json') +var fixtures = require('./fixtures/crypto') describe('Crypto', function () { - describe('HASH160', function () { - it('matches the test vectors', function () { - fixtures.before.hex.forEach(function (hex, i) { - var data = new Buffer(hex, 'hex') - var actual = crypto.hash160(data).toString('hex') + ['hash160', 'hash256', 'ripemd160', 'sha1', 'sha256'].forEach(function (algorithm) { + describe(algorithm, function () { + fixtures.valid.forEach(function (f) { + var fn = crypto[algorithm] + var expected = f[algorithm] - assert.equal(actual, fixtures.after.hash160[i]) - }) - }) - }) + it('returns ' + expected + ' for ' + f.hex, function () { + var data = new Buffer(f.hex, 'hex') + var actual = fn(data).toString('hex') - describe('HASH256', function () { - it('matches the test vectors', function () { - fixtures.before.hex.forEach(function (hex, i) { - var data = new Buffer(hex, 'hex') - var actual = crypto.hash256(data).toString('hex') - - assert.equal(actual, fixtures.after.hash256[i]) - }) - }) - }) - - describe('RIPEMD160', function () { - it('matches the test vectors', function () { - fixtures.before.hex.forEach(function (hex, i) { - var data = new Buffer(hex, 'hex') - var actual = crypto.ripemd160(data).toString('hex') - - assert.equal(actual, fixtures.after.ripemd160[i]) - }) - }) - }) - - describe('SHA1', function () { - it('matches the test vectors', function () { - fixtures.before.hex.forEach(function (hex, i) { - var data = new Buffer(hex, 'hex') - var actual = crypto.sha1(data).toString('hex') - - assert.equal(actual, fixtures.after.sha1[i]) - }) - }) - }) - - describe('SHA256', function () { - it('matches the test vectors', function () { - fixtures.before.hex.forEach(function (hex, i) { - var data = new Buffer(hex, 'hex') - var actual = crypto.sha256(data).toString('hex') - - assert.equal(actual, fixtures.after.sha256[i]) - }) - }) - }) - - describe('HmacSHA256', function () { - it('matches the test vectors', function () { - fixtures.before.hex.forEach(function (hex, i) { - var data = new Buffer(hex, 'hex') - var secret = new Buffer(fixtures.before.secret) - var actual = crypto.HmacSHA256(data, secret).toString('hex') - - assert.equal(actual, fixtures.after.hmacsha256[i]) - }) - }) - }) - - describe('HmacSHA512', function () { - it('matches the test vectors', function () { - fixtures.before.hex.forEach(function (hex, i) { - var data = new Buffer(hex, 'hex') - var secret = new Buffer(fixtures.before.secret) - var actual = crypto.HmacSHA512(data, secret).toString('hex') - - assert.equal(actual, fixtures.after.hmacsha512[i]) + assert.equal(actual, expected) + }) }) }) }) diff --git a/test/ecdsa.js b/test/ecdsa.js index 3e2db40..a5ff8a5 100644 --- a/test/ecdsa.js +++ b/test/ecdsa.js @@ -31,17 +31,6 @@ describe('ecdsa', function () { }) }) - // FIXME: remove in 2.0.0 - fixtures.valid.ecdsa.forEach(function (f) { - it('(deprecated) for "' + f.message + '"', function () { - var d = BigInteger.fromHex(f.d) - var h1 = crypto.sha256(f.message) - - var k = ecdsa.deterministicGenerateK(curve, h1, d) // default checkSig - assert.equal(k.toHex(), f.k) - }) - }) - it('loops until an appropriate k value is found', sinon.test(function () { this.mock(BigInteger).expects('fromBuffer') .exactly(3) diff --git a/test/fixtures/crypto.json b/test/fixtures/crypto.json index 0875eb7..aa66fec 100644 --- a/test/fixtures/crypto.json +++ b/test/fixtures/crypto.json @@ -1,55 +1,36 @@ { - "before": { - "secret": "vires is numeris", - "hex": [ - "0000000000000001", - "0101010101010101", - "FFFFFFFFFFFFFFFF", - "4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e65632061742066617563696275732073617069656e2c2076656c20666163696c6973697320617263752e20536564207574206d61737361206e6962682e205574206d6f6c6c69732070756c76696e6172206d617373612e20557420756c6c616d636f7270657220646f6c6f7220656e696d2c20696e206d6f6c657374696520656e696d20636f6e64696d656e74756d2061632e20416c697175616d206572617420766f6c75747061742e204e756c6c6120736f64616c657320617420647569206e656320" - ] - }, - "after": { - "hash160": [ - "cdb00698f02afd929ffabea308340fa99ac2afa8", - "abaf1119f83e384210fe8e222eac76e2f0da39dc", - "f86221f5a1fca059a865c0b7d374dfa9d5f3aeb4", - "9763e6b367c363bd6b88a7b361c98e6beee243a5" - ], - "hash256": [ - "3ae5c198d17634e79059c2cd735491553d22c4e09d1d9fea3ecf214565df2284", - "728338d99f356175c4945ef5cccfa61b7b56143cbbf426ddd0e0fc7cfe8c3c23", - "752adad0a7b9ceca853768aebb6965eca126a62965f698a0c1bc43d83db632ad", - "033588797115feb3545052670cac2a46584ab3cb460de63756ee0275e66b5799" - ], - "ripemd160": [ - "8d1a05d1bc08870968eb8a81ad4393fd3aac6633", - "5825701b4b9767fd35063b286dca3582853e0630", - "cb760221600ed34337ca3ab70016b5f58c838120", - "cad8593dcdef12ee334c97bab9787f07b3f3a1a5" - ], - "sha1": [ - "cb473678976f425d6ec1339838f11011007ad27d", - "c0357a32ed1f6a03be92dd094476f7f1a2e214ec", - "be673e8a56eaa9d8c1d35064866701c11ef8e089", - "10d96fb43aca84e342206887bbeed3065d4e4344" - ], - "sha256": [ - "cd2662154e6d76b2b2b92e70c0cac3ccf534f9b74eb5b89819ec509083d00a50", - "04abc8821a06e5a30937967d11ad10221cb5ac3b5273e434f1284ee87129a061", - "12a3ae445661ce5dee78d0650d33362dec29c4f82af05e7e57fb595bbbacf0ca", - "a7fb8276035057ed6479c5f2305a96da100ac43f0ac10f277e5ab8c5457429da" - ], - "hmacsha256": [ - "73442dc8dd7f71a106a20fddd49d31856b1db12956c75070c8186b0b3eb71251", - "7204c72af7c73f5e84447a752dc8a2708f91b896f29de5fcf4b7f42f13a30c6e", - "a03c2ac6e9ca86678b5608a3d8682de46d17026f5fac4fd7147d2e5022061833", - "a780cd6e5c29cf11f756536ea5779992687c1b3b5e37f31b027a392d94e91fb8" - ], - "hmacsha512": [ - "4c0595aed1f5d066ea9f797727c060eb86cb55ff29d4d4fd2cd0ad3a012386763aea604c030619c79aa7fd8d03cda1b73a9ebd17906a3d2a350108d1a98b24ac", - "f80b90d63b804b3d2ab03b9bfb3ac94ee271352eb8bddfb6b4f5cf2a4fc9176acea35f517728e64943d1eb8af1e4674a114082c81bc8874d88b408b3b406d6a4", - "134cf60c30a5cd412c7a5cd6c3f878279e139b47c19550b7456fa137fbf90e580ae0a923a22052f42ec801ac658db32821e271161b563eac4926285ba6b8f410", - "7dee95aa3c462d3eb7ecb61536cb215e471d1fa73d8643a967905946e26c536588c5058abd5a049a22b987db95a7fb420f3bff12359dc53d03d7ce7df714e029" - ] - } + "valid": [ + { + "hex": "0000000000000001", + "hash160": "cdb00698f02afd929ffabea308340fa99ac2afa8", + "hash256": "3ae5c198d17634e79059c2cd735491553d22c4e09d1d9fea3ecf214565df2284", + "ripemd160": "8d1a05d1bc08870968eb8a81ad4393fd3aac6633", + "sha1": "cb473678976f425d6ec1339838f11011007ad27d", + "sha256": "cd2662154e6d76b2b2b92e70c0cac3ccf534f9b74eb5b89819ec509083d00a50" + }, + { + "hex": "0101010101010101", + "hash160": "abaf1119f83e384210fe8e222eac76e2f0da39dc", + "hash256": "728338d99f356175c4945ef5cccfa61b7b56143cbbf426ddd0e0fc7cfe8c3c23", + "ripemd160": "5825701b4b9767fd35063b286dca3582853e0630", + "sha1": "c0357a32ed1f6a03be92dd094476f7f1a2e214ec", + "sha256": "04abc8821a06e5a30937967d11ad10221cb5ac3b5273e434f1284ee87129a061" + }, + { + "hex": "ffffffffffffffff", + "hash160": "f86221f5a1fca059a865c0b7d374dfa9d5f3aeb4", + "hash256": "752adad0a7b9ceca853768aebb6965eca126a62965f698a0c1bc43d83db632ad", + "ripemd160": "cb760221600ed34337ca3ab70016b5f58c838120", + "sha1": "be673e8a56eaa9d8c1d35064866701c11ef8e089", + "sha256": "12a3ae445661ce5dee78d0650d33362dec29c4f82af05e7e57fb595bbbacf0ca" + }, + { + "hex": "4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e65632061742066617563696275732073617069656e2c2076656c20666163696c6973697320617263752e20536564207574206d61737361206e6962682e205574206d6f6c6c69732070756c76696e6172206d617373612e20557420756c6c616d636f7270657220646f6c6f7220656e696d2c20696e206d6f6c657374696520656e696d20636f6e64696d656e74756d2061632e20416c697175616d206572617420766f6c75747061742e204e756c6c6120736f64616c657320617420647569206e656320", + "hash160": "9763e6b367c363bd6b88a7b361c98e6beee243a5", + "hash256": "033588797115feb3545052670cac2a46584ab3cb460de63756ee0275e66b5799", + "ripemd160": "cad8593dcdef12ee334c97bab9787f07b3f3a1a5", + "sha1": "10d96fb43aca84e342206887bbeed3065d4e4344", + "sha256": "a7fb8276035057ed6479c5f2305a96da100ac43f0ac10f277e5ab8c5457429da" + } + ] } \ No newline at end of file diff --git a/test/fixtures/mainnet_tx.json b/test/fixtures/mainnet_tx.json deleted file mode 100644 index f78f876..0000000 --- a/test/fixtures/mainnet_tx.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "prevTx": "0100000001e0214ebebb0fd3414d3fdc0dbf3b0f4b247a296cafc984558622c3041b0fcc9b010000008b48304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f30141040cd2d2ce17a1e9b2b3b2cb294d40eecf305a25b7e7bfdafae6bb2639f4ee399b3637706c3d377ec4ab781355add443ae864b134c5e523001c442186ea60f0eb8ffffffff03a0860100000000001976a91400ea3576c8fcb0bc8392f10e23a3425ae24efea888ac40420f00000000001976a91477890e8ec967c5fd4316c489d171fd80cf86997188acf07cd210000000001976a9146fb93c557ee62b109370fd9003e456917401cbfa88ac00000000", - "tx": "0100000001576bc3c3285dbdccd8c3cbd8c03e10d7f77a5c839c744f34c3eb00511059b80c000000006b483045022100a82a31607b837c1ae510ae3338d1d3c7cbd57c15e322ab6e5dc927d49bffa66302205f0db6c90f1fae3c8db4ebfa753d7da1b2343d653ce0331aa94ed375e6ba366c0121020497bfc87c3e97e801414fed6a0db4b8c2e01c46e2cf9dff59b406b52224a76bffffffff02409c0000000000001976a9143443bc45c560866cfeabf1d52f50a6ed358c69f288ac50c30000000000001976a91477890e8ec967c5fd4316c489d171fd80cf86997188ac00000000", - "bigTx": "010000000ee7b73e229790c1e79a02f0c871813b3cf26a4156c5b8d942e88b38fe8d3f43a0000000008c493046022100fd3d8fef44fb0962ba3f07bee1d4cafb84e60e38e6c7d9274504b3638a8d2f520221009fce009044e615b6883d4bf62e04c48f9fe236e19d644b082b2f0ae5c98e045c014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffff7bfc005f3880a606027c7cd7dd02a0f6a6572eeb84a91aa158311be13695a7ea010000008b483045022100e2e61c40f26e2510b76dc72ea2f568ec514fce185c719e18bca9caaef2b20e9e02207f1100fc79eb0584e970c7f18fb226f178951d481767b4092d50d13c50ccba8b014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffff0e0f8e6bf951fbb84d7d8ef833a1cbf5bb046ea7251973ac6e7661c755386ee3010000008a473044022048f1611e403710f248f7caf479965a6a5f63cdfbd9a714fef4ec1b68331ade1d022074919e79376c363d4575b2fc21513d5949471703efebd4c5ca2885e810eb1fa4014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffffe6f17f35bf9f0aa7a4242ab3e29edbdb74c5274bf263e53043dddb8045cb585b000000008b483045022100886c07cad489dfcf4b364af561835d5cf985f07adf8bd1d5bd6ddea82b0ce6b2022045bdcbcc2b5fc55191bb997039cf59ff70e8515c56b62f293a9add770ba26738014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffffe6f17f35bf9f0aa7a4242ab3e29edbdb74c5274bf263e53043dddb8045cb585b010000008a4730440220535d49b819fdf294d27d82aff2865ed4e18580f0ca9796d793f611cb43a44f47022019584d5e300c415f642e37ba2a814a1e1106b4a9b91dc2a30fb57ceafe041181014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffffd3051677216ea53baa2e6d7f6a75434ac338438c59f314801c8496d1e6d1bf6d010000008b483045022100bf612b0fa46f49e70ab318ca3458d1ed5f59727aa782f7fac5503f54d9b43a590220358d7ed0e3cee63a5a7e972d9fad41f825d95de2fd0c5560382468610848d489014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffff1e751ccc4e7d973201e9174ec78ece050ef2fadd6a108f40f76a9fa314979c31010000008b483045022006e263d5f73e05c48a603e3bd236e8314e5420721d5e9020114b93e8c9220e1102210099d3dead22f4a792123347a238c87e67b55b28a94a0bb7793144cc7ad94a0168014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffff25c4cf2c61743b3f4252d921d937cca942cf32e4f3fa4a544d0b26f014337084010000008a47304402207d6e87588be47bf2d97eaf427bdd992e9d6b306255711328aee38533366a88b50220623099595ae442cb77eaddb3f91753a4fc9df56fde69cfec584c7f97e05533c8014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffffecd93c87eb43c48481e6694904305349bdea94b01104579fa9f02bff66c89663010000008a473044022020f59498aee0cf82cb113768ef3cb721000346d381ff439adb4d405f791252510220448de723aa59412266fabbc689ec25dc94b1688c27a614982047513a80173514014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffffa1fdc0a79ff98d5b6154176e321c22f4f8450dbd950bd013ad31135f5604411e010000008b48304502210088167867f87327f9c0db0444267ff0b6a026eedd629d8f16fe44a34c18e706bf0220675c8baebf89930e2d6e4463adefc50922653af99375242e38f5ee677418738a014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffffb89e8249c3573b58bf1ec7433185452dd57ab8e1daab01c3cc6ddc8b66ad3de8000000008b4830450220073d50ac5ec8388d5b3906921f9368c31ad078c8e1fb72f26d36b533f35ee327022100c398b23e6692e11dca8a1b64aae2ff70c6a781ed5ee99181b56a2f583a967cd4014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffff45ee07e182084454dacfad1e61b04ffdf9c7b01003060a6c841a01f4fff8a5a0010000008b483045022100991d1bf60c41358f08b20e53718a24e05ac0608915df4f6305a5b47cb61e5da7022003f14fc1cc5b737e2c3279a4f9be1852b49dbb3d9d6cc4c8af6a666f600dced8014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffff4cba12549f1d70f8e60aea8b546c8357f7c099e7c7d9d8691d6ee16e7dfa3170010000008c493046022100f14e2b0ef8a8e206db350413d204bc0a5cd779e556b1191c2d30b5ec023cde6f022100b90b2d2bf256c98a88f7c3a653b93cec7d25bb6a517db9087d11dbd189e8851c014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffffa4b3aed39eb2a1dc6eae4609d9909724e211c153927c230d02bd33add3026959010000008b483045022100a8cebb4f1c58f5ba1af91cb8bd4a2ed4e684e9605f5a9dc8b432ed00922d289d0220251145d2d56f06d936fd0c51fa884b4a6a5fafd0c3318f72fb05a5c9aa372195014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffff0240d52303000000001976a914167c3e1f10cc3b691c73afbdb211e156e3e3f25c88ac15462e00000000001976a914290f7d617b75993e770e5606335fa0999a28d71388ac00000000" -} diff --git a/test/hdnode.js b/test/hdnode.js index de3c4ee..342f0f5 100644 --- a/test/hdnode.js +++ b/test/hdnode.js @@ -139,15 +139,6 @@ describe('HDNode', function () { assert.equal(hd.toBase58(), f.master.base58Priv) }) }) - - // FIXME: remove in 2.x.y - it('fails when there is no private key', function () { - var hd = HDNode.fromBase58(fixtures.valid[0].master.base58) - - assert.throws(function () { - hd.toBase58(true) - }, /Missing private key/) - }) }) describe('fromBase58', function () { @@ -178,60 +169,6 @@ describe('HDNode', function () { }) }) - describe('fromBuffer/fromHex', function () { - fixtures.valid.forEach(function (f) { - it('imports ' + f.master.hex + ' (public) correctly', function () { - var hd = HDNode.fromHex(f.master.hex) - - assert.equal(hd.toBuffer().toString('hex'), f.master.hex) - }) - }) - - fixtures.valid.forEach(function (f) { - it('imports ' + f.master.hexPriv + ' (private) correctly', function () { - var hd = HDNode.fromHex(f.master.hexPriv) - - assert.equal(hd.toBuffer().toString('hex'), f.master.hexPriv) - }) - }) - - fixtures.invalid.fromBuffer.forEach(function (f) { - it('throws on ' + f.hex, function () { - assert.throws(function () { - HDNode.fromHex(f.hex) - }, new RegExp(f.exception)) - }) - }) - }) - - describe('toBuffer/toHex', function () { - fixtures.valid.forEach(function (f) { - it('exports ' + f.master.hex + ' (public) correctly', function () { - var hd = HDNode.fromSeedHex(f.master.seed).neutered() - - assert.equal(hd.toHex(), f.master.hex) - }) - }) - - fixtures.valid.forEach(function (f) { - it('exports ' + f.master.hexPriv + ' (private) correctly', function () { - var network = networks[f.network] - var hd = HDNode.fromSeedHex(f.master.seed, network) - - assert.equal(hd.toHex(), f.master.hexPriv) - }) - }) - - // FIXME: remove in 2.x.y - it('fails when there is no private key', function () { - var hd = HDNode.fromHex(fixtures.valid[0].master.hex) - - assert.throws(function () { - hd.toHex(true) - }, /Missing private key/) - }) - }) - describe('getIdentifier', function () { var f = fixtures.valid[0] diff --git a/test/integration/advanced.js b/test/integration/advanced.js index 07596b1..3d20228 100644 --- a/test/integration/advanced.js +++ b/test/integration/advanced.js @@ -9,7 +9,7 @@ describe('bitcoinjs-lib (advanced)', function () { var key = bitcoin.ECKey.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') var message = 'This is an example of a signed message.' - var signature = bitcoin.Message.sign(key, message) + var signature = bitcoin.message.sign(key, message) assert.equal(signature.toString('base64'), 'G9L5yLFjti0QTHhPyFrZCT1V/MMnBtXKmoiKDZ78NDBjERki6ZTQZdSMCtkgoNmp17By9ItJr8o7ChX0XxY91nk=') }) @@ -18,7 +18,7 @@ describe('bitcoinjs-lib (advanced)', function () { var signature = 'HJLQlDWLyb1Ef8bQKEISzFbDAKctIlaqOpGbrk3YVtRsjmC61lpE5ErkPRUFtDKtx98vHFGUWlFhsh3DiW6N0rE' var message = 'This is an example of a signed message.' - assert(bitcoin.Message.verify(address, signature, message)) + assert(bitcoin.message.verify(address, signature, message)) }) it('can create an OP_RETURN transaction', function (done) { diff --git a/test/message.js b/test/message.js index 467d1fc..e982a45 100644 --- a/test/message.js +++ b/test/message.js @@ -1,21 +1,21 @@ /* global describe, it */ var assert = require('assert') +var message = require('../src/message') var networks = require('../src/networks') var Address = require('../src/address') var BigInteger = require('bigi') var ECKey = require('../src/eckey') -var Message = require('../src/message') var fixtures = require('./fixtures/message.json') -describe('Message', function () { +describe('message', function () { describe('magicHash', function () { fixtures.valid.magicHash.forEach(function (f) { it('produces the correct magicHash for "' + f.message + '" (' + f.network + ')', function () { var network = networks[f.network] - var actual = Message.magicHash(f.message, network) + var actual = message.magicHash(f.message, network) assert.equal(actual.toString('hex'), f.magicHash) }) @@ -28,24 +28,24 @@ describe('Message', function () { var network = networks[f.network] var address = Address.fromBase58Check(f.address) - assert(Message.verify(address, f.signature, f.message, network)) + assert(message.verify(address, f.signature, f.message, network)) }) fixtures.valid.verify.forEach(function (f) { it('verifies a valid signature for "' + f.message + '" (' + f.network + ')', function () { var network = networks[f.network] - assert(Message.verify(f.address, f.signature, f.message, network)) + assert(message.verify(f.address, f.signature, f.message, network)) if (f.compressed) { - assert(Message.verify(f.compressed.address, f.compressed.signature, f.message, network)) + assert(message.verify(f.compressed.address, f.compressed.signature, f.message, network)) } }) }) fixtures.invalid.verify.forEach(function (f) { it(f.description, function () { - assert(!Message.verify(f.address, f.signature, f.message)) + assert(!message.verify(f.address, f.signature, f.message)) }) }) }) @@ -56,12 +56,12 @@ describe('Message', function () { var network = networks[f.network] var privKey = new ECKey(new BigInteger(f.d), false) - var signature = Message.sign(privKey, f.message, network) + var signature = message.sign(privKey, f.message, network) assert.equal(signature.toString('base64'), f.signature) if (f.compressed) { var compressedPrivKey = new ECKey(new BigInteger(f.d)) - var compressedSignature = Message.sign(compressedPrivKey, f.message) + var compressedSignature = message.sign(compressedPrivKey, f.message) assert.equal(compressedSignature.toString('base64'), f.compressed.signature) } diff --git a/test/transaction.js b/test/transaction.js index a2ab12f..4a64493 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -1,10 +1,7 @@ /* global describe, it, beforeEach */ var assert = require('assert') -var scripts = require('../src/scripts') -var Address = require('../src/address') -var ECKey = require('../src/eckey') var Transaction = require('../src/transaction') var Script = require('../src/script') @@ -65,21 +62,10 @@ describe('Transaction', function () { }) describe('addInput', function () { - // FIXME: not as pretty as could be - // Probably a bit representative of the API - var prevTxHash, prevTxId, prevTx + var prevTxHash beforeEach(function () { var f = fixtures.valid[0] - prevTx = Transaction.fromHex(f.hex) - prevTxHash = prevTx.getHash() - prevTxId = prevTx.getId() - }) - - it('accepts a transaction id', function () { - var tx = new Transaction() - tx.addInput(prevTxId, 0) - - assert.deepEqual(tx.ins[0].hash, prevTxHash) + prevTxHash = new Buffer(f.hash, 'hex') }) it('accepts a transaction hash', function () { @@ -89,13 +75,6 @@ describe('Transaction', function () { assert.deepEqual(tx.ins[0].hash, prevTxHash) }) - it('accepts a Transaction object', function () { - var tx = new Transaction() - tx.addInput(prevTx, 0) - - assert.deepEqual(tx.ins[0].hash, prevTxHash) - }) - it('returns an index', function () { var tx = new Transaction() assert.equal(tx.addInput(prevTxHash, 0), 0) @@ -129,43 +108,19 @@ describe('Transaction', function () { }) describe('addOutput', function () { - // FIXME: not as pretty as could be - // Probably a bit representative of the API - var destAddressB58, destAddress, destScript - beforeEach(function () { - destAddressB58 = '15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3' - destAddress = Address.fromBase58Check(destAddressB58) - destScript = destAddress.toOutputScript() - }) + fixtures.valid.forEach(function (f) { + it('should add the outputs for ' + f.id + ' correctly', function () { + var tx = new Transaction() - it('accepts an address string', function () { - var tx = new Transaction() - tx.addOutput(destAddressB58, 40000) + f.raw.outs.forEach(function (txOut, i) { + var scriptPubKey = Script.fromASM(txOut.script) + var j = tx.addOutput(scriptPubKey, txOut.value) - assert.deepEqual(tx.outs[0].script, destScript) - assert.equal(tx.outs[0].value, 40000) - }) - - it('accepts an Address', function () { - var tx = new Transaction() - tx.addOutput(destAddress, 40000) - - assert.deepEqual(tx.outs[0].script, destScript) - assert.equal(tx.outs[0].value, 40000) - }) - - it('accepts a scriptPubKey', function () { - var tx = new Transaction() - tx.addOutput(destScript, 40000) - - assert.deepEqual(tx.outs[0].script, destScript) - assert.equal(tx.outs[0].value, 40000) - }) - - it('returns an index', function () { - var tx = new Transaction() - assert.equal(tx.addOutput(destScript, 40000), 0) - assert.equal(tx.addOutput(destScript, 40000), 1) + assert.equal(i, j) + assert.equal(tx.outs[i].script, scriptPubKey) + assert.equal(tx.outs[i].value, txOut.value) + }) + }) }) }) @@ -192,9 +147,8 @@ describe('Transaction', function () { fixtures.valid.forEach(function (f) { it('should return the id for ' + f.id, function () { var tx = Transaction.fromHex(f.hex) - var actual = tx.getId() - assert.equal(actual, f.id) + assert.equal(tx.getId(), f.id) }) }) }) @@ -203,48 +157,12 @@ describe('Transaction', function () { fixtures.valid.forEach(function (f) { it('should return the hash for ' + f.id, function () { var tx = Transaction.fromHex(f.hex) - var actual = tx.getHash().toString('hex') - assert.equal(actual, f.hash) + assert.deepEqual(tx.getHash().toString('hex'), f.hash) }) }) }) // TODO: // hashForSignature: [Function], - - // FIXME: remove in 2.x.y - describe('signInput/validateInput', function () { - it('works for multi-sig redeem script', function () { - var tx = new Transaction() - tx.addInput('d6f72aab8ff86ff6289842a0424319bf2ddba85dc7c52757912297f948286389', 0) - tx.addOutput('mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r', 1) - - var privKeys = [ - '5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf', - '5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAvUcVfH' - ].map(function (wif) { - return ECKey.fromWIF(wif) - }) - var pubKeys = privKeys.map(function (eck) { - return eck.pub - }) - var redeemScript = scripts.multisigOutput(2, pubKeys) - - var signatures = privKeys.map(function (privKey) { - return tx.signInput(0, redeemScript, privKey) - }) - - var redeemScriptSig = scripts.multisigInput(signatures) - var scriptSig = scripts.scriptHashInput(redeemScriptSig, redeemScript) - tx.setInputScript(0, scriptSig) - - signatures.forEach(function (sig, i) { - assert(tx.validateInput(0, redeemScript, privKeys[i].pub, sig)) - }) - - var expected = '010000000189632848f99722915727c5c75da8db2dbf194342a0429828f66ff88fab2af7d600000000fd1b0100483045022100e5be20d440b2bbbc886161f9095fa6d0bca749a4e41d30064f30eb97adc7a1f5022061af132890d8e4e90fedff5e9365aeeb77021afd8ef1d5c114d575512e9a130a0147304402205054e38e9d7b5c10481b6b4991fde5704cd94d49e344406e3c2ce4d18a43bf8e022051d7ba8479865b53a48bee0cce86e89a25633af5b2918aa276859489e232f51c014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff0101000000000000001976a914751e76e8199196d454941c45d1b3a323f1433bd688ac00000000' - assert.equal(tx.toHex(), expected) - }) - }) }) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 11172e5..350011b 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -2,6 +2,7 @@ var assert = require('assert') +var Address = require('../src/address') var BigInteger = require('bigi') var ECKey = require('../src/eckey') var Script = require('../src/script') @@ -62,8 +63,8 @@ describe('TransactionBuilder', function () { txb = new TransactionBuilder() prevTx = new Transaction() - prevTx.addOutput('1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH', 0) - prevTx.addOutput('1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', 1) + prevTx.addOutput(Address.fromBase58Check('1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH').toOutputScript(), 0) + prevTx.addOutput(Address.fromBase58Check('1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP').toOutputScript(), 1) prevTxHash = prevTx.getHash() privKey = new ECKey(BigInteger.ONE, false) @@ -121,6 +122,33 @@ describe('TransactionBuilder', function () { }) describe('addOutput', function () { + it('accepts an address string and value', function () { + var vout = txb.addOutput(privAddress.toBase58Check(), 1000) + assert.equal(vout, 0) + + var txout = txb.tx.outs[0] + assert.deepEqual(txout.script, privScript) + assert.equal(txout.value, 1000) + }) + + it('accepts an Address object and value', function () { + var vout = txb.addOutput(privAddress, 1000) + assert.equal(vout, 0) + + var txout = txb.tx.outs[0] + assert.deepEqual(txout.script, privScript) + assert.equal(txout.value, 1000) + }) + + it('accepts a ScriptPubKey and value', function () { + var vout = txb.addOutput(privScript, 1000) + assert.equal(vout, 0) + + var txout = txb.tx.outs[0] + assert.deepEqual(txout.script, privScript) + assert.equal(txout.value, 1000) + }) + it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', function () { txb.addInput(prevTxHash, 0) txb.addOutput(privScript, 2000) diff --git a/test/wallet.js b/test/wallet.js deleted file mode 100644 index 0ebabd1..0000000 --- a/test/wallet.js +++ /dev/null @@ -1,691 +0,0 @@ -/* global describe, it, beforeEach, afterEach */ - -var assert = require('assert') -var bufferutils = require('../src/bufferutils') -var crypto = require('../src/crypto') -var networks = require('../src/networks') -var sinon = require('sinon') -var scripts = require('../src/scripts') - -var Address = require('../src/address') -var HDNode = require('../src/hdnode') -var Transaction = require('../src/transaction') -var TransactionBuilder = require('../src/transaction_builder') -var Wallet = require('../src/wallet') - -var fixtureTxes = require('./fixtures/mainnet_tx') -var fixtureTx1Hex = fixtureTxes.prevTx -var fixtureTx2Hex = fixtureTxes.tx - -function fakeTxHash (i) { - var hash = new Buffer(32) - hash.fill(i) - return hash -} - -function fakeTxId (i) { - var hash = fakeTxHash(i) - Array.prototype.reverse.call(hash) - return hash.toString('hex') -} - -describe('Wallet', function () { - var seed - beforeEach(function () { - seed = crypto.sha256("don't use a string seed like this in real life") - }) - - describe('constructor', function () { - var wallet - beforeEach(function () { - wallet = new Wallet(seed) - }) - - it('defaults to Bitcoin network', function () { - assert.equal(wallet.getMasterKey().network, networks.bitcoin) - }) - - it("generates m/0' as the main account", function () { - var mainAccount = wallet.getAccountZero() - assert.equal(mainAccount.index, 0 + HDNode.HIGHEST_BIT) - assert.equal(mainAccount.depth, 1) - }) - - it("generates m/0'/0 as the external account", function () { - var account = wallet.getExternalAccount() - assert.equal(account.index, 0) - assert.equal(account.depth, 2) - }) - - it("generates m/0'/1 as the internal account", function () { - var account = wallet.getInternalAccount() - assert.equal(account.index, 1) - assert.equal(account.depth, 2) - }) - - describe('when seed is not specified', function () { - it('generates a seed', function () { - var wallet = new Wallet() - assert(wallet.getMasterKey()) - }) - }) - - describe('constructor options', function () { - beforeEach(function () { - wallet = new Wallet(seed, networks.testnet) - }) - - it('uses the network if specified', function () { - assert.equal(wallet.getMasterKey().network, networks.testnet) - }) - }) - }) - - describe('newMasterKey', function () { - it('resets accounts', function () { - var wallet = new Wallet() - var oldAccountZero = wallet.getAccountZero() - var oldExternalAccount = wallet.getExternalAccount() - var oldInternalAccount = wallet.getInternalAccount() - - wallet.newMasterKey(seed) - assertNotEqual(wallet.getAccountZero(), oldAccountZero) - assertNotEqual(wallet.getExternalAccount(), oldExternalAccount) - assertNotEqual(wallet.getInternalAccount(), oldInternalAccount) - }) - - it('resets addresses', function () { - var wallet = new Wallet() - wallet.generateAddress() - wallet.generateChangeAddress() - var oldAddresses = wallet.addresses - var oldChangeAddresses = wallet.changeAddresses - assert.notDeepEqual(oldAddresses, []) - assert.notDeepEqual(oldChangeAddresses, []) - - wallet.newMasterKey(seed) - assert.deepEqual(wallet.addresses, []) - assert.deepEqual(wallet.changeAddresses, []) - }) - }) - - describe('generateAddress', function () { - it('generate receiving addresses', function () { - var wallet = new Wallet(seed, networks.testnet) - var expectedAddresses = [ - 'n1GyUANZand9Kw6hGSV9837cCC9FFUQzQa', - 'n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X' - ] - - assert.equal(wallet.generateAddress(), expectedAddresses[0]) - assert.equal(wallet.generateAddress(), expectedAddresses[1]) - assert.deepEqual(wallet.addresses, expectedAddresses) - }) - }) - - describe('generateChangeAddress', function () { - it('generates change addresses', function () { - var wallet = new Wallet(seed, networks.testnet) - var expectedAddresses = ['mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn'] - - assert.equal(wallet.generateChangeAddress(), expectedAddresses[0]) - assert.deepEqual(wallet.changeAddresses, expectedAddresses) - }) - }) - - describe('getPrivateKey', function () { - it('returns the private key at the given index of external account', function () { - var wallet = new Wallet(seed, networks.testnet) - - assertEqual(wallet.getPrivateKey(0), wallet.getExternalAccount().derive(0).privKey) - assertEqual(wallet.getPrivateKey(1), wallet.getExternalAccount().derive(1).privKey) - }) - }) - - describe('getInternalPrivateKey', function () { - it('returns the private key at the given index of internal account', function () { - var wallet = new Wallet(seed, networks.testnet) - - assertEqual(wallet.getInternalPrivateKey(0), wallet.getInternalAccount().derive(0).privKey) - assertEqual(wallet.getInternalPrivateKey(1), wallet.getInternalAccount().derive(1).privKey) - }) - }) - - describe('getPrivateKeyForAddress', function () { - it('returns the private key for the given address', function () { - var wallet = new Wallet(seed, networks.testnet) - wallet.generateChangeAddress() - wallet.generateAddress() - wallet.generateAddress() - - assertEqual( - wallet.getPrivateKeyForAddress('n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X'), - wallet.getExternalAccount().derive(1).privKey - ) - assertEqual( - wallet.getPrivateKeyForAddress('mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn'), - wallet.getInternalAccount().derive(0).privKey - ) - }) - - it('raises an error when address is not found', function () { - var wallet = new Wallet(seed, networks.testnet) - - assert.throws(function () { - wallet.getPrivateKeyForAddress('n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X') - }, /Unknown address. Make sure the address is from the keychain and has been generated/) - }) - }) - - describe('Unspent Outputs', function () { - var utxo - var wallet - - beforeEach(function () { - utxo = { - 'address': '1AZpKpcfCzKDUeTFBQUL4MokQai3m3HMXv', - 'confirmations': 1, - 'index': 0, - 'txId': fakeTxId(6), - 'value': 20000, - 'pending': false - } - }) - - describe('on construction', function () { - beforeEach(function () { - wallet = new Wallet(seed, networks.bitcoin) - wallet.setUnspentOutputs([utxo]) - }) - - it('matches the expected behaviour', function () { - var output = wallet.unspents[0] - - assert.equal(output.address, utxo.address) - assert.equal(output.value, utxo.value) - }) - }) - - describe('getBalance', function () { - beforeEach(function () { - var utxo1 = cloneObject(utxo) - utxo1.hash = fakeTxId(5) - - wallet = new Wallet(seed, networks.bitcoin) - wallet.setUnspentOutputs([utxo, utxo1]) - }) - - it('sums over utxo values', function () { - assert.equal(wallet.getBalance(), 40000) - }) - }) - - describe('getUnspentOutputs', function () { - beforeEach(function () { - wallet = new Wallet(seed, networks.bitcoin) - wallet.setUnspentOutputs([utxo]) - }) - - it('parses wallet unspents to the expected format', function () { - var outputs = wallet.getUnspentOutputs() - var output = outputs[0] - - assert.equal(utxo.address, output.address) - assert.equal(utxo.index, output.index) - assert.equal(utxo.value, output.value) - - // FIXME: remove in 2.0.0 - assert.equal(utxo.txId, output.hash) - assert.equal(utxo.pending, output.pending) - - // new in 2.0.0 - assert.equal(utxo.txId, output.txId) - assert.equal(utxo.confirmations, output.confirmations) - }) - - it("ignores spent unspents (outputs with 'spent' property)", function () { - var unspent = wallet.unspents[0] - unspent.pending = true - unspent.spent = true - assert.deepEqual(wallet.getUnspentOutputs(), []) - }) - }) - }) - - describe('setUnspentOutputs', function () { - var utxo - var wallet - - beforeEach(function () { - utxo = { - hash: fakeTxId(0), - index: 0, - address: '115qa7iPZqn6as57hxLL8E9VUnhmGQxKWi', - value: 500000 - } - - wallet = new Wallet(seed, networks.bitcoin) - }) - - it('matches the expected behaviour', function () { - wallet.setUnspentOutputs([utxo]) - - var output = wallet.unspents[0] - assert.equal(output.value, utxo.value) - assert.equal(output.address, utxo.address) - }) - - describe('required fields', function () { - ['index', 'address', 'hash', 'value'].forEach(function (field) { - it('throws an error when ' + field + ' is missing', function () { - delete utxo[field] - - assert.throws(function () { - wallet.setUnspentOutputs([utxo]) - }) - }) - }) - }) - }) - - describe('Process transaction', function () { - var wallet - beforeEach(function () { - wallet = new Wallet(seed) - }) - - var addresses - var tx - - beforeEach(function () { - addresses = [ - '115qa7iPZqn6as57hxLL8E9VUnhmGQxKWi', - '1Bu3bhwRmevHLAy1JrRB6AfcxfgDG2vXRd', - '1BBjuhF2jHxu7tPinyQGCuaNhEs6f5u59u' - ] - - tx = Transaction.fromHex(fixtureTx1Hex) - }) - - describe('processPendingTx', function () { - it('incoming: sets the pending flag on output', function () { - wallet.addresses = [addresses[0]] - wallet.processPendingTx(tx) - - verifyOutputAdded(0, true) - }) - - describe('when tx ins outpoint contains a known txhash:i', function () { - var spendTx - beforeEach(function () { - wallet.addresses = [addresses[0]] - wallet.processConfirmedTx(tx) - - spendTx = Transaction.fromHex(fixtureTx2Hex) - }) - - it("outgoing: sets the pending flag and 'spent' on output", function () { - var txIn = spendTx.ins[0] - var txInId = new Buffer(txIn.hash) - Array.prototype.reverse.call(txInId) - txInId = txInId.toString('hex') - - var unspent = wallet.unspents[0] - assert(!unspent.pending) - - wallet.processPendingTx(spendTx) - assert(unspent.pending) - assert(unspent.spent, true) - }) - }) - }) - - describe('processConfirmedTx', function () { - it('does not throw on scripts with no corresponding Address', function () { - var pubKey = wallet.getPrivateKey(0).pub - var script = scripts.pubKeyOutput(pubKey) - var tx2 = new Transaction() - - tx2.addInput(fakeTxHash(1), 0) - tx2.addOutput(script, 10000) - - wallet.processConfirmedTx(tx2) - }) - - describe("when tx outs contains an address owned by the wallet, an 'output' gets added to wallet.unspentMap", function () { - it('works for receive address', function () { - var totalOuts = outputCount() - - wallet.addresses = [addresses[0]] - wallet.processConfirmedTx(tx) - - assert.equal(outputCount(), totalOuts + 1) - verifyOutputAdded(0, false) - }) - - it('works for change address', function () { - var totalOuts = outputCount() - wallet.changeAddresses = [addresses[1]] - - wallet.processConfirmedTx(tx) - - assert.equal(outputCount(), totalOuts + 1) - verifyOutputAdded(1, false) - }) - - function outputCount () { - return Object.keys(wallet.unspentMap).length - } - }) - - describe('when tx ins contains a known txhash:i', function () { - var spendTx - beforeEach(function () { - wallet.addresses = [addresses[0]] // the address fixtureTx2 used as input - wallet.processConfirmedTx(tx) - - spendTx = Transaction.fromHex(fixtureTx2Hex) - }) - - it('does not add to wallet.unspentMap', function () { - wallet.processConfirmedTx(spendTx) - assert.deepEqual(wallet.unspentMap, {}) - }) - - it("deletes corresponding 'unspent'", function () { - var txIn = spendTx.ins[0] - var txInId = bufferutils.reverse(txIn.hash).toString('hex') - - var expected = txInId + ':' + txIn.index - assert(expected in wallet.unspentMap) - - wallet.processConfirmedTx(spendTx) - assert(!(expected in wallet.unspentMap)) - }) - }) - }) - - it('does nothing when none of the involved addresses belong to the wallet', function () { - wallet.processConfirmedTx(tx) - assert.deepEqual(wallet.unspentMap, {}) - }) - - function verifyOutputAdded (index, pending) { - var txOut = tx.outs[index] - - var key = tx.getId() + ':' + index - var output = wallet.unspentMap[key] - assert.deepEqual(output.txHash, tx.getHash()) - assert.equal(output.value, txOut.value) - assert.equal(output.pending, pending) - - var txOutAddress = Address.fromOutputScript(txOut.script).toString() - assert.equal(output.address, txOutAddress) - } - }) - - describe('createTx', function () { - var wallet - var address1, address2 - var to, value - - beforeEach(function () { - to = 'mt7MyTVVEWnbwpF5hBn6fgnJcv95Syk2ue' - value = 500000 - - address1 = 'n1GyUANZand9Kw6hGSV9837cCC9FFUQzQa' - address2 = 'n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X' - - // set up 3 utxos - var utxos = [ - { - 'txId': fakeTxId(1), - 'index': 0, - 'address': address1, - 'value': 400000 // not enough for value - }, - { - 'txId': fakeTxId(2), - 'index': 1, - 'address': address1, - 'value': 500000 // enough for only value - }, - { - 'txId': fakeTxId(3), - 'index': 0, - 'address': address2, - 'value': 510000 // enough for value and fee - } - ] - - wallet = new Wallet(seed, networks.testnet) - wallet.setUnspentOutputs(utxos) - wallet.generateAddress() - wallet.generateAddress() - }) - - describe('transaction fee', function () { - it('allows fee to be specified', function () { - var fee = 30000 - var tx = wallet.createTx(to, value, { - fixedFee: fee - }) - - assert.equal(getFee(wallet, tx), fee) - }) - - it('allows fee to be set to zero', function () { - value = 510000 - var fee = 0 - var tx = wallet.createTx(to, value, { - fixedFee: fee - }) - - assert.equal(getFee(wallet, tx), fee) - }) - - it('does not overestimate fees when network has dustSoftThreshold', function () { - var utxo = { - txId: fakeTxId(0), - index: 0, - address: 'LeyySKbQrRRwodKEj1W4a8y3YQupPLw5os', - value: 500000 - } - - var wallet = new Wallet(seed, networks.litecoin) - wallet.setUnspentOutputs([utxo]) - wallet.generateAddress() - - value = 200000 - var tx = wallet.createTx(utxo.address, value) - - assert.equal(getFee(wallet, tx), 100000) - }) - - function getFee (wallet, tx) { - var inputValue = tx.ins.reduce(function (accum, input) { - var txId = bufferutils.reverse(input.hash).toString('hex') - - return accum + wallet.unspentMap[txId + ':' + input.index].value - }, 0) - - return tx.outs.reduce(function (accum, output) { - return accum - output.value - }, inputValue) - } - }) - - describe('choosing utxo', function () { - it('takes fees into account', function () { - var tx = wallet.createTx(to, value) - - assert.equal(tx.ins.length, 1) - assert.deepEqual(tx.ins[0].hash, fakeTxHash(3)) - assert.equal(tx.ins[0].index, 0) - }) - - it('uses confirmed outputs', function () { - var tx2 = new Transaction() - tx2.addInput(fakeTxId(4), 0) - tx2.addOutput(address2, 530000) - - wallet.processConfirmedTx(tx2) - var tx = wallet.createTx(to, value) - - assert.equal(tx.ins.length, 1) - assert.deepEqual(tx.ins[0].hash, tx2.getHash()) - assert.equal(tx.ins[0].index, 0) - }) - - it('ignores pending outputs', function () { - var tx2 = new Transaction() - tx2.addInput(fakeTxId(4), 0) - tx2.addOutput(address2, 530000) - - wallet.processPendingTx(tx2) - var tx = wallet.createTx(to, value) - - assert.equal(tx.ins.length, 1) - assert.deepEqual(tx.ins[0].hash, fakeTxHash(3)) - assert.equal(tx.ins[0].index, 0) - }) - }) - - describe('changeAddress', function () { - it('should allow custom changeAddress', function () { - var changeAddress = 'mfrFjnKZUvTcvdAK2fUX5D8v1Epu5H8JCk' - var fromValue = 510000 - var toValue = fromValue / 2 - var fee = 1e3 - - var tx = wallet.createTx(to, toValue, { - fixedFee: fee, - changeAddress: changeAddress - }) - assert.equal(tx.outs.length, 2) - - var outAddress0 = Address.fromOutputScript(tx.outs[0].script, networks.testnet) - var outAddress1 = Address.fromOutputScript(tx.outs[1].script, networks.testnet) - - assert.equal(outAddress0.toString(), to) - assert.equal(tx.outs[0].value, toValue) - - assert.equal(outAddress1.toString(), changeAddress) - assert.equal(tx.outs[1].value, fromValue - (toValue + fee)) - }) - }) - - describe('transaction outputs', function () { - it('includes the specified address and amount', function () { - var tx = wallet.createTx(to, value) - - assert.equal(tx.outs.length, 1) - var out = tx.outs[0] - var outAddress = Address.fromOutputScript(out.script, networks.testnet) - - assert.equal(outAddress.toString(), to) - assert.equal(out.value, value) - }) - - describe('change', function () { - it('uses the last change address if there is any', function () { - var fee = 0 - wallet.generateChangeAddress() - wallet.generateChangeAddress() - var tx = wallet.createTx(to, value, { - fixedFee: fee - }) - - assert.equal(tx.outs.length, 2) - var out = tx.outs[1] - var outAddress = Address.fromOutputScript(out.script, networks.testnet) - - assert.equal(outAddress.toString(), wallet.changeAddresses[1]) - assert.equal(out.value, 10000) - }) - - it('generates a change address if there is not any', function () { - var fee = 0 - assert.equal(wallet.changeAddresses.length, 0) - - var tx = wallet.createTx(to, value, { - fixedFee: fee - }) - - assert.equal(wallet.changeAddresses.length, 1) - var out = tx.outs[1] - var outAddress = Address.fromOutputScript(out.script, networks.testnet) - - assert.equal(outAddress.toString(), wallet.changeAddresses[0]) - assert.equal(out.value, 10000) - }) - - it('skips change if it is not above dust threshold', function () { - var tx1 = wallet.createTx(to, value - 546) - assert.equal(tx1.outs.length, 1) - - var tx2 = wallet.createTx(to, value - 547) - assert.equal(tx2.outs.length, 2) - }) - }) - }) - - describe('signing', function () { - afterEach(function () { - TransactionBuilder.prototype.sign.restore() - }) - - it('signs the inputs with respective keys', function () { - var fee = 30000 - sinon.spy(TransactionBuilder.prototype, 'sign') - - wallet.createTx(to, value, { - fixedFee: fee - }) - - var priv1 = wallet.getPrivateKeyForAddress(address1) - var priv2 = wallet.getPrivateKeyForAddress(address2) - - // FIXME: boo, toString invokes reqiuired affine coordinate side effects - priv1.pub.Q.toString() - priv2.pub.Q.toString() - - assert(TransactionBuilder.prototype.sign.calledWith(0, priv2)) - assert(TransactionBuilder.prototype.sign.calledWith(1, priv1)) - }) - }) - - describe('when value is below dust threshold', function () { - it('throws an error', function () { - var value = 546 - - assert.throws(function () { - wallet.createTx(to, value) - }, /546 must be above dust threshold \(546 Satoshis\)/) - }) - }) - - describe('when there is not enough money', function () { - it('throws an error', function () { - var value = 1400001 - - assert.throws(function () { - wallet.createTx(to, value) - }, /Not enough funds \(incl. fee\): 1410000 < 1410001/) - }) - }) - }) - - function assertEqual (obj1, obj2) { - assert.equal(obj1.toString(), obj2.toString()) - } - - function assertNotEqual (obj1, obj2) { - assert.notEqual(obj1.toString(), obj2.toString()) - } - - // quick and dirty: does not deal with functions on object - function cloneObject (obj) { - return JSON.parse(JSON.stringify(obj)) - } -})