aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorrjl493456442 <garyrong0905@gmail.com>2017-08-21 08:47:15 +0800
committerPéter Szilágyi <peterke@gmail.com>2017-08-22 23:35:17 +0800
commit28aea46ac08579f3ecd1c35620915b8e1bfcc8b0 (patch)
treea2d00f77c624c5c255af1d05873eb3c5b0f17591 /core
parent2fd5ba6bd41380cf0e21d3ec0ca4bbc4aa7b6c34 (diff)
downloaddexon-28aea46ac08579f3ecd1c35620915b8e1bfcc8b0.tar
dexon-28aea46ac08579f3ecd1c35620915b8e1bfcc8b0.tar.gz
dexon-28aea46ac08579f3ecd1c35620915b8e1bfcc8b0.tar.bz2
dexon-28aea46ac08579f3ecd1c35620915b8e1bfcc8b0.tar.lz
dexon-28aea46ac08579f3ecd1c35620915b8e1bfcc8b0.tar.xz
dexon-28aea46ac08579f3ecd1c35620915b8e1bfcc8b0.tar.zst
dexon-28aea46ac08579f3ecd1c35620915b8e1bfcc8b0.zip
core: implement Metropolis EIP 658, receipt status byte
Diffstat (limited to 'core')
-rw-r--r--core/database_util_test.go4
-rw-r--r--core/state_processor.go4
-rw-r--r--core/state_transition.go19
-rw-r--r--core/types/gen_receipt_json.go6
-rw-r--r--core/types/receipt.go44
-rw-r--r--core/vm/evm.go6
-rw-r--r--core/vm/instructions.go2
7 files changed, 54 insertions, 31 deletions
diff --git a/core/database_util_test.go b/core/database_util_test.go
index e9a6df97b..5c75d53d0 100644
--- a/core/database_util_test.go
+++ b/core/database_util_test.go
@@ -461,12 +461,12 @@ func TestMipmapChain(t *testing.T) {
var receipts types.Receipts
switch i {
case 1:
- receipt := types.NewReceipt(nil, new(big.Int))
+ receipt := types.NewReceipt(nil, false, new(big.Int))
receipt.Logs = []*types.Log{{Address: addr, Topics: []common.Hash{hash1}}}
gen.AddUncheckedReceipt(receipt)
receipts = types.Receipts{receipt}
case 1000:
- receipt := types.NewReceipt(nil, new(big.Int))
+ receipt := types.NewReceipt(nil, false, new(big.Int))
receipt.Logs = []*types.Log{{Address: addr2}}
gen.AddUncheckedReceipt(receipt)
receipts = types.Receipts{receipt}
diff --git a/core/state_processor.go b/core/state_processor.go
index 4489cfce2..a4b554b10 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -98,7 +98,7 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
// about the transaction and calling mechanisms.
vmenv := vm.NewEVM(context, statedb, config, cfg)
// Apply the transaction to the current state (included in the env)
- _, gas, err := ApplyMessage(vmenv, msg, gp)
+ _, gas, failed, err := ApplyMessage(vmenv, msg, gp)
if err != nil {
return nil, nil, err
}
@@ -114,7 +114,7 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
// 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.
- receipt := types.NewReceipt(root, usedGas)
+ receipt := types.NewReceipt(root, failed, 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/state_transition.go b/core/state_transition.go
index 0ae9d7fcb..bab4540be 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -59,8 +59,7 @@ type StateTransition struct {
value *big.Int
data []byte
state vm.StateDB
-
- evm *vm.EVM
+ evm *vm.EVM
}
// Message represents a message sent to a contract.
@@ -127,11 +126,11 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
// the gas used (which includes gas refunds) and an error if it failed. An error always
// indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block.
-func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
+func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, bool, error) {
st := NewStateTransition(evm, msg, gp)
- ret, _, gasUsed, err := st.TransitionDb()
- return ret, gasUsed, err
+ ret, _, gasUsed, failed, err := st.TransitionDb()
+ return ret, gasUsed, failed, err
}
func (st *StateTransition) from() vm.AccountRef {
@@ -208,7 +207,7 @@ func (st *StateTransition) preCheck() error {
// TransitionDb will transition the state by applying the current message and returning the result
// including the required gas for the operation as well as the used gas. It returns an error if it
// failed. An error indicates a consensus issue.
-func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, err error) {
+func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, failed bool, err error) {
if err = st.preCheck(); err != nil {
return
}
@@ -222,10 +221,10 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
// TODO convert to uint64
intrinsicGas := IntrinsicGas(st.data, contractCreation, homestead)
if intrinsicGas.BitLen() > 64 {
- return nil, nil, nil, vm.ErrOutOfGas
+ return nil, nil, nil, false, vm.ErrOutOfGas
}
if err = st.useGas(intrinsicGas.Uint64()); err != nil {
- return nil, nil, nil, err
+ return nil, nil, nil, false, err
}
var (
@@ -248,7 +247,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
// sufficient balance to make the transfer happen. The first
// balance transfer may never fail.
if vmerr == vm.ErrInsufficientBalance {
- return nil, nil, nil, vmerr
+ return nil, nil, nil, false, vmerr
}
}
requiredGas = new(big.Int).Set(st.gasUsed())
@@ -256,7 +255,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
st.refundGas()
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(st.gasUsed(), st.gasPrice))
- return ret, requiredGas, st.gasUsed(), err
+ return ret, requiredGas, st.gasUsed(), vmerr != nil, err
}
func (st *StateTransition) refundGas() {
diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go
index eb2e5d42b..1e6880c22 100644
--- a/core/types/gen_receipt_json.go
+++ b/core/types/gen_receipt_json.go
@@ -14,6 +14,7 @@ import (
func (r Receipt) MarshalJSON() ([]byte, error) {
type Receipt struct {
PostState hexutil.Bytes `json:"root"`
+ Failed bool `json:"failed"`
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed" gencodec:"required"`
Bloom Bloom `json:"logsBloom" gencodec:"required"`
Logs []*Log `json:"logs" gencodec:"required"`
@@ -23,6 +24,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
}
var enc Receipt
enc.PostState = r.PostState
+ enc.Failed = r.Failed
enc.CumulativeGasUsed = (*hexutil.Big)(r.CumulativeGasUsed)
enc.Bloom = r.Bloom
enc.Logs = r.Logs
@@ -35,6 +37,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
func (r *Receipt) UnmarshalJSON(input []byte) error {
type Receipt struct {
PostState hexutil.Bytes `json:"root"`
+ Failed *bool `json:"failed"`
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed" gencodec:"required"`
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
Logs []*Log `json:"logs" gencodec:"required"`
@@ -49,6 +52,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
if dec.PostState != nil {
r.PostState = dec.PostState
}
+ if dec.Failed != nil {
+ r.Failed = *dec.Failed
+ }
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 c9906b015..9c49648b4 100644
--- a/core/types/receipt.go
+++ b/core/types/receipt.go
@@ -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
+ }
+ 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:
+ // 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,7 +157,7 @@ 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
@@ -152,6 +165,7 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
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)
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 8d654c666..34933a1dc 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -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
+ // 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
}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index ece4d2229..b6d6e22c4 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -33,6 +33,7 @@ var (
errWriteProtection = errors.New("evm: write protection")
errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
errExecutionReverted = errors.New("evm: execution reverted")
+ errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded")
)
func opAdd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
@@ -619,7 +620,6 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta
if value.Sign() != 0 {
gas += params.CallStipend
}
-
ret, returnGas, err := evm.Call(contract, address, args, gas, value)
if err != nil {
stack.push(new(big.Int))