remove deprecated functionality
This commit is contained in:
parent
08cc8f8621
commit
8d3686d046
12 changed files with 12 additions and 1301 deletions
|
@ -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)
|
||||
|
|
5
test/fixtures/mainnet_tx.json
vendored
5
test/fixtures/mainnet_tx.json
vendored
File diff suppressed because one or more lines are too long
|
@ -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]
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ var fixtures = require('./fixtures/message.json')
|
|||
describe('message', function () {
|
||||
describe('magicHash', function () {
|
||||
fixtures.valid.magicHash.forEach(function (f) {
|
||||
it('produces the correct magicHash for \"' + f.message + '\" (' + f.network + ')', function () {
|
||||
it('produces the correct magicHash for "' + f.message + '" (' + f.network + ')', function () {
|
||||
var network = networks[f.network]
|
||||
var actual = message.magicHash(f.message, network)
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
/* 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')
|
||||
|
||||
|
@ -212,39 +210,4 @@ describe('Transaction', function () {
|
|||
|
||||
// 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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
691
test/wallet.js
691
test/wallet.js
|
@ -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))
|
||||
}
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue