Bläddra i källkod

core/vm, params: implement EXTCODEHASH opcode (#17202)

* core/vm, params: implement EXTCODEHASH opcode

* core, params: tiny fixes and polish

* core: add function description
gary rong 7 år sedan
förälder
incheckning
2433349c80
6 ändrade filer med 63 tillägg och 1 borttagningar
  1. 4 0
      core/vm/gas_table.go
  2. 32 0
      core/vm/instructions.go
  3. 6 0
      core/vm/jump_table.go
  4. 3 0
      core/vm/opcodes.go
  5. 2 0
      params/config.go
  6. 16 1
      params/gas_table.go

+ 4 - 0
core/vm/gas_table.go

@@ -241,6 +241,10 @@ func gasExtCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Sta
 	return gas, nil
 }
 
+func gasExtCodeHash(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+	return gt.ExtcodeHash, nil
+}
+
 func gasMLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
 	var overflow bool
 	gas, err := memoryGasCost(mem, memorySize)

+ 32 - 0
core/vm/instructions.go

@@ -496,6 +496,38 @@ func opExtCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, sta
 	return nil, nil
 }
 
+// opExtCodeHash returns the code hash of a specified account.
+// There are several cases when the function is called, while we can relay everything
+// to `state.GetCodeHash` function to ensure the correctness.
+//   (1) Caller tries to get the code hash of a normal contract account, state
+// should return the relative code hash and set it as the result.
+//
+//   (2) Caller tries to get the code hash of a non-existent account, state should
+// return common.Hash{} and zero will be set as the result.
+//
+//   (3) Caller tries to get the code hash for an account without contract code,
+// state should return emptyCodeHash(0xc5d246...) as the result.
+//
+//   (4) Caller tries to get the code hash of a precompiled account, the result
+// should be zero or emptyCodeHash.
+//
+// It is worth noting that in order to avoid unnecessary create and clean,
+// all precompile accounts on mainnet have been transferred 1 wei, so the return
+// here should be emptyCodeHash.
+// If the precompile account is not transferred any amount on a private or
+// customized chain, the return value will be zero.
+//
+//   (5) Caller tries to get the code hash for an account which is marked as suicided
+// in the current transaction, the code hash of this account should be returned.
+//
+//   (6) Caller tries to get the code hash for an account which is marked as deleted,
+// this account should be regarded as a non-existent account and zero should be returned.
+func opExtCodeHash(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+	slot := stack.peek()
+	slot.SetBytes(evm.StateDB.GetCodeHash(common.BigToAddress(slot)).Bytes())
+	return nil, nil
+}
+
 func opGasprice(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
 	stack.push(evm.interpreter.intPool.get().Set(evm.GasPrice))
 	return nil, nil

+ 6 - 0
core/vm/jump_table.go

@@ -80,6 +80,12 @@ func newConstantinopleInstructionSet() [256]operation {
 		validateStack: makeStackFunc(2, 1),
 		valid:         true,
 	}
+	instructionSet[EXTCODEHASH] = operation{
+		execute:       opExtCodeHash,
+		gasCost:       gasExtCodeHash,
+		validateStack: makeStackFunc(1, 1),
+		valid:         true,
+	}
 	instructionSet[CREATE2] = operation{
 		execute:       opCreate2,
 		gasCost:       gasCreate2,

+ 3 - 0
core/vm/opcodes.go

@@ -90,6 +90,7 @@ const (
 	EXTCODECOPY
 	RETURNDATASIZE
 	RETURNDATACOPY
+	EXTCODEHASH
 )
 
 // 0x40 range - block operations.
@@ -267,6 +268,7 @@ var opCodeToString = map[OpCode]string{
 	EXTCODECOPY:    "EXTCODECOPY",
 	RETURNDATASIZE: "RETURNDATASIZE",
 	RETURNDATACOPY: "RETURNDATACOPY",
+	EXTCODEHASH:    "EXTCODEHASH",
 
 	// 0x40 range - block operations.
 	BLOCKHASH:  "BLOCKHASH",
@@ -435,6 +437,7 @@ var stringToOp = map[string]OpCode{
 	"EXTCODECOPY":    EXTCODECOPY,
 	"RETURNDATASIZE": RETURNDATASIZE,
 	"RETURNDATACOPY": RETURNDATACOPY,
+	"EXTCODEHASH":    EXTCODEHASH,
 	"BLOCKHASH":      BLOCKHASH,
 	"COINBASE":       COINBASE,
 	"TIMESTAMP":      TIMESTAMP,

+ 2 - 0
params/config.go

@@ -211,6 +211,8 @@ func (c *ChainConfig) GasTable(num *big.Int) GasTable {
 		return GasTableHomestead
 	}
 	switch {
+	case c.IsConstantinople(num):
+		return GasTableConstantinople
 	case c.IsEIP158(num):
 		return GasTableEIP158
 	case c.IsEIP150(num):

+ 16 - 1
params/gas_table.go

@@ -20,6 +20,7 @@ package params
 type GasTable struct {
 	ExtcodeSize uint64
 	ExtcodeCopy uint64
+	ExtcodeHash uint64
 	Balance     uint64
 	SLoad       uint64
 	Calls       uint64
@@ -63,7 +64,7 @@ var (
 		CreateBySuicide: 25000,
 	}
 	// GasTableEIP158 contain the gas re-prices for
-	// the EIP15* phase.
+	// the EIP155/EIP158 phase.
 	GasTableEIP158 = GasTable{
 		ExtcodeSize: 700,
 		ExtcodeCopy: 700,
@@ -73,6 +74,20 @@ var (
 		Suicide:     5000,
 		ExpByte:     50,
 
+		CreateBySuicide: 25000,
+	}
+	// GasTableConstantinople contain the gas re-prices for
+	// the constantinople phase.
+	GasTableConstantinople = GasTable{
+		ExtcodeSize: 700,
+		ExtcodeCopy: 700,
+		ExtcodeHash: 400,
+		Balance:     400,
+		SLoad:       200,
+		Calls:       700,
+		Suicide:     5000,
+		ExpByte:     50,
+
 		CreateBySuicide: 25000,
 	}
 )