## Why this should be merged
Branch-protection rules only require the Go linter (erroneously called
`lint`) by mistake. All linters are now dependencies of a single `lint`
job that remains as a PR gate. New linters will now be enforced by
default.
Closes#138
## How this works
GitHub Actions `need` configuration. The path limitation of running
`yamllint` is removed because it's such a cheap job so is ok to always
run.
## How this was tested
Inspection of CI run and PR's required jobs:


## Why this should be merged
Support for configurable `core/types.Block` with RLP encoding, including
interplay with `Body`.
## How this works
`Block` doesn't export most of its fields so relies on an internal type,
`extblock`, for RLP encoding. This type is modified to implement the
`rlp.Encoder` and `Decoder` methods as a point to inject hooks using
`rlp.Fields` (as in #120 for `Body`).
`Block` shares the same registered extra type as `Body`. Unlike
`Header`, which has its own field in a `Block`, the fields in `Body` are
promoted to be carried directly. This suggests that (at least for pure
data payloads) the modifications might be equivalent (and
`ava-labs/coreth` evidences this). Should different payloads be
absolutely required in the future, we can split the types—the
`RegisterExtras` signature is already too verbose though 😢.
## How this was tested
Explicit inclusion of a backwards-compatibility test for
`NOOPBlockBodyHooks` + implicit testing via the multiple upstream tests
in `block_test.go`. Re implicit testing: default behaviour is now to use
the noop hooks even when no registration is performed, but if we change
this then the tests in `block_test.go` can still be called as subtests
from a test that explicitly registers noops.
---------
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
Simplify testing in #130.
## How this works
Backwards-compatibility tests were originally created in `package
types_test` because the first one uses `ethtest` and would have caused a
circular dependency. None of the later tests have this issue so they
were moved into `package types`. The `cmpeth` test utility isn't
currently needed anywhere else so its functionality is collapsed inside
this new file.
## How this was tested
N/A as simply moving existing tests to different files.
Breaking change refactoring `core/types.Block.WithBody()` method from signature
WithBody(transactions []*Transaction, uncles []*Header) *Block
to signature
WithBody(body Body) *Block
such that block and body extras can be used within `WithBody`.
Note `geth` made the same change in method signature so the deltas introduced in this PR will disappear once we sync.
## Why this should be merged
#110 introduces changes that we want to revert as part of the next Geth
sync. This label will be applied to issues that track similar tasks.
## How this works
Magic 🧙
## How this was tested
Inspecting [CI dry run, which creates the new
label](https://github.com/ava-labs/libevm/actions/runs/13261362433/job/37018526246?pr=129#step:3:99).
Signed-off-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com>
The action pinned as v6 uses v6.3.1 which is currently misbehaving on the main branch linting all files instead of just the files from the current commit.
## Why this should be merged
Allows for `ava-labs/coreth` equivalent modifications of
`rawdb.InspectDatabase()` through external logic injection.
## How this works
Variadic options to:
1. Record a database statistic;
2. Mark a database statistic as metadata;
3. Filter statistics for printing.
## How this was tested
Testable example acting as a unit test.
---------
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
Simplification of `types.Body` RLP overriding, resulting in reduced code
at both the implementation and consumer ends.
## How this works
Introduction of `rlp.Fields` type, to mirror regular RLP encoding of a
struct. The RLP override hook now only needs to return the fields of
interest, which MAY come from either the `Body` or the registered extra.
This pattern allows for arbitrary modification of upstream fields via
(1) reordering; (2) addition; (3) deletion; and (4) inverting required
vs optional status. While less important for `Body`, this allows for
complete support of `ava-labs/coreth` `Header` modifications, which make
use of 1-3.
## How this was tested
Existing backwards-compatibility tests + new unit tests for introduced
functionality.
---------
Signed-off-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com>
Co-authored-by: Quentin McGaw <quentin.mcgaw@avalabs.org>
- 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>
`goheader` only needs to be run on files changed in the current PR as it will otherwise suggest updating copyright years on untouched files. The other linters, however, should be run in the same manner as a local invocation
- Allow wallets and other clients to parse blocks from both coreth and subnet-evm without registering extra types
- Allow wallets and other clients to make their genesis/chain configurations without registering extra types
- New workflow triggering only on yaml file changes using the built-in `yamllint`
- `.yamllint.yml` configuration added at the root of the project
- Fix existing yml files to satisfy ymllint
- Ignore go-ethereum original yml files
## Why this should be merged
JSON equivalent of #89.
## How this works
The check for registered extras, previously used in `{En,De}codeRLP()`
methods is abstracted into a `Header.hooks() HeaderHooks` method that
either returns (a) an instance of the registered type or (b) a
`NOOPHeaderHooks` if no registration was performed. This is then used
for all hooks, new (JSON) and old (RLP).
## How this was tested
Extension of existing unit tests.
## 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
The `types.Header` fields of both
[`coreth`](https://pkg.go.dev/github.com/ava-labs/coreth/core/types#Header)
and
[`subnet-evm`](https://pkg.go.dev/github.com/ava-labs/subnet-evm/core/types#Header)
have been modified such that their RLP encodings (i.e. block hashes)
aren't compatible with vanilla `geth` nor each other. This PR adds
support for arbitrary RLP encoding coupled with type-safe extra
payloads.
## How this works
Equivalent to #1 (`params`) and #44 (`types.StateAccount`) registration
of pseudo-generic payloads. The only major difference is the guarantee
of a non-nil payload pointer, which means that the payload hooks are
never called on nil pointers as this would make it difficult to decode
RLP into them.
## How this was tested
Round-trip RLP {en,de}coding via a registered stub hook.
---------
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
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
This is a precursor to being able to override `types.Header` RLP
{en,de}coding. As there is already a `Header.EncodeRLP()` method we
either have to modify the generated code or rename the generated
method—this PR does the latter.
## How this works
The `rlpgen -internal_methods` flag changes the generated methods from
`EncodeRLP()` and `DecodeRLP()` to `encodeRLP()` and `decodeRLP()`,
respectively. A new CI job checks that generated code is up to date. We
can then implement our own `Header.EncodeRLP()` that either overrides or
falls back on the original.
It appears that `core/gen_genesis.go` was out of date but only because
of formatting.
## How this was tested
I deliberately excluded the change to `core/types/gen_header_rlp.go` to
confirm that the new workflow
[detects](https://github.com/ava-labs/libevm/actions/runs/12259667481/job/34202386378?pr=86#step:5:92)
the change and fails. The actual change can be inspected via the code
diff.
## 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
I messed up the format of `rc` version tags, resulting in `rc.1` being
considered a later version that `rc.2`.
## How this works
The [release tagging](https://github.com/ava-labs/libevm/discussions/37)
pattern that includes a combination of `geth` and `libevm` semver
triplets (e.g. `1.13.14-0.1.0`) doesn't work well with extra identifiers
like `rc` because more pre-release identifiers (those after `-`) take
higher precedence if all those before them match. We therefore have to
use a `release` suffix (`"release" > "rc"` in ASCII).
This all became too much to expect to be done manually so I chucked it
in code instead.
## How this was tested
Unit test demonstrates expectation of version ordering.
## Why this should be merged
Provides access to extra payloads registered on `types.SlimAccount`, not
just on regular `types.StateAccount`. This is a breaking syntax change
to have the method reflect the behaviour.
## How this works
Modify existing method to accept the `ExtraPayloadCarrier` interface
instead of `*StateAccount`. The interface is implemented by both
`*StateAccount` and `*SlimAccount`.
## How this was tested
Covered by existing testing.
## 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
Allow `ava-labs/coreth` to use arbitrary `triedb` database
implementations.
## How this works
Introduces `HashBackend` and `PathBackend` interfaces that
`triedb.Database` type-asserts to instead of `hashdb.Database` and
`pathdb.Backend` respectively. Other interfaces are introduced to avoid
having to modify `{hash,path}db.Database.Reader()` return values.
The explicit `DBOverride` field means that we don't have to modify any
of the `triedb` constructor beyond adding a single line immediately
before the return. This leaves all modifications of original files
entirely mechanistic. This is, however, at the expense of compile-time
guarantees of the overriding database being either a `HashBackend` or a
`PathBackend`.
## How this was tested
Unit test demonstrating override + plumbing.
---------
Co-authored-by: Darioush Jalali <darioush.jalali@avalabs.org>
## 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
## Why this should be merged
These workflows are required by branch protection for release branches,
but aren't run automatically so release PRs can't currently be merged.
## How this works
Extends the `branches` filters of necessary workflows.
## How this was tested
n/a
## Why this should be merged
Fixes tracing when a stateful precompile calls another contract that
itself accesses storage.
## How this works
The pre-state tracer from `eth/tracers/native` doesn't implement
`CaptureEnter()` (entry of a new context), instead relying on
`CaptureState()` (per-opcode tracing) to detect that a new contract has
been entered. In doing so, it [maintains an
invariant](cb7eb89341/eth/tracers/native/prestate.go (L160))
that is expected when `CaptureState(vm.SLOAD, ...)` is called—breaking
the invariant results in a panic due to a nil map.
The fix involves (a) maintaining the invariant as part of
`CaptureEnter()` (previously a no-op); and (b) calling said method
inside `vm.PrecompileEnvironment.Call()`. The latter has the added
benefit of properly handling all tracing involving an outbound call from
precompiles.
## How this was tested
New integration test demonstrates that the tracer can log the retrieved
storage value.
---------
Co-authored-by: Darioush Jalali <darioush.jalali@avalabs.org>
## Why this should be merged
Fixes invariant broken by introduction of `types.StateAccountExtra`.
## How this works
`state.stateObject.empty()` now also requires that the underlying
account's `Extra` field carries the zero value if a type is registered,
or is itself nil.
## How this was tested
New unit test.
---------
Co-authored-by: Darioush Jalali <darioush.jalali@avalabs.org>
Linter:
- Block imports of `ethereum/go-ethereum` upstream module
- Disable `goimports` checks on upstream code
Go:
- Update flaky-test regex
- Run flaky and non-flaky tests in different steps as this makes it easier to spot incorrect regex
Upstream delta:
- Use env var in workflow instead of `libevm-base` tag -> base changes atomically with the update commit