Merge pull request #215 from weilu/feendust

Fee and dust
This commit is contained in:
Daniel Cousens 2014-06-18 01:37:54 +10:00
commit 9e2e0bd01a
4 changed files with 147 additions and 23 deletions

View file

@ -1,6 +1,7 @@
// https://en.bitcoin.it/wiki/List_of_address_prefixes
// Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731
module.exports = {
var networks = {
bitcoin: {
magicPrefix: '\x18Bitcoin Signed Message:\n',
bip32: {
@ -9,7 +10,10 @@ module.exports = {
},
pubKeyHash: 0x00,
scriptHash: 0x05,
wif: 0x80
wif: 0x80,
dustThreshold: 546, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162
feePerKb: 10000, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/main.cpp#L53
estimateFee: estimateFee('bitcoin')
},
dogecoin: {
magicPrefix: '\x19Dogecoin Signed Message:\n',
@ -19,7 +23,11 @@ module.exports = {
},
pubKeyHash: 0x1e,
scriptHash: 0x16,
wif: 0x9e
wif: 0x9e,
dustThreshold: 0, // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/core.h#L155-L160
dustSoftThreshold: 100000000, // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/main.h#L62
feePerKb: 100000000, // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/main.cpp#L58
estimateFee: estimateFee('dogecoin')
},
litecoin: {
magicPrefix: '\x19Litecoin Signed Message:\n',
@ -29,7 +37,11 @@ module.exports = {
},
pubKeyHash: 0x30,
scriptHash: 0x05,
wif: 0xb0
wif: 0xb0,
dustThreshold: 0, // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L360-L365
dustSoftThreshold: 100000, // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.h#L53
feePerKb: 100000, // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L56
estimateFee: estimateFee('litecoin')
},
testnet: {
magicPrefix: '\x18Bitcoin Signed Message:\n',
@ -39,6 +51,30 @@ module.exports = {
},
pubKeyHash: 0x6f,
scriptHash: 0xc4,
wif: 0xef
wif: 0xef,
dustThreshold: 546,
feePerKb: 10000,
estimateFee: estimateFee('bitcoin')
}
}
function estimateFee(type) {
return function(tx) {
var network = networks[type]
var baseFee = network.feePerKb
var byteSize = tx.toBuffer().length
var fee = baseFee * Math.ceil(byteSize / 1000)
if(network.dustSoftThreshold == undefined) return fee
tx.outs.forEach(function(e){
if(e.value < network.dustSoftThreshold) {
fee += baseFee
}
})
return fee
}
}
module.exports = networks

View file

@ -20,9 +20,6 @@ function Wallet(seed, network) {
this.addresses = []
this.changeAddresses = []
// Dust value
this.dustThreshold = 5430
// Transaction output data
this.outputs = {}
@ -182,7 +179,7 @@ function Wallet(seed, network) {
}
this.createTx = function(to, value, fixedFee, changeAddress) {
assert(value > this.dustThreshold, value + ' must be above dust threshold (' + this.dustThreshold + ' Satoshis)')
assert(value > network.dustThreshold, value + ' must be above dust threshold (' + network.dustThreshold + ' Satoshis)')
var utxos = getCandidateOutputs(value)
var accum = 0
@ -206,7 +203,7 @@ function Wallet(seed, network) {
if (accum >= subTotal) {
var change = accum - subTotal
if (change > this.dustThreshold) {
if (change > network.dustThreshold) {
tx.addOutput(changeAddress || getChangeAddress(), change)
}
@ -235,13 +232,11 @@ function Wallet(seed, network) {
return sortByValueDesc
}
var feePerKb = 20000
function estimateFeePadChangeOutput(tx) {
var tmpTx = tx.clone()
tmpTx.addOutput(getChangeAddress(), 0)
var byteSize = tmpTx.toBuffer().length
return feePerKb * Math.ceil(byteSize / 1000)
return network.estimateFee(tmpTx)
}
function getChangeAddress() {

93
test/network.js Normal file
View file

@ -0,0 +1,93 @@
var assert = require('assert')
var networks = require('../src/networks')
var sinon = require('sinon')
var Transaction = require('../src/transaction')
describe('networks', function() {
var txToBuffer
before(function(){
txToBuffer = sinon.stub(Transaction.prototype, "toBuffer")
})
after(function(){
Transaction.prototype.toBuffer.restore()
})
describe('bitcoin', function() {
describe('estimateFee', function() {
var estimateFee = networks.bitcoin.estimateFee
it('works at boundry', function() {
txToBuffer.returns(new Buffer(1000))
var tx = new Transaction()
assert.equal(estimateFee(tx), 10000)
})
it('rounds up to the closest kb for estimation', function() {
txToBuffer.returns(new Buffer(2800))
var tx = new Transaction()
assert.equal(estimateFee(tx), 30000)
})
})
})
describe('dogecoin', function() {
describe('estimateFee', function() {
var estimateFee = networks.dogecoin.estimateFee
it('regular fee per kb applies when every output has value no less than DUST_SOFT_LIMIT', function() {
txToBuffer.returns(new Buffer(1000))
var tx = new Transaction()
tx.outs[0] = { value: 100000000 }
assert.equal(estimateFee(tx), 100000000)
})
it('applies additional fee on every output with value below DUST_SOFT_LIMIT', function() {
txToBuffer.returns(new Buffer(1000))
var tx = new Transaction()
tx.outs[0] = { value: 99999999 }
tx.outs[1] = { value: 99999999 }
assert.equal(estimateFee(tx), 3 * 100000000)
})
it('rounds up to the closest kb for estimation', function() {
txToBuffer.returns(new Buffer(2800))
var tx = new Transaction()
assert.equal(estimateFee(tx), 300000000)
})
})
})
describe('litecoin', function() {
describe('estimateFee', function() {
var estimateFee = networks.litecoin.estimateFee
it('regular fee per kb applies when every output has value no less than DUST_SOFT_LIMIT', function() {
txToBuffer.returns(new Buffer(1000))
var tx = new Transaction()
tx.outs[0] = { value: 100000 }
assert.equal(estimateFee(tx), 100000)
})
it('applies additional fee on every output with value below DUST_SOFT_LIMIT', function() {
txToBuffer.returns(new Buffer(1000))
var tx = new Transaction()
tx.outs[0] = { value: 99999 }
tx.outs[1] = { value: 99999 }
assert.equal(estimateFee(tx), 3 * 100000)
})
it('rounds up to the closest kb for estimation', function() {
txToBuffer.returns(new Buffer(2800))
var tx = new Transaction()
assert.equal(estimateFee(tx), 300000)
})
})
})
})

View file

@ -389,7 +389,7 @@ describe('Wallet', function() {
"hash": fakeTxId(3),
"outputIndex": 0,
"address" : address2,
"value": 520000 // enough for value and fee
"value": 510000 // enough for value and fee
}
]
wallet.setUnspentOutputs(utxo)
@ -417,7 +417,7 @@ describe('Wallet', function() {
})
it('allows fee to be set to zero', function(){
value = 520000
value = 510000
var fee = 0
var tx = wallet.createTx(to, value, fee)
@ -459,7 +459,7 @@ describe('Wallet', function() {
}])
var to = 'mt7MyTVVEWnbwpF5hBn6fgnJcv95Syk2ue'
var toValue = value - 20000
var toValue = value - 10000
var tx = wallet.createTx(to, toValue)
assert.equal(tx.outs.length, 1)
@ -516,7 +516,7 @@ describe('Wallet', function() {
describe('change', function(){
it('uses the last change address if there is any', function(){
var fee = 5000
var fee = 0
wallet.generateChangeAddress()
wallet.generateChangeAddress()
var tx = wallet.createTx(to, value, fee)
@ -526,11 +526,11 @@ describe('Wallet', function() {
var outAddress = Address.fromOutputScript(out.script)
assert.equal(outAddress.toString(), wallet.changeAddresses[1])
assert.equal(out.value, 15000)
assert.equal(out.value, 10000)
})
it('generates a change address if there is not any', function(){
var fee = 5000
var fee = 0
assert.equal(wallet.changeAddresses.length, 0)
var tx = wallet.createTx(to, value, fee)
@ -540,7 +540,7 @@ describe('Wallet', function() {
var outAddress = Address.fromOutputScript(out.script)
assert.equal(outAddress.toString(), wallet.changeAddresses[0])
assert.equal(out.value, 15000)
assert.equal(out.value, 10000)
})
it('skips change if it is not above dust threshold', function(){
@ -569,11 +569,11 @@ describe('Wallet', function() {
describe('when value is below dust threshold', function(){
it('throws an error', function(){
var value = 5430
var value = 546
assert.throws(function() {
wallet.createTx(to, value)
}, /5430 must be above dust threshold \(5430 Satoshis\)/)
}, /546 must be above dust threshold \(546 Satoshis\)/)
})
})
@ -583,7 +583,7 @@ describe('Wallet', function() {
assert.throws(function() {
wallet.createTx(to, value)
}, /Not enough funds \(incl. fee\): 1420000 < 1420001/)
}, /Not enough funds \(incl. fee\): 1410000 < 1410001/)
})
})
})