Skip to content

Commit

Permalink
estimate gas if missing in sending tx using upstream (#346)
Browse files Browse the repository at this point in the history
I added a call to eth_estimateGas in case gas param is missing when using the upstream. This is a little bit ugly because I create an anonymous struct to match eth_call params. I spotted this struct in go-ethereum but I don't see it's exported.
  • Loading branch information
adambabik authored and tiabc committed Sep 19, 2017
1 parent 9d01f7a commit ba963cc
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 2 deletions.
53 changes: 53 additions & 0 deletions geth/api/backend_txqueue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,59 @@ func (s *BackendTestSuite) TestSendEtherTx() {
s.Zero(s.backend.TxQueueManager().TransactionQueue().Count(), "tx queue must be empty at this point")
}

func (s *BackendTestSuite) TestSendEtherTxUpstream() {
s.StartTestBackend(params.RopstenNetworkID, WithUpstream("https://ropsten.infura.io/z6GCTmjdP3FETEJmMBI4"))
defer s.StopTestBackend()

time.Sleep(TestConfig.Node.SyncSeconds * time.Second) // allow to sync

err := s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
s.NoError(err)

completeQueuedTransaction := make(chan struct{})

// replace transaction notification handler
var txHash = gethcommon.Hash{}
node.SetDefaultNodeNotificationHandler(func(jsonEvent string) { // nolint: dupl
var envelope node.SignalEnvelope
err := json.Unmarshal([]byte(jsonEvent), &envelope)
s.NoError(err, "cannot unmarshal JSON: %s", jsonEvent)

if envelope.Type == node.EventTransactionQueued {
event := envelope.Event.(map[string]interface{})
log.Info("transaction queued (will be completed shortly)", "id", event["id"].(string))

txHash, err = s.backend.CompleteTransaction(
common.QueuedTxID(event["id"].(string)),
TestConfig.Account1.Password,
)
s.NoError(err, "cannot complete queued transaction[%v]", event["id"])

log.Info("contract transaction complete", "URL", "https://ropsten.etherscan.io/tx/"+txHash.Hex())
close(completeQueuedTransaction)
}
})

// This call blocks, up until Complete Transaction is called.
// Explicitly not setting Gas to get it estimated.
txHashCheck, err := s.backend.SendTransaction(nil, common.SendTxArgs{
From: common.FromAddress(TestConfig.Account1.Address),
To: common.ToAddress(TestConfig.Account2.Address),
GasPrice: (*hexutil.Big)(big.NewInt(28000000000)),
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
})
s.NoError(err, "cannot send transaction")

select {
case <-completeQueuedTransaction:
case <-time.After(1 * time.Minute):
s.FailNow("completing transaction timed out")
}

s.Equal(txHash.Hex(), txHashCheck.Hex(), "transaction hash returned from SendTransaction is invalid")
s.Zero(s.backend.TxQueueManager().TransactionQueue().Count(), "tx queue must be empty at this point")
}

func (s *BackendTestSuite) TestDoubleCompleteQueuedTransactions() {
require := s.Require()
require.NotNil(s.backend)
Expand Down
56 changes: 54 additions & 2 deletions geth/node/txqueue_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,12 +227,16 @@ func (m *TxQueueManager) completeRemoteTransaction(queuedTx *common.QueuedTx, pa

chainID := big.NewInt(int64(config.NetworkID))
nonce := uint64(txCount)
gas := (*big.Int)(queuedTx.Args.Gas)
gasPrice := (*big.Int)(queuedTx.Args.GasPrice)
dataVal := []byte(queuedTx.Args.Data)
priceVal := (*big.Int)(queuedTx.Args.Value)

tx := types.NewTransaction(nonce, *queuedTx.Args.To, priceVal, gas, gasPrice, dataVal)
gas, err := m.estimateGas(queuedTx.Args)
if err != nil {
return emptyHash, err
}

tx := types.NewTransaction(nonce, *queuedTx.Args.To, priceVal, (*big.Int)(gas), gasPrice, dataVal)
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), selectedAcct.AccountKey.PrivateKey)
if err != nil {
return emptyHash, err
Expand All @@ -253,6 +257,54 @@ func (m *TxQueueManager) completeRemoteTransaction(queuedTx *common.QueuedTx, pa
return signedTx.Hash(), nil
}

func (m *TxQueueManager) estimateGas(args common.SendTxArgs) (*hexutil.Big, error) {
if args.Gas != nil {
return args.Gas, nil
}

client := m.nodeManager.RPCClient()
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

var gasPrice hexutil.Big
if args.GasPrice != nil {
gasPrice = (hexutil.Big)(*args.GasPrice)
}

var value hexutil.Big
if args.Value != nil {
value = (hexutil.Big)(*args.Value)
}

params := struct {
From gethcommon.Address `json:"from"`
To *gethcommon.Address `json:"to"`
Gas hexutil.Big `json:"gas"`
GasPrice hexutil.Big `json:"gasPrice"`
Value hexutil.Big `json:"value"`
Data hexutil.Bytes `json:"data"`
}{
From: args.From,
To: args.To,
GasPrice: gasPrice,
Value: value,
Data: []byte(args.Data),
}

var estimatedGas hexutil.Big
if err := client.CallContext(
ctx,
&estimatedGas,
"eth_estimateGas",
params,
); err != nil {
log.Warn("failed to estimate gas", "err", err)
return nil, err
}

return &estimatedGas, nil
}

// CompleteTransactions instructs backend to complete sending of multiple transactions
func (m *TxQueueManager) CompleteTransactions(ids []common.QueuedTxID, password string) map[common.QueuedTxID]common.RawCompleteTransactionResult {
results := make(map[common.QueuedTxID]common.RawCompleteTransactionResult)
Expand Down

0 comments on commit ba963cc

Please sign in to comment.