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

test stateless block execution #18

Merged
merged 2 commits into from
Sep 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 88 additions & 1 deletion core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
)

// BlockGen creates blocks for testing.
Expand All @@ -43,6 +44,7 @@ type BlockGen struct {
txs []*types.Transaction
receipts []*types.Receipt
uncles []*types.Header
witness *types.AccessWitness

config *params.ChainConfig
engine consensus.Engine
Expand Down Expand Up @@ -103,10 +105,11 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
b.SetCoinbase(common.Address{})
}
b.statedb.Prepare(tx.Hash(), len(b.txs))
receipt, _, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
receipt, accesses, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
if err != nil {
panic(err)
}
b.witness.Merge(accesses)
b.txs = append(b.txs, tx)
b.receipts = append(b.receipts, receipt)
}
Expand Down Expand Up @@ -250,6 +253,90 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
return blocks, receipts
}

func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
if config == nil {
config = params.TestChainConfig
}
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
chainreader := &fakeChainReader{config: config}
genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) {
b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine, witness: types.NewAccessWitness()}
b.header = makeHeader(chainreader, parent, statedb, b.engine)

// Mutate the state and block according to any hard-fork specs
if daoBlock := config.DAOForkBlock; daoBlock != nil {
limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
if b.header.Number.Cmp(daoBlock) >= 0 && b.header.Number.Cmp(limit) < 0 {
if config.DAOForkSupport {
b.header.Extra = common.CopyBytes(params.DAOForkBlockExtra)
}
}
}
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 {
misc.ApplyDAOHardFork(statedb)
}
// Execute any user modifications to the block
if gen != nil {
gen(i, b)
}
if b.engine != nil {
// Finalize and seal the block
block, err := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts)
if err != nil {
panic(err)
}

// Write state changes to db
root, err := statedb.Commit(config.IsEIP158(b.header.Number))
if err != nil {
panic(fmt.Sprintf("state write error: %v", err))
}
if err := statedb.Database().TrieDB().Commit(root, false, nil); err != nil {
panic(fmt.Sprintf("trie write error: %v", err))
}

// Generate an associated verkle proof
if tr := statedb.GetTrie(); tr.IsVerkle() {
vtr := tr.(*trie.VerkleTrie)
// Generate the proof if we are using a verkle tree
// WORKAROUND: make sure all keys are resolved
// before building the proof. Ultimately, node
// resolution can be done with a prefetcher or
// from GetCommitmentsAlongPath.
keys := b.witness.Keys()
for _, key := range keys {
out, err := vtr.TryGet(key)
if err != nil {
panic(err)
}
if len(out) == 0 {
panic(fmt.Sprintf("%x should be present in the tree", key))
}
}
vtr.Hash()
p, err := vtr.ProveAndSerialize(keys)
b.header.VerkleProof = p
if err != nil {
panic(err)
}
}
return block, b.receipts
}
return nil, nil
}
for i := 0; i < n; i++ {
statedb, err := state.New(parent.Root(), state.NewDatabaseWithConfig(db, &trie.Config{UseVerkle: true}), nil)
if err != nil {
panic(err)
}
block, receipt := genblock(i, parent, statedb)
blocks[i] = block
receipts[i] = receipt
parent = block
}
return blocks, receipts
}

func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header {
var time uint64
if parent.Time() == 0 {
Expand Down
12 changes: 12 additions & 0 deletions core/state/snapshot/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"sync"
"sync/atomic"

"github.com/VictoriaMetrics/fastcache"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/ethdb"
Expand Down Expand Up @@ -193,6 +194,17 @@ func New(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, root comm
}
if err != nil {
if rebuild {
if useVerkle {
snap.layers = map[common.Hash]snapshot{
root: &diskLayer{
diskdb: diskdb,
triedb: triedb,
root: root,
cache: fastcache.New(cache * 1024 * 1024),
},
}
return snap, nil
}
log.Warn("Failed to load snapshot, regenerating", "err", err)
snap.Rebuild(root)
return snap, nil
Expand Down
19 changes: 7 additions & 12 deletions core/state_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,18 +324,13 @@ func TestProcessStateless(t *testing.T) {
genesis := gspec.MustCommit(db, nil)
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
defer blockchain.Stop()
var makeTx = func(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction {
tx, _ := types.SignTx(types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data), signer, testKey)
return tx
}
bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
tooBigNumber := new(big.Int).Set(bigNumber)
tooBigNumber.Add(tooBigNumber, common.Big1)
txs := []*types.Transaction{
makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil),
}
block := GenerateBadBlock(genesis, ethash.NewFaker(), txs, gspec.Config)
_, err := blockchain.InsertChain(types.Blocks{block})
chain, _ := GenerateVerkleChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, func(_ int, gen *BlockGen) {
toaddr := common.Address{}
tx, _ := types.SignTx(types.NewTransaction(0, toaddr, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), signer, testKey)
gen.AddTx(tx)

})
_, err := blockchain.InsertChain(chain)
if err != nil {
t.Fatalf("block imported with error: %v", err)
}
Expand Down
11 changes: 10 additions & 1 deletion core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
trieUtils "github.com/ethereum/go-ethereum/trie/utils"
)

/*
Expand Down Expand Up @@ -112,7 +113,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, isContractCreation, isHomestead, isEIP2028 bool) (uint64, error) {
// Set the starting gas for the raw transaction
var gas uint64
if isContractCreation && isHomestead {
Expand Down Expand Up @@ -289,6 +290,14 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
if st.gas < gas {
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas)
}
if st.evm.TxContext.Accesses != nil {
toBalance := trieUtils.GetTreeKeyBalance(*msg.To())
fromBalance := trieUtils.GetTreeKeyBalance(msg.From())
fromNonce := trieUtils.GetTreeKeyNonce(msg.From())
gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(toBalance)
gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(fromNonce)
gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(fromBalance)
}
st.gas -= gas

// Check clause 6
Expand Down
38 changes: 38 additions & 0 deletions core/types/access_witness.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package types

import "github.com/ethereum/go-ethereum/common"
import "github.com/ethereum/go-ethereum/params"

type AccessWitness struct {
Witness map[common.Hash]map[byte]struct{}
Expand All @@ -26,6 +27,43 @@ func NewAccessWitness() *AccessWitness {
return &AccessWitness{Witness: make(map[common.Hash]map[byte]struct{})}
}

// TouchAddress adds any missing addr to the witness and returns respectively
// true if the stem or the stub weren't arleady present.
func (aw *AccessWitness) TouchAddress(addr []byte) (bool, bool) {
var (
stem common.Hash
newStem bool
newSelector bool
selector = addr[31]
)
copy(stem[:], addr[:31])

// Check for the presence of the stem
if _, newStem := aw.Witness[stem]; !newStem {
aw.Witness[stem] = make(map[byte]struct{})
}

// Check for the presence of the selector
if _, newSelector := aw.Witness[stem][selector]; !newSelector {
aw.Witness[stem][selector] = struct{}{}
}

return newStem, newSelector
}

func (aw *AccessWitness) TouchAddressAndChargeGas(addr []byte) uint64 {
var gas uint64

nstem, nsel := aw.TouchAddress(addr)
if nstem {
gas += params.WitnessBranchCost
}
if nsel {
gas += params.WitnessChunkCost
}
return gas
}

func (aw *AccessWitness) Merge(other *AccessWitness) {
for k, mo := range other.Witness {
if ma, ok := aw.Witness[k]; ok {
Expand Down
23 changes: 10 additions & 13 deletions core/vm/gas_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ func gasExtCodeSize(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
_, ok := evm.TxContext.Accesses.Witness[subtree]
if !ok {
evm.TxContext.Accesses.Witness[subtree] = make(map[byte]struct{})
usedGas += gasWitnessBranchCost
usedGas += params.WitnessBranchCost
}

_, ok = evm.TxContext.Accesses.Witness[subtree][subleaf]
if !ok {
usedGas += gasWitnessChunkCost
usedGas += params.WitnessChunkCost
evm.TxContext.Accesses.Witness[subtree][subleaf] = struct{}{}
}

Expand All @@ -114,9 +114,6 @@ var (
gasCodeCopyStateful = memoryCopierGas(2)
gasExtCodeCopyStateful = memoryCopierGas(3)
gasReturnDataCopy = memoryCopierGas(2)

gasWitnessBranchCost = uint64(1900)
gasWitnessChunkCost = uint64(200)
)

func gasCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
Expand Down Expand Up @@ -147,12 +144,12 @@ func gasCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
_, ok := evm.TxContext.Accesses.Witness[subtree]
if !ok {
evm.TxContext.Accesses.Witness[subtree] = make(map[byte]struct{})
statelessGas += gasWitnessBranchCost
statelessGas += params.WitnessBranchCost
}

_, ok = evm.TxContext.Accesses.Witness[subtree][subleaf]
if !ok {
statelessGas += gasWitnessChunkCost
statelessGas += params.WitnessChunkCost
evm.TxContext.Accesses.Witness[subtree][subleaf] = struct{}{}
}

Expand Down Expand Up @@ -192,12 +189,12 @@ func gasExtCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
_, ok := evm.TxContext.Accesses.Witness[subtree]
if !ok {
evm.TxContext.Accesses.Witness[subtree] = make(map[byte]struct{})
statelessGas += gasWitnessBranchCost
statelessGas += params.WitnessBranchCost
}

_, ok = evm.TxContext.Accesses.Witness[subtree][subleaf]
if !ok {
statelessGas += gasWitnessChunkCost
statelessGas += params.WitnessChunkCost
evm.TxContext.Accesses.Witness[subtree][subleaf] = struct{}{}
}

Expand All @@ -218,12 +215,12 @@ func gasSLoad(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySiz
_, ok := evm.TxContext.Accesses.Witness[subtree]
if !ok {
evm.TxContext.Accesses.Witness[subtree] = make(map[byte]struct{})
usedGas += gasWitnessBranchCost
usedGas += params.WitnessBranchCost
}

_, ok = evm.TxContext.Accesses.Witness[subtree][subleaf]
if !ok {
usedGas += gasWitnessChunkCost
usedGas += params.WitnessChunkCost
evm.TxContext.Accesses.Witness[subtree][subleaf] = struct{}{}
}

Expand Down Expand Up @@ -480,12 +477,12 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
_, ok := evm.TxContext.Accesses.Witness[subtree]
if !ok {
evm.TxContext.Accesses.Witness[subtree] = make(map[byte]struct{})
gas += gasWitnessBranchCost
gas += params.WitnessBranchCost
}

_, ok = evm.TxContext.Accesses.Witness[subtree][subleaf]
if !ok {
gas += gasWitnessChunkCost
gas += params.WitnessChunkCost
evm.TxContext.Accesses.Witness[subtree][subleaf] = struct{}{}
}
}
Expand Down
5 changes: 3 additions & 2 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
trieUtils "github.com/ethereum/go-ethereum/trie/utils"
"github.com/holiman/uint256"
)
Expand Down Expand Up @@ -204,11 +205,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
subleaf := index[31]
if _, ok := in.evm.TxContext.Accesses.Witness[subtree]; !ok {
in.evm.TxContext.Accesses.Witness[subtree] = make(map[byte]struct{})
contract.Gas -= gasWitnessBranchCost
contract.Gas -= params.WitnessBranchCost
}
if _, ok := in.evm.TxContext.Accesses.Witness[subtree][subleaf]; !ok {
in.evm.TxContext.Accesses.Witness[subtree][subleaf] = struct{}{}
contract.Gas -= gasWitnessChunkCost
contract.Gas -= params.WitnessChunkCost
}

if in.evm.accesses != nil {
Expand Down
4 changes: 4 additions & 0 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ const (
// up to half the consumed gas could be refunded. Redefined as 1/5th in EIP-3529
RefundQuotient uint64 = 2
RefundQuotientEIP3529 uint64 = 5

// Verkle tree EIP: costs associated to witness accesses
WitnessBranchCost = uint64(1900)
WitnessChunkCost = uint64(200)
)

// Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations
Expand Down
Loading