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

Record witness access in EXTCODEHASH #370

Merged
merged 4 commits into from
Feb 15, 2024
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
133 changes: 133 additions & 0 deletions core/state_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie/utils"

//"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
Expand Down Expand Up @@ -794,3 +795,135 @@ func TestProcessVerkleContractWithEmptyCode(t *testing.T) {
}
}
}

func TestProcessVerklExtCodeHashOpcode(t *testing.T) {
var (
config = &params.ChainConfig{
ChainID: big.NewInt(69421),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
Ethash: new(params.EthashConfig),
ShanghaiTime: u64(0),
PragueTime: u64(0),
TerminalTotalDifficulty: common.Big0,
TerminalTotalDifficultyPassed: true,
ProofInBlocks: true,
}
signer = types.LatestSigner(config)
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain
gendb = rawdb.NewMemoryDatabase() // Database for the block-generation code, they must be separate as they are path-based.
coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7")
account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e")
account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d")
gspec = &Genesis{
Config: config,
Alloc: GenesisAlloc{
coinbase: GenesisAccount{
Balance: big.NewInt(1000000000000000000), // 1 ether
Nonce: 0,
},
account1: GenesisAccount{
Balance: big.NewInt(1000000000000000000), // 1 ether
Nonce: 0,
},
account2: GenesisAccount{
Balance: big.NewInt(1000000000000000000), // 1 ether
Nonce: 3,
},
},
}
)
// Verkle trees use the snapshot, which must be enabled before the
// data is saved into the tree+database.
genesis := gspec.MustCommit(bcdb)

// Commit the genesis block to the block-generation database as it
// is now independent of the blockchain database.
gspec.MustCommit(gendb)

dummyContract := []byte{
0x60, 2, // PUSH1 2
0x60, 12, // PUSH1 12
0x60, 0x00, // PUSH1 0
0x39, // CODECOPY

0x60, 2, // PUSH1 2
0x60, 0x00, // PUSH1 0
0xF3, // RETURN

// Contract that auto-calls EXTCODEHASH
0x60, 42, // PUSH1 42
}
Comment on lines +854 to +866
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy dummyContract so we have some contract to use EXTCODEHASH later.

dummyContractAddr := common.HexToAddress("3a220f351252089d385b29beca14e27f204c296a")
extCodeHashContract := []byte{
0x60, 22, // PUSH1 22
0x60, 12, // PUSH1 12
0x60, 0x00, // PUSH1 0
0x39, // CODECOPY

0x60, 22, // PUSH1 22
0x60, 0x00, // PUSH1 0
0xF3, // RETURN

// Contract that auto-calls EXTCODEHASH
0x73, // PUSH20
0x3a, 0x22, 0x0f, 0x35, 0x12, 0x52, 0x08, 0x9d, 0x38, 0x5b, 0x29, 0xbe, 0xca, 0x14, 0xe2, 0x7f, 0x20, 0x4c, 0x29, 0x6a,
0x3F, // EXTCODEHASH
}
Comment on lines +868 to +882
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy another contract that actually makes the EXTCODEHASH to the previously deployed contract.

This was required to be super clear about witness checks. Initially, I made the dummy contract "auto call" EXTCODEHASH, but then is hard to distinguish why the witness have the contract code hash (e.g: from deployment or from EXTCODEHASH exec).

extCodeHashContractAddr := common.HexToAddress("db7d6ab1f17c6b31909ae466702703daef9269cf")
_, _, _, statediff := GenerateVerkleChain(gspec.Config, genesis, beacon.New(ethash.NewFaker()), gendb, 2, func(i int, gen *BlockGen) {
gen.SetPoS()

if i == 0 {
// Create dummy contract.
tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(0), 100_000, big.NewInt(875000000), dummyContract), signer, testKey)
gen.AddTx(tx)

// Create contract with EXTCODEHASH opcode.
tx, _ = types.SignTx(types.NewContractCreation(1, big.NewInt(0), 100_000, big.NewInt(875000000), extCodeHashContract), signer, testKey)
gen.AddTx(tx)
Comment on lines +888 to +894
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the first block deploy both contracts.

} else {
tx, _ := types.SignTx(types.NewTransaction(2, extCodeHashContractAddr, big.NewInt(0), 100_000, big.NewInt(875000000), nil), signer, testKey)
gen.AddTx(tx)
Comment on lines +896 to +897
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the next block do the actual execution, so if the witness shows the contract code hash address then we know its' from EXTCODEHASH execution and not contract deployments.

}

})

contractKeccakTreeKey := utils.GetTreeKeyCodeKeccak(dummyContractAddr[:])

var stateDiffIdx = -1
for i, stemStateDiff := range statediff[1] {
if bytes.Equal(stemStateDiff.Stem[:], contractKeccakTreeKey[:31]) {
stateDiffIdx = i
break
}
}
if stateDiffIdx == -1 {
t.Fatalf("no state diff found for stem")
}

codeHashStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[0]
if codeHashStateDiff.Suffix != utils.CodeKeccakLeafKey {
t.Fatalf("code hash invalid suffix")
}
if codeHashStateDiff.CurrentValue == nil {
t.Fatalf("codeHash.CurrentValue must not be empty")
}
expCodeHash := crypto.Keccak256Hash(dummyContract[12:])
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoiding magical hashes, so we know it's correct.

if *codeHashStateDiff.CurrentValue != expCodeHash {
t.Fatalf("codeHash.CurrentValue unexpected code hash")
}
if codeHashStateDiff.NewValue != nil {
t.Fatalf("codeHash.NewValue must be nil")
}
}
7 changes: 7 additions & 0 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,13 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.peek()
address := common.Address(slot.Bytes20())
if interpreter.evm.chainRules.IsPrague {
statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(slot.Bytes(), uint256.Int{}, trieUtils.CodeKeccakLeafKey)
if !scope.Contract.UseGas(statelessGas) {
scope.Contract.Gas = 0
return nil, ErrOutOfGas
}
}
if interpreter.evm.StateDB.Empty(address) {
slot.Clear()
} else {
Expand Down
Loading