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

Firo light wallet (electrum) support #2426

Merged
merged 10 commits into from
Aug 31, 2023
3 changes: 3 additions & 0 deletions client/asset/btc/livetest/livetest.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func tBackend(ctx context.Context, t *testing.T, cfg *Config, dir string, wallet
reportName := fmt.Sprintf("%s:%s-%s", cfg.Asset.Symbol, walletName.Node, walletName.Name)

walletCfg := &asset.WalletConfig{
Type: walletName.WalletType,
Settings: settings,
TipChange: func(err error) {
blkFunc(reportName, err)
Expand Down Expand Up @@ -136,6 +137,8 @@ func randBytes(l int) []byte {
type WalletName struct {
Node string
Name string
// WalletType is optional
WalletType string
// Filename is optional. If specified, it will be used instead of
// [node].conf.
Filename string
Expand Down
65 changes: 42 additions & 23 deletions client/asset/firo/firo.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,19 @@ import (
)

const (
version = 0
BipID = 136 // Zcoin XZC
minNetworkVersion = 141201 // bitcoin 0.14 base
walletTypeRPC = "firodRPC"
estimateFeeConfs = 2 // 2 blocks should be enough
version = 0
BipID = 136 // Zcoin XZC
minNetworkVersion = 141201 // bitcoin 0.14 base
walletTypeRPC = "firodRPC"
walletTypeElectrum = "electrumRPC"
estimateFeeConfs = 2 // 2 blocks should be enough

mainnetExplorerFeeAPI = "https://explorer.firo.org/insight-api-zcoin/utils/estimatefee"
testnetExplorerFeeAPI = "https://testexplorer.firo.org/insight-api-zcoin/utils/estimatefee"
)

var (
configOpts = append(btc.RPCConfigOpts("Firo", "8168"), []*asset.ConfigOption{
configOpts = append(btc.RPCConfigOpts("Firo", "8888"), []*asset.ConfigOption{
{
Key: "fallbackfee",
DisplayName: "Fallback fee rate",
Expand Down Expand Up @@ -70,13 +71,21 @@ var (
Version: version,
SupportedVersions: []uint32{version},
UnitInfo: dexfiro.UnitInfo,
AvailableWallets: []*asset.WalletDefinition{{
Type: walletTypeRPC,
Tab: "External",
Description: "Connect to firod",
DefaultConfigPath: dexbtc.SystemConfigPath("firo"),
ConfigOpts: configOpts,
}},
AvailableWallets: []*asset.WalletDefinition{
{
Type: walletTypeRPC,
Tab: "Firo Core (external)",
Description: "Connect to firod",
DefaultConfigPath: dexbtc.SystemConfigPath("firo"),
ConfigOpts: configOpts,
},
{
Type: walletTypeElectrum,
Tab: "Electrum-Firo (external)",
Description: "Use an external Electrum-Firo Wallet",
ConfigOpts: append(btc.ElectrumConfigOpts, btc.CommonConfigOpts("FIRO", false)...),
},
},
}
)

Expand Down Expand Up @@ -128,8 +137,6 @@ func NewWallet(cfg *asset.WalletConfig, logger dex.Logger, network dex.Network)
Simnet: "28888",
}

var w *btc.ExchangeWalletFullNode

cloneCFG := &btc.BTCCloneCFG{
WalletCFG: cfg,
MinNetworkVersion: minNetworkVersion,
Expand All @@ -144,7 +151,7 @@ func NewWallet(cfg *asset.WalletConfig, logger dex.Logger, network dex.Network)
Segwit: false,
InitTxSize: dexbtc.InitTxSize,
InitTxSizeBase: dexbtc.InitTxSizeBase,
LegacyBalance: true,
LegacyBalance: cfg.Type == walletTypeRPC,
LegacyRawFeeLimit: true, // sendrawtransaction Has single arg allowhighfees
ArglessChangeAddrRPC: true, // getrawchangeaddress has No address-type arg
OmitAddressType: true, // getnewaddress has No address-type arg
Expand All @@ -153,18 +160,30 @@ func NewWallet(cfg *asset.WalletConfig, logger dex.Logger, network dex.Network)
NumericGetRawRPC: false, // getrawtransaction uses either 0/1 Or true/false
LegacyValidateAddressRPC: true, // use validateaddress to read 'ismine' bool
SingularWallet: true, // one wallet/node
UnlockSpends: false, // checked after sendtoaddress
UnlockSpends: true, // Firo chain wallet does Not unlock coins after sendrawtransaction
AssetID: BipID,
FeeEstimator: estimateFee,
ExternalFeeEstimator: fetchExternalFee,
PrivKeyFunc: func(addr string) (*btcec.PrivateKey, error) {
return privKeyForAddress(w, addr)
},
PrivKeyFunc: nil, // set only for walletTypeRPC below
}

var err error
w, err = btc.BTCCloneWallet(cloneCFG)
return w, err
switch cfg.Type {
case walletTypeRPC:
var exw *btc.ExchangeWalletFullNode
// override PrivKeyFunc - we need our own Firo dumpprivkey fn
cloneCFG.PrivKeyFunc = func(addr string) (*btcec.PrivateKey, error) {
return privKeyForAddress(exw, addr)
}
var err error
exw, err = btc.BTCCloneWallet(cloneCFG)
return exw, err
dev-warrior777 marked this conversation as resolved.
Show resolved Hide resolved
case walletTypeElectrum:
// override Ports - no default ports
cloneCFG.Ports = dexbtc.NetPorts{}
return btc.ElectrumWallet(cloneCFG)
default:
return nil, fmt.Errorf("unknown wallet type %q for firo", cfg.Type)
}
}

// rpcCaller is satisfied by ExchangeWalletFullNode (baseWallet), providing
Expand Down
25 changes: 11 additions & 14 deletions client/asset/firo/regnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,9 @@ package firo
// Regnet tests expect the Firo test harness to be running.
//
// Sim harness info:
// The harness has three wallets, alpha, beta, and gamma.
// All three wallets have confirmed UTXOs.
// The beta wallet has only coinbase outputs.
// The alpha wallet has coinbase outputs too, but has sent some to the gamma
// wallet, so also has some change outputs.
// The gamma wallet has regular transaction outputs of varying size and
// confirmation count. Value:Confirmations =
// 10:8, 18:7, 5:6, 7:5, 1:4, 15:3, 3:2, 25:1
// The harness has four nodes: alpha, beta, gamma and delta with one wallet
// per node. All wallets have confirmed UTXOs. The alpha wallet has only
// coinbase outputs.

import (
"context"
Expand All @@ -38,29 +33,31 @@ var (
}
)

// Run harness with NOMINER="1"
func TestWallet(t *testing.T) {
livetest.Run(t, &livetest.Config{
NewWallet: NewWallet,
LotSize: tLotSize,
Asset: tFIRO,
SplitTx: true,
FirstWallet: &livetest.WalletName{
Node: "alpha",
Name: "alpha",
WalletType: walletTypeRPC,
Node: "alpha",
},
SecondWallet: &livetest.WalletName{
Node: "beta",
Name: "beta",
WalletType: walletTypeRPC,
Node: "gamma",
},
})
}

// Tests only mainnet, testnet expected to return -1 normally
// Tests only mainnet, testnet is expected to return -1 normally
func TestFetchExternalFee(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
rate, err := fetchExternalFee(ctx, dex.Mainnet)
if err != nil {
t.Fatal(err)
}
fmt.Printf("#### External fee rate fetched:: %d sat/B\n", rate)
fmt.Printf("External fee rate fetched:: %d sat/B\n", rate)
}
5 changes: 5 additions & 0 deletions client/cmd/simnet-trade-tests/run
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ case $1 in
./simnet-trade-tests --base1node trading1 --base2node trading2 --quote firo ${@:2}
;;

dcrfiroelectrum)
./simnet-trade-tests --base1node trading1 --base2node trading2 --quote firo --quote1type electrum ${@:2}
;;

zecbtc)
./simnet-trade-tests --base zec --quote btc --regasset btc ${@:2}
;;
Expand Down Expand Up @@ -98,6 +102,7 @@ dcrdoge - RPC wallets on DCR-DOGE market
dcrdgb - RPC wallets on DCR-DGB market
dcreth - Decred RPC wallet and Ethereum native wallet on DCR-ETH market
dcrfiro - RPC wallets on DCR-FIRO market
dcrfiroelectrum - Decred RPC wallet and Firo Electrum wallet on DCR-FIRO market
zecbtc - RPC wallets on ZEC-BTC market
dcrdextt - Decred RPC wallet and Ethereum wallet with dextt.eth test token on market DCR-DEXTT.ETH

Expand Down
10 changes: 5 additions & 5 deletions client/core/simnet_trade.go
Original file line number Diff line number Diff line change
Expand Up @@ -1612,6 +1612,10 @@ func bchWallet(wt SimWalletType, node string) (*tWallet, error) {
return btcCloneWallet(bch.BipID, node, wt)
}

func firoWallet(wt SimWalletType, node string) (*tWallet, error) {
return btcCloneWallet(firo.BipID, node, wt)
}

func ethWallet() (*tWallet, error) {
return &tWallet{
fund: true,
Expand Down Expand Up @@ -1721,10 +1725,6 @@ func dgbWallet(node string) (*tWallet, error) {
return btcCloneWallet(dgb.BipID, node, WTCoreClone)
}

func firoWallet(node string) (*tWallet, error) {
return btcCloneWallet(firo.BipID, node, WTCoreClone)
}

func zecWallet(node string) (*tWallet, error) {
return btcCloneWallet(zec.BipID, node, WTCoreClone)
}
Expand Down Expand Up @@ -1754,7 +1754,7 @@ func (s *simulationTest) newClient(name string, cl *SimClient) (*simulationClien
case dash.BipID:
tw, err = dashWallet(node)
case firo.BipID:
tw, err = firoWallet(node)
tw, err = firoWallet(wt, node)
case zec.BipID:
tw, err = zecWallet(node)
default:
Expand Down
135 changes: 135 additions & 0 deletions dex/testing/firo/README_ELECTRUM_HARNESSES.md
dev-warrior777 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@

# Electrum-Firo Harness Support

Harnesses to run Electrum-Firo wallet on regtest.

## 1. Firo Chain Server Test Harness

The **harness.sh** chain server harness is a collection of tmux scripts that
collectively create a sandboxed environment for testing dex swap transactions.
See Also: README_HARNESS.md

## 2. ElectrumX-Firo Test Harness

The harness is a script named **electrumx.sh** which downloads a git repo
containing a release version of ElectrumX-Firo server.

It requires **harness.sh** Firo chain server harness running.

## 3. Electrum-Firo Test Harness

The harness is a script named **electrum.sh** which downloads a git repo containing
a release version of Electrum-Firo wallet client.

It requires **electrumx.sh** Firo ElectrumX-Firo server harness running.

Which in turn requires **harness.sh** Firo chain server harness running.

## Dependencies

The **harness.sh** script depends on [firod] and [firo-cli] to run.
Go to <https://github.com/firoorg/firo/releases> for binaries or source.

The **electrumx.sh** script depends on [python3], [python3 pip] and [git] to run.
Python3 v3.6 is coded but this script was tested using python3.10. Some testing
(minimal) was done with 3.7. Git should be latest. Pip will be downloaded and it's version upgraded by the script into a virtual environment each time.

The **electrum.sh** script depends on [python3], [python3 pip] and [git] to run.
Python3 v3.6 is coded but this script was tested using python3.10. Some testing
(minimal) was done with 3.7. Python 3.8, 3.9 would be expected to work although
untested. However using Python 3.11 there are dependencies which will not build
and are unsupported by their respective maintainers on PyPI. Git should be latest.
Pip will be re-downloaded and it's version upgraded by the script into a virtual environment each time.

dev-warrior777 marked this conversation as resolved.
Show resolved Hide resolved
### Architecture

- ELECTRUM WALLET CLIENT
- ELECTRUMX SERVER
- FIRO CHAIN HARNESS

All three scripts store data in **~/dextest/...** dirsectory tree.

## Using

You must have `firod` and `firo-cli` in `PATH` to use the chain server harness. Use 3 tty's
and run each line below in a **separate** tty:

```bash
$ ./harness.sh
$ ./electrumx.sh
$ ./electrum.sh
```

The Electrum-Firo wallet client will have a prepared, empty but encrypted
regtest wallet.

Password is "abc".

The script starts the Electrum-Firo wallet client in CLI mode with debug level
logging to stderr. Change STARTUP= in the script to "GUI" to start the Gui or
"DAEMON" to start as a daemon. Stop the daemon with the `stop_daemon` script.

## Development

### Firo Chain Server

For the Firo chain server see the README_HARNESS.md in this directory.

### Server

The **electrumx.sh** script first cleans part of the **~/dextest/electrum/firo/server...**
directory tree.

The script then downloads a specific commit from:
<https://github.com/firoorg/electrumx-firo.git> to the .../server/electrumx-repo directory.

It then creates a python virtual environment (venv) sandbox and installs the
requesting python3 interpreter, latest version of pip and all required modules
in `setup.py` into the sandbox.
If any need building from C source it will be done at this point.

Server certificates are created. Then all the required environment variables to
configure the server exported; and the electrumX daemon started

The electrumX daemon will connect to the firo chain harness node **alpha**

### Client

The **electrum.sh** script first cleans part of the **~/dextest/electrum/firo/client...**
directory tree.

The script then downloads a specific commit from:
<https://github.com/firoorg/electrum-firo.git> to the ../client/electrum-repo directory.

It then creates a python virtual environment (venv) sandbox and installs the
requesting python3 interpreter, latest version of pip and all required modules
from files in `contrib/requirements` directory into the sandbox. If any need building from C source it will be done at
this point.

A prepared, empty but encrypted electrum wallet is copied to the electrum data
directory at **~/dextest/electrum/firo/client/wallet/regtest/wallets**.

By default the electrum client is started as a daemon and the default wallet is loaded.
Use **stop-daemon** script to stop the daemon.

The electrum client wallet will connect to the firo electrumX daemon.

### Simnet Trade Testing

In seperate tty's start up:

- btc harness
- dcr harness
- firo harness
- electrumx.sh Electrum-Firo regtest server
- electrum.sh Electrum-Firo regtest wallet
- dcrdex harness

Ensure dcrctl, firod and firo-cli are in PATH for simnet-trade-tests exec process
mining.

```bash
$ ./run dcrfiroelectrum --runonce --all
```

Best results by nuking everything after each run of the simnet-trade-test test suite.
Loading