Skip to content
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

Implement DATAHASH opcode and sharding fork #2

Merged
merged 10 commits into from
Feb 20, 2022
1 change: 1 addition & 0 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,7 @@ func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
func (m callMsg) Data() []byte { return m.CallMsg.Data }
func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
func (m callMsg) DataHashes() []common.Hash { return m.CallMsg.DataHashes }

// filterBackend implements filters.Backend to support filtering for logs without
// taking bloom-bits acceleration structures into account.
Expand Down
2 changes: 1 addition & 1 deletion cmd/evm/internal/t8ntool/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func Transaction(ctx *cli.Context) error {
r.Address = sender
}
// Check intrinsic gas
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil,
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), len(tx.BlobVersionedHashes()), tx.To() == nil,
chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int))); err != nil {
r.Error = err
results = append(results, r)
Expand Down
2 changes: 1 addition & 1 deletion core/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
return func(i int, gen *BlockGen) {
toaddr := common.Address{}
data := make([]byte, nbytes)
gas, _ := IntrinsicGas(data, nil, false, false, false)
gas, _ := IntrinsicGas(data, nil, 0, false, false, false)
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)))
gasPrice := big.NewInt(0)
if gen.header.BaseFee != nil {
Expand Down
85 changes: 85 additions & 0 deletions core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/protolambda/ztyp/view"
)

// So we can deterministically seed different blockchains
Expand Down Expand Up @@ -3704,3 +3705,87 @@ func TestEIP1559Transition(t *testing.T) {
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
}
}

// TestDataBlobTxs tests the following:
//
// 1. Writes data hash from transaction to storage.
func TestDataBlobTxs(t *testing.T) {
var (
one = common.Hash{1}
two = common.Hash{2}
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")

// Generate a canonical chain to act as the main dataset
engine = ethash.NewFaker()
db = rawdb.NewMemoryDatabase()

// A sender who makes transactions, has some funds
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
gspec = &Genesis{
Config: params.AllEthashProtocolChanges,
Alloc: GenesisAlloc{
addr1: {Balance: funds},
// The address 0xAAAA writes dataHashes[1] to storage slot 0x0.
aa: {
Code: []byte{
byte(vm.PUSH1),
byte(0x1),
byte(vm.DATAHASH),
byte(vm.PUSH1),
byte(0x0),
byte(vm.SSTORE),
},
Nonce: 0,
Balance: big.NewInt(0),
},
},
}
)

gspec.Config.BerlinBlock = common.Big0
gspec.Config.LondonBlock = common.Big0
gspec.Config.ShardingForkBlock = common.Big0
genesis := gspec.MustCommit(db)
signer := types.LatestSigner(gspec.Config)

blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{1})
msg := types.BlobTxMessage{
Nonce: 0,
Gas: 500000,
}
msg.To.Address = (*types.AddressSSZ)(&aa)
msg.ChainID.SetFromBig((*big.Int)(gspec.Config.ChainID))
msg.Nonce = view.Uint64View(0)
msg.GasFeeCap.SetFromBig(newGwei(5))
msg.GasTipCap.SetFromBig(big.NewInt(2))
msg.BlobVersionedHashes = []common.Hash{one, two}
txdata := &types.SignedBlobTx{Message: msg}

tx := types.NewTx(txdata)
tx, _ = types.SignTx(tx, signer, key1)

b.AddTx(tx)
})

diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb)

chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
if n, err := chain.InsertChain(blocks); err != nil {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}

state, _ := chain.State()

// 1. Check that the storage slot is set to dataHashes[1].
actual := state.GetState(aa, common.Hash{0})
if actual != two {
t.Fatalf("incorrect data hash written to state (want: %s, got: %s)", two, actual)
}
}
5 changes: 3 additions & 2 deletions core/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
// NewEVMTxContext creates a new transaction context for a single transaction.
func NewEVMTxContext(msg Message) vm.TxContext {
return vm.TxContext{
Origin: msg.From(),
GasPrice: new(big.Int).Set(msg.GasPrice()),
Origin: msg.From(),
GasPrice: new(big.Int).Set(msg.GasPrice()),
DataHashes: msg.DataHashes(),
}
}

Expand Down
6 changes: 4 additions & 2 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ type Message interface {
IsFake() bool
Data() []byte
AccessList() types.AccessList
DataHashes() []common.Hash
}

// ExecutionResult includes all output after executing given evm
Expand Down Expand Up @@ -115,7 +116,7 @@ func (result *ExecutionResult) Revert() []byte {
}

// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool) (uint64, error) {
func IntrinsicGas(data []byte, accessList types.AccessList, blobCount int, isContractCreation bool, isHomestead, isEIP2028 bool) (uint64, error) {
// Set the starting gas for the raw transaction
var gas uint64
if isContractCreation && isHomestead {
Expand Down Expand Up @@ -152,6 +153,7 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
}
gas += uint64(blobCount) * params.BlobGas
return gas, nil
}

Expand Down Expand Up @@ -295,7 +297,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
contractCreation := msg.To() == nil

// Check clauses 4-5, subtract intrinsic gas if everything is correct
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, istanbul)
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), len(st.msg.DataHashes()), contractCreation, homestead, istanbul)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
return ErrInsufficientFunds
}
// Ensure the transaction has more gas than the basic tx fee.
intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul)
intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), 0, tx.To() == nil, true, pool.istanbul)
if err != nil {
return err
}
Expand Down
7 changes: 4 additions & 3 deletions core/types/data_blob_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import (
"encoding/hex"
"errors"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
"github.com/protolambda/ztyp/codec"
"github.com/protolambda/ztyp/conv"
"github.com/protolambda/ztyp/tree"
. "github.com/protolambda/ztyp/view"
"math/big"
)

type ECDSASignature struct {
Expand Down Expand Up @@ -432,6 +433,6 @@ func (stx *SignedBlobTx) rawSignatureValues() (v, r, s *big.Int) {
func (stx *SignedBlobTx) setSignatureValues(chainID, v, r, s *big.Int) {
(*uint256.Int)(&stx.Message.ChainID).SetFromBig(chainID)
stx.Signature.V = Uint8View(v.Uint64())
(*uint256.Int)(&stx.Signature.R).SetFromBig(chainID)
(*uint256.Int)(&stx.Signature.S).SetFromBig(chainID)
(*uint256.Int)(&stx.Signature.R).SetFromBig(r)
(*uint256.Int)(&stx.Signature.S).SetFromBig(s)
}
28 changes: 16 additions & 12 deletions core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ import (
"bytes"
"container/heap"
"errors"
"github.com/protolambda/ztyp/codec"
"io"
"math/big"
"sync/atomic"
"time"

"github.com/protolambda/ztyp/codec"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -596,6 +597,7 @@ type Message struct {
gasTipCap *big.Int
data []byte
accessList AccessList
dataHashes []common.Hash
isFake bool
}

Expand Down Expand Up @@ -627,6 +629,7 @@ func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) {
amount: tx.Value(),
data: tx.Data(),
accessList: tx.AccessList(),
dataHashes: tx.BlobVersionedHashes(),
isFake: false,
}
// If baseFee provided, set gasPrice to effectiveGasPrice.
Expand All @@ -638,17 +641,18 @@ func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) {
return msg, err
}

func (m Message) From() common.Address { return m.from }
func (m Message) To() *common.Address { return m.to }
func (m Message) GasPrice() *big.Int { return m.gasPrice }
func (m Message) GasFeeCap() *big.Int { return m.gasFeeCap }
func (m Message) GasTipCap() *big.Int { return m.gasTipCap }
func (m Message) Value() *big.Int { return m.amount }
func (m Message) Gas() uint64 { return m.gasLimit }
func (m Message) Nonce() uint64 { return m.nonce }
func (m Message) Data() []byte { return m.data }
func (m Message) AccessList() AccessList { return m.accessList }
func (m Message) IsFake() bool { return m.isFake }
func (m Message) From() common.Address { return m.from }
func (m Message) To() *common.Address { return m.to }
func (m Message) GasPrice() *big.Int { return m.gasPrice }
func (m Message) GasFeeCap() *big.Int { return m.gasFeeCap }
func (m Message) GasTipCap() *big.Int { return m.gasTipCap }
func (m Message) Value() *big.Int { return m.amount }
func (m Message) Gas() uint64 { return m.gasLimit }
func (m Message) Nonce() uint64 { return m.nonce }
func (m Message) Data() []byte { return m.data }
func (m Message) AccessList() AccessList { return m.accessList }
func (m Message) DataHashes() []common.Hash { return m.dataHashes }
func (m Message) IsFake() bool { return m.isFake }

// copyAddressPtr copies an address.
func copyAddressPtr(a *common.Address) *common.Address {
Expand Down
5 changes: 5 additions & 0 deletions core/types/transaction_signing.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ type sigCache struct {
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
var signer Signer
switch {
case config.IsSharding(blockNumber):
signer = NewDankSigner(config.ChainID)
case config.IsLondon(blockNumber):
signer = NewLondonSigner(config.ChainID)
case config.IsBerlin(blockNumber):
Expand All @@ -63,6 +65,9 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
// have the current block number available, use MakeSigner instead.
func LatestSigner(config *params.ChainConfig) Signer {
if config.ChainID != nil {
if config.ShardingForkBlock != nil {
return NewDankSigner(config.ChainID)
}
if config.LondonBlock != nil {
return NewLondonSigner(config.ChainID)
}
Expand Down
23 changes: 23 additions & 0 deletions core/vm/eips.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,26 @@ func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
scope.Stack.push(baseFee)
return nil, nil
}

// enable3198 applies mini-danksharding (DATAHASH Opcode)
// - Adds an opcode that returns the versioned data hash of the tx at a index.
func enableSharding(jt *JumpTable) {
jt[DATAHASH] = &operation{
execute: opDataHash,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
}
}

// opDataHash implements DATAHASH opcode
func opDataHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
idx := scope.Stack.pop()
if uint64(len(interpreter.evm.TxContext.DataHashes)) < idx.Uint64() {
scope.Stack.push(uint256.NewInt(0))
} else {
hash := interpreter.evm.TxContext.DataHashes[idx.Uint64()]
scope.Stack.push(new(uint256.Int).SetBytes(hash.Bytes()))
}
return nil, nil
}
5 changes: 3 additions & 2 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ type BlockContext struct {
// All fields can change between transactions.
type TxContext struct {
// Message information
Origin common.Address // Provides information for ORIGIN
GasPrice *big.Int // Provides information for GASPRICE
Origin common.Address // Provides information for ORIGIN
GasPrice *big.Int // Provides information for GASPRICE
DataHashes []common.Hash // Provides versioned data hashes for DATAHASH
}

// EVM is the Ethereum Virtual Machine base object and provides
Expand Down
41 changes: 41 additions & 0 deletions core/vm/instructions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -688,3 +688,44 @@ func TestRandom(t *testing.T) {
}
}
}

func TestDataHash(t *testing.T) {
type testcase struct {
name string
idx uint64
expect common.Hash
hashes []common.Hash
}
var (
zero = common.Hash{0}
one = common.Hash{1}
two = common.Hash{2}
three = common.Hash{3}
)
for _, tt := range []testcase{
{name: "[{1}]", idx: 0, expect: one, hashes: []common.Hash{one}},
{name: "[1,{2},3]", idx: 2, expect: three, hashes: []common.Hash{one, two, three}},
{name: "out-of-bounds (empty)", idx: 10, expect: zero, hashes: []common.Hash{}},
{name: "out-of-bounds", idx: 25, expect: zero, hashes: []common.Hash{one, two, three}},
} {
var (
env = NewEVM(BlockContext{}, TxContext{DataHashes: tt.hashes}, nil, params.TestChainConfig, Config{})
stack = newstack()
pc = uint64(0)
evmInterpreter = env.interpreter
)
stack.push(uint256.NewInt(tt.idx))
opDataHash(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
if len(stack.data) != 1 {
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
}
actual := stack.pop()
expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.expect.Bytes()))
if overflow {
t.Errorf("Testcase %v: invalid overflow", tt.name)
}
if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %v: expected %x, got %x", tt.name, expected, actual)
}
}
}
2 changes: 2 additions & 0 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
// If jump table was not initialised we set the default one.
if cfg.JumpTable == nil {
switch {
case evm.chainRules.IsSharding:
cfg.JumpTable = &shardingInstructionSet
case evm.chainRules.IsMerge:
cfg.JumpTable = &mergeInstructionSet
case evm.chainRules.IsLondon:
Expand Down
7 changes: 7 additions & 0 deletions core/vm/jump_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ var (
berlinInstructionSet = newBerlinInstructionSet()
londonInstructionSet = newLondonInstructionSet()
mergeInstructionSet = newMergeInstructionSet()
shardingInstructionSet = newShardingInstructionSet()
)

// JumpTable contains the EVM opcodes supported at a given fork.
Expand All @@ -78,6 +79,12 @@ func validate(jt JumpTable) JumpTable {
return jt
}

func newShardingInstructionSet() JumpTable {
instructionSet := newLondonInstructionSet()
enableSharding(&instructionSet)
return validate(instructionSet)
}

func newMergeInstructionSet() JumpTable {
instructionSet := newLondonInstructionSet()
instructionSet[RANDOM] = &operation{
Expand Down
Loading