diff --git a/.travis.yml b/.travis.yml index aa3c5ced23..d7b8580d8c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ install: # /home/travis/gopath/src/github.com/harmony-one/harmony # https://docs.travis-ci.com/user/languages/go/#go-import-path - echo $TRAVIS_PULL_REQUEST_BRANCH - - TEST_REPO_BRANCH="master" + - TEST_REPO_BRANCH="feature/effective-gas-price" - git clone https://github.com/harmony-one/mcl.git $GOPATH/src/github.com/harmony-one/mcl - git clone https://github.com/harmony-one/bls.git $GOPATH/src/github.com/harmony-one/bls - git clone --branch $TEST_REPO_BRANCH https://github.com/harmony-one/harmony-test.git $GOPATH/src/github.com/harmony-one/harmony-test diff --git a/core/state_processor.go b/core/state_processor.go index fb7290c077..9601357813 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -334,6 +334,7 @@ func ApplyTransaction(bc ChainContext, author *common.Address, gp *GasPool, stat receipt := types.NewReceipt(root, failedExe, *usedGas) receipt.TxHash = tx.Hash() receipt.GasUsed = result.UsedGas + receipt.EffectiveGasPrice = tx.EffectiveGasPrice(big.NewInt(0), nil) // if the transaction created a contract, store the creation address in the receipt. if msg.To() == nil { receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce()) diff --git a/core/types/receipt.go b/core/types/receipt.go index d0a6c6dc76..6207332d4e 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -20,6 +20,7 @@ import ( "bytes" "fmt" "io" + "math/big" "unsafe" "github.com/ethereum/go-ethereum/common" @@ -54,9 +55,10 @@ type Receipt struct { Logs []*Log `json:"logs" gencodec:"required"` // Implementation fields (don't reorder!) - TxHash common.Hash `json:"transactionHash" gencodec:"required"` - ContractAddress common.Address `json:"contractAddress"` - GasUsed uint64 `json:"gasUsed" gencodec:"required"` + TxHash common.Hash `json:"transactionHash" gencodec:"required"` + ContractAddress common.Address `json:"contractAddress"` + GasUsed uint64 `json:"gasUsed" gencodec:"required"` + EffectiveGasPrice *big.Int `json:"effectiveGasPrice"` // required, but tag omitted for backwards compatibility } type receiptMarshaling struct { @@ -82,6 +84,7 @@ type receiptStorageRLP struct { ContractAddress common.Address Logs []*LogForStorage GasUsed uint64 + EffectiveGasPrice *big.Int `rlp:"optional"` } // NewReceipt creates a barebone transaction receipt, copying the init fields. @@ -166,6 +169,7 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error { ContractAddress: r.ContractAddress, Logs: make([]*LogForStorage, len(r.Logs)), GasUsed: r.GasUsed, + EffectiveGasPrice: r.EffectiveGasPrice, } for i, log := range r.Logs { enc.Logs[i] = (*LogForStorage)(log) @@ -191,6 +195,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { } // Assign the implementation fields r.TxHash, r.ContractAddress, r.GasUsed = dec.TxHash, dec.ContractAddress, dec.GasUsed + r.EffectiveGasPrice = dec.EffectiveGasPrice return nil } diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index 5a8891cf44..514546e032 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -1,13 +1,15 @@ package types import ( + "math/big" "reflect" "testing" ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" "github.com/harmony-one/harmony/staking" + "github.com/stretchr/testify/require" ) func TestFindLogsWithTopic(t *testing.T) { @@ -112,3 +114,67 @@ func TestFindLogsWithTopic(t *testing.T) { } } } + +// Test we can still parse receipt without EffectiveGasPrice for backwards compatibility, even +// though it is required per the spec. +func TestEffectiveGasPriceNotRequired(t *testing.T) { + r := &Receipt{ + Status: ReceiptStatusFailed, + CumulativeGasUsed: 1, + Logs: []*Log{}, + // derived fields: + TxHash: ethcommon.BytesToHash([]byte{0x03, 0x14}), + ContractAddress: ethcommon.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"), + GasUsed: 1, + } + + r.EffectiveGasPrice = nil + b, err := r.MarshalJSON() + if err != nil { + t.Fatal("error marshaling receipt to json:", err) + } + r2 := Receipt{} + err = r2.UnmarshalJSON(b) + if err != nil { + t.Fatal("error unmarshalling receipt from json:", err) + } +} + +func TestReceiptEncDec(t *testing.T) { + r := ReceiptForStorage(Receipt{ + Status: ReceiptStatusFailed, + CumulativeGasUsed: 1, + Logs: []*Log{}, + // derived fields: + TxHash: ethcommon.BytesToHash([]byte{0x03, 0x14}), + ContractAddress: ethcommon.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"), + GasUsed: 1, + EffectiveGasPrice: big.NewInt(1), + }) + + bytes, err := rlp.EncodeToBytes(&r) + if err != nil { + t.Fatal("error encoding receipt to bytes:", err) + } + + r2 := ReceiptForStorage{} + err = rlp.DecodeBytes(bytes, &r2) + if err != nil { + t.Fatal("error decoding receipt from bytes:", err) + } + + require.Equal(t, r, r2) +} + +func TestReceiptDecodeEmptyEffectiveGasPrice(t *testing.T) { + r := ReceiptForStorage(Receipt{}) + + bytes, err := rlp.EncodeToBytes(&r) + require.NoError(t, err, "error encoding receipt to bytes") + + r2 := ReceiptForStorage{} + err = rlp.DecodeBytes(bytes, &r2) + require.NoError(t, err, "error decoding receipt from bytes") + + require.EqualValues(t, r.EffectiveGasPrice, r2.EffectiveGasPrice) +} diff --git a/core/types/transaction.go b/core/types/transaction.go index bee69a393f..590c99b8b8 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -178,6 +178,10 @@ func (d *txdata) CopyFrom(d2 *txdata) { d.Hash = copyHash(d2.Hash) } +func (d *txdata) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { + return dst.Set(d.Price) +} + type txdataMarshaling struct { AccountNonce hexutil.Uint64 Price *hexutil.Big @@ -532,6 +536,11 @@ func (tx *Transaction) SenderAddress() (common.Address, error) { return addr, nil } +// EffectiveGasPrice returns the effective gas price of the transaction. +func (tx *Transaction) EffectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { + return tx.data.effectiveGasPrice(dst, baseFee) +} + // TxByNonce implements the sort interface to allow sorting a list of transactions // by their nonces. This is usually only useful for sorting transactions from a // single account, otherwise a nonce comparison doesn't make much sense. diff --git a/rpc/harmony/eth/types.go b/rpc/harmony/eth/types.go index a319a8fc12..120129f45d 100644 --- a/rpc/harmony/eth/types.go +++ b/rpc/harmony/eth/types.go @@ -152,6 +152,7 @@ func NewReceipt(senderAddr common.Address, tx *types.EthTransaction, blockHash c "contractAddress": nil, "logs": receipt.Logs, "logsBloom": receipt.Bloom, + "effectiveGasPrice": hexutil.Big(*receipt.EffectiveGasPrice), } // Assign receipt status or post state. diff --git a/rpc/harmony/v1/types.go b/rpc/harmony/v1/types.go index c297cfbe7e..0a8e02acfb 100644 --- a/rpc/harmony/v1/types.go +++ b/rpc/harmony/v1/types.go @@ -188,6 +188,7 @@ type TxReceipt struct { To string `json:"to"` Root hexutil.Bytes `json:"root"` Status hexutil.Uint `json:"status"` + EffectiveGasPrice hexutil.Big `json:"effectiveGasPrice"` } // StakingTxReceipt represents a staking transaction receipt that will serialize to the RPC representation. @@ -359,6 +360,7 @@ func NewTxReceipt( To: receiver, Root: receipt.PostState, Status: hexutil.Uint(receipt.Status), + EffectiveGasPrice: hexutil.Big(*receipt.EffectiveGasPrice), } // Set empty array for empty logs diff --git a/rpc/harmony/v2/types.go b/rpc/harmony/v2/types.go index c9abbead05..003a690861 100644 --- a/rpc/harmony/v2/types.go +++ b/rpc/harmony/v2/types.go @@ -218,6 +218,7 @@ type TxReceipt struct { To string `json:"to"` Root hexutil.Bytes `json:"root"` Status uint `json:"status"` + EffectiveGasPrice hexutil.Big `json:"effectiveGasPrice"` } // StakingTxReceipt represents a staking transaction receipt that will serialize to the RPC representation. @@ -388,6 +389,7 @@ func NewTxReceipt( To: receiver, Root: receipt.PostState, Status: uint(receipt.Status), + EffectiveGasPrice: hexutil.Big(*receipt.EffectiveGasPrice), } // Set optionals