From 665b12721b097acbdf434a022c8d754175d04a37 Mon Sep 17 00:00:00 2001 From: Dimitri Roche Date: Thu, 2 May 2019 15:32:58 -0400 Subject: [PATCH] intro TxReceiptStatus for statuses defined by EIP658 https://github.com/ethereum/EIPs/pull/658 --- core/adapters/eth_tx_test.go | 6 ++-- core/store/eth_client.go | 36 +++++++++++++++++-- core/store/eth_client_test.go | 66 +++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 5 deletions(-) diff --git a/core/adapters/eth_tx_test.go b/core/adapters/eth_tx_test.go index a2fbdbb009b..983e41c3dc0 100644 --- a/core/adapters/eth_tx_test.go +++ b/core/adapters/eth_tx_test.go @@ -297,7 +297,7 @@ func TestEthTxAdapter_Perform_FromPendingConfirmations_ConfirmCompletes(t *testi ethMock := app.MockEthClient() ethMock.Register("eth_getTransactionReceipt", strpkg.TxReceipt{}) confirmedHash := cltest.NewHash() - receipt := strpkg.TxReceipt{Hash: confirmedHash, BlockNumber: cltest.Int(sentAt)} + receipt := strpkg.TxReceipt{Hash: confirmedHash, BlockNumber: cltest.Int(sentAt), Status: strpkg.TxReceiptSuccess} ethMock.Register("eth_getTransactionReceipt", receipt) confirmedAt := sentAt + config.MinOutgoingConfirmations() - 1 // confirmations are 0-based idx ethMock.Register("eth_blockNumber", utils.Uint64ToHex(confirmedAt)) @@ -352,7 +352,7 @@ func TestEthTxAdapter_Perform_AppendingTransactionReceipts(t *testing.T) { sentAt := uint64(23456) ethMock := app.MockEthClient() - receipt := strpkg.TxReceipt{Hash: cltest.NewHash(), BlockNumber: cltest.Int(sentAt)} + receipt := strpkg.TxReceipt{Hash: cltest.NewHash(), BlockNumber: cltest.Int(sentAt), Status: strpkg.TxReceiptSuccess} ethMock.Register("eth_getTransactionReceipt", receipt) confirmedAt := sentAt + config.MinOutgoingConfirmations() - 1 // confirmations are 0-based idx ethMock.Register("eth_blockNumber", utils.Uint64ToHex(confirmedAt)) @@ -368,7 +368,7 @@ func TestEthTxAdapter_Perform_AppendingTransactionReceipts(t *testing.T) { input := sentResult input.MarkPendingConfirmations() - previousReceipt := strpkg.TxReceipt{Hash: cltest.NewHash(), BlockNumber: cltest.Int(sentAt - 10)} + previousReceipt := strpkg.TxReceipt{Hash: cltest.NewHash(), BlockNumber: cltest.Int(sentAt - 10), Status: strpkg.TxReceiptSuccess} input.Add("ethereumReceipts", []strpkg.TxReceipt{previousReceipt}) output := adapter.Perform(input, store) diff --git a/core/store/eth_client.go b/core/store/eth_client.go index e6bd95986ef..3ed7eafc76a 100644 --- a/core/store/eth_client.go +++ b/core/store/eth_client.go @@ -2,6 +2,7 @@ package store import ( "context" + "fmt" "math/big" ethereum "github.com/ethereum/go-ethereum" @@ -157,8 +158,9 @@ func (eth *EthClient) SubscribeToNewHeads( // TxReceipt holds the block number and the transaction hash of a signed // transaction that has been written to the blockchain. type TxReceipt struct { - BlockNumber *models.Big `json:"blockNumber" gorm:"type:numeric"` - Hash common.Hash `json:"transactionHash"` + BlockNumber *models.Big `json:"blockNumber" gorm:"type:numeric"` + Hash common.Hash `json:"transactionHash"` + Status TxReceiptStatus `json:"status"` } var emptyHash = common.Hash{} @@ -167,3 +169,33 @@ var emptyHash = common.Hash{} func (txr *TxReceipt) Unconfirmed() bool { return txr.Hash == emptyHash || txr.BlockNumber == nil } + +// TxReceiptStatus encapsulates the different return values available +// for an ethereum transaction receipt as defined by +// http://eips.ethereum.org/EIPS/eip-658 +type TxReceiptStatus string + +const ( + // TxReceiptRevert represents the status that results in any failure and + // resulting revert. + TxReceiptRevert TxReceiptStatus = "0x0" + // TxReceiptSuccess represents a successful transaction, not resulting in a + // revert. + TxReceiptSuccess = "0x1" +) + +// UnmarshalText parses text input to a predefined transaction receipt +// status as defined by Ethereum or an EIP. Returns an error if we encounter +// unexpected input. +func (trs *TxReceiptStatus) UnmarshalText(input []byte) error { + switch TxReceiptStatus(input) { + case TxReceiptRevert: + *trs = TxReceiptRevert + case TxReceiptSuccess: + *trs = TxReceiptSuccess + default: + return fmt.Errorf("unable to convert %s to %T", string(input), trs) + } + + return nil +} diff --git a/core/store/eth_client_test.go b/core/store/eth_client_test.go index 6b712830bca..a8ce36dbefa 100644 --- a/core/store/eth_client_test.go +++ b/core/store/eth_client_test.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink/core/internal/cltest" + "github.com/smartcontractkit/chainlink/core/store" strpkg "github.com/smartcontractkit/chainlink/core/store" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -124,3 +125,68 @@ func TestEthClient_GetERC20Balance(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expected, result) } + +func TestTxReceiptStatus_UnmarshalText_Success(t *testing.T) { + t.Parallel() + + tests := []struct { + input string + expected store.TxReceiptStatus + }{ + {"0x0", store.TxReceiptRevert}, + {"0x1", store.TxReceiptSuccess}, + } + + for _, test := range tests { + t.Run(test.input, func(t *testing.T) { + var ptrs store.TxReceiptStatus + err := ptrs.UnmarshalText([]byte(test.input)) + assert.NoError(t, err) + assert.Equal(t, test.expected, ptrs) + }) + } +} + +func TestTxReceiptStatus_UnmarshalText_Error(t *testing.T) { + t.Parallel() + + tests := []struct { + input string + }{ + {""}, + {"0x"}, + } + + for _, test := range tests { + t.Run(test.input, func(t *testing.T) { + var ptrs store.TxReceiptStatus + err := ptrs.UnmarshalText([]byte(test.input)) + assert.Error(t, err) + }) + } +} + +func TestTxReceiptStatus_UnmarshalJSON_Success(t *testing.T) { + t.Parallel() + + type subject struct { + Status store.TxReceiptStatus `json:"status"` + } + + tests := []struct { + input string + expected store.TxReceiptStatus + }{ + {`{"status": "0x0"}`, store.TxReceiptRevert}, + {`{"status": "0x1"}`, store.TxReceiptSuccess}, + } + + for _, test := range tests { + t.Run(test.input, func(t *testing.T) { + var dst subject + err := json.Unmarshal([]byte(test.input), &dst) + require.NoError(t, err) + assert.Equal(t, test.expected, dst.Status) + }) + } +}