add --gas-price, --gas-limit flags, net_setGasPrice RPC (#55)

This commit is contained in:
noot
2021-12-06 22:00:55 -05:00
committed by GitHub
parent d9bd457465
commit acee21ce19
15 changed files with 119 additions and 143 deletions

View File

@@ -55,7 +55,7 @@ func (a *alice) initiate(providesAmount common.EtherAmount, desiredAmount common
return errors.New("protocol already in progress")
}
balance, err := a.ethClient.BalanceAt(a.ctx, a.auth.From, nil)
balance, err := a.ethClient.BalanceAt(a.ctx, a.callOpts.From, nil)
if err != nil {
return err
}
@@ -65,7 +65,11 @@ func (a *alice) initiate(providesAmount common.EtherAmount, desiredAmount common
return errors.New("balance lower than amount to be provided")
}
a.swapState = newSwapState(a, providesAmount, desiredAmount)
a.swapState, err = newSwapState(a, providesAmount, desiredAmount)
if err != nil {
return err
}
log.Info(color.New(color.Bold).Sprintf("**initiated swap with ID=%d**", a.swapState.id))
log.Info(color.New(color.Bold).Sprint("DO NOT EXIT THIS PROCESS OR FUNDS MAY BE LOST!"))
return nil

View File

@@ -41,8 +41,10 @@ type alice struct {
ethPrivKey *ecdsa.PrivateKey
ethClient *ethclient.Client
auth *bind.TransactOpts
callOpts *bind.CallOpts
chainID *big.Int
gasPrice *big.Int
gasLimit uint64
net net.MessageSender
@@ -59,6 +61,8 @@ type Config struct {
EthereumPrivateKey string
Environment common.Environment
ChainID int64
GasPrice *big.Int
GasLimit uint64
}
// NewAlice returns a new instance of Alice.
@@ -75,11 +79,6 @@ func NewAlice(cfg *Config) (*alice, error) {
return nil, err
}
auth, err := bind.NewKeyedTransactorWithChainID(pk, big.NewInt(cfg.ChainID))
if err != nil {
return nil, err
}
// TODO: add --gas-limit flag and default params for L2
// auth.GasLimit = 35323600
// auth.GasPrice = big.NewInt(2000000000)
@@ -95,11 +94,11 @@ func NewAlice(cfg *Config) (*alice, error) {
ethPrivKey: pk,
ethClient: ec,
client: monero.NewClient(cfg.MoneroWalletEndpoint),
auth: auth,
callOpts: &bind.CallOpts{
From: crypto.PubkeyToAddress(*pub),
Context: cfg.Ctx,
},
chainID: big.NewInt(cfg.ChainID),
}, nil
}
@@ -107,6 +106,10 @@ func (a *alice) SetMessageSender(n net.MessageSender) {
a.net = n
}
func (a *alice) SetGasPrice(gasPrice uint64) {
a.gasPrice = big.NewInt(0).SetUint64(gasPrice)
}
// generateKeys generates Alice's monero spend and view keys (S_b, V_b)
// It returns Alice's public spend key
func (s *swapState) generateKeys() (*monero.PublicKeyPair, error) {
@@ -154,12 +157,12 @@ func (s *swapState) deployAndLockETH(amount common.EtherAmount) (ethcommon.Addre
copy(pkb[:], common.Reverse(pkBob))
// TODO: put auth in swapState
s.alice.auth.Value = amount.BigInt()
s.txOpts.Value = amount.BigInt()
defer func() {
s.alice.auth.Value = nil
s.txOpts.Value = nil
}()
address, tx, swap, err := swap.DeploySwap(s.alice.auth, s.alice.ethClient, pkb, pka, s.bobAddress, defaultTimeoutDuration)
address, tx, swap, err := swap.DeploySwap(s.txOpts, s.alice.ethClient, pkb, pka, s.bobAddress, defaultTimeoutDuration)
if err != nil {
return ethcommon.Address{}, fmt.Errorf("failed to deploy Swap.sol: %w", err)
}
@@ -184,7 +187,7 @@ func (s *swapState) deployAndLockETH(amount common.EtherAmount) (ethcommon.Addre
// call Claim(). Ready() should only be called once Alice sees Bob lock his XMR.
// If time t_0 has passed, there is no point of calling Ready().
func (s *swapState) ready() error {
tx, err := s.contract.SetReady(s.alice.auth)
tx, err := s.contract.SetReady(s.txOpts)
if err != nil {
return err
}
@@ -267,7 +270,7 @@ func (s *swapState) refund() (string, error) {
}
log.Infof("attempting to call Refund()...")
tx, err := s.contract.Refund(s.alice.auth, sc)
tx, err := s.contract.Refund(s.txOpts, sc)
if err != nil {
return "", err
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/noot/atomic-swap/net"
"github.com/noot/atomic-swap/swap-contract"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/fatih/color"
)
@@ -48,6 +49,7 @@ type swapState struct {
// swap contract and timeouts in it; set once contract is deployed
contract *swap.Swap
t0, t1 time.Time
txOpts *bind.TransactOpts
// next expected network message
nextExpectedMessage net.Message // TODO: change to type?
@@ -60,7 +62,15 @@ type swapState struct {
success bool
}
func newSwapState(a *alice, providesAmount common.EtherAmount, desiredAmount common.MoneroAmount) *swapState {
func newSwapState(a *alice, providesAmount common.EtherAmount, desiredAmount common.MoneroAmount) (*swapState, error) {
txOpts, err := bind.NewKeyedTransactorWithChainID(a.ethPrivKey, a.chainID)
if err != nil {
return nil, err
}
txOpts.GasPrice = a.gasPrice
txOpts.GasLimit = a.gasLimit
ctx, cancel := context.WithCancel(a.ctx)
s := &swapState{
@@ -70,13 +80,14 @@ func newSwapState(a *alice, providesAmount common.EtherAmount, desiredAmount com
id: nextID,
providesAmount: providesAmount,
desiredAmount: desiredAmount,
txOpts: txOpts,
nextExpectedMessage: &net.SendKeysMessage{},
xmrLockedCh: make(chan struct{}),
claimedCh: make(chan struct{}),
}
nextID++
return s
return s, nil
}
func (s *swapState) SendKeysMessage() (*net.SendKeysMessage, error) {

View File

@@ -41,7 +41,8 @@ func newTestAlice(t *testing.T) (*alice, *swapState) {
alice, err := NewAlice(cfg)
require.NoError(t, err)
swapState := newSwapState(alice, common.NewEtherAmount(1), common.MoneroAmount(1))
swapState, err := newSwapState(alice, common.NewEtherAmount(1), common.MoneroAmount(1))
require.NoError(t, err)
return alice, swapState
}
@@ -288,7 +289,7 @@ func TestSwapState_NotifyClaimed(t *testing.T) {
var sc [32]byte
copy(sc[:], common.Reverse(secret))
tx, err := s.contract.Claim(s.alice.auth, sc)
tx, err := s.contract.Claim(s.txOpts, sc)
require.NoError(t, err)
cmsg := &net.NotifyClaimed{

View File

@@ -40,7 +40,11 @@ func (b *bob) initiate(providesAmount common.MoneroAmount, desiredAmount common.
return errors.New("balance lower than amount to be provided")
}
b.swapState = newSwapState(b, providesAmount, desiredAmount)
b.swapState, err = newSwapState(b, providesAmount, desiredAmount)
if err != nil {
return err
}
log.Info(color.New(color.Bold).Sprintf("**initiated swap with ID=%d**", b.swapState.id))
log.Info(color.New(color.Bold).Sprint("DO NOT EXIT THIS PROCESS OR FUNDS MAY BE LOST!"))
return nil

View File

@@ -39,9 +39,11 @@ type bob struct {
ethClient *ethclient.Client
ethPrivKey *ecdsa.PrivateKey
auth *bind.TransactOpts
callOpts *bind.CallOpts
ethAddress ethcommon.Address
chainID *big.Int
gasPrice *big.Int
gasLimit uint64
net net.MessageSender
@@ -59,6 +61,8 @@ type Config struct {
EthereumPrivateKey string
Environment common.Environment
ChainID int64
GasPrice *big.Int
GasLimit uint64
}
// NewBob returns a new instance of Bob.
@@ -78,11 +82,6 @@ func NewBob(cfg *Config) (*bob, error) {
return nil, err
}
auth, err := bind.NewKeyedTransactorWithChainID(pk, big.NewInt(cfg.ChainID)) // ganache chainID
if err != nil {
return nil, err
}
// auth.GasLimit = 3027733
// auth.GasPrice = big.NewInt(2000000000)
@@ -112,12 +111,12 @@ func NewBob(cfg *Config) (*bob, error) {
walletPassword: cfg.WalletPassword,
ethClient: ec,
ethPrivKey: pk,
auth: auth,
callOpts: &bind.CallOpts{
From: addr,
Context: cfg.Ctx,
},
ethAddress: addr,
chainID: big.NewInt(cfg.ChainID),
}, nil
}
@@ -125,6 +124,10 @@ func (b *bob) SetMessageSender(n net.MessageSender) {
b.net = n
}
func (b *bob) SetGasPrice(gasPrice uint64) {
b.gasPrice = big.NewInt(0).SetUint64(gasPrice)
}
func (b *bob) openWallet() error { //nolint
return b.client.OpenWallet(b.walletFile, b.walletPassword)
}
@@ -327,7 +330,7 @@ func (s *swapState) claimFunds() (string, error) {
var sc [32]byte
copy(sc[:], common.Reverse(secret))
tx, err := s.contract.Claim(s.bob.auth, sc)
tx, err := s.contract.Claim(s.txOpts, sc)
if err != nil {
return "", err
}

View File

@@ -10,6 +10,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/fatih/color"
@@ -44,6 +45,7 @@ type swapState struct {
contract *swap.Swap
contractAddr ethcommon.Address
t0, t1 time.Time
txOpts *bind.TransactOpts
// Alice's keys for this session
alicePublicKeys *monero.PublicKeyPair
@@ -58,7 +60,15 @@ type swapState struct {
success bool
}
func newSwapState(b *bob, providesAmount common.MoneroAmount, desiredAmount common.EtherAmount) *swapState {
func newSwapState(b *bob, providesAmount common.MoneroAmount, desiredAmount common.EtherAmount) (*swapState, error) {
txOpts, err := bind.NewKeyedTransactorWithChainID(b.ethPrivKey, b.chainID)
if err != nil {
return nil, err
}
txOpts.GasPrice = b.gasPrice
txOpts.GasLimit = b.gasLimit
ctx, cancel := context.WithCancel(b.ctx)
s := &swapState{
@@ -70,10 +80,11 @@ func newSwapState(b *bob, providesAmount common.MoneroAmount, desiredAmount comm
desiredAmount: desiredAmount,
nextExpectedMessage: &net.SendKeysMessage{},
readyCh: make(chan struct{}),
txOpts: txOpts,
}
nextID++
return s
return s, nil
}
func (s *swapState) SendKeysMessage() (*net.SendKeysMessage, error) {
@@ -216,6 +227,7 @@ func (s *swapState) HandleProtocolMessage(msg net.Message) (net.Message, bool, e
txHash, err := s.claimFunds()
if err != nil {
log.Errorf("failed to claim: err=%s", err)
// TODO: retry claim, depending on error
return
}

View File

@@ -11,9 +11,9 @@ import (
"github.com/noot/atomic-swap/net"
"github.com/noot/atomic-swap/swap-contract"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
// "github.com/ethereum/go-ethereum/accounts/abi/bind"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
// "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
logging "github.com/ipfs/go-log"
"github.com/stretchr/testify/require"
@@ -57,7 +57,8 @@ func newTestBob(t *testing.T) (*bob, *swapState) {
_ = bob.daemonClient.GenerateBlocks(bobAddr.Address, 61)
swapState := newSwapState(bob, common.MoneroAmount(33), common.NewEtherAmount(33))
swapState, err := newSwapState(bob, common.MoneroAmount(33), common.NewEtherAmount(33))
require.NoError(t, err)
return bob, swapState
}
@@ -80,18 +81,18 @@ func TestSwapState_ClaimFunds(t *testing.T) {
conn, err := ethclient.Dial(common.DefaultEthEndpoint)
require.NoError(t, err)
pkBob, err := crypto.HexToECDSA(common.DefaultPrivKeyBob)
require.NoError(t, err)
// pkBob, err := crypto.HexToECsDSA(common.DefaultPrivKeyBob)
// require.NoError(t, err)
bob.auth, err = bind.NewKeyedTransactorWithChainID(pkBob, big.NewInt(common.GanacheChainID))
require.NoError(t, err)
// swapState.txOpts, err = bind.NewKeyedTransactorWithChainID(pkBob, big.NewInt(common.GanacheChainID))
// require.NoError(t, err)
var claimKey [32]byte
copy(claimKey[:], common.Reverse(swapState.privkeys.SpendKey().Public().Bytes()))
swapState.contractAddr, _, swapState.contract, err = swap.DeploySwap(bob.auth, conn, claimKey, [32]byte{}, bob.ethAddress, defaultTimeoutDuration)
swapState.contractAddr, _, swapState.contract, err = swap.DeploySwap(swapState.txOpts, conn, claimKey, [32]byte{}, bob.ethAddress, defaultTimeoutDuration)
require.NoError(t, err)
_, err = swapState.contract.SetReady(bob.auth)
_, err = swapState.contract.SetReady(swapState.txOpts)
require.NoError(t, err)
txHash, err := swapState.claimFunds()
@@ -130,7 +131,8 @@ func deploySwap(t *testing.T, bob *bob, swapState *swapState, refundKey [32]byte
var claimKey [32]byte
copy(claimKey[:], common.Reverse(swapState.privkeys.SpendKey().Public().Bytes()))
addr, _, contract, err := swap.DeploySwap(bob.auth, conn, claimKey, refundKey, bob.ethAddress, tm)
addr, _, contract, err := swap.DeploySwap(swapState.txOpts, conn, claimKey, refundKey, bob.ethAddress, tm)
require.NoError(t, err)
return addr, contract
}
@@ -223,7 +225,7 @@ func TestSwapState_HandleProtocolMessage_NotifyReady(t *testing.T) {
require.NoError(t, err)
_, s.contract = deploySwap(t, bob, s, [32]byte{}, duration)
_, err = s.contract.SetReady(bob.auth)
_, err = s.contract.SetReady(s.txOpts)
require.NoError(t, err)
msg := &net.NotifyReady{}
@@ -261,7 +263,7 @@ func TestSwapState_handleRefund(t *testing.T) {
var sc [32]byte
copy(sc[:], common.Reverse(secret))
tx, err := s.contract.Refund(s.bob.auth, sc)
tx, err := s.contract.Refund(s.txOpts, sc)
require.NoError(t, err)
addr, err := s.handleRefund(tx.Hash().String())
@@ -295,7 +297,7 @@ func TestSwapState_HandleProtocolMessage_NotifyRefund(t *testing.T) {
var sc [32]byte
copy(sc[:], common.Reverse(secret))
tx, err := s.contract.Refund(s.bob.auth, sc)
tx, err := s.contract.Refund(s.txOpts, sc)
require.NoError(t, err)
msg := &net.NotifyRefund{

View File

@@ -2,6 +2,7 @@ package client
import (
"encoding/json"
"fmt"
"github.com/noot/atomic-swap/common"
"github.com/noot/atomic-swap/rpc"
@@ -31,7 +32,7 @@ func (c *Client) Initiate(maddr string, provides common.ProvidesCoin, providesAm
}
if resp.Error != nil {
return false, resp.Error
return false, fmt.Errorf("failed to call net_initiate: %w", resp.Error)
}
var res *rpc.InitiateResponse

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"math/big"
"os"
"path/filepath"
"strings"
@@ -120,6 +121,14 @@ var (
Name: "bootnodes",
Usage: "comma-separated string of libp2p bootnodes",
},
&cli.UintFlag{
Name: "gas-price",
Usage: "ethereum gas price to use for transactions (in gwei). if not set, the gas price is set via oracle.",
},
&cli.UintFlag{
Name: "gas-limit",
Usage: "ethereum gas limit to use for transactions. if not set, the gas limit is estimated for each transaction.",
},
},
}
)
@@ -224,6 +233,13 @@ func runDaemon(c *cli.Context) error {
SetMessageSender(net.MessageSender)
}
var gasPrice *big.Int
if c.Uint("gas-price") != 0 {
gasPrice = big.NewInt(int64(c.Uint("gas-price")))
}
// TODO: add configs for different eth testnets + L2 and set gas limit based on those, if not set
var (
handler Handler
err error
@@ -238,6 +254,8 @@ func runDaemon(c *cli.Context) error {
EthereumPrivateKey: ethPrivKey,
Environment: env,
ChainID: chainID,
GasPrice: gasPrice,
GasLimit: uint64(c.Uint("gas-limit")),
}
handler, err = alice.NewAlice(aliceCfg)
@@ -264,6 +282,8 @@ func runDaemon(c *cli.Context) error {
EthereumPrivateKey: ethPrivKey,
Environment: env,
ChainID: chainID,
GasPrice: gasPrice,
GasLimit: uint64(c.Uint("gas-limit")),
}
handler, err = bob.NewBob(bobCfg)

View File

@@ -1,95 +0,0 @@
package monero
// import (
// "bytes"
// "context"
// "encoding/json"
// "fmt"
// "io/ioutil"
// "net"
// "net/http"
// "time"
// )
// var (
// contentTypeJSON = "application/json"
// dialTimeout = 60 * time.Second
// httpClientTimeout = 120 * time.Second
// transport = &http.Transport{
// Dial: (&net.Dialer{
// Timeout: dialTimeout,
// }).Dial,
// }
// httpClient = &http.Client{
// Transport: transport,
// Timeout: httpClientTimeout,
// }
// )
// // ServerResponse is the JSON format of a response
// type ServerResponse struct {
// // JSON-RPC Version
// Version string `json:"jsonrpc"`
// // Resulting values
// Result json.RawMessage `json:"result"`
// // Any generated errors
// Error *Error `json:"error"`
// // Request id
// ID *json.RawMessage `json:"id"`
// }
// // ErrCode is a int type used for the rpc error codes
// type ErrCode int
// // Error is a struct that holds the error message and the error code for a error
// type Error struct {
// Message string `json:"message"`
// ErrorCode ErrCode `json:"code"`
// Data map[string]interface{} `json:"data"`
// }
// // Error ...
// func (e *Error) Error() string {
// return fmt.Sprintf("message=%s; code=%d; data=%v", e.Message, e.ErrorCode, e.Data)
// }
// func postRPC(endpoint, method, params string) (*ServerResponse, error) {
// data := []byte(`{"jsonrpc":"2.0","method":"` + method + `","params":` + params + `,"id":0}`)
// buf := &bytes.Buffer{}
// _, err := buf.Write(data)
// if err != nil {
// return nil, err
// }
// r, err := http.NewRequest("POST", endpoint, buf)
// if err != nil {
// return nil, err
// }
// r.Header.Set("Content-Type", contentTypeJSON)
// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
// defer cancel()
// r = r.WithContext(ctx)
// resp, err := httpClient.Do(r)
// if err != nil {
// return nil, err
// }
// defer func() {
// _ = resp.Body.Close()
// }()
// body, err := ioutil.ReadAll(resp.Body)
// if err != nil {
// return nil, err
// }
// var sv *ServerResponse
// if err = json.Unmarshal(body, &sv); err != nil {
// return nil, err
// }
// return sv, nil
// }

View File

@@ -24,6 +24,7 @@ type Net interface {
type Protocol interface {
Provides() common.ProvidesCoin
InitiateProtocol(providesAmount, desiredAmount float64) (net.SwapState, error)
SetGasPrice(gasPrice uint64)
}
type NetService struct {
@@ -162,3 +163,12 @@ func (s *NetService) Initiate(_ *http.Request, req *InitiateRequest, resp *Initi
resp.Success = true
return nil
}
type SetGasPriceRequest struct {
GasPrice uint64
}
func (s *NetService) SetGasPrice(_ *http.Request, req *SetGasPriceRequest, _ *interface{}) error {
s.protocol.SetGasPrice(req.GasPrice)
return nil
}

View File

@@ -66,7 +66,7 @@ func PostRPC(endpoint, method, params string) (*ServerResponse, error) {
r, err := http.NewRequest("POST", endpoint, buf)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
}
r.Header.Set("Content-Type", contentTypeJSON)
@@ -76,7 +76,7 @@ func PostRPC(endpoint, method, params string) (*ServerResponse, error) {
resp, err := httpClient.Do(r)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to post request: %w", err)
}
defer func() {
@@ -85,7 +85,7 @@ func PostRPC(endpoint, method, params string) (*ServerResponse, error) {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to read response body: %w", err)
}
var sv *ServerResponse

View File

@@ -33,7 +33,7 @@ sleep 10
# run unit tests
echo "running unit tests..."
go test ./... -v -short
go test ./... -v -short -timeout=30m
OK=$?
# kill processes

View File

@@ -13,7 +13,7 @@ echo "starting monero-wallet-rpc on port 18084..."
# open Bob's wallet (must have funds)
sleep 5
curl http://localhost:1803/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"open_wallet","params":{"filename":"stagenet-wallet","password":""}}' -H 'Content-Type: application/json'
curl http://localhost:18083/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"open_wallet","params":{"filename":"stagenet-wallet","password":""}}' -H 'Content-Type: application/json'
# check balance
curl http://localhost:18083/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":get_accounts","params":{}}' -H 'Content-Type: application/json'