Skip to content

Commit

Permalink
electrum: Use wallet version 4.5.5 and up.
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeGruffins committed Jun 21, 2024
1 parent 8152105 commit 4ba2bc0
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 30 deletions.
24 changes: 24 additions & 0 deletions client/asset/btc/electrum.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
)

const needElectrumVersion = "4.5.5"

// ExchangeWalletElectrum is the asset.Wallet for an external Electrum wallet.
type ExchangeWalletElectrum struct {
*baseWallet
Expand Down Expand Up @@ -125,6 +127,28 @@ func (btc *ExchangeWalletElectrum) Connect(ctx context.Context) (*sync.WaitGroup
genesis.String(), serverFeats.Genesis)
}

verStr, err := btc.ew.wallet.Version(ctx)
if err != nil {
return nil, err
}
wantVer, err := dex.SemverFromString(needElectrumVersion)
if err != nil {
return nil, err
}
gotVer, err := dex.SemverFromString(verStr)
if err != nil {
return nil, err
}
if !dex.SemverCompatible(*wantVer, *gotVer) {
return nil, fmt.Errorf("wanted electrum wallet version %s but got %s", wantVer, gotVer)
}
// Patch 4.5.5 contains a breaking change that requires "iknowwhatimdoing"
// to be set to true when signing transactions. Lower versions will error
// if an unused arg is included.
if gotVer.Major == 4 && gotVer.Minor == 5 && gotVer.Patch < 5 {
return nil, fmt.Errorf("wanted electrum wallet version at or above 4.5.5 but got %s", gotVer)
}

wg.Add(1)
go func() {
defer wg.Done()
Expand Down
48 changes: 24 additions & 24 deletions client/asset/btc/electrum/wallet_methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const (
methodSignTransaction = "signtransaction" // requires password for protected wallets
methodFreezeUTXO = "freeze_utxo"
methodUnfreezeUTXO = "unfreeze_utxo"
methodVersion = "version"
)

// Commands gets a list of the supported wallet RPCs.
Expand Down Expand Up @@ -107,32 +108,20 @@ func (wc *WalletClient) GetServers(ctx context.Context) ([]*GetServersResult, er
return servers, nil
}

type feeRateReq struct {
Method string `json:"fee_method"`
Level float64 `json:"fee_level"`
}

// FeeRate gets a fee rate estimate for a block confirmation target, where 1
// indicates the next block.
func (wc *WalletClient) FeeRate(ctx context.Context, confTarget int64) (int64, error) {
if confTarget > 10 {
confTarget = 10
} else if confTarget < 1 {
confTarget = 1
func (wc *WalletClient) FeeRate(ctx context.Context, _ int64) (int64, error) {
var res struct {
Method string `json:"method"`
SatPerKB int64 `json:"sat/kvB"`
Tooltip string `json:"tooltip"`
Value int64 `json:"value"`
}

// Based on the Electrum wallet UI:
// "mempool": 1.0 corresponds to 0.1 MB from tip, 0.833 to 0.2 MB, 0.667 to 0.5 MB, 0.5 to 1.0 MB, 0.333 to 2 MB
// "eta": 1.0 corresponds to "next block", 0.75 to "within 2 blocks", 0.5 to 5 blks, 0.25 to 10 blks (non-linear)
target := map[int64]float64{1: 1.0, 2: 0.75, 3: 0.66, 4: 0.56, 5: 0.5,
6: 0.445, 7: 0.39, 8: 0.333, 9: 0.278, 10: 0.25}[confTarget] // "eta", roughly interpolated

var satPerKB int64
err := wc.Call(ctx, methodGetFeeRate, feeRateReq{"eta", target}, &satPerKB) // or anylist{"mempool", target}
err := wc.Call(ctx, methodGetFeeRate, nil, &res)
if err != nil {
return 0, err
}
return satPerKB, nil
return res.SatPerKB, nil
}

type walletReq struct {
Expand Down Expand Up @@ -414,17 +403,19 @@ type signTransactionArgs struct {
// signtransaction_with_privkey request. (this RPC should not use positional
// arguments)
// Privkey string `json:"privkey,omitempty"` // sign with wallet if empty
Wallet string `json:"wallet,omitempty"`
Wallet string `json:"wallet,omitempty"`
IgnoreWarnings bool `json:"iknowwhatimdoing,omitempty"`
}

// SignTx signs the base-64 encoded PSBT with the wallet's keys, returning the
// signed transaction.
func (wc *WalletClient) SignTx(ctx context.Context, walletPass string, psbtB64 string) ([]byte, error) {
var res string
err := wc.Call(ctx, methodSignTransaction, &signTransactionArgs{
Tx: psbtB64,
Pass: walletPass,
Wallet: wc.walletFile},
Tx: psbtB64,
Pass: walletPass,
Wallet: wc.walletFile,
IgnoreWarnings: true},
&res)
if err != nil {
return nil, err
Expand Down Expand Up @@ -498,3 +489,12 @@ func (wc *WalletClient) GetPrivateKeys(ctx context.Context, walletPass, addr str
}
return privSplit[1], nil
}

func (wc *WalletClient) Version(ctx context.Context) (string, error) {
var res string
err := wc.Call(ctx, methodVersion, &walletReq{wc.walletFile}, &res)
if err != nil {
return "", err
}
return res, nil
}
1 change: 1 addition & 0 deletions client/asset/btc/electrum_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type electrumWalletClient interface {
GetRawTransaction(ctx context.Context, txid string) ([]byte, error) // wallet method
GetAddressHistory(ctx context.Context, addr string) ([]*electrum.GetAddressHistoryResult, error)
GetAddressUnspent(ctx context.Context, addr string) ([]*electrum.GetAddressUnspentResult, error)
Version(ctx context.Context) (string, error)
}

type electrumNetworkClient interface {
Expand Down
29 changes: 28 additions & 1 deletion dex/semver.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

package dex

import "fmt"
import (
"fmt"
"strconv"
"strings"
)

// Semver models a semantic version major.minor.patch
type Semver struct {
Expand Down Expand Up @@ -46,3 +50,26 @@ func SemverCompatibleAny(compatible []Semver, actual Semver) bool {
func (s Semver) String() string {
return fmt.Sprintf("%d.%d.%d", s.Major, s.Minor, s.Patch)
}

func SemverFromString(ver string) (*Semver, error) {
fields := strings.Split(ver, ".")
if len(fields) < 2 || len(fields) > 3 {
return nil, fmt.Errorf("expected semver with 2 or 3 fields but got %d", len(fields))
}
major, err := strconv.ParseUint(fields[0], 10, 32)
if err != nil {
return nil, fmt.Errorf("cannot parse semver major: %v", err)
}
minor, err := strconv.ParseUint(fields[1], 10, 32)
if err != nil {
return nil, fmt.Errorf("cannot parse semver minor: %v", err)
}
var patch uint64
if len(fields) == 3 {
patch, err = strconv.ParseUint(fields[2], 10, 32)
if err != nil {
return nil, fmt.Errorf("cannot parse semver major: %v", err)
}
}
return &Semver{uint32(major), uint32(minor), uint32(patch)}, nil
}
2 changes: 0 additions & 2 deletions dex/testing/btc/electrum-base.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ python -m ensurepip --upgrade
pip install .
pip install requests cryptography pycryptodomex pyqt5 scrypt

./contrib/pull_locale # I think we need this?

cp "${SCRIPT_DIR}/electrum_default_wallet" "${NET_DIR}/wallets/default_wallet"

cat > "${NET_DIR}/config" <<EOF
Expand Down
3 changes: 1 addition & 2 deletions dex/testing/btc/electrum.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
export SCRIPT_DIR=$(pwd)
export SYMBOL=btc
export REPO=https://github.com/spesmilo/electrum.git
# 4.3.0a somewhere on master
export COMMIT=0fca35fa4068ea7c9da60cd5330b85c2ca1d0398
export COMMIT=7263a49129d14db288a01b0b9d569422baddf5e1 # 4.5.5
export GENESIS=0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206
export RPCPORT=16789
export EX_PORT=54002
Expand Down
2 changes: 1 addition & 1 deletion dex/testing/btc/electrumx.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ if [ ! -d "${REPO_DIR}/.git" ]; then
git remote add origin https://github.com/spesmilo/electrumx.git
fi

git fetch --depth 1 origin fb037fbd23b8ce418cd67d68bbf8d32e69ecef62
git fetch --depth 1 origin 914938264e5621ea8980be6d3e69964e7f219d16
git reset --hard FETCH_HEAD

if [ ! -d "${ELECTRUMX_DIR}/venv" ]; then
Expand Down

0 comments on commit 4ba2bc0

Please sign in to comment.