Skip to content

Commit

Permalink
multi: Add ETHSwapV0.
Browse files Browse the repository at this point in the history
Have the eth harness start with a contract. Add contract bindings and
basic calls to clients.
  • Loading branch information
JoeGruffins committed Mar 24, 2021
1 parent 4600635 commit bc9890b
Show file tree
Hide file tree
Showing 11 changed files with 1,240 additions and 118 deletions.
62 changes: 45 additions & 17 deletions client/asset/eth/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"decred.org/dcrdex/server/asset/eth"
"github.com/decred/dcrd/dcrutil/v3"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/node"
Expand All @@ -32,8 +33,7 @@ func init() {
const (
// BipID is the BIP-0044 asset ID.
BipID = 60
coinIDSize = 52
defaultGasFee = 82000000000
defaultGasFee = 82e9
)

var (
Expand Down Expand Up @@ -74,7 +74,8 @@ var (
DefaultConfigPath: defaultAppDir, // Incorrect if changed by user?
ConfigOpts: configOpts,
}
notImplementedErr = errors.New("not implemented")
notImplementedErr = errors.New("not implemented")
mainnetContractAddr = common.HexToAddress("")
)

// Driver implements asset.Driver.
Expand All @@ -98,21 +99,27 @@ func (d *Driver) Info() *asset.WalletInfo {
// ethFetcher represents a blockchain information fetcher. In practice, it is
// satisfied by rpcclient. For testing, it can be satisfied by a stub.
type ethFetcher interface {
accounts(ctx context.Context) ([]common.Address, error)
accounts() []*accounts.Account
addPeer(ctx context.Context, peer string) error
balance(ctx context.Context, acct common.Address) (*big.Int, error)
balance(ctx context.Context, acct *accounts.Account) (*big.Int, error)
bestBlockHash(ctx context.Context) (common.Hash, error)
block(ctx context.Context, hash common.Hash) (*types.Block, error)
blockNumber(ctx context.Context) (uint64, error)
connect(ctx context.Context, node *node.Node) error
importAccount(pw string, privKeyB []byte) (accounts.Account, error)
lock(ctx context.Context, acct common.Address) error
locked(ctx context.Context, acct common.Address) (bool, error)
connect(ctx context.Context, node *node.Node, contractAddr common.Address) error
importAccount(pw string, privKeyB []byte) (*accounts.Account, error)
initiate(opts *bind.TransactOpts, netID int64, refundTimestamp int64, secretHash [32]byte, participant common.Address) (*types.Transaction, error)
lock(ctx context.Context, acct *accounts.Account) error
locked(ctx context.Context, acct *accounts.Account) (bool, error)
nodeInfo(ctx context.Context) (*p2p.NodeInfo, error)
sendToAddr(ctx context.Context, acct, addr common.Address, amt, gasFee *big.Int) error
pendingTransactions(ctx context.Context) ([]*types.Transaction, error)
transactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
redeem(opts *bind.TransactOpts, netID int64, secret, secretHash [32]byte) (*types.Transaction, error)
refund(opts *bind.TransactOpts, netID int64, secretHash [32]byte) (*types.Transaction, error)
sendToAddr(ctx context.Context, acct *accounts.Account, addr common.Address, amt, gasFee *big.Int) (common.Hash, error)
shutdown()
swap(ctx context.Context, from *accounts.Account, secretHash [32]byte) (*eth.ETHSwapSwap, error)
syncStatus(ctx context.Context) (bool, float32, error)
unlock(ctx context.Context, pw string, acct common.Address) error
unlock(ctx context.Context, pw string, acct *accounts.Account) error
}

// Check that ExchangeWallet satisfies the asset.Wallet interface.
Expand All @@ -128,13 +135,15 @@ type ExchangeWallet struct {
ctx context.Context // the asset subsystem starts with Connect(ctx)
node ethFetcher
log dex.Logger
acct common.Address
tipChange func(error)

internalNode *node.Node

tipMtx sync.RWMutex
currentTip *types.Block

acctMtx sync.RWMutex
acct *accounts.Account
}

// Info returns basic information about the wallet and asset.
Expand Down Expand Up @@ -162,6 +171,7 @@ func NewWallet(assetCFG *asset.WalletConfig, logger dex.Logger, network dex.Netw
log: logger,
tipChange: assetCFG.TipChange,
internalNode: node,
acct: new(accounts.Account),
}, nil
}

Expand All @@ -174,7 +184,7 @@ func (eth *ExchangeWallet) shutdown() {
// Connect connects to the node RPC server. A dex.Connector.
func (eth *ExchangeWallet) Connect(ctx context.Context) (*sync.WaitGroup, error) {
c := rpcclient{}
if err := c.connect(ctx, eth.internalNode); err != nil {
if err := c.connect(ctx, eth.internalNode, mainnetContractAddr); err != nil {
return nil, err
}
eth.node = &c
Expand Down Expand Up @@ -211,7 +221,9 @@ func (eth *ExchangeWallet) Connect(ctx context.Context) (*sync.WaitGroup, error)
//
// TODO: Consider adding multiple accounts.
func (eth *ExchangeWallet) OwnsAddress(address string) (bool, error) {
return eth.acct.String() == address, nil
eth.acctMtx.RLock()
defer eth.acctMtx.RUnlock()
return eth.acct.Address.String() == address, nil
}

// Balance returns the total available funds in the account.
Expand All @@ -221,6 +233,8 @@ func (eth *ExchangeWallet) OwnsAddress(address string) (bool, error) {
// TODO: Ethereum balances can easily go over the max value of a uint64.
// asset.Balance must be changed in a way to accomodate this.
func (eth *ExchangeWallet) Balance() (*asset.Balance, error) {
eth.acctMtx.RLock()
defer eth.acctMtx.RUnlock()
bigbal, err := eth.node.balance(eth.ctx, eth.acct)
if err != nil {
return nil, err
Expand Down Expand Up @@ -334,21 +348,29 @@ func (*ExchangeWallet) Refund(coinID, contract dex.Bytes) (dex.Bytes, error) {

// Address returns an address for the exchange wallet.
func (eth *ExchangeWallet) Address() (string, error) {
return eth.acct.String(), nil
eth.acctMtx.RLock()
defer eth.acctMtx.RUnlock()
return eth.acct.Address.String(), nil
}

// Unlock unlocks the exchange wallet.
func (eth *ExchangeWallet) Unlock(pw string) error {
eth.acctMtx.RLock()
defer eth.acctMtx.RUnlock()
return eth.node.unlock(eth.ctx, pw, eth.acct)
}

// Lock locks the exchange wallet.
func (eth *ExchangeWallet) Lock() error {
eth.acctMtx.RLock()
defer eth.acctMtx.RUnlock()
return eth.node.lock(eth.ctx, eth.acct)
}

// Locked will be true if the wallet is currently locked.
func (eth *ExchangeWallet) Locked() bool {
eth.acctMtx.RLock()
defer eth.acctMtx.RUnlock()
locked, err := eth.node.locked(eth.ctx, eth.acct)
if err != nil {
eth.log.Errorf("unable to get locked status of account: %v", err)
Expand All @@ -373,8 +395,14 @@ func (*ExchangeWallet) PayFee(address string, regFee uint64) (asset.Coin, error)
//
// TODO: Value could be larger than a uint64. Deal with it...
func (eth *ExchangeWallet) Withdraw(addr string, value uint64) (asset.Coin, error) {
return nil, eth.node.sendToAddr(eth.ctx, eth.acct, common.HexToAddress(addr),
new(big.Int).SetUint64(value), new(big.Int).SetUint64(defaultGasFee))
eth.acctMtx.RLock()
defer eth.acctMtx.RUnlock()
_, err := eth.node.sendToAddr(eth.ctx, eth.acct, common.HexToAddress(addr),
big.NewInt(0).SetUint64(value), new(big.Int).SetUint64(defaultGasFee))
if err != nil {
return nil, err
}
return nil, nil
}

// ValidateSecret checks that the secret satisfies the contract.
Expand Down
42 changes: 31 additions & 11 deletions client/asset/eth/eth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import (
"testing"

"decred.org/dcrdex/dex"
"decred.org/dcrdex/server/asset/eth"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/node"
Expand All @@ -31,7 +33,7 @@ type testNode struct {
blkErr error
}

func (n *testNode) connect(ctx context.Context, node *node.Node) error {
func (n *testNode) connect(ctx context.Context, node *node.Node, addr common.Address) error {
return n.connectErr
}
func (n *testNode) shutdown() {}
Expand All @@ -41,29 +43,29 @@ func (n *testNode) bestBlockHash(ctx context.Context) (common.Hash, error) {
func (n *testNode) block(ctx context.Context, hash common.Hash) (*types.Block, error) {
return n.blk, n.blkErr
}
func (n *testNode) accounts(ctx context.Context) ([]common.Address, error) {
return nil, nil
func (n *testNode) accounts() []*accounts.Account {
return nil
}
func (n *testNode) balance(ctx context.Context, acct common.Address) (*big.Int, error) {
func (n *testNode) balance(ctx context.Context, acct *accounts.Account) (*big.Int, error) {
return nil, nil
}
func (n *testNode) sendToAddr(ctx context.Context, acct, addr common.Address, amt, gasFee *big.Int) error {
return nil
func (n *testNode) sendToAddr(ctx context.Context, acct *accounts.Account, addr common.Address, amt, gasFee *big.Int) (common.Hash, error) {
return common.Hash{}, nil
}
func (n *testNode) syncStatus(ctx context.Context) (bool, float32, error) {
return false, 0, nil
}
func (n *testNode) unlock(ctx context.Context, pw string, acct common.Address) error {
func (n *testNode) unlock(ctx context.Context, pw string, acct *accounts.Account) error {
return nil
}
func (n *testNode) lock(ctx context.Context, acct common.Address) error {
func (n *testNode) lock(ctx context.Context, acct *accounts.Account) error {
return nil
}
func (n *testNode) locked(ctx context.Context, acct common.Address) (bool, error) {
func (n *testNode) locked(ctx context.Context, acct *accounts.Account) (bool, error) {
return false, nil
}
func (n *testNode) importAccount(pw string, privKeyB []byte) (accounts.Account, error) {
return accounts.Account{}, nil
func (n *testNode) importAccount(pw string, privKeyB []byte) (*accounts.Account, error) {
return nil, nil
}
func (n *testNode) addPeer(ctx context.Context, peer string) error {
return nil
Expand All @@ -74,6 +76,24 @@ func (n *testNode) nodeInfo(ctx context.Context) (*p2p.NodeInfo, error) {
func (n *testNode) blockNumber(ctx context.Context) (uint64, error) {
return 0, nil
}
func (n *testNode) pendingTransactions(ctx context.Context) ([]*types.Transaction, error) {
return nil, nil
}
func (n *testNode) initiate(opts *bind.TransactOpts, netID int64, refundTimestamp int64, secretHash [32]byte, participant common.Address) (*types.Transaction, error) {
return nil, nil
}
func (n *testNode) redeem(opts *bind.TransactOpts, netID int64, secret, secretHash [32]byte) (*types.Transaction, error) {
return nil, nil
}
func (n *testNode) refund(opts *bind.TransactOpts, netID int64, secretHash [32]byte) (*types.Transaction, error) {
return nil, nil
}
func (n *testNode) swap(ctx context.Context, from *accounts.Account, secretHash [32]byte) (*eth.ETHSwapSwap, error) {
return nil, nil
}
func (n *testNode) transactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
return nil, nil
}

func TestLoadConfig(t *testing.T) {
tests := []struct {
Expand Down
38 changes: 7 additions & 31 deletions client/asset/eth/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,46 +20,22 @@ import (
"github.com/ethereum/go-ethereum/params"
)

// TODO: Store this in a file accessible by the harness and this.
const (
simnetGenesis = `{
"config": {
"chainId": 42,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"clique": {
"period": 1,
"epoch": 30000
}
},
"difficulty": "1",
"gasLimit": "12487783",
"extradata": "0x00000000000000000000000000000000000000000000000000000000000000009ebba10a6136607688ca4f27fab70e23938cd0270000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"alloc": {
"18d65fb8d60c1199bb1ad381be47aa692b482605": {
"balance": "11000000000000000000000"
},
"4f8ef3892b65ed7fc356ff473a2ef2ae5ec27a06": {
"balance": "11000000000000000000000"
},
"2b84C791b79Ee37De042AD2ffF1A253c3ce9bc27": {
"balance": "11000000000000000000000"
}
}
}`
maxPeers = 10
)

var simnetGenesis string

type nodeConfig struct {
net dex.Network
listenAddr, appDir string
}

// SetSimnetGenesis should be set before using on simnet.
func SetSimnetGenesis(sng string) {
simnetGenesis = sng
}

func runNode(cfg *nodeConfig) (*node.Node, error) {
stackConf := &node.Config{DataDir: cfg.appDir}

Expand Down
Loading

0 comments on commit bc9890b

Please sign in to comment.