aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPéter Szilágyi <peterke@gmail.com>2017-07-17 16:34:53 +0800
committerFelix Lange <fjl@users.noreply.github.com>2017-07-17 16:34:53 +0800
commita56f3dc0d94417e5d88bbb32b124b0889dfcbd6a (patch)
treed9b19042938bbffcfd2afde26f81e33f47a629e3
parent47359301a24b3682b5bf9bbfae0cf8ae21a9ee62 (diff)
downloadgo-tangerine-a56f3dc0d94417e5d88bbb32b124b0889dfcbd6a.tar.gz
go-tangerine-a56f3dc0d94417e5d88bbb32b124b0889dfcbd6a.tar.zst
go-tangerine-a56f3dc0d94417e5d88bbb32b124b0889dfcbd6a.zip
core, ethclient: implement Metropolis EIP 98 (#14750)
Implements ethereum/EIPs#98
-rw-r--r--core/state_processor.go10
-rw-r--r--core/types/gen_receipt_json.go9
-rw-r--r--core/types/receipt.go75
-rw-r--r--ethclient/ethclient.go2
4 files changed, 76 insertions, 20 deletions
diff --git a/core/state_processor.go b/core/state_processor.go
index 90f5a4f60..4489cfce2 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -104,11 +104,17 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
}
// Update the state with pending changes
+ var root []byte
+ if config.IsMetropolis(header.Number) {
+ statedb.Finalise()
+ } else {
+ root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
+ }
usedGas.Add(usedGas, gas)
+
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
// based on the eip phase, we're passing wether the root touch-delete accounts.
- root := statedb.IntermediateRoot(config.IsEIP158(header.Number))
- receipt := types.NewReceipt(root.Bytes(), usedGas)
+ receipt := types.NewReceipt(root, usedGas)
receipt.TxHash = tx.Hash()
receipt.GasUsed = new(big.Int).Set(gas)
// if the transaction created a contract, store the creation address in the receipt.
diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go
index edbd64ba4..eb2e5d42b 100644
--- a/core/types/gen_receipt_json.go
+++ b/core/types/gen_receipt_json.go
@@ -13,7 +13,7 @@ import (
func (r Receipt) MarshalJSON() ([]byte, error) {
type Receipt struct {
- PostState hexutil.Bytes `json:"root" gencodec:"required"`
+ PostState hexutil.Bytes `json:"root"`
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed" gencodec:"required"`
Bloom Bloom `json:"logsBloom" gencodec:"required"`
Logs []*Log `json:"logs" gencodec:"required"`
@@ -34,7 +34,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
func (r *Receipt) UnmarshalJSON(input []byte) error {
type Receipt struct {
- PostState hexutil.Bytes `json:"root" gencodec:"required"`
+ PostState hexutil.Bytes `json:"root"`
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed" gencodec:"required"`
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
Logs []*Log `json:"logs" gencodec:"required"`
@@ -46,10 +46,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
- if dec.PostState == nil {
- return errors.New("missing required field 'root' for Receipt")
+ if dec.PostState != nil {
+ r.PostState = dec.PostState
}
- r.PostState = dec.PostState
if dec.CumulativeGasUsed == nil {
return errors.New("missing required field 'cumulativeGasUsed' for Receipt")
}
diff --git a/core/types/receipt.go b/core/types/receipt.go
index ef6f6a2bb..c9906b015 100644
--- a/core/types/receipt.go
+++ b/core/types/receipt.go
@@ -31,7 +31,7 @@ import (
// Receipt represents the results of a transaction.
type Receipt struct {
// Consensus fields
- PostState []byte `json:"root" gencodec:"required"`
+ PostState []byte `json:"root"`
CumulativeGasUsed *big.Int `json:"cumulativeGasUsed" gencodec:"required"`
Bloom Bloom `json:"logsBloom" gencodec:"required"`
Logs []*Log `json:"logs" gencodec:"required"`
@@ -48,35 +48,88 @@ type receiptMarshaling struct {
GasUsed *hexutil.Big
}
+// homesteadReceiptRLP contains the receipt's Homestead consensus fields, used
+// during RLP serialization.
+type homesteadReceiptRLP struct {
+ PostState []byte
+ CumulativeGasUsed *big.Int
+ Bloom Bloom
+ Logs []*Log
+}
+
+// metropolisReceiptRLP contains the receipt's Metropolis consensus fields, used
+// during RLP serialization.
+type metropolisReceiptRLP struct {
+ CumulativeGasUsed *big.Int
+ Bloom Bloom
+ Logs []*Log
+}
+
// NewReceipt creates a barebone transaction receipt, copying the init fields.
func NewReceipt(root []byte, cumulativeGasUsed *big.Int) *Receipt {
return &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumulativeGasUsed)}
}
// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt
-// into an RLP stream.
+// into an RLP stream. If no post state is present, metropolis fork is assumed.
func (r *Receipt) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs})
+ if r.PostState == nil {
+ return rlp.Encode(w, &metropolisReceiptRLP{r.CumulativeGasUsed, r.Bloom, r.Logs})
+ }
+ return rlp.Encode(w, &homesteadReceiptRLP{r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs})
}
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
// from an RLP stream.
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
- var receipt struct {
- PostState []byte
- CumulativeGasUsed *big.Int
- Bloom Bloom
- Logs []*Log
+ // Load the raw bytes since we have multiple possible formats
+ raw, err := s.Raw()
+ if err != nil {
+ return err
}
- if err := s.Decode(&receipt); err != nil {
+ list, _, err := rlp.SplitList(raw)
+ if err != nil {
return err
}
- r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs = receipt.PostState, receipt.CumulativeGasUsed, receipt.Bloom, receipt.Logs
- return nil
+ items, err := rlp.CountValues(list)
+ if err != nil {
+ return err
+ }
+ // Deserialize based on the number of content items
+ switch items {
+ case 3:
+ // Metropolis receipts have 3 components
+ var metro metropolisReceiptRLP
+ if err := rlp.DecodeBytes(raw, &metro); err != nil {
+ return err
+ }
+ r.CumulativeGasUsed = metro.CumulativeGasUsed
+ r.Bloom = metro.Bloom
+ r.Logs = metro.Logs
+ return nil
+
+ case 4:
+ // Homestead receipts have 4 components
+ var home homesteadReceiptRLP
+ if err := rlp.DecodeBytes(raw, &home); err != nil {
+ return err
+ }
+ r.PostState = home.PostState[:]
+ r.CumulativeGasUsed = home.CumulativeGasUsed
+ r.Bloom = home.Bloom
+ r.Logs = home.Logs
+ return nil
+
+ default:
+ return fmt.Errorf("invalid receipt components: %v", items)
+ }
}
// String implements the Stringer interface.
func (r *Receipt) String() string {
+ if r.PostState == nil {
+ return fmt.Sprintf("receipt{cgas=%v bloom=%x logs=%v}", r.CumulativeGasUsed, r.Bloom, r.Logs)
+ }
return fmt.Sprintf("receipt{med=%x cgas=%v bloom=%x logs=%v}", r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs)
}
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index 45bb87322..02df03fff 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -203,8 +203,6 @@ func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*
if err == nil {
if r == nil {
return nil, ethereum.NotFound
- } else if len(r.PostState) == 0 {
- return nil, fmt.Errorf("server returned receipt without post state")
}
}
return r, err