-
Notifications
You must be signed in to change notification settings - Fork 13
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
Update witness gas charging to differentiate between access events and write events #37
Conversation
There was a problem hiding this 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()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TouchAddressOnWrite
newStem bool | ||
newSelector bool | ||
stemRead bool | ||
selectorRead bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
linter issue
b8981f6
to
86bdc3f
Compare
ca3ccb1
to
0ede6e2
Compare
@@ -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) |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename to ChunkState
.
const ( | ||
AccessWitnessReadFlag = 1 | ||
AccessWitnessWriteFlag = 2 | ||
) |
There was a problem hiding this comment.
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:
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
if chunk, exists := aw.Chunks[common.BytesToHash(addr)]; exists { | ||
chunk.value = value | ||
} else { | ||
panic(fmt.Sprintf("address not in access witness: %x", addr)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
func (aw *AccessWitness) touchAddressOnWrite(addr []byte) (bool, bool, bool) { | ||
var stem VerkleStem | ||
var stemWrite, chunkWrite, chunkFill bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
*/ | ||
|
||
return stemWrite, chunkWrite, chunkFill |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return stemWrite, chunkWrite, chunkFill | |
return |
chunkValue := aw.Chunks[common.BytesToHash(addr)] | ||
if chunkValue.mode & AccessWitnessWriteFlag != 0 { | ||
chunkWrite = true | ||
chunkValue.mode |= AccessWitnessWriteFlag | ||
aw.Chunks[common.BytesToHash(addr)] = chunkValue | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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) { |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
@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. |
@gballet as we discussed in #39 , the proof is embedded by the miner so it can't be tested by go-ethereum/miner/worker_test.go Line 320 in 6af78cb
|
…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 <garyrong0905@gmail.com>
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):
* 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.<- done in Refactor witness-accumulation in EVM #42PUSH32
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.