Skip to content

Commit

Permalink
sig and serialization improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
buck54321 committed May 14, 2022
1 parent d77380b commit 69cbf21
Show file tree
Hide file tree
Showing 21 changed files with 604 additions and 513 deletions.
10 changes: 3 additions & 7 deletions client/asset/bch/bch.go
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/gcash/bchd/bchec"
bchscript "github.com/gcash/bchd/txscript"
bchwire "github.com/gcash/bchd/wire"
Expand Down Expand Up @@ -162,11 +161,8 @@ func NewWallet(cfg *asset.WalletConfig, logger dex.Logger, network dex.Network)
LegacyBalance: true,
// Bitcoin Cash uses the Cash Address encoding, which is Bech32, but
// not indicative of segwit. We provide a custom encoder.
AddressDecoder: dexbch.DecodeCashAddress,
AddressStringer: func(addr btcutil.Address) string {
a, _ := dexbch.RecodeCashAddress(addr.String(), params)
return a
},
AddressDecoder: dexbch.DecodeCashAddress,
AddressStringer: dexbch.EncodeCashAddress,
// Bitcoin Cash has a custom signature hash algorithm. Since they don't
// have segwit, Bitcoin Cash implemented a variation of the withdrawn
// BIP0062 that utilizes Shnorr signatures.
Expand Down Expand Up @@ -231,7 +227,7 @@ func translateTx(btcTx *wire.MsgTx) (*bchwire.MsgTx, error) {
return nil, err
}

bchTx := bchwire.NewMsgTx(bchwire.TxVersion)
bchTx := new(bchwire.MsgTx)
err = bchTx.Deserialize(bytes.NewBuffer(txB))
if err != nil {
return nil, err
Expand Down
120 changes: 66 additions & 54 deletions client/asset/btc/btc.go
Expand Up @@ -272,10 +272,13 @@ type BTCCloneCFG struct {
// TxHasher is a function that generates a tx hash from a MsgTx.
TxHasher func(*wire.MsgTx) *chainhash.Hash
// AddressStringer is a function to convert an address to a string.
AddressStringer func(btcutil.Address) string
AddressStringer dexbtc.AddressStringer
// TxSizeCalculator is an optional function that will be used to calculate
// the size of a transaction.
TxSizeCalculator func(*wire.MsgTx) uint64
// TxVersion is an optional function that returns a version to use for
// new transactions.
TxVersion func() int32
}

// outPoint is the hash and output index of a transaction output.
Expand Down Expand Up @@ -610,7 +613,8 @@ type baseWallet struct {
serializeTx func(*wire.MsgTx) ([]byte, error)
calcTxSize func(*wire.MsgTx) uint64
hashTx func(*wire.MsgTx) *chainhash.Hash
stringifyAddress func(btcutil.Address) string
stringifyAddress dexbtc.AddressStringer
txVersion func() int32

tipMtx sync.RWMutex
currentTip *block
Expand Down Expand Up @@ -869,6 +873,11 @@ func newUnconnectedWallet(cfg *BTCCloneCFG, walletCfg *WalletConfig) (*baseWalle
addressStringer = stringifyAddress
}

txVersion := cfg.TxVersion
if txVersion == nil {
txVersion = func() int32 { return wire.TxVersion }
}

w := &baseWallet{
symbol: cfg.Symbol,
chainParams: cfg.ChainParams,
Expand All @@ -895,6 +904,7 @@ func newUnconnectedWallet(cfg *BTCCloneCFG, walletCfg *WalletConfig) (*baseWalle
hashTx: txHasher,
stringifyAddress: addressStringer,
calcTxSize: txSizeCalculator,
txVersion: txVersion,
}

if w.estimateFee == nil {
Expand Down Expand Up @@ -1004,11 +1014,16 @@ func (btc *baseWallet) IsDust(txOut *wire.TxOut, minRelayTxFee uint64) bool {
// getBlockchainInfoResult models the data returned from the getblockchaininfo
// command.
type getBlockchainInfoResult struct {
Blocks int64 `json:"blocks"`
Headers int64 `json:"headers"`
BestBlockHash string `json:"bestblockhash"`
InitialBlockDownload *bool `json:"initialblockdownload"`
InitialBlockDownloadComplete *bool `json:"initial_block_download_complete"`
Blocks int64 `json:"blocks"`
Headers int64 `json:"headers"`
BestBlockHash string `json:"bestblockhash"`
// InitialBlockDownload will be true if the node is still in the initial
// block download mode.
InitialBlockDownload *bool `json:"initialblockdownload"`
// InitialBlockDownloadComplete will be true if this node has completed its
// initial block download and is expected to be synced to the network.
// ZCash uses this terminology instead of initialblockdownload.
InitialBlockDownloadComplete *bool `json:"initial_block_download_complete"`
}

func (r *getBlockchainInfoResult) syncing() bool {
Expand Down Expand Up @@ -1796,11 +1811,16 @@ func (btc *baseWallet) split(value uint64, lots uint64, outputs []*output,
txHash := btc.hashTx(msgTx)
op := newOutput(txHash, 0, reqFunds)

sAddr, err := btc.stringifyAddress(addr, btc.chainParams)
if err != nil {
return nil, false, err
}

// Need to save one funding coin (in the deferred function).
fundingCoins = map[outPoint]*utxo{op.pt: {
txHash: op.txHash(),
vout: op.vout(),
address: btc.stringifyAddress(addr),
address: sAddr,
amount: reqFunds,
}}

Expand Down Expand Up @@ -1965,7 +1985,7 @@ func (btc *baseWallet) Locked() bool {

// fundedTx creates and returns a new MsgTx with the provided coins as inputs.
func (btc *baseWallet) fundedTx(coins asset.Coins) (*wire.MsgTx, uint64, []outPoint, error) {
baseTx := wire.NewMsgTx(wire.TxVersion)
baseTx := wire.NewMsgTx(btc.txVersion())
var totalIn uint64
// Add the funding utxos.
pts := make([]outPoint, 0, len(coins))
Expand Down Expand Up @@ -2120,12 +2140,17 @@ func (btc *baseWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, ui
btc.log.Errorf("failed to lock change output: %v", err)
}

sAddr, err := btc.stringifyAddress(changeAddr, btc.chainParams)
if err != nil {
return nil, nil, 0, err
}

// Log it as a fundingCoin, since it is expected that this will be
// chained into further matches.
btc.fundingCoins[change.pt] = &utxo{
txHash: change.txHash(),
vout: change.vout(),
address: btc.stringifyAddress(changeAddr),
address: sAddr,
amount: change.value,
}
}
Expand All @@ -2141,7 +2166,7 @@ func (btc *baseWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, ui
// Redeem sends the redemption transaction, completing the atomic swap.
func (btc *baseWallet) Redeem(form *asset.RedeemForm) ([]dex.Bytes, asset.Coin, uint64, error) {
// Create a transaction that spends the referenced contract.
msgTx := wire.NewMsgTx(wire.TxVersion)
msgTx := wire.NewMsgTx(btc.txVersion())
var totalIn uint64
var contracts [][]byte
var addresses []btcutil.Address
Expand Down Expand Up @@ -2414,50 +2439,20 @@ func (btc *baseWallet) AuditContract(coinID, contract, txData dex.Bytes, rebroad
}()
}

sAddr, err := btc.stringifyAddress(receiver, btc.chainParams)
if err != nil {
return nil, err
}

return &asset.AuditInfo{
Coin: newOutput(txHash, vout, uint64(txOut.Value)),
Recipient: btc.stringifyAddress(receiver),
Recipient: sAddr,
Contract: contract,
SecretHash: secretHash,
Expiration: time.Unix(int64(stamp), 0).UTC(),
}, nil
}

// AuditContract retrieves information about a swap contract from the provided
// txData. The extracted information would be used to audit the counter-party's
// contract during a swap. Since this wallet is backed by a full node, txData
// may be empty to attempt retrieval of the transaction output from the network.
func (btc *ExchangeWalletFullNode) AuditContract(coinID, contract, txData dex.Bytes, rebroadcast bool) (*asset.AuditInfo, error) {
if len(txData) != 0 {
return btc.baseWallet.AuditContract(coinID, contract, txData, rebroadcast)
}

full, ok := btc.node.(*rpcClient)
if !ok {
return nil, fmt.Errorf("wallet backend not a *rpcClient: %T", btc.node)
}

// In the off chance that the transaction is in mempool or txindex is
// enabled, try getrawtransaction.
txHash, _, err := decodeCoinID(coinID)
if err != nil {
return nil, err
}
tx, err := full.GetRawTransaction(txHash)
if err != nil {
btc.log.Warnf("Contract transaction %v could not be retrieved: %v", txHash, err)
// Try with gettxout in the baseWallet method.
return btc.baseWallet.AuditContract(coinID, contract, txData, rebroadcast)
}

txData, err = btc.serializeTx(tx) // if error, we'll just pass nil and let it try
if err != nil {
return nil, err
}

return btc.baseWallet.AuditContract(coinID, contract, txData, rebroadcast)
}

// LocktimeExpired returns true if the specified contract's locktime has
// expired, making it possible to issue a Refund.
func (btc *baseWallet) LocktimeExpired(contract dex.Bytes) (bool, time.Time, error) {
Expand Down Expand Up @@ -2773,7 +2768,7 @@ func (btc *baseWallet) refundTx(txHash *chainhash.Hash, vout uint32, contract de

// Create the transaction that spends the contract.
feeRate := btc.targetFeeRateWithFallback(2, feeSuggestion) // meh level urgency
msgTx := wire.NewMsgTx(wire.TxVersion)
msgTx := wire.NewMsgTx(btc.txVersion())
msgTx.LockTime = uint32(lockTime)
prevOut := wire.NewOutPoint(txHash, vout)
txIn := wire.NewTxIn(prevOut, []byte{}, nil)
Expand Down Expand Up @@ -2842,7 +2837,7 @@ func (btc *baseWallet) Address() (string, error) {
if err != nil {
return "", err
}
return btc.stringifyAddress(addr), nil
return btc.stringifyAddress(addr, btc.chainParams)
}

// NewAddress returns a new address from the wallet. This satisfies the
Expand Down Expand Up @@ -3347,7 +3342,8 @@ func (btc *baseWallet) signTxAndAddChange(baseTx *wire.MsgTx, addr btcutil.Addre
vSize0 := btc.calcTxSize(baseTx)
baseTx.AddTxOut(changeOutput)
changeSize := btc.calcTxSize(baseTx) - vSize0 // may be dexbtc.P2WPKHOutputSize
btc.log.Debugf("Change output size = %d, addr = %s", changeSize, btc.stringifyAddress(addr))

btc.log.Debugf("Change output size = %d, addr = %s", changeSize, addr)

vSize += changeSize
fee := feeRate * vSize
Expand Down Expand Up @@ -3444,7 +3440,12 @@ func (btc *baseWallet) txOutFromTxBytes(txB []byte, vout uint32) (*wire.TxOut, e
// createSig creates and returns the serialized raw signature and compressed
// pubkey for a transaction input signature.
func (btc *baseWallet) createSig(tx *wire.MsgTx, idx int, pkScript []byte, addr btcutil.Address, val uint64) (sig, pubkey []byte, err error) {
privKey, err := btc.node.privKeyForAddress(btc.stringifyAddress(addr))
sAddr, err := btc.stringifyAddress(addr, btc.chainParams)
if err != nil {
return nil, nil, err
}

privKey, err := btc.node.privKeyForAddress(sAddr)
if err != nil {
return nil, nil, err
}
Expand All @@ -3462,7 +3463,12 @@ func (btc *baseWallet) createSig(tx *wire.MsgTx, idx int, pkScript []byte, addr
func (btc *baseWallet) createWitnessSig(tx *wire.MsgTx, idx int, pkScript []byte,
addr btcutil.Address, val uint64, sigHashes *txscript.TxSigHashes) (sig, pubkey []byte, err error) {

privKey, err := btc.node.privKeyForAddress(btc.stringifyAddress(addr))
sAddr, err := btc.stringifyAddress(addr, btc.chainParams)
if err != nil {
return nil, nil, err
}

privKey, err := btc.node.privKeyForAddress(sAddr)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -3609,6 +3615,12 @@ func (btc *baseWallet) wireBytes(tx *wire.MsgTx) []byte {
return buf.Bytes()
}

// GetBestBlockHeight is exported for use by clone wallets. Not part of the
// asset.Wallet interface.
func (btc *baseWallet) GetBestBlockHeight() (int32, error) {
return btc.node.getBestBlockHeight()
}

// Convert the BTC value to satoshi.
func toSatoshi(v float64) uint64 {
return uint64(math.Round(v * conventionalConversionFactor))
Expand Down Expand Up @@ -3806,8 +3818,8 @@ func hashTx(tx *wire.MsgTx) *chainhash.Hash {
return &h
}

func stringifyAddress(addr btcutil.Address) string {
return addr.String()
func stringifyAddress(addr btcutil.Address, _ *chaincfg.Params) (string, error) {
return addr.String(), nil
}

func deserializeBlock(b []byte) (*wire.MsgBlock, error) {
Expand Down
11 changes: 8 additions & 3 deletions client/asset/btc/rpcclient.go
Expand Up @@ -89,7 +89,7 @@ type rpcCore struct {
hashTx func(*wire.MsgTx) *chainhash.Hash
numericGetRawTxRPC bool
legacyValidateAddressRPC bool
stringifyAddress func(btcutil.Address) string
stringifyAddress dexbtc.AddressStringer
}

// rpcClient is a bitcoind JSON RPC client that uses rpcclient.Client's
Expand Down Expand Up @@ -505,8 +505,13 @@ func (wc *rpcClient) GetWalletInfo() (*GetWalletInfoResult, error) {
// GetAddressInfo gets information about the given address by calling
// getaddressinfo RPC command.
func (wc *rpcClient) getAddressInfo(addr btcutil.Address, method string) (*GetAddressInfoResult, error) {
sAddr, err := wc.stringifyAddress(addr, wc.chainParams)
if err != nil {
return nil, err
}

ai := new(GetAddressInfoResult)
return ai, wc.call(method, anylist{wc.stringifyAddress(addr)}, ai)
return ai, wc.call(method, anylist{sAddr}, ai)
}

// ownsAddress indicates if an address belongs to the wallet.
Expand Down Expand Up @@ -729,7 +734,7 @@ func msgTxFromHex(txHex string) (*wire.MsgTx, error) {

// msgTxFromBytes creates a wire.MsgTx by deserializing the transaction.
func msgTxFromBytes(txB []byte) (*wire.MsgTx, error) {
msgTx := wire.NewMsgTx(wire.TxVersion)
msgTx := new(wire.MsgTx)
if err := msgTx.Deserialize(bytes.NewReader(txB)); err != nil {
return nil, err
}
Expand Down

0 comments on commit 69cbf21

Please sign in to comment.