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

Update witness gas charging to differentiate between access events and write events #37

Conversation

jwasinger
Copy link
Collaborator

@jwasinger jwasinger commented Nov 25, 2021

This PR updates the witness gas charging scheme to differentiate between witness access events and write events.
Relevant spec doc: https://notes.ethereum.org/-fJSOrnYQl-mqoWKpaTIsQ

TODOs that can be addressed in future PR(s):

  • Charging for filling a previously-empty leaf is omitted from this PR as it is currently unclear how to expose the db to the EVM in a non-hacky way.
    * witness access events/costs are not handled for PUSH. Because executed code is accounted for in the interpreter loop, I think the costs/witnesses would still be correct in most cases. However, I can see issues with witnesses/costs being missed (e.g. PUSH32 could advance the PC over a slot and cause the leaf for that code to not be charged/included). To properly make this change, PUSH will have to be modified to be treated as a dynamically-priced opcode in Geth's EVM. <- done in Refactor witness-accumulation in EVM #42

@gballet gballet requested review from gballet and removed request for holiman November 25, 2021 08:27
Copy link
Owner

@gballet gballet left a comment

Choose a reason for hiding this comment

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

Undefined is used to mark that a location has been accessed in the gas accounting code, a point at which the db isn't available. if value is nil, TouchAddress will put the chunk in Undefined instead of Chunks, in order to write down that the data needs to be resolved at a later time. This has some problems, like the inability to tell the difference between a missing value (need for a proof of absence) and an unresolved value (i.e. a bug). Using the suggested mode field in Chunks, it should be possible to tell the difference, and to get rid of Undefined altogether. And also not to pass a confusing nil parameter in the gas accounting code.

@@ -669,5 +669,5 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header
}
state.AddBalance(header.Coinbase, reward)
coinbase := utils.GetTreeKeyBalance(header.Coinbase.Bytes())
state.Witness().TouchAddress(coinbase, state.GetBalance(header.Coinbase).Bytes())
Copy link
Owner

Choose a reason for hiding this comment

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

TouchAddressOnWrite

core/types/access_witness.go Outdated Show resolved Hide resolved
core/types/access_witness.go Outdated Show resolved Hide resolved
core/types/access_witness.go Outdated Show resolved Hide resolved
core/types/access_witness.go Outdated Show resolved Hide resolved
newStem bool
newSelector bool
stemRead bool
selectorRead bool
Copy link
Owner

Choose a reason for hiding this comment

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

linter issue

core/vm/evm.go Outdated Show resolved Hide resolved
core/vm/evm.go Outdated Show resolved Hide resolved
@gballet gballet force-pushed the verkle-trie-proof-in-block-rebased branch from b8981f6 to 86bdc3f Compare November 26, 2021 15:50
… in. Adjust TestProcessStateless to account for increased intrinsic gas cost
@@ -669,5 +669,6 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header
}
state.AddBalance(header.Coinbase, reward)
coinbase := utils.GetTreeKeyBalance(header.Coinbase.Bytes())
state.Witness().TouchAddress(coinbase, state.GetBalance(header.Coinbase).Bytes())
state.Witness().TouchAddressOnReadAndChargeGas(coinbase)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

note to self TouchAddressOnWrite.

@@ -378,11 +378,11 @@ func TestProcessStateless(t *testing.T) {
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
defer blockchain.Stop()
chain, _ := GenerateVerkleChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, func(_ int, gen *BlockGen) {
tx, _ := types.SignTx(types.NewTransaction(0, common.Address{1, 2, 3}, big.NewInt(999), params.TxGas, big.NewInt(875000000), nil), signer, testKey)
Copy link
Owner

Choose a reason for hiding this comment

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

add a comment or a formula so that people understand where this value comes from

Chunks: make(map[common.Hash][]byte),
Undefined: make(map[common.Hash]struct{}),
Branches: make(map[VerkleStem]byte),
Chunks: make(map[common.Hash]ChunkValue),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

rename to ChunkState.

Comment on lines +54 to +57
const (
AccessWitnessReadFlag = 1
AccessWitnessWriteFlag = 2
)
Copy link
Owner

Choose a reason for hiding this comment

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

Move this above ChunkState, declare a Mode type and set it like:

Suggested change
const (
AccessWitnessReadFlag = 1
AccessWitnessWriteFlag = 2
)
const (
AccessWitnessReadFlag = Mode(1)
AccessWitnessWriteFlag = Mode(2)
)

AccessWitnessWriteFlag = 2
)

// because of the way Geth's EVM is implemented, the gas cost of an operation
Copy link
Owner

Choose a reason for hiding this comment

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

start comment with name of function

// because of the way Geth's EVM is implemented, the gas cost of an operation
// may be needed before the value of the leaf-key can be retrieved. Hence, we
// break witness access (for the purpose of gas accounting), and filling witness
// values into two methods
Copy link
Owner

Choose a reason for hiding this comment

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

please replace with a comment that explains what the function does

Comment on lines +67 to +71
if chunk, exists := aw.Chunks[common.BytesToHash(addr)]; exists {
chunk.value = value
} else {
panic(fmt.Sprintf("address not in access witness: %x", addr))
}
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
if chunk, exists := aw.Chunks[common.BytesToHash(addr)]; exists {
chunk.value = value
} else {
panic(fmt.Sprintf("address not in access witness: %x", addr))
}
chunk, exists := aw.Chunks[common.BytesToHash(addr)]
if !exists {
panic(fmt.Sprintf("address not in access witness: %x", addr))
}
chunk.value = value

Comment on lines +74 to +76
func (aw *AccessWitness) touchAddressOnWrite(addr []byte) (bool, bool, bool) {
var stem VerkleStem
var stemWrite, chunkWrite, chunkFill bool
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
func (aw *AccessWitness) touchAddressOnWrite(addr []byte) (bool, bool, bool) {
var stem VerkleStem
var stemWrite, chunkWrite, chunkFill bool
func (aw *AccessWitness) touchAddressOnWrite(addr []byte) (stemWrite, chunkWrite, chunkFill bool) {
var stem VerkleStem

// NOTE: stem, selector access flags already exist in their
// respective maps because this function is called at the end of
// processing a read access event

Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change

}
*/

return stemWrite, chunkWrite, chunkFill
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
return stemWrite, chunkWrite, chunkFill
return

Comment on lines +88 to +93
chunkValue := aw.Chunks[common.BytesToHash(addr)]
if chunkValue.mode & AccessWitnessWriteFlag != 0 {
chunkWrite = true
chunkValue.mode |= AccessWitnessWriteFlag
aw.Chunks[common.BytesToHash(addr)] = chunkValue
}
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
chunkValue := aw.Chunks[common.BytesToHash(addr)]
if chunkValue.mode & AccessWitnessWriteFlag != 0 {
chunkWrite = true
chunkValue.mode |= AccessWitnessWriteFlag
aw.Chunks[common.BytesToHash(addr)] = chunkValue
}
chunkValue := &aw.Chunks[common.BytesToHash(addr)]
if chunkValue.mode & AccessWitnessWriteFlag != 0 {
chunkWrite = true
chunkValue.mode |= AccessWitnessWriteFlag
}

}

// 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, value []byte) (bool, bool) {
func (aw *AccessWitness) touchAddress(addr []byte, isWrite bool) (bool, bool, bool, bool, bool) {
Copy link
Owner

Choose a reason for hiding this comment

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

same trick as above, declare return variables here

return false
}

evm.Accesses.SetLeafValue(key, value)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Move this above gas balance check.

return false
}

evm.Accesses.SetLeafValue(key, value)
Copy link
Owner

Choose a reason for hiding this comment

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

problem here: SetLeafValue overwrites the original value

@@ -961,7 +962,8 @@ func makePush(size uint64, pushByteSize int) executionFunc {
value[0] = byte(count)
copy(value[1:], scope.Contract.Code[chunk*31:endMin])
index := trieUtils.GetTreeKeyCodeChunk(scope.Contract.Address().Bytes(), uint256.NewInt(chunk))
interpreter.evm.TxContext.Accesses.TouchAddressAndChargeGas(index, nil)
interpreter.evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(index)
Copy link
Owner

Choose a reason for hiding this comment

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

move the gas charge to a gas_table.go equivalent of makePush

@gballet
Copy link
Owner

gballet commented Dec 2, 2021

@jwasinger when verifying the proof in the test, this is the PR in which I did this before. #26 It has "bit-rotted" to quite an extent, but it might still be useful as an inspiration. The code that deserializes the proof is here https://github.com/gballet/go-ethereum/pull/26/files#diff-6303aaa1a681f7a75b66a759f7997d98b7795865af5fb26b4310688b14f1f9fbR215 and verification just above.

It's particularly horrible because it wasn't rebased when the reference branch was. I can try to do that if it's really impractical.

@jwasinger
Copy link
Collaborator Author

@gballet as we discussed in #39 , the proof is embedded by the miner so it can't be tested by TestProcessStateless right now. I suggest that the test be augmented to include a breakdown which calculates the tx gas values that need to be supplied in the test (witness costs + TxGas). Then I can, in another PR, add more validation to the proofs embedded in the block in this test:

if block.Header().VerkleProof == nil {

@jwasinger jwasinger changed the title Update witness gas charging Update witness gas charging to differentiate between access events and write events Dec 14, 2021
@jwasinger jwasinger closed this Jan 21, 2022
gballet pushed a commit that referenced this pull request Jun 11, 2022
…unt-check (ethereum#24765)

* cmd/geth, core/state/snapshot: rework journal loading, implement account-check

* core/state/snapshot, cmd/geth: polish code (#37)

* core/state/snapshot: minor nits

* core/state/snapshot: simplify error logic

* cmd/geth: go format

Co-authored-by: rjl493456442 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants