commit
f4940ccd48
2 changed files with 83 additions and 29 deletions
|
@ -77,20 +77,21 @@ function Wallet(seed, network) {
|
||||||
utxo.forEach(function(uo){
|
utxo.forEach(function(uo){
|
||||||
validateUnspentOutput(uo)
|
validateUnspentOutput(uo)
|
||||||
var o = unspentOutputToOutput(uo)
|
var o = unspentOutputToOutput(uo)
|
||||||
outputs[o.receive] = o
|
outputs[o.from] = o
|
||||||
})
|
})
|
||||||
|
|
||||||
this.outputs = outputs
|
this.outputs = outputs
|
||||||
}
|
}
|
||||||
|
|
||||||
function outputToUnspentOutput(output){
|
function outputToUnspentOutput(output){
|
||||||
var hashAndIndex = output.receive.split(":")
|
var hashAndIndex = output.from.split(":")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hash: hashAndIndex[0],
|
hash: hashAndIndex[0],
|
||||||
outputIndex: parseInt(hashAndIndex[1]),
|
outputIndex: parseInt(hashAndIndex[1]),
|
||||||
address: output.address,
|
address: output.address,
|
||||||
value: output.value
|
value: output.value,
|
||||||
|
pending: output.pending
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +99,7 @@ function Wallet(seed, network) {
|
||||||
var hash = o.hash
|
var hash = o.hash
|
||||||
var key = hash + ":" + o.outputIndex
|
var key = hash + ":" + o.outputIndex
|
||||||
return {
|
return {
|
||||||
receive: key,
|
from: key,
|
||||||
address: o.address,
|
address: o.address,
|
||||||
value: o.value,
|
value: o.value,
|
||||||
pending: o.pending
|
pending: o.pending
|
||||||
|
@ -158,7 +159,7 @@ function Wallet(seed, network) {
|
||||||
var output = txid + ':' + i
|
var output = txid + ':' + i
|
||||||
|
|
||||||
me.outputs[output] = {
|
me.outputs[output] = {
|
||||||
receive: output,
|
from: output,
|
||||||
value: txOut.value,
|
value: txOut.value,
|
||||||
address: address,
|
address: address,
|
||||||
pending: isPending
|
pending: isPending
|
||||||
|
@ -174,7 +175,13 @@ function Wallet(seed, network) {
|
||||||
|
|
||||||
var output = txinId + ':' + txIn.index
|
var output = txinId + ':' + txIn.index
|
||||||
|
|
||||||
if(me.outputs[output]) delete me.outputs[output]
|
if (!(output in me.outputs)) return
|
||||||
|
|
||||||
|
if (isPending) {
|
||||||
|
return me.outputs[output].pending = true
|
||||||
|
}
|
||||||
|
|
||||||
|
delete me.outputs[output]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +200,7 @@ function Wallet(seed, network) {
|
||||||
var utxo = utxos[i]
|
var utxo = utxos[i]
|
||||||
addresses.push(utxo.address)
|
addresses.push(utxo.address)
|
||||||
|
|
||||||
var outpoint = utxo.receive.split(':')
|
var outpoint = utxo.from.split(':')
|
||||||
tx.addInput(outpoint[0], parseInt(outpoint[1]))
|
tx.addInput(outpoint[0], parseInt(outpoint[1]))
|
||||||
|
|
||||||
var fee = fixedFee == undefined ? estimateFeePadChangeOutput(tx) : fixedFee
|
var fee = fixedFee == undefined ? estimateFeePadChangeOutput(tx) : fixedFee
|
||||||
|
@ -234,7 +241,7 @@ function Wallet(seed, network) {
|
||||||
|
|
||||||
function estimateFeePadChangeOutput(tx) {
|
function estimateFeePadChangeOutput(tx) {
|
||||||
var tmpTx = tx.clone()
|
var tmpTx = tx.clone()
|
||||||
tmpTx.addOutput(getChangeAddress(), 0)
|
tmpTx.addOutput(getChangeAddress(), network.dustSoftThreshold || 0)
|
||||||
|
|
||||||
return network.estimateFee(tmpTx)
|
return network.estimateFee(tmpTx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,7 +175,8 @@ describe('Wallet', function() {
|
||||||
"hash":"6a4062273ac4f9ea4ffca52d9fd102b08f6c32faa0a4d1318e3a7b2e437bb9c7",
|
"hash":"6a4062273ac4f9ea4ffca52d9fd102b08f6c32faa0a4d1318e3a7b2e437bb9c7",
|
||||||
"outputIndex": 0,
|
"outputIndex": 0,
|
||||||
"address" : "1AZpKpcfCzKDUeTFBQUL4MokQai3m3HMXv",
|
"address" : "1AZpKpcfCzKDUeTFBQUL4MokQai3m3HMXv",
|
||||||
"value": 20000
|
"value": 20000,
|
||||||
|
"pending": true
|
||||||
}
|
}
|
||||||
expectedOutputKey = expectedUtxo.hash + ":" + expectedUtxo.outputIndex
|
expectedOutputKey = expectedUtxo.hash + ":" + expectedUtxo.outputIndex
|
||||||
})
|
})
|
||||||
|
@ -183,9 +184,10 @@ describe('Wallet', function() {
|
||||||
function addUtxoToOutput(utxo){
|
function addUtxoToOutput(utxo){
|
||||||
var key = utxo.hash + ":" + utxo.outputIndex
|
var key = utxo.hash + ":" + utxo.outputIndex
|
||||||
wallet.outputs[key] = {
|
wallet.outputs[key] = {
|
||||||
receive: key,
|
from: key,
|
||||||
address: utxo.address,
|
address: utxo.address,
|
||||||
value: utxo.value
|
value: utxo.value,
|
||||||
|
pending: utxo.pending
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,12 +264,35 @@ describe('Wallet', function() {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("processPendingTx", function(){
|
describe("processPendingTx", function(){
|
||||||
it("sets the pending flag on output", function(){
|
it("incoming: sets the pending flag on output", function(){
|
||||||
wallet.addresses = [addresses[0]]
|
wallet.addresses = [addresses[0]]
|
||||||
wallet.processPendingTx(tx)
|
wallet.processPendingTx(tx)
|
||||||
|
|
||||||
verifyOutputAdded(0, true)
|
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 on output instead of deleting it", function(){
|
||||||
|
var txIn = spendTx.ins[0]
|
||||||
|
var txInId = new Buffer(txIn.hash)
|
||||||
|
Array.prototype.reverse.call(txInId)
|
||||||
|
txInId = txInId.toString('hex')
|
||||||
|
|
||||||
|
var key = txInId + ':' + txIn.index
|
||||||
|
assert(!wallet.outputs[key].pending)
|
||||||
|
|
||||||
|
wallet.processPendingTx(spendTx)
|
||||||
|
assert(wallet.outputs[key].pending)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('processConfirmedTx', function(){
|
describe('processConfirmedTx', function(){
|
||||||
|
@ -350,7 +375,7 @@ describe('Wallet', function() {
|
||||||
var txOut = tx.outs[index]
|
var txOut = tx.outs[index]
|
||||||
var key = tx.getId() + ":" + index
|
var key = tx.getId() + ":" + index
|
||||||
var output = wallet.outputs[key]
|
var output = wallet.outputs[key]
|
||||||
assert.equal(output.receive, key)
|
assert.equal(output.from, key)
|
||||||
assert.equal(output.value, txOut.value)
|
assert.equal(output.value, txOut.value)
|
||||||
assert.equal(output.pending, pending)
|
assert.equal(output.pending, pending)
|
||||||
|
|
||||||
|
@ -395,25 +420,12 @@ describe('Wallet', function() {
|
||||||
wallet.setUnspentOutputs(utxo)
|
wallet.setUnspentOutputs(utxo)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('choosing utxo', function(){
|
describe('transaction fee', function(){
|
||||||
it('calculates fees', 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('allows fee to be specified', function(){
|
it('allows fee to be specified', function(){
|
||||||
var fee = 30000
|
var fee = 30000
|
||||||
var tx = wallet.createTx(to, value, fee)
|
var tx = wallet.createTx(to, value, fee)
|
||||||
|
|
||||||
assert.equal(tx.ins.length, 2)
|
assert.equal(getFee(wallet, tx), fee)
|
||||||
|
|
||||||
assert.deepEqual(tx.ins[0].hash, fakeTxHash(3))
|
|
||||||
assert.equal(tx.ins[0].index, 0)
|
|
||||||
assert.deepEqual(tx.ins[1].hash, fakeTxHash(2))
|
|
||||||
assert.equal(tx.ins[1].index, 1)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('allows fee to be set to zero', function(){
|
it('allows fee to be set to zero', function(){
|
||||||
|
@ -421,6 +433,41 @@ describe('Wallet', function() {
|
||||||
var fee = 0
|
var fee = 0
|
||||||
var tx = wallet.createTx(to, value, fee)
|
var tx = wallet.createTx(to, value, fee)
|
||||||
|
|
||||||
|
assert.equal(getFee(wallet, tx), fee)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not overestimate fees when network has dustSoftThreshold', function(){
|
||||||
|
var wallet = new Wallet(seed, networks.litecoin)
|
||||||
|
var address = wallet.generateAddress()
|
||||||
|
wallet.setUnspentOutputs([{
|
||||||
|
hash: fakeTxId(0),
|
||||||
|
outputIndex: 0,
|
||||||
|
address: address,
|
||||||
|
value: 500000
|
||||||
|
}])
|
||||||
|
|
||||||
|
value = 200000
|
||||||
|
var tx = wallet.createTx(address, value)
|
||||||
|
|
||||||
|
assert.equal(getFee(wallet, tx), 100000)
|
||||||
|
})
|
||||||
|
|
||||||
|
function getFee(wallet, tx) {
|
||||||
|
var inputValue = tx.ins.reduce(function(memo, input){
|
||||||
|
var id = Array.prototype.reverse.call(input.hash).toString('hex')
|
||||||
|
return memo + wallet.outputs[id + ':' + input.index].value
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
return tx.outs.reduce(function(memo, output){
|
||||||
|
return memo - 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.equal(tx.ins.length, 1)
|
||||||
assert.deepEqual(tx.ins[0].hash, fakeTxHash(3))
|
assert.deepEqual(tx.ins[0].hash, fakeTxHash(3))
|
||||||
assert.equal(tx.ins[0].index, 0)
|
assert.equal(tx.ins[0].index, 0)
|
||||||
|
@ -446,7 +493,7 @@ describe('Wallet', function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe(networks.testnet, function(){
|
describe('works for testnet', function(){
|
||||||
it('should create transaction', function(){
|
it('should create transaction', function(){
|
||||||
var wallet = new Wallet(seed, networks.testnet)
|
var wallet = new Wallet(seed, networks.testnet)
|
||||||
var address = wallet.generateAddress()
|
var address = wallet.generateAddress()
|
||||||
|
|
Loading…
Add table
Reference in a new issue