Calculate the minimum tx fee when creating transactions.
This commit is contained in:
parent
378af40329
commit
48a685e85e
3 changed files with 265 additions and 125 deletions
27
cmdmgr.go
27
cmdmgr.go
|
@ -566,15 +566,9 @@ func SendFrom(frontend chan []byte, icmd btcjson.Cmd) {
|
||||||
cmd.ToAddress: cmd.Amount,
|
cmd.ToAddress: cmd.Amount,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get fee to add to tx.
|
|
||||||
// TODO(jrick): this needs to be fee per kB.
|
|
||||||
TxFee.Lock()
|
|
||||||
fee := TxFee.i
|
|
||||||
TxFee.Unlock()
|
|
||||||
|
|
||||||
// Create transaction, replying with an error if the creation
|
// Create transaction, replying with an error if the creation
|
||||||
// was not successful.
|
// was not successful.
|
||||||
createdTx, err := a.txToPairs(pairs, fee, cmd.MinConf)
|
createdTx, err := a.txToPairs(pairs, cmd.MinConf)
|
||||||
switch {
|
switch {
|
||||||
case err == ErrNonPositiveAmount:
|
case err == ErrNonPositiveAmount:
|
||||||
e := &btcjson.Error{
|
e := &btcjson.Error{
|
||||||
|
@ -664,15 +658,9 @@ func SendMany(frontend chan []byte, icmd btcjson.Cmd) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get fee to add to tx.
|
|
||||||
// TODO(jrick): this needs to be fee per kB.
|
|
||||||
TxFee.Lock()
|
|
||||||
fee := TxFee.i
|
|
||||||
TxFee.Unlock()
|
|
||||||
|
|
||||||
// Create transaction, replying with an error if the creation
|
// Create transaction, replying with an error if the creation
|
||||||
// was not successful.
|
// was not successful.
|
||||||
createdTx, err := a.txToPairs(cmd.Amounts, fee, cmd.MinConf)
|
createdTx, err := a.txToPairs(cmd.Amounts, cmd.MinConf)
|
||||||
switch {
|
switch {
|
||||||
case err == ErrNonPositiveAmount:
|
case err == ErrNonPositiveAmount:
|
||||||
e := &btcjson.Error{
|
e := &btcjson.Error{
|
||||||
|
@ -840,7 +828,7 @@ func handleSendRawTxReply(frontend chan []byte, icmd btcjson.Cmd,
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTxFee sets the global transaction fee added to transactions.
|
// SetTxFee sets the transaction fee per kilobyte added to transactions.
|
||||||
func SetTxFee(frontend chan []byte, icmd btcjson.Cmd) {
|
func SetTxFee(frontend chan []byte, icmd btcjson.Cmd) {
|
||||||
// Type assert icmd to access parameters.
|
// Type assert icmd to access parameters.
|
||||||
cmd, ok := icmd.(*btcjson.SetTxFeeCmd)
|
cmd, ok := icmd.(*btcjson.SetTxFeeCmd)
|
||||||
|
@ -860,12 +848,9 @@ func SetTxFee(frontend chan []byte, icmd btcjson.Cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set global tx fee.
|
// Set global tx fee.
|
||||||
//
|
TxFeeIncrement.Lock()
|
||||||
// TODO(jrick): this must be a fee per kB.
|
TxFeeIncrement.i = cmd.Amount
|
||||||
// TODO(jrick): need to notify all frontends of new tx fee.
|
TxFeeIncrement.Unlock()
|
||||||
TxFee.Lock()
|
|
||||||
TxFee.i = cmd.Amount
|
|
||||||
TxFee.Unlock()
|
|
||||||
|
|
||||||
// A boolean true result is returned upon success.
|
// A boolean true result is returned upon success.
|
||||||
ReplySuccess(frontend, cmd.Id(), true)
|
ReplySuccess(frontend, cmd.Id(), true)
|
||||||
|
|
213
createtx.go
213
createtx.go
|
@ -46,14 +46,18 @@ var ErrNonPositiveAmount = errors.New("amount is not positive")
|
||||||
// negative.
|
// negative.
|
||||||
var ErrNegativeFee = errors.New("fee is negative")
|
var ErrNegativeFee = errors.New("fee is negative")
|
||||||
|
|
||||||
// TxFee represents the global transaction fee added to newly-created
|
// minTxFee is the default minimum transation fee (0.0001 BTC,
|
||||||
// transactions and sent as a reward to the block miner. i is measured
|
// measured in satoshis) added to transactions requiring a fee.
|
||||||
// in satoshis.
|
const minTxFee = 10000
|
||||||
var TxFee = struct {
|
|
||||||
|
// TxFeeIncrement represents the global transaction fee per KB of Tx
|
||||||
|
// added to newly-created transactions and sent as a reward to the block
|
||||||
|
// miner. i is measured in satoshis.
|
||||||
|
var TxFeeIncrement = struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
i int64
|
i int64
|
||||||
}{
|
}{
|
||||||
i: 10000, // This is a fee of 0.0001 BTC.
|
i: minTxFee,
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreatedTx is a type holding information regarding a newly-created
|
// CreatedTx is a type holding information regarding a newly-created
|
||||||
|
@ -64,7 +68,7 @@ type CreatedTx struct {
|
||||||
time time.Time
|
time time.Time
|
||||||
inputs []*tx.Utxo
|
inputs []*tx.Utxo
|
||||||
outputs []tx.Pair
|
outputs []tx.Pair
|
||||||
btcout int64
|
btcspent int64
|
||||||
fee int64
|
fee int64
|
||||||
changeAddr string
|
changeAddr string
|
||||||
changeUtxo *tx.Utxo
|
changeUtxo *tx.Utxo
|
||||||
|
@ -152,11 +156,11 @@ func selectInputs(s tx.UtxoStore, amt uint64, minconf int) (inputs []*tx.Utxo, b
|
||||||
// address, changeUtxo will point to a unconfirmed (height = -1, zeroed
|
// address, changeUtxo will point to a unconfirmed (height = -1, zeroed
|
||||||
// block hash) Utxo. ErrInsufficientFunds is returned if there are not
|
// block hash) Utxo. ErrInsufficientFunds is returned if there are not
|
||||||
// enough eligible unspent outputs to create the transaction.
|
// enough eligible unspent outputs to create the transaction.
|
||||||
func (w *Account) txToPairs(pairs map[string]int64, fee int64, minconf int) (*CreatedTx, error) {
|
func (a *Account) txToPairs(pairs map[string]int64, minconf int) (*CreatedTx, error) {
|
||||||
// Recorded unspent transactions should not be modified until this
|
// Recorded unspent transactions should not be modified until this
|
||||||
// finishes.
|
// finishes.
|
||||||
w.UtxoStore.RLock()
|
a.UtxoStore.RLock()
|
||||||
defer w.UtxoStore.RUnlock()
|
defer a.UtxoStore.RUnlock()
|
||||||
|
|
||||||
// Create a new transaction which will include all input scripts.
|
// Create a new transaction which will include all input scripts.
|
||||||
msgtx := btcwire.NewMsgTx()
|
msgtx := btcwire.NewMsgTx()
|
||||||
|
@ -171,17 +175,6 @@ func (w *Account) txToPairs(pairs map[string]int64, fee int64, minconf int) (*Cr
|
||||||
amt += v
|
amt += v
|
||||||
}
|
}
|
||||||
|
|
||||||
if fee < 0 {
|
|
||||||
return nil, ErrNegativeFee
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select unspent outputs to be used in transaction.
|
|
||||||
inputs, btcout, err := selectInputs(w.UtxoStore.s, uint64(amt+fee),
|
|
||||||
minconf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// outputs is a tx.Pair slice representing each output that is created
|
// outputs is a tx.Pair slice representing each output that is created
|
||||||
// by the transaction.
|
// by the transaction.
|
||||||
outputs := make([]tx.Pair, 0, len(pairs)+1)
|
outputs := make([]tx.Pair, 0, len(pairs)+1)
|
||||||
|
@ -209,31 +202,61 @@ func (w *Account) txToPairs(pairs map[string]int64, fee int64, minconf int) (*Cr
|
||||||
outputs = append(outputs, out)
|
outputs = append(outputs, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if there are leftover unspent outputs, and return coins back to
|
|
||||||
// a new address we own.
|
|
||||||
var changeUtxo *tx.Utxo
|
|
||||||
var changeAddr string
|
|
||||||
if btcout > uint64(amt+fee) {
|
|
||||||
// Create a new address to spend leftover outputs to.
|
|
||||||
// TODO(jrick): use the next chained address, not the next unused.
|
|
||||||
var err error
|
|
||||||
// Get current block's height and hash.
|
// Get current block's height and hash.
|
||||||
bs, err := GetCurBlock()
|
bs, err := GetCurBlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
changeAddr, err = w.NextChainedAddress(&bs)
|
// Make a copy of msgtx before any inputs are added. This will be
|
||||||
|
// used as a starting point when trying a fee and starting over with
|
||||||
|
// a higher fee if not enough was originally chosen.
|
||||||
|
txNoInputs := msgtx.Copy()
|
||||||
|
|
||||||
|
// These are nil/zeroed until a change address is needed, and reused
|
||||||
|
// again in case a change utxo has already been chosen.
|
||||||
|
var changeAddrHash []byte
|
||||||
|
var changeAddr string
|
||||||
|
|
||||||
|
var btcspent int64
|
||||||
|
var selectedInputs []*tx.Utxo
|
||||||
|
var finalChangeUtxo *tx.Utxo
|
||||||
|
|
||||||
|
// Get the number of satoshis to increment fee by when searching for
|
||||||
|
// the minimum tx fee needed.
|
||||||
|
var fee int64 = 0
|
||||||
|
for {
|
||||||
|
msgtx = txNoInputs.Copy()
|
||||||
|
|
||||||
|
// Select unspent outputs to be used in transaction based on the amount
|
||||||
|
// neededing to sent, and the current fee estimation.
|
||||||
|
inputs, btcin, err := selectInputs(a.UtxoStore.s, uint64(amt+fee),
|
||||||
|
minconf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there are leftover unspent outputs, and return coins back to
|
||||||
|
// a new address we own.
|
||||||
|
var changeUtxo *tx.Utxo
|
||||||
|
change := btcin - uint64(amt+fee)
|
||||||
|
if change > 0 {
|
||||||
|
// Create a new address to spend leftover outputs to.
|
||||||
|
|
||||||
|
// Get a new change address if one has not already been found.
|
||||||
|
if changeAddrHash == nil {
|
||||||
|
changeAddr, err = a.NextChainedAddress(&bs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get next address: %s", err)
|
return nil, fmt.Errorf("failed to get next address: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spend change
|
changeAddrHash, _, err = btcutil.DecodeAddress(changeAddr)
|
||||||
change := btcout - uint64(amt+fee)
|
|
||||||
changeAddrHash, _, err := btcutil.DecodeAddress(changeAddr)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot decode new address: %s", err)
|
return nil, fmt.Errorf("cannot decode new address: %s", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spend change.
|
||||||
pkScript, err := btcscript.PayToPubKeyHashScript(changeAddrHash)
|
pkScript, err := btcscript.PayToPubKeyHashScript(changeAddrHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot create txout script: %s", err)
|
return nil, fmt.Errorf("cannot create txout script: %s", err)
|
||||||
|
@ -252,13 +275,6 @@ func (w *Account) txToPairs(pairs map[string]int64, fee int64, minconf int) (*Cr
|
||||||
Subscript: pkScript,
|
Subscript: pkScript,
|
||||||
}
|
}
|
||||||
copy(changeUtxo.AddrHash[:], changeAddrHash)
|
copy(changeUtxo.AddrHash[:], changeAddrHash)
|
||||||
|
|
||||||
// Add change to outputs.
|
|
||||||
out := tx.Pair{
|
|
||||||
Amount: int64(change),
|
|
||||||
PubkeyHash: changeAddrHash,
|
|
||||||
}
|
|
||||||
outputs = append(outputs, out)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Selected unspent outputs become new transaction's inputs.
|
// Selected unspent outputs become new transaction's inputs.
|
||||||
|
@ -266,29 +282,61 @@ func (w *Account) txToPairs(pairs map[string]int64, fee int64, minconf int) (*Cr
|
||||||
msgtx.AddTxIn(btcwire.NewTxIn((*btcwire.OutPoint)(&ip.Out), nil))
|
msgtx.AddTxIn(btcwire.NewTxIn((*btcwire.OutPoint)(&ip.Out), nil))
|
||||||
}
|
}
|
||||||
for i, ip := range inputs {
|
for i, ip := range inputs {
|
||||||
addrstr, err := btcutil.EncodeAddress(ip.AddrHash[:], w.Wallet.Net())
|
addrstr, err := btcutil.EncodeAddress(ip.AddrHash[:],
|
||||||
|
a.Wallet.Net())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
privkey, err := w.AddressKey(addrstr)
|
privkey, err := a.AddressKey(addrstr)
|
||||||
if err == wallet.ErrWalletLocked {
|
if err == wallet.ErrWalletLocked {
|
||||||
return nil, wallet.ErrWalletLocked
|
return nil, wallet.ErrWalletLocked
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, fmt.Errorf("cannot get address key: %v", err)
|
return nil, fmt.Errorf("cannot get address key: %v", err)
|
||||||
}
|
}
|
||||||
ai, err := w.AddressInfo(addrstr)
|
ai, err := a.AddressInfo(addrstr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot get address info: %v", err)
|
return nil, fmt.Errorf("cannot get address info: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sigscript, err := btcscript.SignatureScript(msgtx, i,
|
sigscript, err := btcscript.SignatureScript(msgtx, i,
|
||||||
ip.Subscript, btcscript.SigHashAll, privkey, ai.Compressed)
|
ip.Subscript, btcscript.SigHashAll, privkey,
|
||||||
|
ai.Compressed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot create sigscript: %s", err)
|
return nil, fmt.Errorf("cannot create sigscript: %s", err)
|
||||||
}
|
}
|
||||||
msgtx.TxIn[i].SignatureScript = sigscript
|
msgtx.TxIn[i].SignatureScript = sigscript
|
||||||
}
|
}
|
||||||
|
|
||||||
|
noFeeAllowed := allowFree(bs.Height, inputs, msgtx.SerializeSize())
|
||||||
|
if minFee := minimumFee(msgtx, noFeeAllowed); fee < minFee {
|
||||||
|
fee = minFee
|
||||||
|
} else {
|
||||||
|
// Fill Tx hash of change outpoint with transaction hash.
|
||||||
|
if changeUtxo != nil {
|
||||||
|
txHash, err := msgtx.TxSha()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot create transaction hash: %s", err)
|
||||||
|
}
|
||||||
|
copy(changeUtxo.Out.Hash[:], txHash[:])
|
||||||
|
|
||||||
|
// Add change to outputs.
|
||||||
|
out := tx.Pair{
|
||||||
|
Amount: int64(change),
|
||||||
|
PubkeyHash: changeAddrHash,
|
||||||
|
}
|
||||||
|
outputs = append(outputs, out)
|
||||||
|
|
||||||
|
finalChangeUtxo = changeUtxo
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedInputs = inputs
|
||||||
|
|
||||||
|
btcspent = int64(btcin)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Validate msgtx before returning the raw transaction.
|
// Validate msgtx before returning the raw transaction.
|
||||||
flags := btcscript.ScriptCanonicalSignatures
|
flags := btcscript.ScriptCanonicalSignatures
|
||||||
bip16 := time.Now().After(btcscript.Bip16Activation)
|
bip16 := time.Now().After(btcscript.Bip16Activation)
|
||||||
|
@ -296,8 +344,8 @@ func (w *Account) txToPairs(pairs map[string]int64, fee int64, minconf int) (*Cr
|
||||||
flags |= btcscript.ScriptBip16
|
flags |= btcscript.ScriptBip16
|
||||||
}
|
}
|
||||||
for i, txin := range msgtx.TxIn {
|
for i, txin := range msgtx.TxIn {
|
||||||
engine, err := btcscript.NewScript(txin.SignatureScript, inputs[i].Subscript, i,
|
engine, err := btcscript.NewScript(txin.SignatureScript,
|
||||||
msgtx, flags)
|
selectedInputs[i].Subscript, i, msgtx, flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot create script engine: %s", err)
|
return nil, fmt.Errorf("cannot create script engine: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -306,26 +354,77 @@ func (w *Account) txToPairs(pairs map[string]int64, fee int64, minconf int) (*Cr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill Tx hash of change outpoint with transaction hash.
|
|
||||||
if changeUtxo != nil {
|
|
||||||
txHash, err := msgtx.TxSha()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot create transaction hash: %s", err)
|
|
||||||
}
|
|
||||||
copy(changeUtxo.Out.Hash[:], txHash[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
msgtx.BtcEncode(buf, btcwire.ProtocolVersion)
|
msgtx.BtcEncode(buf, btcwire.ProtocolVersion)
|
||||||
info := &CreatedTx{
|
info := &CreatedTx{
|
||||||
rawTx: buf.Bytes(),
|
rawTx: buf.Bytes(),
|
||||||
time: time.Now(),
|
time: time.Now(),
|
||||||
inputs: inputs,
|
inputs: selectedInputs,
|
||||||
outputs: outputs,
|
outputs: outputs,
|
||||||
btcout: int64(btcout),
|
btcspent: btcspent,
|
||||||
fee: fee,
|
fee: fee,
|
||||||
changeAddr: changeAddr,
|
changeAddr: changeAddr,
|
||||||
changeUtxo: changeUtxo,
|
changeUtxo: finalChangeUtxo,
|
||||||
}
|
}
|
||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// minimumFee calculates the minimum fee required for a transaction.
|
||||||
|
// If allowFree is true, a fee may be zero so long as the entire
|
||||||
|
// transaction has a serialized length less than 1 kilobyte
|
||||||
|
// and none of the outputs contain a value less than 1 bitcent.
|
||||||
|
// Otherwise, the fee will be calculated using TxFeeIncrement,
|
||||||
|
// incrementing the fee for each kilobyte of transaction.
|
||||||
|
func minimumFee(tx *btcwire.MsgTx, allowFree bool) int64 {
|
||||||
|
txLen := tx.SerializeSize()
|
||||||
|
TxFeeIncrement.Lock()
|
||||||
|
incr := TxFeeIncrement.i
|
||||||
|
TxFeeIncrement.Unlock()
|
||||||
|
fee := int64(1+txLen/1000) * incr
|
||||||
|
|
||||||
|
if allowFree && txLen < 1000 {
|
||||||
|
fee = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if fee < incr {
|
||||||
|
for _, txOut := range tx.TxOut {
|
||||||
|
if txOut.Value < btcutil.SatoshiPerBitcent {
|
||||||
|
return incr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fee < 0 || fee > btcutil.MaxSatoshi {
|
||||||
|
fee = btcutil.MaxSatoshi
|
||||||
|
}
|
||||||
|
|
||||||
|
return fee
|
||||||
|
}
|
||||||
|
|
||||||
|
// allowFree calculates the transaction priority and checks that the
|
||||||
|
// priority reaches a certain threshhold. If the threshhold is
|
||||||
|
// reached, a free transaction fee is allowed.
|
||||||
|
func allowFree(curHeight int32, inputs []*tx.Utxo, txSize int) bool {
|
||||||
|
const blocksPerDayEstimate = 144
|
||||||
|
const txSizeEstimate = 250
|
||||||
|
|
||||||
|
var weightedSum int64
|
||||||
|
for _, utxo := range inputs {
|
||||||
|
depth := chainDepth(utxo.Height, curHeight)
|
||||||
|
weightedSum += int64(utxo.Amt) * int64(depth)
|
||||||
|
}
|
||||||
|
priority := float64(weightedSum) / float64(txSize)
|
||||||
|
return priority > float64(btcutil.SatoshiPerBitcoin)*blocksPerDayEstimate/txSizeEstimate
|
||||||
|
}
|
||||||
|
|
||||||
|
// chainDepth returns the chaindepth of a target given the current
|
||||||
|
// blockchain height.
|
||||||
|
func chainDepth(target, current int32) int32 {
|
||||||
|
if target == -1 {
|
||||||
|
// target is not yet in a block.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// target is in a block.
|
||||||
|
return current - target + 1
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,62 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type allowFreeTest struct {
|
||||||
|
name string
|
||||||
|
inputs []*tx.Utxo
|
||||||
|
curHeight int32
|
||||||
|
txSize int
|
||||||
|
free bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var allowFreeTests = []allowFreeTest{
|
||||||
|
allowFreeTest{
|
||||||
|
name: "priority < 57,600,000",
|
||||||
|
inputs: []*tx.Utxo{
|
||||||
|
&tx.Utxo{
|
||||||
|
Amt: uint64(btcutil.SatoshiPerBitcoin),
|
||||||
|
Height: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
curHeight: 142, // 143 confirmations
|
||||||
|
txSize: 250,
|
||||||
|
free: false,
|
||||||
|
},
|
||||||
|
allowFreeTest{
|
||||||
|
name: "priority == 57,600,000",
|
||||||
|
inputs: []*tx.Utxo{
|
||||||
|
&tx.Utxo{
|
||||||
|
Amt: uint64(btcutil.SatoshiPerBitcoin),
|
||||||
|
Height: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
curHeight: 143, // 144 confirmations
|
||||||
|
txSize: 250,
|
||||||
|
free: false,
|
||||||
|
},
|
||||||
|
allowFreeTest{
|
||||||
|
name: "priority > 57,600,000",
|
||||||
|
inputs: []*tx.Utxo{
|
||||||
|
&tx.Utxo{
|
||||||
|
Amt: uint64(btcutil.SatoshiPerBitcoin),
|
||||||
|
Height: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
curHeight: 144, // 145 confirmations
|
||||||
|
txSize: 250,
|
||||||
|
free: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllowFree(t *testing.T) {
|
||||||
|
for _, test := range allowFreeTests {
|
||||||
|
calcFree := allowFree(test.curHeight, test.inputs, test.txSize)
|
||||||
|
if calcFree != test.free {
|
||||||
|
t.Errorf("Allow free test '%v' failed.", test.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFakeTxs(t *testing.T) {
|
func TestFakeTxs(t *testing.T) {
|
||||||
// First we need a wallet.
|
// First we need a wallet.
|
||||||
w, err := wallet.NewWallet("banana wallet", "", []byte("banana"),
|
w, err := wallet.NewWallet("banana wallet", "", []byte("banana"),
|
||||||
|
@ -17,7 +73,7 @@ func TestFakeTxs(t *testing.T) {
|
||||||
t.Errorf("Can not create encrypted wallet: %s", err)
|
t.Errorf("Can not create encrypted wallet: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
btcw := &Account{
|
a := &Account{
|
||||||
Wallet: w,
|
Wallet: w,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,9 +106,9 @@ func TestFakeTxs(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
utxo.Subscript = tx.PkScript(ss)
|
utxo.Subscript = tx.PkScript(ss)
|
||||||
utxo.Amt = 10000
|
utxo.Amt = 1000000
|
||||||
utxo.Height = 12345
|
utxo.Height = 12345
|
||||||
btcw.UtxoStore.s = append(btcw.UtxoStore.s, utxo)
|
a.UtxoStore.s = append(a.UtxoStore.s, utxo)
|
||||||
|
|
||||||
// Fake our current block height so btcd doesn't need to be queried.
|
// Fake our current block height so btcd doesn't need to be queried.
|
||||||
curBlock.BlockStamp.Height = 12346
|
curBlock.BlockStamp.Height = 12346
|
||||||
|
@ -61,7 +117,7 @@ func TestFakeTxs(t *testing.T) {
|
||||||
pairs := map[string]int64{
|
pairs := map[string]int64{
|
||||||
"17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH": 5000,
|
"17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH": 5000,
|
||||||
}
|
}
|
||||||
_, err = btcw.txToPairs(pairs, 100, 0)
|
_, err = a.txToPairs(pairs, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Tx creation failed: %s", err)
|
t.Errorf("Tx creation failed: %s", err)
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in a new issue