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 }