- remaining gas higher than the input gas is not allowed (otherwise it would overflow as well)
- remaining gas can be equal to the input gas
- add new test and `vm.PrecompileEnvironment` stub
## Why this should be merged
Allows for modification of `types.Body` payload data + RLP encoding
without placing the entire RLP burden on the `libevm` user as we did
with `types.HeaderHooks`.
## How this works
RLP encoding of a struct is simply a concatenation of RLP encodings of
fields, encompassed by an RLP "list". The
`AppendRLPFields(rlp.EncoderBuffer, ...)` hook exploits this and plugs
in before all `rlp:"optional"`-tagged fields to allow for inclusion of
any new fields. The `EncoderBuffer` SHOULD be used as the `io.Writer`
passed when encoding each field: `rlp.Encode(buffer, fieldValue)`.
`Body` doesn't have `{En,De}codeRLP` methods so they are implemented to
identically replicate original behaviour when a no-op hook is present.
This pattern is sufficient for the `ava-labs/coreth` modifications of
`Body` but can be modified / extended for more complex scenarios, like
`Header`.
> [!NOTE]
> This PR does not include registration of the hooks as that was not the
initial goal and adding them would create too much PR bloat. There is a
placeholder `var todoRegisteredBodyHooks` global variable that can only
be set in tests.
## How this was tested
- Backwards compatibility: the new methods are fuzzed against a `type
withoutMethods Body` passed directly to `rlp.{En,De}code()`
- `coreth` compatibility: unit test of a local implementation of
`BodyHooks` demonstrating reproducibility of RLP encoding.
---------
Signed-off-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com>
Co-authored-by: Quentin McGaw <quentin.mcgaw@avalabs.org>
## Why this should be merged
Aligns precompiled contracts with the pattern used in
`vm.EVMInterpreter` for regular contracts and simplifies gas accounting
by using existing mechanisms. The most notable simplification occurs
when there are multiple error paths that return early and have to
account for consumed gas (any local solution would likely just mirror
this one).
This forms part of a broader, long-term direction of feature parity
between precompiled and bytecode contracts, exposed via
`vm.PrecompileEnvironment`.
The `vm.Contract.Value()` method is also exposed as a natural
accompaniment to this change.
## How this works
The original signature for `vm.PrecompiledStatefulContract` received the
amount of gas available and then returned the amount remaining; this has
been removed in lieu of exposing the existing `vm.Contract.UseGas()`
method via the `vm.PrecompileEnvironment`. A new `legacy` package wraps
the old signature and converts it to a new one so `ava-labs/coreth`
doesn't need to be refactored.
```go
func oldPrecompile(env vm.PrecompileEnvironment, input []byte, gas uint64) ([]byte, uint64, error) {
// ...
if err != nil {
return nil, gas - gasCost, err // pattern susceptible to bugs; should it be `nil, gas, err` ?
}
// ...
return output, gas - gasCost, nil
}
func newPrecompile(env vm.PrecompileEnvironment, input []byte) ([]byte, error) {
// The original `gas` argument is still available as `env.Gas()`
// ...
if !env.UseGas(gasCost) { // an explicit point at which gas is consumed
return nil, vm.ErrOutOfGas
}
// ...
if err != nil {
return nil, err
}
// ...
return output, nil
}
```
## How this was tested
Existing unit test modified to use the `legacy` adaptor.
---------
Signed-off-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com>
Co-authored-by: Quentin McGaw <quentin.mcgaw@gmail.com>
## Why this should be merged
Replaces #86. That approach couldn't be replicated for generated JSON
marshalling, and this once also reduces the upstream delta.
## How this works
AST manipulation of a specified file, which finds specified methods and
modifies their names to be unexported.
## How this was tested
Inspection of the output (`core/types/gen_header_rlp.go` remains
unchanged).
## Why this should be merged
We're making changes to very sensitive code that, if broken, can result
in block hashes being different.
## How this works
Change-detector test.
## How this was tested
A randomly filled `types.Header` with expected RLP locked as a constant
in the test.
## Why this should be merged
There are 3 places at which we perform the same, sensitive logic to
access registered payloads and as we modify more types this is likely to
expand. (e.g. `types.Header`).
## How this works
Introduces `pseudo.Accessor` to abstract the reusable code.
## How this was tested
Existing unit tests. Note that the `types.StateAccount` tests needed a
minor refactor to provide the assertions with access to the
`ExtraPayloads[T]` without introducing generic types anywhere.
## Why this should be merged
Consolidates duplicated logic. Similar rationale to #84.
## How this works
New `register.AtMostOnce[T]` type is responsible for limiting calls to
`Register()`.
## How this was tested
Existing unit tests of `params`. Note that the equivalent functionality
in `types` wasn't tested but now is.
## Why this should be merged
Allows for a drop-in replacement of `snapshot.Tree` (i.e. one that uses
block hashes instead of state roots). This is intended as a temporary
solution while we investigate having the state root affected by the
block hash to remove path ambiguity.
## How this works
Introduction of:
1. `state.SnapshotTree` interface to match methods required on
`snapshot.Tree` as used by `state.StateDB`; and
2. `stateconf` package for variadic options plumbed by
`StateDB.Commit()` through to `SnapshotTree.Update()`.
Although variadic (to maintain function call-signature compatibility)
only the `stateconf.WithUpdatePayload(any)` is expected to be used.
Recipients of the options can access the payload with
`stateconf.ExtractUpdatePayload()`.
## How this was tested
Unit test demonstrating propagation of `stateconf.UpdateOption` payload.
## Why this should be merged
Performs trie prefetching concurrently, required for equivalent
performance with `coreth` / `subnet-evm` implementations.
## How this works
`StateDB.StartPrefetcher()` accepts variadic options (for backwards
compatibility of function signatures). An option to specify a
`WorkerPool` is provided which, if present, is used to call
`Trie.Get{Account,Storage}()`; the pool is responsible for concurrency
but does not need to be able to wait on the work as that is handled by
this change.
## How this was tested
Unit test demonstrating hand-off of work to a `WorkerPool` as well as
API-guaranteed ordering of events.
## Why this should be merged
Simplifies the creation and application of variadic options; this is
also applicable to many of my other WIP branches (e.g. `snapshot`).
## How this works
See `libevm/options/options.go`, which is very simple.
## How this was tested
Existing tests of `vm.PrecompileEnvironment.Call()`, which itself uses
refactored options.
## Why this should be merged
Cleans up loose ends after renaming the Go module. Also adds an
introduction to the README to explain the purpose of libevm.
## How this works
The changed hash in the workflow is just a fix (although a no-op). The
spaces after the copyright headers are to stop them from [showing up in
documentation](https://pkg.go.dev/github.com/ava-labs/libevm@v1.13.14-0.1.0-rc.1/params).
## How this was tested
n/a
* refactor: abstract `testonly` package
* test: `StateAccount.Extra` via `trie.StateTrie.{Update,Get}Account()`
* chore: `types.TestOnlyClearRegisteredExtras()` at beginning of tests
This is a purely defensive approach in case future tests forget to clean up.
* chore: placate the linter
Some of the changes in the full commit history were merged into `libevm` as part of #43 in `336a289` and then merged back into this branch as `5b15698`. Cherry-picking commits was not possible as some touched both halves of the changes; the squash-merges will, however, make this convoluted history irrelevant.
* feat: `types.StateAccount` pseudo-generic payload
* feat: registration of `StateAccount` payload type
* chore: mark `eth/tracers/logger` flaky
* chore: copyright header + `gci`
* test: lock default `types.SlimAccount` RLP encoding
* feat: `vm.SlimAccount.Extra` from `StateAccount` equiv
* chore: placate the linter
* test: `pseudo.Type.EncodeRLP()`
* test: `pseudo.Type.DecodeRLP()`
* fix: `pseudo.Type.DecodeRLP()` with non-pointer type
* feat: `pseudo.Type.IsZero()` and `Type.Equal(*Type)`
* feat: `types.StateAccountExtra.DecodeRLP()`
* fix: remove unnecessary `StateAccountExtra.clone()`
* refactor: readability
* feat: `pseudo.Type.Format()` implements `fmt.Formatter`
All commits except the last two constitute PRs #43 and #44. The last two reverted files such that only changes to the `pseudo` and `ethtest` packages remain; once this is merged into the `libevm` branch then `libevm` will be merged into the branch for #44 too. Cherry-picking commits was not possible as some touched both halves of the changes; the squash-merges will, however, make this convoluted history irrelevant.
* feat: `types.StateAccount` pseudo-generic payload
* feat: registration of `StateAccount` payload type
* chore: mark `eth/tracers/logger` flaky
* chore: copyright header + `gci`
* test: lock default `types.SlimAccount` RLP encoding
* feat: `vm.SlimAccount.Extra` from `StateAccount` equiv
* chore: placate the linter
* test: `pseudo.Type.EncodeRLP()`
* test: `pseudo.Type.DecodeRLP()`
* fix: `pseudo.Type.DecodeRLP()` with non-pointer type
* feat: `pseudo.Type.IsZero()` and `Type.Equal(*Type)`
* feat: `types.StateAccountExtra.DecodeRLP()`
* chore: revert non-pseudo-package modifications
* chore: delete non-pseudo-package additions
* refactor!: `RulesHooks.CanCreateContract()` via `defer`
`TestCanCreateContract` is unchanged and merely moved to the appropriate file.
* feat!: `RulesHooks.CanCreateContract()` accepts and returns gas
* chore: move `TestCanCreateContract` to original file
Simplifies code review
* chore: pacify linter
* refactor!: revert to non-deferred call (not at start)
* chore: `golangci-lint` CI workflow
* fix: make `golangci-lint` happy
* chore: bump `actions/{checkout,setup-go}` versions
* chore: overhaul `.golanci.yml` config
* fix: all linter issues
* chore: exclude non-libevm linters + change deprecated option
* fix: add overflow check in example
* fix: try again; different local version?
* chore: this is trying my patience
* chore: enable `gci` and fix ordering
* chore: mark `ethclient/gethclient` test as flaky
* chore: mark `eth/catalyst` test as flaky
* refactor: extra types `C` + `R` are never plumbed as `*C` / `*R`
* refactor: force use of `pseudo.Constructor.Zero()` instead of `NilPointer()`
* feat: `pseudo.PointerTo()`
* feat: `params.ExtraPayloadGetter[C,R].PointerFromChainConfig(...) *C` and `Rules => *R` equiv
* test: shallow copy of `ChainConfig`/`Rules` includes extras
* feat: pseudo-generic extra payloads in `params.ChainConfig` and `params.Rules`
* feat: `params.ExtraPayloadGetter` for end-user type safety
* refactor: payloads only available through `params.ExtraPayloadGetter`
* chore: make `libevm/examples/extraparams` a `params` testable example
* doc: `libevm/pseudo` package comments and improved readability
* doc: `params.*Extra*` comments and improved readability
* doc: `params.ExtraPayloadGetter` comments and improved readability
* doc: `params/config.libevm_test.go` comments and improved readability
* refactor: simplify `params.ChainConfig.UnmarshalJSON()`
* refactor: abstract new/nil-pointer creation into `pseudo.Constructor`s
* feat: precompile override via `params.Extras` hooks
* doc: flesh out `PrecompileOverride()` in example
* doc: complete commentary and improve readability
* refactor: `ChainConfig.Hooks()` + `Rules` equivalent
* chore: rename precompiles test file in keeping with geth equivalent
* feat: stateful precompiles + allowlist hooks
The allowlist hooks are included in this commit because they allow for the same functionality as stateful precompiles in `ava-labs/coreth` and `ava-labs/subnet-evm`.
* fix: `StateTransition.canExecuteTransaction()` used `msg.From` instead of `To`
* test: `params.RulesHooks.CanCreateContract` integration
* test: `params.RulesHooks.CanExecuteTransaction` integration
* test: `vm.NewStatefulPrecompile()` integration
* refactor: simplify test of `CanCreateContract`
* refactor: abstract generation of random `Address`/`Hash` values
* doc: full documentation + readability refactoring/renaming
* fix: remove circular dependency in tests