Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Number Precision changes to the multiwallet #82

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
33 changes: 19 additions & 14 deletions bitcoin/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"encoding/hex"
"errors"
"fmt"
"math/big"
"strconv"
"time"

"github.com/btcsuite/btcd/chaincfg"
Expand Down Expand Up @@ -80,7 +82,8 @@ func (w *BitcoinWallet) buildTx(amount int64, addr btc.Address, feeLevel wi.FeeL
}

// Get the fee per kilobyte
feePerKB := int64(w.GetFeePerByte(feeLevel)) * 1000
f := w.GetFeePerByte(feeLevel)
feePerKB := f.Int64() * 1000

// outputs
out := wire.NewTxOut(amount, script)
Expand Down Expand Up @@ -149,7 +152,8 @@ func (w *BitcoinWallet) buildSpendAllTx(addr btc.Address, feeLevel wi.FeeLevel)
}

// Get the fee
feePerByte := int64(w.GetFeePerByte(feeLevel))
fee0 := w.GetFeePerByte(feeLevel)
feePerByte := fee0.Int64()
estimatedSize := EstimateSerializeSize(1, []*wire.TxOut{wire.NewTxOut(0, script)}, false, P2PKH)
fee := int64(estimatedSize) * feePerByte

Expand Down Expand Up @@ -278,11 +282,13 @@ func (w *BitcoinWallet) bumpFee(txid chainhash.Hash) (*chainhash.Hash, error) {
if err != nil {
return nil, err
}
n := new(big.Int)
n, _ = n.SetString(u.Value, 10)
in := wi.TransactionInput{
LinkedAddress: addr,
OutpointIndex: u.Op.Index,
OutpointHash: h,
Value: int64(u.Value),
Value: *n,
}
transactionID, err := w.sweepAddress([]wi.TransactionInput{in}, nil, key, nil, wi.FEE_BUMP)
if err != nil {
Expand Down Expand Up @@ -310,7 +316,7 @@ func (w *BitcoinWallet) sweepAddress(ins []wi.TransactionInput, address *btc.Add
var inputs []*wire.TxIn
additionalPrevScripts := make(map[wire.OutPoint][]byte)
for _, in := range ins {
val += in.Value
val += in.Value.Int64()
ch, err := chainhash.NewHashFromStr(hex.EncodeToString(in.OutpointHash))
if err != nil {
return nil, err
Expand All @@ -337,7 +343,8 @@ func (w *BitcoinWallet) sweepAddress(ins []wi.TransactionInput, address *btc.Add
estimatedSize := EstimateSerializeSize(len(ins), []*wire.TxOut{out}, false, txType)

// Calculate the fee
feePerByte := int(w.GetFeePerByte(feeLevel))
f := w.GetFeePerByte(feeLevel)
feePerByte := int(f.Int64())
fee := estimatedSize * feePerByte

outVal := val - int64(fee)
Expand All @@ -359,13 +366,10 @@ func (w *BitcoinWallet) sweepAddress(ins []wi.TransactionInput, address *btc.Add
// Sign tx
privKey, err := key.ECPrivKey()
if err != nil {
return nil, fmt.Errorf("retrieving private key: %s", err.Error())
return nil, err
}
pk := privKey.PubKey().SerializeCompressed()
addressPub, err := btc.NewAddressPubKey(pk, w.params)
if err != nil {
return nil, fmt.Errorf("generating address pub key: %s", err.Error())
}

getKey := txscript.KeyClosure(func(addr btc.Address) (*btcec.PrivateKey, bool, error) {
if addressPub.EncodeAddress() == addr.EncodeAddress() {
Expand Down Expand Up @@ -413,7 +417,7 @@ func (w *BitcoinWallet) sweepAddress(ins []wi.TransactionInput, address *btc.Add
}
txIn.SignatureScript = script
} else {
sig, err := txscript.RawTxInWitnessSignature(tx, hashes, i, ins[i].Value, *redeemScript, txscript.SigHashAll, privKey)
sig, err := txscript.RawTxInWitnessSignature(tx, hashes, i, ins[i].Value.Int64(), *redeemScript, txscript.SigHashAll, privKey)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -453,7 +457,7 @@ func (w *BitcoinWallet) createMultisigSignature(ins []wi.TransactionInput, outs
if err != nil {
return sigs, err
}
output := wire.NewTxOut(out.Value, scriptPubKey)
output := wire.NewTxOut(out.Value.Int64(), scriptPubKey)
tx.TxOut = append(tx.TxOut, output)
}

Expand Down Expand Up @@ -482,7 +486,7 @@ func (w *BitcoinWallet) createMultisigSignature(ins []wi.TransactionInput, outs

hashes := txscript.NewTxSigHashes(tx)
for i := range tx.TxIn {
sig, err := txscript.RawTxInWitnessSignature(tx, hashes, i, ins[i].Value, redeemScript, txscript.SigHashAll, signingKey)
sig, err := txscript.RawTxInWitnessSignature(tx, hashes, i, ins[i].Value.Int64(), redeemScript, txscript.SigHashAll, signingKey)
if err != nil {
continue
}
Expand All @@ -508,7 +512,7 @@ func (w *BitcoinWallet) multisign(ins []wi.TransactionInput, outs []wi.Transacti
if err != nil {
return nil, err
}
output := wire.NewTxOut(out.Value, scriptPubKey)
output := wire.NewTxOut(out.Value.Int64(), scriptPubKey)
tx.TxOut = append(tx.TxOut, output)
}

Expand Down Expand Up @@ -658,7 +662,8 @@ func (w *BitcoinWallet) estimateSpendFee(amount int64, feeLevel wi.FeeLevel) (ui
for _, input := range tx.TxIn {
for _, utxo := range utxos {
if utxo.Op.Hash.IsEqual(&input.PreviousOutPoint.Hash) && utxo.Op.Index == input.PreviousOutPoint.Index {
inval += utxo.Value
val, _ := strconv.ParseInt(utxo.Value, 10, 64)
inval += val
break
}
}
Expand Down
37 changes: 21 additions & 16 deletions bitcoin/sign_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,10 @@ package bitcoin
import (
"bytes"
"encoding/hex"
"github.com/OpenBazaar/multiwallet/util"
"math/big"
"testing"
"time"

"github.com/OpenBazaar/multiwallet/cache"
"github.com/OpenBazaar/multiwallet/datastore"
"github.com/OpenBazaar/multiwallet/keys"
"github.com/OpenBazaar/multiwallet/model/mock"
"github.com/OpenBazaar/multiwallet/service"
"github.com/OpenBazaar/spvwallet"
"github.com/OpenBazaar/wallet-interface"
"github.com/btcsuite/btcd/chaincfg"
Expand All @@ -20,6 +15,13 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/hdkeychain"

"github.com/OpenBazaar/multiwallet/cache"
"github.com/OpenBazaar/multiwallet/datastore"
"github.com/OpenBazaar/multiwallet/keys"
"github.com/OpenBazaar/multiwallet/model/mock"
"github.com/OpenBazaar/multiwallet/service"
"github.com/OpenBazaar/multiwallet/util"
)

type FeeResponse struct {
Expand Down Expand Up @@ -348,7 +350,8 @@ func TestBitcoinWallet_newUnsignedTransaction(t *testing.T) {
}

inputSource := func(target btcutil.Amount) (total btcutil.Amount, inputs []*wire.TxIn, inputValues []btcutil.Amount, scripts [][]byte, err error) {
total += btcutil.Amount(utxos[0].Value)
val, _ := new(big.Int).SetString(utxos[0].Value, 10)
total += btcutil.Amount(val.Int64())
in := wire.NewTxIn(&utxos[0].Op, []byte{}, [][]byte{})
in.Sequence = 0 // Opt-in RBF so we can bump fees
inputs = append(inputs, in)
Expand Down Expand Up @@ -390,7 +393,7 @@ func TestBitcoinWallet_CreateMultisigSignature(t *testing.T) {
t.Error(err)
}

sigs, err := w.CreateMultisigSignature(ins, outs, key1, redeemScript, 50)
sigs, err := w.CreateMultisigSignature(ins, outs, key1, redeemScript, *big.NewInt(50))
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -432,7 +435,7 @@ func buildTxData(w *BitcoinWallet) ([]wallet.TransactionInput, []wallet.Transact
}

out := wallet.TransactionOutput{
Value: 20000,
Value: *big.NewInt(20000),
Address: addr,
}
return []wallet.TransactionInput{in1, in2}, []wallet.TransactionOutput{out}, redeemScriptBytes, nil
Expand All @@ -458,21 +461,21 @@ func TestBitcoinWallet_Multisign(t *testing.T) {
t.Error(err)
}

sigs1, err := w.CreateMultisigSignature(ins, outs, key1, redeemScript, 50)
sigs1, err := w.CreateMultisigSignature(ins, outs, key1, redeemScript, *big.NewInt(50))
if err != nil {
t.Error(err)
}
if len(sigs1) != 2 {
t.Error(err)
}
sigs2, err := w.CreateMultisigSignature(ins, outs, key2, redeemScript, 50)
sigs2, err := w.CreateMultisigSignature(ins, outs, key2, redeemScript, *big.NewInt(50))
if err != nil {
t.Error(err)
}
if len(sigs2) != 2 {
t.Error(err)
}
txBytes, err := w.Multisign(ins, outs, sigs1, sigs2, redeemScript, 50, false)
txBytes, err := w.Multisign(ins, outs, sigs1, sigs2, redeemScript, *big.NewInt(50), false)
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -549,7 +552,8 @@ func TestBitcoinWallet_sweepAddress(t *testing.T) {
var in wallet.TransactionInput
var key *hdkeychain.ExtendedKey
for _, ut := range utxos {
if ut.Value > 0 && !ut.WatchOnly {
val, _ := new(big.Int).SetString(ut.Value, 10)
if val.Int64() > 0 && !ut.WatchOnly {
addr, err := w.ScriptToAddress(ut.ScriptPubkey)
if err != nil {
t.Error(err)
Expand All @@ -564,7 +568,7 @@ func TestBitcoinWallet_sweepAddress(t *testing.T) {
}
in = wallet.TransactionInput{
LinkedAddress: addr,
Value: ut.Value,
Value: *val,
OutpointIndex: ut.Op.Index,
OutpointHash: h,
}
Expand All @@ -579,7 +583,8 @@ func TestBitcoinWallet_sweepAddress(t *testing.T) {

// 1 of 2 P2WSH
for _, ut := range utxos {
if ut.Value > 0 && ut.WatchOnly {
val, _ := new(big.Int).SetString(ut.Value, 10)
if val.Int64() > 0 && ut.WatchOnly {
addr, err := w.ScriptToAddress(ut.ScriptPubkey)
if err != nil {
t.Error(err)
Expand All @@ -590,7 +595,7 @@ func TestBitcoinWallet_sweepAddress(t *testing.T) {
}
in = wallet.TransactionInput{
LinkedAddress: addr,
Value: ut.Value,
Value: *val,
OutpointIndex: ut.Op.Index,
OutpointHash: h,
}
Expand Down
57 changes: 35 additions & 22 deletions bitcoin/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"errors"
"fmt"
"io"
"math/big"
"strconv"
"time"

"github.com/OpenBazaar/multiwallet/cache"
Expand Down Expand Up @@ -43,6 +45,13 @@ type BitcoinWallet struct {
exchangeRates wi.ExchangeRates
}

var (
BitcoinCurrencyDefinition = wi.CurrencyDefinition{
Code: "BTC",
Divisibility: 8,
}
)

func NewBitcoinWallet(cfg config.CoinConfig, mnemonic string, params *chaincfg.Params, proxy proxy.Dialer, cache cache.Cacher, disableExchangeRates bool) (*BitcoinWallet, error) {
seed := bip39.NewSeed(mnemonic, "")

Expand Down Expand Up @@ -99,8 +108,8 @@ func (w *BitcoinWallet) CurrencyCode() string {
}
}

func (w *BitcoinWallet) IsDust(amount int64) bool {
return txrules.IsDustAmount(btc.Amount(amount), 25, txrules.DefaultRelayFeePerKb)
func (w *BitcoinWallet) IsDust(amount big.Int) bool {
return txrules.IsDustAmount(btc.Amount(amount.Int64()), 25, txrules.DefaultRelayFeePerKb)
}

func (w *BitcoinWallet) MasterPrivateKey() *hd.ExtendedKey {
Expand Down Expand Up @@ -171,10 +180,12 @@ func (w *BitcoinWallet) HasKey(addr btc.Address) bool {
return true
}

func (w *BitcoinWallet) Balance() (confirmed, unconfirmed int64) {
func (w *BitcoinWallet) Balance() (wi.CurrencyValue, wi.CurrencyValue) {
utxos, _ := w.db.Utxos().GetAll()
txns, _ := w.db.Txns().GetAll(false)
return util.CalcBalance(utxos, txns)
c, u := util.CalcBalance(utxos, txns)
return wi.CurrencyValue{Value: *big.NewInt(c), Currency: BitcoinCurrencyDefinition},
wi.CurrencyValue{Value: *big.NewInt(u), Currency: BitcoinCurrencyDefinition}
}

func (w *BitcoinWallet) Transactions() ([]wi.Txn, error) {
Expand Down Expand Up @@ -220,11 +231,11 @@ func (w *BitcoinWallet) ChainTip() (uint32, chainhash.Hash) {
return w.ws.ChainTip()
}

func (w *BitcoinWallet) GetFeePerByte(feeLevel wi.FeeLevel) uint64 {
return w.fp.GetFeePerByte(feeLevel)
func (w *BitcoinWallet) GetFeePerByte(feeLevel wi.FeeLevel) big.Int {
return *big.NewInt(int64(w.fp.GetFeePerByte(feeLevel)))
}

func (w *BitcoinWallet) Spend(amount int64, addr btc.Address, feeLevel wi.FeeLevel, referenceID string, spendAll bool) (*chainhash.Hash, error) {
func (w *BitcoinWallet) Spend(amount big.Int, addr btc.Address, feeLevel wi.FeeLevel, referenceID string, spendAll bool) (*chainhash.Hash, error) {
var (
tx *wire.MsgTx
err error
Expand All @@ -235,7 +246,7 @@ func (w *BitcoinWallet) Spend(amount int64, addr btc.Address, feeLevel wi.FeeLev
return nil, err
}
} else {
tx, err = w.buildTx(amount, addr, feeLevel, nil)
tx, err = w.buildTx(amount.Int64(), addr, feeLevel, nil)
if err != nil {
return nil, err
}
Expand All @@ -252,32 +263,33 @@ func (w *BitcoinWallet) BumpFee(txid chainhash.Hash) (*chainhash.Hash, error) {
return w.bumpFee(txid)
}

func (w *BitcoinWallet) EstimateFee(ins []wi.TransactionInput, outs []wi.TransactionOutput, feePerByte uint64) uint64 {
func (w *BitcoinWallet) EstimateFee(ins []wi.TransactionInput, outs []wi.TransactionOutput, feePerByte big.Int) big.Int {
tx := new(wire.MsgTx)
for _, out := range outs {
scriptPubKey, _ := txscript.PayToAddrScript(out.Address)
output := wire.NewTxOut(out.Value, scriptPubKey)
output := wire.NewTxOut(out.Value.Int64(), scriptPubKey)
tx.TxOut = append(tx.TxOut, output)
}
estimatedSize := EstimateSerializeSize(len(ins), tx.TxOut, false, P2PKH)
fee := estimatedSize * int(feePerByte)
return uint64(fee)
fee := estimatedSize * int(feePerByte.Int64())
return *big.NewInt(int64(fee))
}

func (w *BitcoinWallet) EstimateSpendFee(amount int64, feeLevel wi.FeeLevel) (uint64, error) {
return w.estimateSpendFee(amount, feeLevel)
func (w *BitcoinWallet) EstimateSpendFee(amount big.Int, feeLevel wi.FeeLevel) (big.Int, error) {
val, err := w.estimateSpendFee(amount.Int64(), feeLevel)
return *big.NewInt(int64(val)), err
}

func (w *BitcoinWallet) SweepAddress(ins []wi.TransactionInput, address *btc.Address, key *hd.ExtendedKey, redeemScript *[]byte, feeLevel wi.FeeLevel) (*chainhash.Hash, error) {
return w.sweepAddress(ins, address, key, redeemScript, feeLevel)
}

func (w *BitcoinWallet) CreateMultisigSignature(ins []wi.TransactionInput, outs []wi.TransactionOutput, key *hd.ExtendedKey, redeemScript []byte, feePerByte uint64) ([]wi.Signature, error) {
return w.createMultisigSignature(ins, outs, key, redeemScript, feePerByte)
func (w *BitcoinWallet) CreateMultisigSignature(ins []wi.TransactionInput, outs []wi.TransactionOutput, key *hd.ExtendedKey, redeemScript []byte, feePerByte big.Int) ([]wi.Signature, error) {
return w.createMultisigSignature(ins, outs, key, redeemScript, feePerByte.Uint64())
}

func (w *BitcoinWallet) Multisign(ins []wi.TransactionInput, outs []wi.TransactionOutput, sigs1 []wi.Signature, sigs2 []wi.Signature, redeemScript []byte, feePerByte uint64, broadcast bool) ([]byte, error) {
return w.multisign(ins, outs, sigs1, sigs2, redeemScript, feePerByte, broadcast)
func (w *BitcoinWallet) Multisign(ins []wi.TransactionInput, outs []wi.TransactionOutput, sigs1 []wi.Signature, sigs2 []wi.Signature, redeemScript []byte, feePerByte big.Int, broadcast bool) ([]byte, error) {
return w.multisign(ins, outs, sigs1, sigs2, redeemScript, feePerByte.Uint64(), broadcast)
}

func (w *BitcoinWallet) GenerateMultisigScript(keys []hd.ExtendedKey, threshold int, timeout time.Duration, timeoutKey *hd.ExtendedKey) (addr btc.Address, redeemScript []byte, err error) {
Expand Down Expand Up @@ -330,12 +342,12 @@ func (w *BitcoinWallet) DumpTables(wr io.Writer) {
fmt.Fprintln(wr, "Transactions-----")
txns, _ := w.db.Txns().GetAll(true)
for _, tx := range txns {
fmt.Fprintf(wr, "Hash: %s, Height: %d, Value: %d, WatchOnly: %t\n", tx.Txid, int(tx.Height), int(tx.Value), tx.WatchOnly)
fmt.Fprintf(wr, "Hash: %s, Height: %d, Value: %s, WatchOnly: %t\n", tx.Txid, int(tx.Height), tx.Value, tx.WatchOnly)
}
fmt.Fprintln(wr, "\nUtxos-----")
utxos, _ := w.db.Utxos().GetAll()
for _, u := range utxos {
fmt.Fprintf(wr, "Hash: %s, Index: %d, Height: %d, Value: %d, WatchOnly: %t\n", u.Op.Hash.String(), int(u.Op.Index), int(u.AtHeight), int(u.Value), u.WatchOnly)
fmt.Fprintf(wr, "Hash: %s, Index: %d, Height: %d, Value: %s, WatchOnly: %t\n", u.Op.Hash.String(), int(u.Op.Index), int(u.AtHeight), u.Value, u.WatchOnly)
}
}

Expand Down Expand Up @@ -367,6 +379,7 @@ func (w *BitcoinWallet) Broadcast(tx *wire.MsgTx) error {
if err != nil {
return err
}
val, _ := strconv.ParseInt(u.Value, 10, 64)
input := model.Input{
Txid: in.PreviousOutPoint.Hash.String(),
Vout: int(in.PreviousOutPoint.Index),
Expand All @@ -376,8 +389,8 @@ func (w *BitcoinWallet) Broadcast(tx *wire.MsgTx) error {
Sequence: uint32(in.Sequence),
N: n,
Addr: addr.String(),
Satoshis: u.Value,
Value: float64(u.Value) / util.SatoshisPerCoin(wi.Bitcoin),
Satoshis: val,
Value: float64(val) / util.SatoshisPerCoin(wi.Bitcoin),
}
cTxn.Inputs = append(cTxn.Inputs, input)
}
Expand Down
Loading