github.com/ChainSafe/chainbridge-core@v1.4.2/chains/evm/calls/transactor/itx/itx.go (about)

     1  package itx
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math/big"
     7  
     8  	"github.com/ChainSafe/chainbridge-core/chains/evm/calls/transactor"
     9  	"github.com/ethereum/go-ethereum/accounts/abi"
    10  	"github.com/ethereum/go-ethereum/common"
    11  	"github.com/ethereum/go-ethereum/common/hexutil"
    12  	"github.com/ethereum/go-ethereum/crypto"
    13  )
    14  
    15  var (
    16  	DefaultTransactionOptions = transactor.TransactOptions{
    17  		GasLimit: 400000,
    18  		GasPrice: big.NewInt(1),
    19  		Priority: 1, // slow
    20  		Value:    big.NewInt(0),
    21  	}
    22  )
    23  
    24  // itx only supports priority as string - we use uint8 to save on data
    25  // currently we support only "slow" and "fast" - that's why 2 (medium) defaults to slow
    26  var ItxTxPriorities = map[uint8]string{
    27  	1: "slow",
    28  	2: "slow",
    29  	3: "fast",
    30  }
    31  
    32  type RelayTx struct {
    33  	to   common.Address
    34  	data []byte
    35  	opts transactor.TransactOptions
    36  }
    37  
    38  type SignedRelayTx struct {
    39  	*RelayTx
    40  	txID common.Hash
    41  	sig  []byte
    42  }
    43  
    44  type RelayCaller interface {
    45  	CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error
    46  }
    47  
    48  type Forwarder interface {
    49  	ForwarderAddress() common.Address
    50  	ChainId() *big.Int
    51  	UnsafeNonce() (*big.Int, error)
    52  	UnsafeIncreaseNonce()
    53  	LockNonce()
    54  	UnlockNonce()
    55  	ForwarderData(to *common.Address, data []byte, opts transactor.TransactOptions) ([]byte, error)
    56  }
    57  
    58  type Signer interface {
    59  	CommonAddress() common.Address
    60  	Sign(digestHash []byte) ([]byte, error)
    61  }
    62  
    63  type ITXTransactor struct {
    64  	forwarder   Forwarder
    65  	relayCaller RelayCaller
    66  	signer      Signer
    67  }
    68  
    69  func NewITXTransactor(relayCaller RelayCaller, forwarder Forwarder, signer Signer) *ITXTransactor {
    70  	return &ITXTransactor{
    71  		relayCaller: relayCaller,
    72  		forwarder:   forwarder,
    73  		signer:      signer,
    74  	}
    75  }
    76  
    77  // Transact packs tx into a forwarded transaction, signs it and sends the relayed transaction to Infura ITX
    78  func (itx *ITXTransactor) Transact(to *common.Address, data []byte, opts transactor.TransactOptions) (*common.Hash, error) {
    79  	err := transactor.MergeTransactionOptions(&opts, &DefaultTransactionOptions)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	opts.ChainID = itx.forwarder.ChainId()
    84  
    85  	defer itx.forwarder.UnlockNonce()
    86  	itx.forwarder.LockNonce()
    87  
    88  	nonce, err := itx.forwarder.UnsafeNonce()
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	opts.Nonce = nonce
    93  
    94  	forwarderData, err := itx.forwarder.ForwarderData(to, data, opts)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	// increase gas limit because of forwarder overhead
   100  	opts.GasLimit = opts.GasLimit * 11 / 10
   101  	signedTx, err := itx.signRelayTx(&RelayTx{
   102  		to:   itx.forwarder.ForwarderAddress(),
   103  		data: forwarderData,
   104  		opts: opts,
   105  	})
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  
   110  	h, err := itx.sendTransaction(context.Background(), signedTx)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	itx.forwarder.UnsafeIncreaseNonce()
   116  	return &h, nil
   117  }
   118  
   119  func (itx *ITXTransactor) signRelayTx(tx *RelayTx) (*SignedRelayTx, error) {
   120  	uint256Type, _ := abi.NewType("uint256", "uint256", nil)
   121  	addressType, _ := abi.NewType("address", "address", nil)
   122  	bytesType, _ := abi.NewType("bytes", "bytes", nil)
   123  	stringType, _ := abi.NewType("string", "string", nil)
   124  	arguments := abi.Arguments{
   125  		{Type: addressType},
   126  		{Type: bytesType},
   127  		{Type: uint256Type},
   128  		{Type: uint256Type},
   129  		{Type: stringType},
   130  	}
   131  	packed, err := arguments.Pack(
   132  		tx.to,
   133  		tx.data,
   134  		big.NewInt(int64(tx.opts.GasLimit)),
   135  		tx.opts.ChainID,
   136  		ItxTxPriorities[tx.opts.Priority],
   137  	)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	txID := crypto.Keccak256Hash(packed)
   143  	msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(txID), string(txID.Bytes()))
   144  	hash := crypto.Keccak256Hash([]byte(msg))
   145  	sig, err := itx.signer.Sign(hash.Bytes())
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	return &SignedRelayTx{
   151  		RelayTx: tx,
   152  		sig:     sig,
   153  		txID:    txID,
   154  	}, nil
   155  }
   156  
   157  func (itx *ITXTransactor) sendTransaction(ctx context.Context, signedTx *SignedRelayTx) (common.Hash, error) {
   158  	sig := "0x" + common.Bytes2Hex(signedTx.sig)
   159  	txArg := map[string]interface{}{
   160  		"to":       &signedTx.to,
   161  		"data":     "0x" + common.Bytes2Hex(signedTx.data),
   162  		"gas":      fmt.Sprint(signedTx.opts.GasLimit),
   163  		"schedule": ItxTxPriorities[signedTx.opts.Priority],
   164  	}
   165  
   166  	resp := struct {
   167  		RelayTransactionHash hexutil.Bytes
   168  	}{}
   169  	err := itx.relayCaller.CallContext(ctx, &resp, "relay_sendTransaction", txArg, sig)
   170  	if err != nil {
   171  		return common.Hash{}, err
   172  	}
   173  
   174  	return common.HexToHash(resp.RelayTransactionHash.String()), nil
   175  }