-
Notifications
You must be signed in to change notification settings - Fork 20.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
core: add status as a consensus field in receipt #15014
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,10 +28,16 @@ import ( | |
|
||
//go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go | ||
|
||
const ( | ||
receiptStatusSuccessful = byte(0x01) | ||
receiptStatusFailed = byte(0x00) | ||
) | ||
|
||
// Receipt represents the results of a transaction. | ||
type Receipt struct { | ||
// Consensus fields | ||
PostState []byte `json:"root"` | ||
Failed bool `json:"failed"` | ||
CumulativeGasUsed *big.Int `json:"cumulativeGasUsed" gencodec:"required"` | ||
Bloom Bloom `json:"logsBloom" gencodec:"required"` | ||
Logs []*Log `json:"logs" gencodec:"required"` | ||
|
@@ -60,21 +66,26 @@ type homesteadReceiptRLP struct { | |
// metropolisReceiptRLP contains the receipt's Metropolis consensus fields, used | ||
// during RLP serialization. | ||
type metropolisReceiptRLP struct { | ||
Status byte | ||
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)} | ||
func NewReceipt(root []byte, failed bool, cumulativeGasUsed *big.Int) *Receipt { | ||
return &Receipt{PostState: common.CopyBytes(root), Failed: failed, CumulativeGasUsed: new(big.Int).Set(cumulativeGasUsed)} | ||
} | ||
|
||
// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt | ||
// into an RLP stream. If no post state is present, metropolis fork is assumed. | ||
func (r *Receipt) EncodeRLP(w io.Writer) error { | ||
if r.PostState == nil { | ||
return rlp.Encode(w, &metropolisReceiptRLP{r.CumulativeGasUsed, r.Bloom, r.Logs}) | ||
status := receiptStatusSuccessful | ||
if r.Failed { | ||
status = receiptStatusFailed | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be done a bit more compactly with: status := receiptStatusSuccessful
if r.Failed {
status = receiptStatusFailed
} |
||
return rlp.Encode(w, &metropolisReceiptRLP{status, r.CumulativeGasUsed, r.Bloom, r.Logs}) | ||
} | ||
return rlp.Encode(w, &homesteadReceiptRLP{r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs}) | ||
} | ||
|
@@ -87,29 +98,31 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { | |
if err != nil { | ||
return err | ||
} | ||
list, _, err := rlp.SplitList(raw) | ||
content, _, err := rlp.SplitList(raw) | ||
if err != nil { | ||
return err | ||
} | ||
items, err := rlp.CountValues(list) | ||
kind, cnt, _, err := rlp.Split(content) | ||
if err != nil { | ||
return err | ||
} | ||
// Deserialize based on the number of content items | ||
switch items { | ||
case 3: | ||
// Metropolis receipts have 3 components | ||
// Deserialize based on the first component type. | ||
switch { | ||
case kind == rlp.Byte || kind == rlp.String && len(cnt) == 0: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't need the string magic here, it's only byte we're working with. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should definitely be removed, or else we'll have a potential consensus failure by accepting an invalid receipt. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Arachnid Since now the empty value is encoded as 0x80. When we use rlp.Split, it returns a String type instead of the byte type we expected. So add these statements in order to deal with this situation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Arachnid This is another solution to fix it. Change receipt status type from byte to []byte. Since rlp will treat byte as uint8, and byte(0x00) will be encoded as 0x80, which make confused. If we use []byte to represent receipt status, []byte{0x00} can be encoded as 0x00 correctly. But the receipt definition can cause ambiguity. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this part is correct, since 0x01 is decoded as a byte, but 0x80 as an empty string (0 number). It's a bit weird, but that's the way RLP guesses the type unless it's given explicitly. One thing that did occur to me however, is that we don't enforce the status byte to be 0 or 1. That's a possible consensus problem. I'll add a fix for this. |
||
// The first component of metropolis receipts is Byte | ||
// or empty String(byte with 0x00 value). | ||
var metro metropolisReceiptRLP | ||
if err := rlp.DecodeBytes(raw, &metro); err != nil { | ||
return err | ||
} | ||
r.Failed = metro.Status == receiptStatusFailed | ||
r.CumulativeGasUsed = metro.CumulativeGasUsed | ||
r.Bloom = metro.Bloom | ||
r.Logs = metro.Logs | ||
return nil | ||
|
||
case 4: | ||
// Homestead receipts have 4 components | ||
case kind == rlp.String: | ||
// The first component of homestead receipts is non-empty String. | ||
var home homesteadReceiptRLP | ||
if err := rlp.DecodeBytes(raw, &home); err != nil { | ||
return err | ||
|
@@ -121,14 +134,14 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { | |
return nil | ||
|
||
default: | ||
return fmt.Errorf("invalid receipt components: %v", items) | ||
return fmt.Errorf("invalid first receipt component: %v", kind) | ||
} | ||
} | ||
|
||
// 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{failed=%t cgas=%v bloom=%x logs=%v}", r.Failed, 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) | ||
} | ||
|
@@ -144,14 +157,15 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error { | |
for i, log := range r.Logs { | ||
logs[i] = (*LogForStorage)(log) | ||
} | ||
return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed}) | ||
return rlp.Encode(w, []interface{}{r.PostState, r.Failed, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed}) | ||
} | ||
|
||
// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation | ||
// fields of a receipt from an RLP stream. | ||
func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { | ||
var receipt struct { | ||
PostState []byte | ||
Failed bool | ||
CumulativeGasUsed *big.Int | ||
Bloom Bloom | ||
TxHash common.Hash | ||
|
@@ -163,7 +177,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { | |
return err | ||
} | ||
// Assign the consensus fields | ||
r.PostState, r.CumulativeGasUsed, r.Bloom = receipt.PostState, receipt.CumulativeGasUsed, receipt.Bloom | ||
r.PostState, r.Failed, r.CumulativeGasUsed, r.Bloom = receipt.PostState, receipt.Failed, receipt.CumulativeGasUsed, receipt.Bloom | ||
r.Logs = make([]*Log, len(receipt.Logs)) | ||
for i, log := range receipt.Logs { | ||
r.Logs[i] = (*Log)(log) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -158,7 +158,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas | |
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) | ||
|
||
// initialise a new contract and set the code that is to be used by the | ||
// E The contract is a scoped evmironment for this execution context | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what a great typo - love this word!-) |
||
// E The contract is a scoped environment for this execution context | ||
// only. | ||
contract := NewContract(caller, to, value, gas) | ||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) | ||
|
@@ -351,6 +351,10 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I | |
contract.UseGas(contract.Gas) | ||
} | ||
} | ||
// Assign err if contract code size exceeds the max while the err is still empty. | ||
if maxCodeSizeExceeded && err == nil { | ||
err = errMaxCodeSizeExceeded | ||
} | ||
return ret, contractAddr, contract.Gas, err | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should be private (since we want to work with bools API wise) and please document them.