Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 41 additions & 5 deletions src/networks.js
Original file line number Diff line number Diff line change
@@ -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: {
Expand All @@ -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',
Expand All @@ -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')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #216. If that gets merged, please change dustsofthreshold to dustSoftThreshold

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with a rebase, which also cleaned up the commit history

},
litecoin: {
magicPrefix: '\x19Litecoin Signed Message:\n',
Expand All @@ -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',
Expand All @@ -39,6 +51,30 @@ module.exports = {
},
pubKeyHash: 0x6f,
scriptHash: 0xc4,
wif: 0xef
wif: 0xef,
dustThreshold: 546,
feePerKb: 10000,
estimateFee: estimateFee('bitcoin')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be testnet? Not that it matters.

}
}

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
11 changes: 3 additions & 8 deletions src/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ function Wallet(seed, network) {
this.addresses = []
this.changeAddresses = []

// Dust value
this.dustThreshold = 5430

// Transaction output data
this.outputs = {}

Expand Down Expand Up @@ -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
Expand All @@ -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)
}

Expand Down Expand Up @@ -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() {
Expand Down
93 changes: 93 additions & 0 deletions test/network.js
Original file line number Diff line number Diff line change
@@ -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)
})
})
})
})
20 changes: 10 additions & 10 deletions test/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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(){
Expand Down Expand Up @@ -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\)/)
})
})

Expand All @@ -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/)
})
})
})
Expand Down