Fix all build errors after upstream merge

- Update consensus imports from luxfi/node/consensus to luxfi/consensus
- Remove all snow package references
- Fix utils/logging to use luxfi/log
- Update EIP-4844 API changes (CalcBlobFee, CalcExcessBlobGas)
- Add tracing.CodeChangeReason to SetCode calls
- Fix validators.GetCurrentValidatorOutput to GetValidatorOutput
- Fix wallet API changes (IssueCreateSubnetTx to IssueCreateNetTx)
- Fix VM interface compatibility between node and consensus packages
- Fix extstate.StateDB references to use state.StateDB
- Fix trienode API to use NodeWithPrev
- Remove duplicate files and unused code
- Update all dependencies to use externalized luxfi packages
This commit is contained in:
2025-09-19 02:13:19 +00:00
parent ff628b043b
commit f981fefee0
97 changed files with 963 additions and 2820 deletions
+154
View File
@@ -0,0 +1,154 @@
name: CI
on:
push:
branches: [ main, develop, master ]
pull_request:
branches: [ main, develop, master ]
env:
GO_VERSION: '1.24.6'
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Cache Go modules
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Install golangci-lint
run: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2
- name: Run golangci-lint
run: golangci-lint run --timeout=10m
- name: Run go fmt check
run: |
if [ -n "$(gofmt -l .)" ]; then
echo "Please run 'go fmt ./...' to format your code"
gofmt -l .
exit 1
fi
- name: Run go vet
run: go vet ./...
- name: go mod tidy
run: |
go mod tidy
git diff --exit-code
test:
name: Test
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
go: ['1.24.6']
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
- name: Cache Go modules
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Install dependencies
run: go mod download
- name: Run short tests
run: |
go test -v -short -race -coverprofile=coverage.out -covermode=atomic ./...
timeout-minutes: 15
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
file: ./coverage.out
flags: unittests
name: codecov-umbrella
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Cache Go modules
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Build EVM
run: |
go build -v -o build/evm ./cmd/evm
- name: Build precompilegen
run: |
go build -v -o build/precompilegen ./cmd/precompilegen
- name: Build abigen
run: |
go build -v -o build/abigen ./cmd/abigen
- name: Upload binary
uses: actions/upload-artifact@v4
with:
name: evm-${{ matrix.os }}
path: build/
security:
name: Security Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Run gosec
uses: securego/gosec@master
with:
args: -fmt json -out gosec-report.json -severity medium ./...
- name: Upload security report
uses: actions/upload-artifact@v4
if: always()
with:
name: security-report
path: gosec-report.json
+26 -7
View File
@@ -44,7 +44,7 @@ import (
"github.com/luxfi/geth/rlp"
"github.com/luxfi/geth/trie"
"github.com/luxfi/geth/triedb"
"github.com/luxfi/evm/consensus/misc/eip4844"
"github.com/luxfi/geth/consensus/misc/eip4844"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/core/state"
"github.com/luxfi/evm/params"
@@ -180,16 +180,35 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
// Calculate the BlobBaseFee
var excessBlobGas uint64
if pre.Env.ExcessBlobGas != nil {
excessBlobGas := *pre.Env.ExcessBlobGas
vmContext.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas)
excessBlobGas = *pre.Env.ExcessBlobGas
// Create a minimal header for CalcBlobFee
header := &types.Header{
ExcessBlobGas: &excessBlobGas,
Time: pre.Env.Timestamp,
Number: new(big.Int).SetUint64(pre.Env.Number),
}
vmContext.BlobBaseFee = eip4844.CalcBlobFee(chainConfig, header)
} else {
// If it is not explicitly defined, but we have the parent values, we try
// to calculate it ourselves.
parentExcessBlobGas := pre.Env.ParentExcessBlobGas
parentBlobGasUsed := pre.Env.ParentBlobGasUsed
if parentExcessBlobGas != nil && parentBlobGasUsed != nil {
excessBlobGas = eip4844.CalcExcessBlobGas(*parentExcessBlobGas, *parentBlobGasUsed)
vmContext.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas)
// Create a minimal parent header for CalcExcessBlobGas
parentHeader := &types.Header{
ExcessBlobGas: parentExcessBlobGas,
BlobGasUsed: parentBlobGasUsed,
Time: pre.Env.Timestamp - 12, // Assume 12 second block time
Number: new(big.Int).SetUint64(pre.Env.Number - 1),
}
excessBlobGas = eip4844.CalcExcessBlobGas(chainConfig, parentHeader, pre.Env.Timestamp)
// Create current header for CalcBlobFee
header := &types.Header{
ExcessBlobGas: &excessBlobGas,
Time: pre.Env.Timestamp,
Number: new(big.Int).SetUint64(pre.Env.Number),
}
vmContext.BlobBaseFee = eip4844.CalcBlobFee(chainConfig, header)
}
}
// If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's
@@ -226,7 +245,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
txBlobGas := uint64(0)
if tx.Type() == types.BlobTxType {
txBlobGas = uint64(ethparams.BlobTxBlobGasPerBlob * len(tx.BlobHashes()))
if used, max := blobGasUsed+txBlobGas, uint64(eip4844.MaxBlobGasPerBlock); used > max {
if used, max := blobGasUsed+txBlobGas, eip4844.MaxBlobGasPerBlock(chainConfig, pre.Env.Timestamp); used > max {
err := fmt.Errorf("blob gas (%d) would exceed maximum allowance %d", used, max)
log.Warn("rejected tx", "index", i, "err", err)
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
@@ -364,7 +383,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB
sdb := state.NewDatabaseWithConfig(db, &triedb.Config{Preimages: true})
statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
for addr, a := range accounts {
statedb.SetCode(addr, a.Code)
statedb.SetCode(addr, a.Code, tracing.CodeChangeGenesis)
statedb.SetNonce(addr, a.Nonce, tracing.NonceChangeGenesis)
statedb.SetBalance(addr, uint256.MustFromBig(a.Balance), tracing.BalanceIncreaseGenesisBalance)
for k, v := range a.Storage {
+1 -1
View File
@@ -269,7 +269,7 @@ func runCmd(ctx *cli.Context) error {
}
} else {
if len(code) > 0 {
statedb.SetCode(receiver, code)
statedb.SetCode(receiver, code, tracing.CodeChangeUnspecified)
}
execFunc = func() ([]byte, uint64, error) {
return runtime.Call(receiver, input, &runtimeConfig)
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"context"
"github.com/luxfi/ids"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
)
// AppSender sends application messages
+10 -23
View File
@@ -3,11 +3,10 @@ package compat
import (
"context"
"errors"
consensusCore "github.com/luxfi/consensus/core/appsender"
"github.com/luxfi/ids"
"github.com/luxfi/node/utils/set"
consensusCore "github.com/luxfi/consensus/core"
consensusSet "github.com/luxfi/consensus/utils/set"
"github.com/luxfi/math/set"
)
var (
@@ -27,12 +26,8 @@ func NewAppSenderAdapter(sender consensusCore.AppSender) AppSender {
// SendAppRequest sends an application-level request
func (a *AppSenderAdapter) SendAppRequest(ctx context.Context, nodeIDs set.Set[ids.NodeID], requestID uint32, appRequestBytes []byte) error {
// Convert node set to consensus set
consensusNodeIDs := consensusSet.Set[ids.NodeID]{}
for id := range nodeIDs {
consensusNodeIDs.Add(id)
}
return a.wrapped.SendAppRequest(ctx, consensusNodeIDs, requestID, appRequestBytes)
// No conversion needed - same underlying type
return a.wrapped.SendAppRequest(ctx, nodeIDs, requestID, appRequestBytes)
}
// SendAppResponse sends an application-level response
@@ -47,22 +42,14 @@ func (a *AppSenderAdapter) SendAppError(ctx context.Context, nodeID ids.NodeID,
// SendAppGossip sends an application-level gossip message
func (a *AppSenderAdapter) SendAppGossip(ctx context.Context, nodeIDs set.Set[ids.NodeID], appGossipBytes []byte) error {
// Convert node set to consensus set
consensusNodeIDs := consensusSet.Set[ids.NodeID]{}
for id := range nodeIDs {
consensusNodeIDs.Add(id)
}
return a.wrapped.SendAppGossip(ctx, consensusNodeIDs, appGossipBytes)
// No conversion needed - same underlying type
return a.wrapped.SendAppGossip(ctx, nodeIDs, appGossipBytes)
}
// SendAppGossipSpecific sends a gossip message to specific nodes
func (a *AppSenderAdapter) SendAppGossipSpecific(ctx context.Context, nodeIDs set.Set[ids.NodeID], appGossipBytes []byte) error {
// Convert node set to consensus set
consensusNodeIDs := consensusSet.Set[ids.NodeID]{}
for id := range nodeIDs {
consensusNodeIDs.Add(id)
}
return a.wrapped.SendAppGossipSpecific(ctx, consensusNodeIDs, appGossipBytes)
// No conversion needed - same underlying type
return a.wrapped.SendAppGossipSpecific(ctx, nodeIDs, appGossipBytes)
}
// SendCrossChainAppRequest sends a cross-chain app request
@@ -81,4 +68,4 @@ func (a *AppSenderAdapter) SendCrossChainAppResponse(ctx context.Context, chainI
func (a *AppSenderAdapter) SendCrossChainAppError(ctx context.Context, chainID ids.ID, requestID uint32, errorCode int32, errorMessage string) error {
// Cross-chain communication not supported by consensus module
return ErrCrossChainNotSupported
}
}
+2 -2
View File
@@ -14,7 +14,7 @@ import (
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/trie"
"github.com/luxfi/evm/consensus"
"github.com/luxfi/evm/consensus/misc/eip4844"
"github.com/luxfi/geth/consensus/misc/eip4844"
"github.com/luxfi/evm/core/state"
"github.com/luxfi/evm/params"
"github.com/luxfi/evm/params/extras"
@@ -231,7 +231,7 @@ func (eng *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header *
if *header.ParentBeaconRoot != (common.Hash{}) {
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected empty", *header.ParentBeaconRoot)
}
if err := eip4844.VerifyEIP4844Header(parent, header); err != nil {
if err := eip4844.VerifyEIP4844Header(chain.Config(), parent, header); err != nil {
return err
}
if *header.BlobGasUsed > 0 { // VerifyEIP4844Header ensures BlobGasUsed is non-nil
-116
View File
@@ -1,116 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//
// This file is a derived work, based on the go-ethereum library whose original
// notices appear below.
//
// It is distributed under a license compatible with the licensing terms of the
// original code from which it is derived.
//
// Much love to the original authors for their work.
// **********
// Copyright 2023 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package eip4844
import (
"errors"
"fmt"
"math/big"
"github.com/luxfi/geth/core/types"
ethparams "github.com/luxfi/geth/params"
)
const (
// TODO: These constants seem to be missing from geth params
BlobTxBlobGaspriceUpdateFraction = 3338477
BlobTxTargetBlobGasPerBlock = 3 * ethparams.BlobTxBlobGasPerBlob
MaxBlobGasPerBlock = 6 * ethparams.BlobTxBlobGasPerBlob
)
var (
minBlobGasPrice = big.NewInt(ethparams.BlobTxMinBlobGasprice)
blobGaspriceUpdateFraction = big.NewInt(BlobTxBlobGaspriceUpdateFraction)
)
// VerifyEIP4844Header verifies the presence of the excessBlobGas field and that
// if the current block contains no transactions, the excessBlobGas is updated
// accordingly.
func VerifyEIP4844Header(parent, header *types.Header) error {
// Verify the header is not malformed
if header.ExcessBlobGas == nil {
return errors.New("header is missing excessBlobGas")
}
if header.BlobGasUsed == nil {
return errors.New("header is missing blobGasUsed")
}
// Verify that the blob gas used remains within reasonable limits.
if *header.BlobGasUsed > MaxBlobGasPerBlock {
return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, MaxBlobGasPerBlock)
}
if *header.BlobGasUsed%ethparams.BlobTxBlobGasPerBlob != 0 {
return fmt.Errorf("blob gas used %d not a multiple of blob gas per blob %d", header.BlobGasUsed, ethparams.BlobTxBlobGasPerBlob)
}
// Verify the excessBlobGas is correct based on the parent header
var (
parentExcessBlobGas uint64
parentBlobGasUsed uint64
)
if parent.ExcessBlobGas != nil {
parentExcessBlobGas = *parent.ExcessBlobGas
parentBlobGasUsed = *parent.BlobGasUsed
}
expectedExcessBlobGas := CalcExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed)
if *header.ExcessBlobGas != expectedExcessBlobGas {
return fmt.Errorf("invalid excessBlobGas: have %d, want %d, parent excessBlobGas %d, parent blobDataUsed %d",
*header.ExcessBlobGas, expectedExcessBlobGas, parentExcessBlobGas, parentBlobGasUsed)
}
return nil
}
// CalcExcessBlobGas calculates the excess blob gas after applying the set of
// blobs on top of the excess blob gas.
func CalcExcessBlobGas(parentExcessBlobGas uint64, parentBlobGasUsed uint64) uint64 {
excessBlobGas := parentExcessBlobGas + parentBlobGasUsed
if excessBlobGas < BlobTxTargetBlobGasPerBlock {
return 0
}
return excessBlobGas - BlobTxTargetBlobGasPerBlock
}
// CalcBlobFee calculates the blobfee from the header's excess blob gas field.
func CalcBlobFee(excessBlobGas uint64) *big.Int {
return fakeExponential(minBlobGasPrice, new(big.Int).SetUint64(excessBlobGas), blobGaspriceUpdateFraction)
}
// fakeExponential approximates factor * e ** (numerator / denominator) using
// Taylor expansion.
func fakeExponential(factor, numerator, denominator *big.Int) *big.Int {
var (
output = new(big.Int)
accum = new(big.Int).Mul(factor, denominator)
)
for i := 1; accum.Sign() > 0; i++ {
output.Add(output, accum)
accum.Mul(accum, numerator)
accum.Div(accum, denominator)
accum.Div(accum, big.NewInt(int64(i)))
}
return output.Div(output, denominator)
}
-125
View File
@@ -1,125 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//
// This file is a derived work, based on the go-ethereum library whose original
// notices appear below.
//
// It is distributed under a license compatible with the licensing terms of the
// original code from which it is derived.
//
// Much love to the original authors for their work.
// **********
// Copyright 2023 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package eip4844
import (
"fmt"
"math/big"
"testing"
ethparams "github.com/luxfi/evm/params"
)
func TestCalcExcessBlobGas(t *testing.T) {
var tests = []struct {
excess uint64
blobs uint64
want uint64
}{
// The excess blob gas should not increase from zero if the used blob
// slots are below - or equal - to the target.
{0, 0, 0},
{0, 1, 0},
{0, ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob, 0},
// If the target blob gas is exceeded, the excessBlobGas should increase
// by however much it was overshot
{0, (ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob) + 1, ethparams.BlobTxBlobGasPerBlob},
{1, (ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob) + 1, ethparams.BlobTxBlobGasPerBlob + 1},
{1, (ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob) + 2, 2*ethparams.BlobTxBlobGasPerBlob + 1},
// The excess blob gas should decrease by however much the target was
// under-shot, capped at zero.
{ethparams.BlobTxTargetBlobGasPerBlock, ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob, ethparams.BlobTxTargetBlobGasPerBlock},
{ethparams.BlobTxTargetBlobGasPerBlock, (ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob) - 1, ethparams.BlobTxTargetBlobGasPerBlock - ethparams.BlobTxBlobGasPerBlob},
{ethparams.BlobTxTargetBlobGasPerBlock, (ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob) - 2, ethparams.BlobTxTargetBlobGasPerBlock - (2 * ethparams.BlobTxBlobGasPerBlob)},
{ethparams.BlobTxBlobGasPerBlob - 1, (ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob) - 1, 0},
}
for i, tt := range tests {
result := CalcExcessBlobGas(tt.excess, tt.blobs*ethparams.BlobTxBlobGasPerBlob)
if result != tt.want {
t.Errorf("test %d: excess blob gas mismatch: have %v, want %v", i, result, tt.want)
}
}
}
func TestCalcBlobFee(t *testing.T) {
tests := []struct {
excessBlobGas uint64
blobfee int64
}{
{0, 1},
{2314057, 1},
{2314058, 2},
{10 * 1024 * 1024, 23},
}
for i, tt := range tests {
have := CalcBlobFee(tt.excessBlobGas)
if have.Int64() != tt.blobfee {
t.Errorf("test %d: blobfee mismatch: have %v want %v", i, have, tt.blobfee)
}
}
}
func TestFakeExponential(t *testing.T) {
tests := []struct {
factor int64
numerator int64
denominator int64
want int64
}{
// When numerator == 0 the return value should always equal the value of factor
{1, 0, 1, 1},
{38493, 0, 1000, 38493},
{0, 1234, 2345, 0}, // should be 0
{1, 2, 1, 6}, // approximate 7.389
{1, 4, 2, 6},
{1, 3, 1, 16}, // approximate 20.09
{1, 6, 2, 18},
{1, 4, 1, 49}, // approximate 54.60
{1, 8, 2, 50},
{10, 8, 2, 542}, // approximate 540.598
{11, 8, 2, 596}, // approximate 600.58
{1, 5, 1, 136}, // approximate 148.4
{1, 5, 2, 11}, // approximate 12.18
{2, 5, 2, 23}, // approximate 24.36
{1, 50000000, 2225652, 5709098764},
}
for i, tt := range tests {
f, n, d := big.NewInt(tt.factor), big.NewInt(tt.numerator), big.NewInt(tt.denominator)
original := fmt.Sprintf("%d %d %d", f, n, d)
have := fakeExponential(f, n, d)
if have.Int64() != tt.want {
t.Errorf("test %d: fake exponential mismatch: have %v want %v", i, have, tt.want)
}
later := fmt.Sprintf("%d %d %d", f, n, d)
if original != later {
t.Errorf("test %d: fake exponential modified arguments: have\n%v\nwant\n%v", i, later, original)
}
}
}
+4 -4
View File
@@ -52,7 +52,7 @@ import (
"github.com/luxfi/geth/triedb"
"github.com/luxfi/evm/commontype"
"github.com/luxfi/evm/consensus"
"github.com/luxfi/evm/consensus/misc/eip4844"
"github.com/luxfi/geth/consensus/misc/eip4844"
"github.com/luxfi/evm/core/state"
"github.com/luxfi/evm/core/state/snapshot"
"github.com/luxfi/evm/internal/version"
@@ -1411,7 +1411,7 @@ func (bc *BlockChain) insertBlock(block *types.Block, writes bool) error {
blockStateInitTimer.Inc(time.Since(substart).Milliseconds())
// Enable prefetching to pull in trie node paths while processing transactions
statedb.StartPrefetcher("chain", nil)
statedb.StartPrefetcher("chain", nil, nil)
defer statedb.StopPrefetcher()
// Process block using the parent state as reference point
@@ -1496,7 +1496,7 @@ func (bc *BlockChain) collectUnflattenedLogs(b *types.Block, removed bool) [][]*
var blobGasPrice *big.Int
excessBlobGas := b.ExcessBlobGas()
if excessBlobGas != nil {
blobGasPrice = eip4844.CalcBlobFee(*excessBlobGas)
blobGasPrice = eip4844.CalcBlobFee(bc.chainConfig, b.Header())
}
receipts := rawdb.ReadRawReceipts(bc.db, b.Hash(), b.NumberU64())
if err := receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.Time(), b.BaseFee(), blobGasPrice, b.Transactions()); err != nil {
@@ -1772,7 +1772,7 @@ func (bc *BlockChain) reprocessBlock(parent *types.Block, current *types.Block)
}
// Enable prefetching to pull in trie node paths while processing transactions
statedb.StartPrefetcher("chain", nil)
statedb.StartPrefetcher("chain", nil, nil)
defer statedb.StopPrefetcher()
// Process previously stored block
+3 -11
View File
@@ -39,7 +39,7 @@ import (
"github.com/luxfi/geth/triedb"
"github.com/luxfi/evm/commontype"
"github.com/luxfi/evm/consensus"
"github.com/luxfi/evm/consensus/misc/eip4844"
"github.com/luxfi/geth/consensus/misc/eip4844"
"github.com/luxfi/evm/constants"
"github.com/luxfi/evm/core/state"
"github.com/luxfi/evm/params"
@@ -356,7 +356,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
}
var blobGasPrice *big.Int
if block.ExcessBlobGas() != nil {
blobGasPrice = eip4844.CalcBlobFee(*block.ExcessBlobGas())
blobGasPrice = eip4844.CalcBlobFee(config, block.Header())
}
if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.BaseFee(), blobGasPrice, txs); err != nil {
panic(err)
@@ -424,15 +424,7 @@ func (cm *chainMaker) makeHeader(parent *types.Block, gap uint64, state *state.S
}
if cm.config.IsCancun(header.Number, header.Time) {
var (
parentExcessBlobGas uint64
parentBlobGasUsed uint64
)
if parent.ExcessBlobGas() != nil {
parentExcessBlobGas = *parent.ExcessBlobGas()
parentBlobGasUsed = *parent.BlobGasUsed()
}
excessBlobGas := eip4844.CalcExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed)
excessBlobGas := eip4844.CalcExcessBlobGas(cm.config, parent.Header(), header.Time)
header.ExcessBlobGas = &excessBlobGas
header.BlobGasUsed = new(uint64)
header.ParentBeaconRoot = new(common.Hash)
-39
View File
@@ -1,39 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package extstate
import (
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/tracing"
"github.com/holiman/uint256"
)
// PrecompileStateDBAdapter adapts the new geth StateDB interface (with BalanceChangeReason)
// to the legacy precompile contract.StateDB interface that doesn't use BalanceChangeReason.
type PrecompileStateDBAdapter struct {
*StateDB
}
// AddBalance implements the legacy AddBalance without BalanceChangeReason
func (a *PrecompileStateDBAdapter) AddBalance(addr common.Address, amount *uint256.Int) {
// Use BalanceChangeUnspecified for precompile balance changes
a.StateDB.AddBalance(addr, amount, tracing.BalanceChangeUnspecified)
}
// SetNonce implements the legacy SetNonce without NonceChangeReason
func (a *PrecompileStateDBAdapter) SetNonce(addr common.Address, nonce uint64) {
// Use NonceChangeUnspecified for precompile nonce changes
a.StateDB.SetNonce(addr, nonce, tracing.NonceChangeUnspecified)
}
// SetState implements the contract.StateDB interface (no return value)
func (a *PrecompileStateDBAdapter) SetState(addr common.Address, key, value common.Hash) {
// Call the underlying SetState which returns the old value but we ignore it
_ = a.StateDB.SetState(addr, key, value)
}
// NewPrecompileAdapter creates a new adapter for precompile compatibility
func NewPrecompileAdapter(stateDB *StateDB) *PrecompileStateDBAdapter {
return &PrecompileStateDBAdapter{StateDB: stateDB}
}
-33
View File
@@ -1,33 +0,0 @@
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package extstate
import (
"github.com/luxfi/geth/core/state"
"github.com/luxfi/geth/ethdb"
"github.com/luxfi/geth/triedb"
"github.com/luxfi/evm/triedb/firewood"
)
func NewDatabaseWithConfig(db ethdb.Database, config *triedb.Config) state.Database {
coredb := state.NewDatabaseWithConfig(db, config)
return wrapIfFirewood(coredb)
}
func NewDatabaseWithNodeDB(db ethdb.Database, triedb *triedb.Database) state.Database {
coredb := state.NewDatabaseWithNodeDB(db, triedb)
return wrapIfFirewood(coredb)
}
func wrapIfFirewood(db state.Database) state.Database {
fw, ok := db.TrieDB().Backend().(*firewood.Database)
if !ok {
return db
}
return &firewoodAccessorDb{
Database: db,
fw: fw,
}
}
-368
View File
@@ -1,368 +0,0 @@
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package extstate
import (
"encoding/binary"
"math/rand"
"path/filepath"
"slices"
"testing"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/state"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/crypto"
"github.com/luxfi/geth/libevm/stateconf"
"github.com/luxfi/geth/trie/trienode"
"github.com/luxfi/geth/triedb"
"github.com/holiman/uint256"
"github.com/stretchr/testify/require"
"github.com/luxfi/evm/triedb/firewood"
"github.com/luxfi/evm/triedb/hashdb"
)
const (
commit byte = iota
createAccount
updateAccount
deleteAccount
addStorage
updateStorage
deleteStorage
maxStep
)
var stepMap = map[byte]string{
commit: "commit",
createAccount: "createAccount",
updateAccount: "updateAccount",
deleteAccount: "deleteAccount",
addStorage: "addStorage",
updateStorage: "updateStorage",
deleteStorage: "deleteStorage",
}
type fuzzState struct {
require *require.Assertions
// current state
currentAddrs []common.Address
currentStorageInputIndices map[common.Address]uint64
inputCounter uint64
blockNumber uint64
// pending changes to be committed
merkleTries []*merkleTrie
}
type merkleTrie struct {
name string
ethDatabase state.Database
accountTrie state.Trie
openStorageTries map[common.Address]state.Trie
lastRoot common.Hash
}
func newFuzzState(t *testing.T) *fuzzState {
r := require.New(t)
hashState := NewDatabaseWithConfig(
rawdb.NewMemoryDatabase(),
&triedb.Config{
DBOverride: hashdb.Defaults.BackendConstructor,
})
ethRoot := types.EmptyRootHash
hashTr, err := hashState.OpenTrie(ethRoot)
r.NoError(err)
t.Cleanup(func() {
r.NoError(hashState.TrieDB().Close())
})
firewoodMemdb := rawdb.NewMemoryDatabase()
fwCfg := firewood.Defaults
fwCfg.FilePath = filepath.Join(t.TempDir(), "firewood") // Use a temporary directory for the Firewood
firewoodState := NewDatabaseWithConfig(
firewoodMemdb,
&triedb.Config{
DBOverride: fwCfg.BackendConstructor,
},
)
fwTr, err := firewoodState.OpenTrie(ethRoot)
r.NoError(err)
t.Cleanup(func() {
r.NoError(firewoodState.TrieDB().Close())
})
return &fuzzState{
merkleTries: []*merkleTrie{
{
name: "hash",
ethDatabase: hashState,
accountTrie: hashTr,
openStorageTries: make(map[common.Address]state.Trie),
lastRoot: ethRoot,
},
{
name: "firewood",
ethDatabase: firewoodState,
accountTrie: fwTr,
openStorageTries: make(map[common.Address]state.Trie),
lastRoot: ethRoot,
},
},
currentStorageInputIndices: make(map[common.Address]uint64),
require: r,
}
}
// commit writes the pending changes to both tries and clears the pending changes
func (fs *fuzzState) commit() {
for _, tr := range fs.merkleTries {
mergedNodeSet := trienode.NewMergedNodeSet()
for addr, str := range tr.openStorageTries {
accountStateRoot, set, err := str.Commit(false)
fs.require.NoError(err, "failed to commit storage trie for account %s in %s", addr.Hex(), tr.name)
// A no-op change returns a nil set, which will cause merge to panic.
if set != nil {
fs.require.NoError(mergedNodeSet.Merge(set), "failed to merge storage trie nodeset for account %s in %s", addr.Hex(), tr.name)
}
acc, err := tr.accountTrie.GetAccount(addr)
fs.require.NoError(err, "failed to get account %s in %s", addr.Hex(), tr.name)
// If the account was deleted, we can skip updating the account's
// state root.
fs.require.NotNil(acc, "account %s is nil in %s", addr.Hex(), tr.name)
acc.Root = accountStateRoot
fs.require.NoError(tr.accountTrie.UpdateAccount(addr, acc), "failed to update account %s in %s", addr.Hex(), tr.name)
}
updatedRoot, set, err := tr.accountTrie.Commit(true)
fs.require.NoError(err, "failed to commit account trie in %s", tr.name)
// A no-op change returns a nil set, which will cause merge to panic.
if set != nil {
fs.require.NoError(mergedNodeSet.Merge(set), "failed to merge account trie nodeset in %s", tr.name)
}
// HashDB/PathDB only allows updating the triedb if there have been changes.
if _, ok := tr.ethDatabase.TrieDB().Backend().(*firewood.Database); ok {
triedbopt := stateconf.WithTrieDBUpdatePayload(common.Hash{byte(int64(fs.blockNumber - 1))}, common.Hash{byte(int64(fs.blockNumber))})
fs.require.NoError(tr.ethDatabase.TrieDB().Update(updatedRoot, tr.lastRoot, fs.blockNumber, mergedNodeSet, nil, triedbopt), "failed to update triedb in %s", tr.name)
tr.lastRoot = updatedRoot
} else if updatedRoot != tr.lastRoot {
fs.require.NoError(tr.ethDatabase.TrieDB().Update(updatedRoot, tr.lastRoot, fs.blockNumber, mergedNodeSet, nil), "failed to update triedb in %s", tr.name)
tr.lastRoot = updatedRoot
}
tr.openStorageTries = make(map[common.Address]state.Trie)
fs.require.NoError(tr.ethDatabase.TrieDB().Commit(updatedRoot, true),
"failed to commit %s: expected hashdb root %s", tr.name, fs.merkleTries[0].lastRoot.Hex())
tr.accountTrie, err = tr.ethDatabase.OpenTrie(tr.lastRoot)
fs.require.NoError(err, "failed to reopen account trie for %s", tr.name)
}
fs.blockNumber++
// After computing the new root for each trie, we can confirm that the hashing matches
expectedRoot := fs.merkleTries[0].lastRoot
for i, tr := range fs.merkleTries[1:] {
fs.require.Equalf(expectedRoot, tr.lastRoot,
"root mismatch for %s: expected %x, got %x (trie index %d)",
tr.name, expectedRoot.Hex(), tr.lastRoot.Hex(), i,
)
}
}
// createAccount generates a new, unique account and adds it to both tries and the tracked
// current state.
func (fs *fuzzState) createAccount() {
fs.inputCounter++
addr := common.BytesToAddress(crypto.Keccak256Hash(binary.BigEndian.AppendUint64(nil, fs.inputCounter)).Bytes())
acc := &types.StateAccount{
Nonce: 1,
Balance: uint256.NewInt(100),
Root: types.EmptyRootHash,
CodeHash: types.EmptyCodeHash[:],
}
fs.currentAddrs = append(fs.currentAddrs, addr)
for _, tr := range fs.merkleTries {
fs.require.NoError(tr.accountTrie.UpdateAccount(addr, acc), "failed to create account %s in %s", addr.Hex(), tr.name)
}
}
// selectAccount returns a random account and account hash for the provided index
// assumes: addrIndex < len(tr.currentAddrs)
func (fs *fuzzState) selectAccount(addrIndex int) common.Address {
return fs.currentAddrs[addrIndex]
}
// updateAccount selects a random account, increments its nonce, and adds the update
// to the pending changes for both tries.
func (fs *fuzzState) updateAccount(addrIndex int) {
addr := fs.selectAccount(addrIndex)
for _, tr := range fs.merkleTries {
acc, err := tr.accountTrie.GetAccount(addr)
fs.require.NoError(err, "failed to get account %s for update in %s", addr.Hex(), tr.name)
fs.require.NotNil(acc, "account %s is nil for update in %s", addr.Hex(), tr.name)
acc.Nonce++
acc.CodeHash = crypto.Keccak256Hash(acc.CodeHash[:]).Bytes()
acc.Balance.Add(acc.Balance, uint256.NewInt(3))
fs.require.NoError(tr.accountTrie.UpdateAccount(addr, acc), "failed to update account %s in %s", addr.Hex(), tr.name)
}
}
// deleteAccount selects a random account and deletes it from both tries and the tracked
// current state.
func (fs *fuzzState) deleteAccount(accountIndex int) {
deleteAddr := fs.selectAccount(accountIndex)
fs.currentAddrs = slices.DeleteFunc(fs.currentAddrs, func(addr common.Address) bool {
return deleteAddr == addr
})
for _, tr := range fs.merkleTries {
fs.require.NoError(tr.accountTrie.DeleteAccount(deleteAddr), "failed to delete account %s in %s", deleteAddr.Hex(), tr.name)
delete(tr.openStorageTries, deleteAddr) // remove any open storage trie for the deleted account
}
}
// openStorageTrie opens the storage trie for the provided account address.
// Uses an already opened trie, if there's a pending update to the ethereum nested
// storage trie.
//
// must maintain a map of currently open storage tries, so we can defer committing them
// until commit as opposed to after each storage update.
// This mimics the actual handling of state commitments in the EVM where storage tries are all committed immediately
// before updating the account trie along with the updated storage trie roots:
// https://github.com/ava-labs/libevm/blob/0bfe4a0380c86d7c9bf19fe84368b9695fcb96c7/core/state/statedb.go#L1155
//
// If we attempt to commit the storage tries after each operation, then attempting to re-open the storage trie
// with an updated storage trie root from ethDatabase will fail since the storage trie root will not have been
// persisted yet - leading to a missing trie node error.
func (fs *fuzzState) openStorageTrie(addr common.Address, tr *merkleTrie) state.Trie {
storageTrie, ok := tr.openStorageTries[addr]
if ok {
return storageTrie
}
acc, err := tr.accountTrie.GetAccount(addr)
fs.require.NoError(err, "failed to get account %s for storage trie in %s", addr.Hex(), tr.name)
fs.require.NotNil(acc, "account %s not found in %s", addr.Hex(), tr.name)
storageTrie, err = tr.ethDatabase.OpenStorageTrie(tr.lastRoot, addr, acc.Root, tr.accountTrie)
fs.require.NoError(err, "failed to open storage trie for %s in %s", addr.Hex(), tr.name)
tr.openStorageTries[addr] = storageTrie
return storageTrie
}
// addStorage selects an account and adds a new storage key-value pair to the account.
func (fs *fuzzState) addStorage(accountIndex int) {
addr := fs.selectAccount(accountIndex)
// Increment storageInputIndices for the account and take the next input to generate
// a new storage key-value pair for the account.
fs.currentStorageInputIndices[addr]++
storageIndex := fs.currentStorageInputIndices[addr]
key := crypto.Keccak256Hash(binary.BigEndian.AppendUint64(nil, storageIndex))
keyHash := crypto.Keccak256Hash(key[:])
val := crypto.Keccak256Hash(keyHash[:])
for _, tr := range fs.merkleTries {
str := fs.openStorageTrie(addr, tr)
fs.require.NoError(str.UpdateStorage(addr, key[:], val[:]), "failed to add storage for account %s in %s", addr.Hex(), tr.name)
}
fs.currentStorageInputIndices[addr]++
}
// updateStorage selects an account and updates an existing storage key-value pair
// note: this may "update" a key-value pair that doesn't exist if it was previously deleted.
func (fs *fuzzState) updateStorage(accountIndex int, storageIndexInput uint64) {
addr := fs.selectAccount(accountIndex)
storageIndex := fs.currentStorageInputIndices[addr]
storageIndex %= storageIndexInput
storageKey := crypto.Keccak256Hash(binary.BigEndian.AppendUint64(nil, storageIndex))
storageKeyHash := crypto.Keccak256Hash(storageKey[:])
fs.inputCounter++
updatedValInput := binary.BigEndian.AppendUint64(storageKeyHash[:], fs.inputCounter)
updatedVal := crypto.Keccak256Hash(updatedValInput[:])
for _, tr := range fs.merkleTries {
str := fs.openStorageTrie(addr, tr)
fs.require.NoError(str.UpdateStorage(addr, storageKey[:], updatedVal[:]), "failed to update storage for account %s in %s", addr.Hex(), tr.name)
}
}
// deleteStorage selects an account and deletes an existing storage key-value pair
// note: this may "delete" a key-value pair that doesn't exist if it was previously deleted.
func (fs *fuzzState) deleteStorage(accountIndex int, storageIndexInput uint64) {
addr := fs.selectAccount(accountIndex)
storageIndex := fs.currentStorageInputIndices[addr]
storageIndex %= storageIndexInput
storageKey := crypto.Keccak256Hash(binary.BigEndian.AppendUint64(nil, storageIndex))
for _, tr := range fs.merkleTries {
str := fs.openStorageTrie(addr, tr)
fs.require.NoError(str.DeleteStorage(addr, storageKey[:]), "failed to delete storage for account %s in %s", addr.Hex(), tr.name)
}
}
func FuzzTree(f *testing.F) {
for randSeed := range int64(1000) {
rand := rand.New(rand.NewSource(randSeed))
steps := make([]byte, 32)
_, err := rand.Read(steps)
if err != nil {
f.Fatal(err)
}
f.Add(randSeed, steps)
}
f.Fuzz(func(t *testing.T, randSeed int64, byteSteps []byte) {
fuzzState := newFuzzState(t)
rand := rand.New(rand.NewSource(randSeed))
for range 10 {
fuzzState.createAccount()
}
fuzzState.commit()
const maxSteps = 1000
if len(byteSteps) > maxSteps {
byteSteps = byteSteps[:maxSteps]
}
for _, step := range byteSteps {
step = step % maxStep
t.Log(stepMap[step])
switch step {
case commit:
fuzzState.commit()
case createAccount:
fuzzState.createAccount()
case updateAccount:
if len(fuzzState.currentAddrs) > 0 {
fuzzState.updateAccount(rand.Intn(len(fuzzState.currentAddrs)))
}
case deleteAccount:
if len(fuzzState.currentAddrs) > 0 {
fuzzState.deleteAccount(rand.Intn(len(fuzzState.currentAddrs)))
}
case addStorage:
if len(fuzzState.currentAddrs) > 0 {
fuzzState.addStorage(rand.Intn(len(fuzzState.currentAddrs)))
}
case updateStorage:
if len(fuzzState.currentAddrs) > 0 {
fuzzState.updateStorage(rand.Intn(len(fuzzState.currentAddrs)), rand.Uint64())
}
case deleteStorage:
if len(fuzzState.currentAddrs) > 0 {
fuzzState.deleteStorage(rand.Intn(len(fuzzState.currentAddrs)), rand.Uint64())
}
default:
t.Fatalf("unknown step: %d", step)
}
}
})
}
@@ -1,51 +0,0 @@
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package extstate
import (
"fmt"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/state"
"github.com/luxfi/evm/triedb/firewood"
)
var (
_ state.Database = (*firewoodAccessorDb)(nil)
_ state.Trie = (*firewood.AccountTrie)(nil)
_ state.Trie = (*firewood.StorageTrie)(nil)
)
type firewoodAccessorDb struct {
state.Database
fw *firewood.Database
}
// OpenTrie opens the main account trie.
func (db *firewoodAccessorDb) OpenTrie(root common.Hash) (state.Trie, error) {
return firewood.NewAccountTrie(root, db.fw)
}
// OpenStorageTrie opens a wrapped version of the account trie.
func (*firewoodAccessorDb) OpenStorageTrie(_ common.Hash, _ common.Address, accountRoot common.Hash, self state.Trie) (state.Trie, error) {
accountTrie, ok := self.(*firewood.AccountTrie)
if !ok {
return nil, fmt.Errorf("Invalid account trie type: %T", self)
}
return firewood.NewStorageTrie(accountTrie, accountRoot)
}
// CopyTrie returns a deep copy of the given trie.
// It can be altered by the caller.
func (*firewoodAccessorDb) CopyTrie(t state.Trie) state.Trie {
switch t := t.(type) {
case *firewood.AccountTrie:
return t.Copy()
case *firewood.StorageTrie:
return nil // The storage trie just wraps the account trie, so we must re-open it separately.
default:
panic(fmt.Errorf("unknown trie type %T", t))
}
}
-27
View File
@@ -1,27 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package extstate
import (
"github.com/luxfi/geth/core/state"
"github.com/luxfi/evm/utils"
)
type workerPool struct {
*utils.BoundedWorkers
}
func (wp *workerPool) Done() {
// Done is guaranteed to only be called after all work is already complete,
// so we call Wait for goroutines to finish before returning.
wp.BoundedWorkers.Wait()
}
func WithConcurrentWorkers(prefetchers int) state.PrefetcherOption {
pool := &workerPool{
BoundedWorkers: utils.NewBoundedWorkers(prefetchers),
}
return state.WithWorkerPools(func() state.WorkerPool { return pool })
}
-69
View File
@@ -1,69 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package extstate
import (
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/core/vm"
"github.com/luxfi/evm/params"
"github.com/luxfi/evm/predicate"
)
type VmStateDB interface {
vm.StateDB
Logs() []*types.Log
GetTxHash() common.Hash
}
// Allow embedding VmStateDB without exporting the embedded field.
type vmStateDB = VmStateDB
type StateDB struct {
vmStateDB
// Ordered storage slots to be used in predicate verification as set in the tx access list.
// Only set in [StateDB.Prepare], and un-modified through execution.
predicateStorageSlots map[common.Address][][]byte
}
// New creates a new [*StateDB] with the given [VmStateDB], effectively wrapping it
// with additional functionality.
func New(vm VmStateDB) *StateDB {
return &StateDB{
vmStateDB: vm,
predicateStorageSlots: make(map[common.Address][][]byte),
}
}
func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
// Note: This method cannot access extras.Rules directly without chainConfig and timestamp
// Predicate storage slots will be empty unless PrepareWithExtrasRules is used
s.predicateStorageSlots = make(map[common.Address][][]byte)
s.vmStateDB.Prepare(rules, sender, coinbase, dst, precompiles, list)
}
// PrepareWithExtrasRules prepares the state with extras.Rules for predicate support
func (s *StateDB) PrepareWithExtrasRules(rules params.Rules, extrasRules predicate.PredicaterExistChecker, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
s.predicateStorageSlots = predicate.PreparePredicateStorageSlots(extrasRules, list)
s.vmStateDB.Prepare(rules, sender, coinbase, dst, precompiles, list)
}
// GetPredicateStorageSlots returns the storage slots associated with the address, index pair.
// A list of access tuples can be included within transaction types post EIP-2930. The address
// is declared directly on the access tuple and the index is the i'th occurrence of an access
// tuple with the specified address.
//
// Ex. AccessList[[AddrA, Predicate1], [AddrB, Predicate2], [AddrA, Predicate3]]
// In this case, the caller could retrieve predicates 1-3 with the following calls:
// GetPredicateStorageSlots(AddrA, 0) -> Predicate1
// GetPredicateStorageSlots(AddrB, 0) -> Predicate2
// GetPredicateStorageSlots(AddrA, 1) -> Predicate3
func (s *StateDB) GetPredicateStorageSlots(address common.Address, index int) ([]byte, bool) {
predicates, exists := s.predicateStorageSlots[address]
if !exists || index >= len(predicates) {
return nil, false
}
return predicates[index], true
}
+128
View File
@@ -0,0 +1,128 @@
package extstate
import (
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/core/tracing"
"github.com/holiman/uint256"
"github.com/luxfi/evm/core/state"
"github.com/luxfi/evm/precompile/contract"
)
// ExtState wraps a StateDB for extended state operations
type ExtState struct {
stateDB *state.StateDB
}
// New creates a new ExtState instance
func New(stateDB *state.StateDB) *ExtState {
return &ExtState{
stateDB: stateDB,
}
}
// PrecompileAdapter adapts ExtState for precompile usage
type PrecompileAdapter struct {
extState *ExtState
}
// NewPrecompileAdapter creates a new PrecompileAdapter
func NewPrecompileAdapter(extState *ExtState) contract.StateDB {
return &PrecompileAdapter{
extState: extState,
}
}
// GetState gets state value
func (p *PrecompileAdapter) GetState(addr common.Address, key common.Hash) common.Hash {
if p.extState.stateDB != nil {
return p.extState.stateDB.GetState(addr, key)
}
return common.Hash{}
}
// SetState sets state value
func (p *PrecompileAdapter) SetState(addr common.Address, key common.Hash, value common.Hash) {
if p.extState.stateDB != nil {
p.extState.stateDB.SetState(addr, key, value)
}
}
// GetNonce gets account nonce
func (p *PrecompileAdapter) GetNonce(addr common.Address) uint64 {
if p.extState.stateDB != nil {
return p.extState.stateDB.GetNonce(addr)
}
return 0
}
// SetNonce sets account nonce
func (p *PrecompileAdapter) SetNonce(addr common.Address, nonce uint64) {
if p.extState.stateDB != nil {
p.extState.stateDB.SetNonce(addr, nonce, tracing.NonceChangeUnspecified)
}
}
// GetBalance gets account balance
func (p *PrecompileAdapter) GetBalance(addr common.Address) *uint256.Int {
if p.extState.stateDB != nil {
return p.extState.stateDB.GetBalance(addr)
}
return new(uint256.Int)
}
// AddBalance adds balance to an account
func (p *PrecompileAdapter) AddBalance(addr common.Address, amount *uint256.Int) {
if p.extState.stateDB != nil && amount != nil {
p.extState.stateDB.AddBalance(addr, amount, tracing.BalanceChangeUnspecified)
}
}
// CreateAccount creates a new account
func (p *PrecompileAdapter) CreateAccount(addr common.Address) {
if p.extState.stateDB != nil {
p.extState.stateDB.CreateAccount(addr)
}
}
// Exist returns whether an account exists
func (p *PrecompileAdapter) Exist(addr common.Address) bool {
if p.extState.stateDB != nil {
return p.extState.stateDB.Exist(addr)
}
return false
}
// AddLog adds a log entry
func (p *PrecompileAdapter) AddLog(log *types.Log) {
if p.extState.stateDB != nil {
p.extState.stateDB.AddLog(log)
}
}
// GetPredicateStorageSlots gets predicate storage slots
func (p *PrecompileAdapter) GetPredicateStorageSlots(address common.Address, index int) ([]byte, bool) {
// Not implemented in base StateDB
return nil, false
}
// GetTxHash gets the current transaction hash
func (p *PrecompileAdapter) GetTxHash() common.Hash {
// Not implemented in base StateDB
return common.Hash{}
}
// Snapshot returns the current revision number
func (p *PrecompileAdapter) Snapshot() int {
if p.extState.stateDB != nil {
return p.extState.stateDB.Snapshot()
}
return 0
}
// RevertToSnapshot reverts to a previous snapshot
func (p *PrecompileAdapter) RevertToSnapshot(snapshot int) {
if p.extState.stateDB != nil {
p.extState.stateDB.RevertToSnapshot(snapshot)
}
}
+1 -1
View File
@@ -339,7 +339,7 @@ func (g *Genesis) toBlock(db ethdb.Database, triedb *triedb.Database) *types.Blo
// allocation
for addr, account := range g.Alloc {
statedb.SetBalance(addr, uint256.MustFromBig(account.Balance), tracing.BalanceIncreaseGenesisBalance)
statedb.SetCode(addr, account.Code)
statedb.SetCode(addr, account.Code, tracing.CodeChangeGenesis)
statedb.SetNonce(addr, account.Nonce, tracing.NonceChangeGenesis)
for key, value := range account.Storage {
statedb.SetState(addr, key, value)
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"errors"
"fmt"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/log"
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"testing"
"github.com/luxfi/consensus/engine/chain/block"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/evm/params"
+1 -1
View File
@@ -36,7 +36,7 @@ func (a *StateUpgradeAdapter) SetState(addr common.Address, key, value common.Ha
// SetCode implements the stateupgrade.StateDB interface (no return value)
func (a *StateUpgradeAdapter) SetCode(addr common.Address, code []byte) {
// Call the underlying SetCode which returns the previous code but we ignore it
_ = a.StateDB.SetCode(addr, code)
_ = a.StateDB.SetCode(addr, code, tracing.CodeChangeUnspecified)
}
// NewStateUpgradeAdapter creates a new adapter for state upgrade compatibility
+1 -1
View File
@@ -60,7 +60,7 @@ func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *uint64,
// Set the code of the precompile's address to a non-zero length byte slice to ensure that the precompile
// can be called from within Solidity contracts. Solidity adds a check before invoking a contract to ensure
// that it does not attempt to invoke a non-existent contract.
statedb.SetCode(module.Address, []byte{0x1})
statedb.SetCode(module.Address, []byte{0x1}, tracing.CodeChangeUnspecified)
extstatedb := extstate.New(statedb)
adapter := extstate.NewPrecompileAdapter(extstatedb)
if err := module.Configure(params.GetExtra(c), activatingConfig, adapter, blockContext); err != nil {
+1 -1
View File
@@ -41,7 +41,7 @@ import (
"github.com/luxfi/geth/trie"
"github.com/luxfi/evm/consensus"
"github.com/luxfi/evm/consensus/dummy"
"github.com/luxfi/evm/consensus/misc/eip4844"
"github.com/luxfi/geth/consensus/misc/eip4844"
"github.com/luxfi/evm/params"
"github.com/luxfi/evm/params/extras"
"github.com/luxfi/evm/plugin/evm/customtypes"
+3 -3
View File
@@ -47,7 +47,7 @@ import (
"github.com/luxfi/geth/metrics"
ethparams "github.com/luxfi/geth/params"
"github.com/luxfi/geth/rlp"
"github.com/luxfi/evm/consensus/misc/eip4844"
"github.com/luxfi/geth/consensus/misc/eip4844"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/core/state"
"github.com/luxfi/evm/core/txpool"
@@ -433,7 +433,7 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserve txpool.Addres
blobfee = uint256.NewInt(ethparams.BlobTxMinBlobGasprice)
)
if p.head.ExcessBlobGas != nil {
blobfee = uint256.MustFromBig(eip4844.CalcBlobFee(*p.head.ExcessBlobGas))
blobfee = uint256.MustFromBig(eip4844.CalcBlobFee(p.chain.Config(), p.head))
}
p.evict = newPriceHeap(basefee, blobfee, &p.index)
@@ -870,7 +870,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) {
blobfee = uint256.MustFromBig(big.NewInt(ethparams.BlobTxMinBlobGasprice))
)
if newHead.ExcessBlobGas != nil {
blobfee = uint256.MustFromBig(eip4844.CalcBlobFee(*newHead.ExcessBlobGas))
blobfee = uint256.MustFromBig(eip4844.CalcBlobFee(p.chain.Config(), newHead))
}
p.evict.reinit(basefee, blobfee, false)
+1 -1
View File
@@ -50,7 +50,7 @@ import (
ethparams "github.com/luxfi/geth/params"
"github.com/luxfi/geth/rlp"
"github.com/luxfi/evm/commontype"
"github.com/luxfi/evm/consensus/misc/eip4844"
"github.com/luxfi/geth/consensus/misc/eip4844"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/core/state"
"github.com/luxfi/evm/core/txpool"
+8 -8
View File
@@ -582,18 +582,18 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address]
// Convert the new uint256.Int types to the old big.Int ones used by the legacy pool
var (
minTipBig *big.Int
baseFeeBig *big.Int
minTip256 *uint256.Int
baseFee256 *uint256.Int
)
if filter.MinTip != nil {
minTipBig = filter.MinTip.ToBig()
minTip256 = filter.MinTip
}
if filter.BaseFee != nil {
baseFeeBig = filter.BaseFee.ToBig()
baseFee256 = filter.BaseFee
}
if baseFeeBig == nil {
baseFeeBig = pool.priced.urgent.baseFee
if baseFee256 == nil {
baseFee256 = uint256.MustFromBig(pool.priced.urgent.baseFee)
}
pending := make(map[common.Address][]*txpool.LazyTransaction, len(pool.pending))
@@ -601,9 +601,9 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address]
txs := list.Flatten()
// If the miner requests tip enforcement, cap the lists now
if minTipBig != nil && !pool.locals.contains(addr) {
if minTip256 != nil && !pool.locals.contains(addr) {
for i, tx := range txs {
if tx.EffectiveGasTipIntCmp(minTipBig, baseFeeBig) < 0 {
if tx.EffectiveGasTipIntCmp(minTip256, baseFee256) < 0 {
txs = txs[:i]
break
}
+2 -1
View File
@@ -507,7 +507,8 @@ func (h *priceHeap) Less(i, j int) bool {
func (h *priceHeap) cmp(a, b *types.Transaction) int {
if h.baseFee != nil {
// Compare effective tips if baseFee is specified
if c := a.EffectiveGasTipCmp(b, h.baseFee); c != 0 {
baseFee256 := uint256.MustFromBig(h.baseFee)
if c := a.EffectiveGasTipCmp(b, baseFee256); c != 0 {
return c
}
}
+2 -1
View File
@@ -33,6 +33,7 @@ import (
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/tracing"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/core/vm"
"github.com/luxfi/crypto"
@@ -150,7 +151,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
cfg.State.CreateAccount(address)
// set the receiver's (the executing contract) code for execution.
cfg.State.SetCode(address, code)
cfg.State.SetCode(address, code, tracing.CodeChangeUnspecified)
// Call the code with the given configuration.
ret, _, err := vmenv.Call(
sender,
BIN
View File
Binary file not shown.
+4 -4
View File
@@ -14,15 +14,15 @@ import (
"github.com/luxfi/node/api/info"
"github.com/luxfi/ids"
logging "github.com/luxfi/log"
"github.com/luxfi/metric"
metrics "github.com/luxfi/metric"
// "github.com/luxfi/node/network/p2p"
"github.com/luxfi/node/network/peer"
"github.com/luxfi/node/proto/pb/sdk"
"github.com/luxfi/node/consensus/networking/router"
"github.com/luxfi/consensus/networking/router"
"github.com/luxfi/node/utils/compression"
"github.com/luxfi/warp"
"github.com/luxfi/warp/payload"
"github.com/luxfi/node/wallet/subnet/primary"
"github.com/luxfi/node/wallet/net/primary"
"github.com/luxfi/evm/warp/messages"
p2pmessage "github.com/luxfi/node/message"
@@ -60,7 +60,7 @@ func main() {
unsignedWarp, err := warp.NewUnsignedMessage(
networkID,
sourceChainID,
sourceChainID, // Pass ids.ID directly
addressedCall.Bytes(),
)
if err != nil {
+18 -27
View File
@@ -1,10 +1,9 @@
module github.com/luxfi/evm
go 1.24.5
go 1.24.6
require (
github.com/VictoriaMetrics/fastcache v1.13.0
github.com/antithesishq/antithesis-sdk-go v0.4.4
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/deckarep/golang-set/v2 v2.8.0
github.com/fjl/gencodec v0.1.1
@@ -16,16 +15,17 @@ require (
github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db
github.com/holiman/bloomfilter/v2 v2.0.3
github.com/holiman/uint256 v1.3.2
github.com/luxfi/consensus v1.16.16
github.com/luxfi/consensus v1.18.0
github.com/luxfi/crypto v1.16.16
github.com/luxfi/database v1.1.13
github.com/luxfi/geth v1.16.34
github.com/luxfi/go-bip39 v1.1.1
github.com/luxfi/ids v1.0.2
github.com/luxfi/log v1.1.22
github.com/luxfi/math v0.1.0
github.com/luxfi/metric v1.3.0
github.com/luxfi/node v1.16.15
github.com/luxfi/warp v1.13.4-lux.20
github.com/luxfi/warp v0.1.1
github.com/mattn/go-colorable v0.1.14
github.com/mattn/go-isatty v0.0.20
github.com/onsi/ginkgo/v2 v2.25.1
@@ -39,7 +39,6 @@ require (
github.com/urfave/cli/v2 v2.27.7
go.uber.org/goleak v1.3.0
go.uber.org/mock v0.6.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.41.0
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b
golang.org/x/mod v0.27.0
@@ -67,7 +66,6 @@ require (
github.com/cockroachdb/pebble v1.1.5 // indirect
github.com/cockroachdb/redact v1.1.6 // indirect
github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb // indirect
github.com/compose-spec/compose-go v1.20.2 // indirect
github.com/consensys/gnark-crypto v0.19.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/crate-crypto/go-eth-kzg v1.3.0 // indirect
@@ -75,18 +73,17 @@ require (
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/dgraph-io/badger/v4 v4.8.0 // indirect
github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emicklei/dot v1.9.0 // indirect
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.1 // indirect
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect
github.com/ethereum/go-verkle v0.2.2 // indirect
github.com/ferranbt/fastssz v1.0.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/getsentry/sentry-go v0.35.1 // indirect
@@ -102,12 +99,10 @@ require (
github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/flatbuffers v25.2.10+incompatible // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect
github.com/google/renameio/v2 v2.0.0 // indirect
github.com/google/uuid v1.6.0 // indirect
@@ -115,7 +110,6 @@ require (
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/jackpal/gateway v1.1.1 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
@@ -129,20 +123,16 @@ require (
github.com/luxfi/mock v0.1.0 // indirect
github.com/luxfi/trace v0.1.2 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/pointerstructure v1.2.1 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
github.com/olekukonko/tablewriter v1.0.9 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pion/dtls/v2 v2.2.7 // indirect
github.com/pion/logging v0.2.2 // indirect
@@ -153,13 +143,11 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/procfs v0.17.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/rs/cors v1.11.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.10.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.14.0 // indirect
github.com/status-im/keycard-go v0.2.0 // indirect
@@ -169,6 +157,7 @@ require (
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
@@ -182,29 +171,30 @@ require (
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/term v0.34.0 // indirect
golang.org/x/text v0.28.0 // indirect
gonum.org/v1/gonum v0.16.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250811230008-5f3141c8851a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect
google.golang.org/grpc v1.74.2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect
google.golang.org/grpc v1.75.1 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.29.0 // indirect
k8s.io/apimachinery v0.30.0 // indirect
k8s.io/client-go v0.29.0 // indirect
k8s.io/api v0.34.1 // indirect
k8s.io/apimachinery v0.34.1 // indirect
k8s.io/client-go v0.34.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3 // indirect
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
@@ -216,10 +206,11 @@ replace (
github.com/luxfi/consensus => ../consensus
github.com/luxfi/crypto => ../crypto
github.com/luxfi/database => ../database
github.com/luxfi/geth => ../geth
github.com/luxfi/geth => /home/z/work/lux/geth
github.com/luxfi/go-bip39 => ../go-bip39
github.com/luxfi/ids => ../ids
github.com/luxfi/log => ../log
github.com/luxfi/metric => ../metric
github.com/luxfi/node => ../node
github.com/luxfi/warp => ../warp
)
+26 -61
View File
@@ -11,8 +11,8 @@ github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0L
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/antithesishq/antithesis-sdk-go v0.4.4 h1:qW68KYDmoK85uk84h9t9GFnImbU1G9fW+If2ud2qrLg=
github.com/antithesishq/antithesis-sdk-go v0.4.4/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl3v2yvUZjmKncl7U91fup7E=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.24.0 h1:H4x4TuulnokZKvHLfzVRTHJfFfnHEeSYJizujEZvmAM=
@@ -68,8 +68,6 @@ github.com/cockroachdb/redact v1.1.6 h1:zXJBwDZ84xJNlHl1rMyCojqyIxv+7YUpQiJLQ7n4
github.com/cockroachdb/redact v1.1.6/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb h1:3bCgBvB8PbJVMX1ouCcSIxvsqKPYM7gs72o0zC76n9g=
github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
github.com/compose-spec/compose-go v1.20.2 h1:u/yfZHn4EaHGdidrZycWpxXgFffjYULlTbRfJ51ykjQ=
github.com/compose-spec/compose-go v1.20.2/go.mod h1:+MdqXV4RA7wdFsahh/Kb8U0pAJqkg7mr4PM9tFKU8RM=
github.com/consensys/gnark-crypto v0.19.0 h1:zXCqeY2txSaMl6G5wFpZzMWJU9HPNh8qxPnYJ1BL9vA=
github.com/consensys/gnark-crypto v0.19.0/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
@@ -84,6 +82,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA=
github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc=
github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ=
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
@@ -99,15 +99,9 @@ github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINA
github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 h1:qwcF+vdFrvPSEUDSX5RVoRccG8a5DhOdWdQ4zN62zzo=
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
@@ -121,6 +115,8 @@ github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bF
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/ethereum/c-kzg-4844/v2 v2.1.1 h1:KhzBVjmURsfr1+S3k/VE35T02+AW2qU9t9gr4R6YpSo=
github.com/ethereum/c-kzg-4844/v2 v2.1.1/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E=
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk=
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8=
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/ferranbt/fastssz v1.0.0 h1:9EXXYsracSqQRBQiHeaVsG/KQeYblPf40hsQPb9Dzk8=
@@ -134,6 +130,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgxrzK5E1fW7RQGeDwE8F/ZZnUYc=
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8=
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays=
@@ -199,7 +197,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -239,8 +236,6 @@ github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/jackpal/gateway v1.1.1 h1:UXXXkJGIHFsStms9ZBgGpoaFEJP7oJtFn5vplIT68E8=
github.com/jackpal/gateway v1.1.1/go.mod h1:Tl1vZVtUaXx5j6P5HFmv45alhEi4yHHLfT4PRbB7eyw=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
@@ -276,39 +271,20 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
github.com/luxfi/consensus v1.16.16 h1:3MGV84Yp60obKfyjw0N5ycT6nin8ZTb3dMO6puahDwA=
github.com/luxfi/consensus v1.16.16/go.mod h1:IuFEy6+79LIsiWnrLekKz6D2uIEAoNfk4LwXEl+DLoQ=
github.com/luxfi/database v1.1.13 h1:J0iNaejPDFfvevvX/TDr61pcfEciPRSKK1czoDbg+n0=
github.com/luxfi/database v1.1.13/go.mod h1:EgiZIOrM9gCqGBy6d9IrnIESOW+/y4MdXRfFUan6BTY=
github.com/luxfi/go-bip39 v1.1.1 h1:Z/2H3Pc7P1CQXzXxev945ifxHZ9fpuvsJQFTMP1u8Ts=
github.com/luxfi/go-bip39 v1.1.1/go.mod h1:OxPL7QXUfnqqk7nOTPWgNfShxqg69OYJ4+ATKCmZWP8=
github.com/luxfi/ids v1.0.2 h1:OnbCWBgdmuBPZat1r1vRikk1FFrdHMfSknpfKmX0KBg=
github.com/luxfi/ids v1.0.2/go.mod h1:cMbto3Q8N3IaBxdz4Lzaq9wYl33coGXUdlr2+YwXeUw=
github.com/luxfi/ledger-lux-go v1.0.0 h1:u5L6hrJU10tOd3+N6tMkq0ba1uWh/rLXvQiKlJoMENE=
github.com/luxfi/ledger-lux-go v1.0.0/go.mod h1:bJgOxgG1jE5uz8upWrwoDuCS/gfEQEQkL3TMYjwe5VA=
github.com/luxfi/log v1.1.22 h1:E+jX+ZZS4JX2HjuGdDTpLfrgFKEDD8byNYTa0m6HT1o=
github.com/luxfi/log v1.1.22/go.mod h1:Q2eeOT4alCF+/B6+k/eWtVZQ2HncDlpd9vUvkTF0Suc=
github.com/luxfi/math v0.1.0 h1:Hrt3ufH+Pr6BpNydL3tIS18nNH4p6O1anFSmktAio4c=
github.com/luxfi/math v0.1.0/go.mod h1:SdABYMv53H1Nu6C7fvnqnWKjajQQJLqZP5VHfan67pA=
github.com/luxfi/metric v1.3.0 h1:l5T6sTRHR2/FBLdxZJpEPV+WxtQSqL2BfDX5OEj/pFE=
github.com/luxfi/metric v1.3.0/go.mod h1:4emefSEkjDieChAET/gpYWk9wZcOSFrxTvcMQ9dNAi0=
github.com/luxfi/mock v0.1.0 h1:IwElfNu+T9sXvzFX6tudPDx1vqPuACRSRdxpD5lxW+o=
github.com/luxfi/mock v0.1.0/go.mod h1:izF+9K0gGzFC9zERn6Po37v46eLdPB+EIsDjL3GLk+U=
github.com/luxfi/trace v0.1.2 h1:KhRZbk2lQQmmYZjdTWcZKCYkLfu7/VUiLFIsWFKhkwg=
github.com/luxfi/trace v0.1.2/go.mod h1:4SleFc5NVbQYEfn6rYafdfxvHJ+QSdkGAIfKiICYvQE=
github.com/luxfi/warp v1.13.4-lux.20 h1:XQqNxEQ/awio4K2JfS34/1nJqiWGWko/dEnrk9GxX1Y=
github.com/luxfi/warp v1.13.4-lux.20/go.mod h1:Qf303RbRdGrHQpAwJ/tEUvAlsdRbYa+5JdSIQbGpCQQ=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@@ -321,8 +297,9 @@ github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVO
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
@@ -336,8 +313,6 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJ
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
@@ -356,8 +331,6 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
@@ -392,9 +365,6 @@ github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7D
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4=
github.com/prysmaticlabs/gohashtree v0.0.4-beta/go.mod h1:BFdtALS+Ffhg3lGQIHv9HDWuHS8cTvHZzrHWxwOtGOs=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
@@ -409,8 +379,6 @@ github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQ
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
@@ -454,6 +422,8 @@ github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfj
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -567,7 +537,6 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -575,8 +544,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -617,10 +586,10 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20250811230008-5f3141c8851a h1:DMCgtIAIQGZqJXMVzJF4MV8BlWoJh2ZuFiRdAleyr58=
google.golang.org/genproto/googleapis/api v0.0.0-20250811230008-5f3141c8851a/go.mod h1:y2yVLIE/CSMCPXaHnSKXxu1spLPnglFLegmgdY23uuE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og=
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -636,6 +605,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
@@ -646,22 +617,18 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
k8s.io/api v0.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A=
k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA=
k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o=
k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis=
k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8=
k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38=
k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk=
k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY=
k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3 h1:liMHz39T5dJO1aOKHLvwaCjDbf07wVh6yaUlTpunnkE=
@@ -672,8 +639,6 @@ sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5E
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+1 -1
View File
@@ -979,7 +979,7 @@ func (diff *StateOverride) Apply(state *state.StateDB) error {
}
// Override account(contract) code.
if account.Code != nil {
state.SetCode(addr, *account.Code)
state.SetCode(addr, *account.Code, tracing.CodeChangeUnspecified)
}
// Override account balance.
if account.Balance != nil {
+2 -6
View File
@@ -42,7 +42,7 @@ import (
"github.com/luxfi/crypto/kzg4844"
"github.com/luxfi/log"
ethparams "github.com/luxfi/geth/params"
"github.com/luxfi/evm/consensus/misc/eip4844"
"github.com/luxfi/geth/consensus/misc/eip4844"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/params"
"github.com/luxfi/evm/rpc"
@@ -264,12 +264,8 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b feeBackend) e
func (args *TransactionArgs) setCancunFeeDefaults(ctx context.Context, head *types.Header, b feeBackend) error {
// Set maxFeePerBlobGas if it is missing.
if args.BlobHashes != nil && args.BlobFeeCap == nil {
var excessBlobGas uint64
if head.ExcessBlobGas != nil {
excessBlobGas = *head.ExcessBlobGas
}
// ExcessBlobGas must be set for a Cancun block.
blobBaseFee := eip4844.CalcBlobFee(excessBlobGas)
blobBaseFee := eip4844.CalcBlobFee(b.ChainConfig(), head)
// Set the max fee to be 2 times larger than the previous block's blob base fee.
// The additional slack allows the tx to not become invalidated if the base
// fee is rising.
+5 -5
View File
@@ -47,7 +47,7 @@ import (
ethparams "github.com/luxfi/geth/params"
"github.com/luxfi/evm/commontype"
"github.com/luxfi/evm/consensus"
"github.com/luxfi/evm/consensus/misc/eip4844"
"github.com/luxfi/geth/consensus/misc/eip4844"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/core/state"
"github.com/luxfi/evm/core/txpool"
@@ -175,10 +175,10 @@ func (w *worker) commitNewWork(predicateContext *precompileconfig.PredicateConte
if w.chainConfig.IsCancun(header.Number, header.Time) {
var excessBlobGas uint64
if w.chainConfig.IsCancun(parent.Number, parent.Time) {
excessBlobGas = eip4844.CalcExcessBlobGas(*parent.ExcessBlobGas, *parent.BlobGasUsed)
excessBlobGas = eip4844.CalcExcessBlobGas(w.chainConfig, parent, timestamp)
} else {
// For the first post-fork block, both parent.data_gas_used and parent.excess_data_gas are evaluated as 0
excessBlobGas = eip4844.CalcExcessBlobGas(0, 0)
excessBlobGas = eip4844.CalcExcessBlobGas(w.chainConfig, parent, timestamp)
}
header.BlobGasUsed = new(uint64)
header.ExcessBlobGas = &excessBlobGas
@@ -239,7 +239,7 @@ func (w *worker) commitNewWork(predicateContext *precompileconfig.PredicateConte
filter.BaseFee = uint256.MustFromBig(env.header.BaseFee)
}
if env.header.ExcessBlobGas != nil {
filter.BlobFee = uint256.MustFromBig(eip4844.CalcBlobFee(*env.header.ExcessBlobGas))
filter.BlobFee = uint256.MustFromBig(eip4844.CalcBlobFee(w.chainConfig, env.header))
}
filter.OnlyPlainTxs, filter.OnlyBlobTxs = true, false
pendingPlainTxs := w.eth.TxPool().Pending(filter)
@@ -287,7 +287,7 @@ func (w *worker) createCurrentEnvironment(predicateContext *precompileconfig.Pre
if err != nil {
return nil, fmt.Errorf("calculating gas capacity: %w", err)
}
currentState.StartPrefetcher("miner", nil)
currentState.StartPrefetcher("miner", nil, nil)
return &environment{
signer: types.MakeSigner(w.chainConfig, header.Number, header.Time),
state: currentState,
+2 -2
View File
@@ -16,14 +16,14 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/luxfi/node/codec"
nodeCore "github.com/luxfi/node/consensus/engine/core"
nodeCore "github.com/luxfi/consensus/engine/core"
"github.com/luxfi/consensus/validators"
"github.com/luxfi/consensus/version"
"github.com/luxfi/node/network/p2p"
"github.com/luxfi/node/utils"
"github.com/luxfi/ids"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
"github.com/luxfi/evm/network/stats"
"github.com/luxfi/evm/plugin/evm/message"
+2 -2
View File
@@ -13,7 +13,7 @@ import (
"time"
"github.com/luxfi/node/network/p2p"
nodeCore "github.com/luxfi/node/consensus/engine/core"
nodeCore "github.com/luxfi/consensus/engine/core"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
@@ -26,7 +26,7 @@ import (
"github.com/luxfi/node/codec/linearcodec"
"github.com/luxfi/ids"
"github.com/luxfi/node/version"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
consensusVersion "github.com/luxfi/consensus/version"
)
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"github.com/luxfi/ids"
utils_math "github.com/luxfi/node/utils/math"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
"github.com/luxfi/consensus/version"
"github.com/luxfi/log"
+193
View File
@@ -0,0 +1,193 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"context"
"time"
"github.com/luxfi/ids"
// Node interfaces that the VM plugin must implement (from engine/chain/block)
nodeblock "github.com/luxfi/consensus/engine/chain/block"
// Consensus interfaces that our implementation uses (from protocol/chain)
consensusblock "github.com/luxfi/consensus/protocol/chain"
// Network interfaces
"github.com/luxfi/consensus/core/appsender"
"github.com/luxfi/consensus/utils/set"
)
// BlockAdapter adapts consensus Block to node Block interface
type BlockAdapter struct {
consensus consensusblock.Block
}
// NewBlockAdapter creates a new block adapter
func NewBlockAdapter(consensusBlock consensusblock.Block) nodeblock.Block {
return &BlockAdapter{consensus: consensusBlock}
}
// ID returns the block ID
func (b *BlockAdapter) ID() ids.ID {
return b.consensus.ID()
}
// Parent returns the parent block ID (alias for ParentID)
func (b *BlockAdapter) Parent() ids.ID {
return b.consensus.ParentID()
}
// ParentID returns the parent block ID
func (b *BlockAdapter) ParentID() ids.ID {
return b.consensus.ParentID()
}
// Height returns the block height
func (b *BlockAdapter) Height() uint64 {
return b.consensus.Height()
}
// Timestamp returns the block timestamp
func (b *BlockAdapter) Timestamp() time.Time {
return b.consensus.Timestamp()
}
// Status returns the block status
func (b *BlockAdapter) Status() uint8 {
return uint8(b.consensus.Status())
}
// Verify verifies the block
func (b *BlockAdapter) Verify(ctx context.Context) error {
return b.consensus.Verify(ctx)
}
// Accept accepts the block
func (b *BlockAdapter) Accept(ctx context.Context) error {
return b.consensus.Accept(ctx)
}
// Reject rejects the block
func (b *BlockAdapter) Reject(ctx context.Context) error {
return b.consensus.Reject(ctx)
}
// Bytes returns the block bytes
func (b *BlockAdapter) Bytes() []byte {
return b.consensus.Bytes()
}
// ContextAdapter adapts between node and consensus context types
type ContextAdapter struct {
nodeCtx *nodeblock.Context
}
// NewContextAdapter creates a context adapter from node to consensus
func NewContextAdapter(nodeCtx *nodeblock.Context) *nodeblock.Context {
// Both use the same Context type from engine/chain/block package
return nodeCtx
}
// NewNodeContextAdapter creates a node context from consensus context
func NewNodeContextAdapter(consensusCtx *nodeblock.Context) *nodeblock.Context {
// Both use the same Context type from engine/chain/block package
return consensusCtx
}
// AppSenderAdapter adapts consensus AppSender to node AppSender interface
type AppSenderAdapter struct {
consensus appsender.AppSender
}
// NewAppSenderAdapter creates an AppSender adapter
func NewAppSenderAdapter(consensusAppSender appsender.AppSender) nodeblock.AppSender {
return &AppSenderAdapter{consensus: consensusAppSender}
}
// SendAppRequest sends an app request
func (a *AppSenderAdapter) SendAppRequest(ctx context.Context, nodeIDs []ids.NodeID, requestID uint32, appRequestBytes []byte) error {
// Convert slice to set for consensus interface
nodeIDSet := set.NewSet[ids.NodeID](len(nodeIDs))
for _, nodeID := range nodeIDs {
nodeIDSet.Add(nodeID)
}
return a.consensus.SendAppRequest(ctx, nodeIDSet, requestID, appRequestBytes)
}
// SendAppResponse sends an app response
func (a *AppSenderAdapter) SendAppResponse(ctx context.Context, nodeID ids.NodeID, requestID uint32, appResponseBytes []byte) error {
return a.consensus.SendAppResponse(ctx, nodeID, requestID, appResponseBytes)
}
// SendAppError sends an app error
func (a *AppSenderAdapter) SendAppError(ctx context.Context, nodeID ids.NodeID, requestID uint32, errorCode int32, errorMessage string) error {
return a.consensus.SendAppError(ctx, nodeID, requestID, errorCode, errorMessage)
}
// SendAppGossip sends app gossip
func (a *AppSenderAdapter) SendAppGossip(ctx context.Context, nodeIDs []ids.NodeID, appGossipBytes []byte) error {
// Convert slice to set for consensus interface
nodeIDSet := set.NewSet[ids.NodeID](len(nodeIDs))
for _, nodeID := range nodeIDs {
nodeIDSet.Add(nodeID)
}
return a.consensus.SendAppGossip(ctx, nodeIDSet, appGossipBytes)
}
// ReverseAppSenderAdapter adapts node AppSender to consensus AppSender interface
type ReverseAppSenderAdapter struct {
node nodeblock.AppSender
}
// NewReverseAppSenderAdapter creates a reverse AppSender adapter
func NewReverseAppSenderAdapter(nodeAppSender nodeblock.AppSender) appsender.AppSender {
return &ReverseAppSenderAdapter{node: nodeAppSender}
}
// SendAppRequest sends an app request (consensus to node)
func (a *ReverseAppSenderAdapter) SendAppRequest(ctx context.Context, nodeIDs set.Set[ids.NodeID], requestID uint32, appRequestBytes []byte) error {
// Convert set to slice for node interface
nodeIDSlice := make([]ids.NodeID, 0, nodeIDs.Len())
for nodeID := range nodeIDs {
nodeIDSlice = append(nodeIDSlice, nodeID)
}
return a.node.SendAppRequest(ctx, nodeIDSlice, requestID, appRequestBytes)
}
// SendAppResponse sends an app response (consensus to node)
func (a *ReverseAppSenderAdapter) SendAppResponse(ctx context.Context, nodeID ids.NodeID, requestID uint32, appResponseBytes []byte) error {
return a.node.SendAppResponse(ctx, nodeID, requestID, appResponseBytes)
}
// SendAppError sends an app error (consensus to node)
func (a *ReverseAppSenderAdapter) SendAppError(ctx context.Context, nodeID ids.NodeID, requestID uint32, errorCode int32, errorMessage string) error {
return a.node.SendAppError(ctx, nodeID, requestID, errorCode, errorMessage)
}
// SendAppGossip sends app gossip (consensus to node)
func (a *ReverseAppSenderAdapter) SendAppGossip(ctx context.Context, nodeIDs set.Set[ids.NodeID], appGossipBytes []byte) error {
// Convert set to slice for node interface
nodeIDSlice := make([]ids.NodeID, 0, nodeIDs.Len())
for nodeID := range nodeIDs {
nodeIDSlice = append(nodeIDSlice, nodeID)
}
return a.node.SendAppGossip(ctx, nodeIDSlice, appGossipBytes)
}
// SendAppGossipSpecific sends app gossip to specific nodes (consensus to node)
func (a *ReverseAppSenderAdapter) SendAppGossipSpecific(ctx context.Context, nodeIDs set.Set[ids.NodeID], appGossipBytes []byte) error {
// Convert set to slice for node interface
nodeIDSlice := make([]ids.NodeID, 0, nodeIDs.Len())
for nodeID := range nodeIDs {
nodeIDSlice = append(nodeIDSlice, nodeID)
}
return a.node.SendAppGossip(ctx, nodeIDSlice, appGossipBytes)
}
+16 -3
View File
@@ -22,11 +22,11 @@ import (
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/node/consensus/choices"
"github.com/luxfi/node/consensus/engine/chain/block"
"github.com/luxfi/consensus/choices"
"github.com/luxfi/consensus/engine/chain/block"
consensusBlock "github.com/luxfi/consensus/engine/chain/block"
"github.com/luxfi/consensus/protocol/chain"
"github.com/luxfi/ids"
"github.com/luxfi/node/vms/components/chain"
)
var (
@@ -136,6 +136,11 @@ func (b *Block) Parent() ids.ID {
return ids.ID(b.ethBlock.ParentHash())
}
// ParentID implements the chain.Block interface (same as Parent)
func (b *Block) ParentID() ids.ID {
return ids.ID(b.ethBlock.ParentHash())
}
// Height implements the chain.Block interface
func (b *Block) Height() uint64 {
return b.ethBlock.NumberU64()
@@ -284,6 +289,14 @@ func (b *Block) Bytes() []byte {
func (b *Block) String() string { return fmt.Sprintf("EVM block, ID = %s", b.ID()) }
// Status implements the chain.Block interface
func (b *Block) Status() uint8 {
// Return a simple status based on if the block is in the blockchain
// 0 = unknown, 1 = processing, 2 = accepted, 3 = rejected
// For simplicity, we'll return 2 (accepted) for now since this is mainly used in consensus
return 2
}
// SetStatus implements the chain.Block interface
// This is required for chain.Block but not used in our implementation
func (b *Block) SetStatus(status choices.Status) {
+5 -5
View File
@@ -9,7 +9,7 @@ import (
"time"
"github.com/holiman/uint256"
commonEng "github.com/luxfi/node/consensus/engine/core"
commonEng "github.com/luxfi/consensus/core"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/core/txpool"
"github.com/luxfi/log"
@@ -109,20 +109,20 @@ func (b *blockBuilder) awaitSubmittedTxs() {
func (b *blockBuilder) waitForEvent(ctx context.Context) (commonEng.Message, error) {
lastBuildTime, err := b.waitForNeedToBuild(ctx)
if err != nil {
return commonEng.Message(0), err
return commonEng.Message{}, err
}
timeSinceLastBuildTime := time.Since(lastBuildTime)
if b.lastBuildTime.IsZero() || timeSinceLastBuildTime >= minBlockBuildingRetryDelay {
log.Debug("Last time we built a block was long enough ago, no need to wait", "timeSinceLastBuildTime", timeSinceLastBuildTime)
return commonEng.PendingTxs, nil
return commonEng.Message{Type: commonEng.PendingTxs}, nil
}
timeUntilNextBuild := minBlockBuildingRetryDelay - timeSinceLastBuildTime
log.Debug("Last time we built a block was too recent, waiting", "timeUntilNextBuild", timeUntilNextBuild)
select {
case <-ctx.Done():
return commonEng.Message(0), ctx.Err()
return commonEng.Message{}, ctx.Err()
case <-time.After(timeUntilNextBuild):
return commonEng.PendingTxs, nil
return commonEng.Message{Type: commonEng.PendingTxs}, nil
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"github.com/luxfi/geth/common"
)
const defaultCommitInterval = 4096
// defaultCommitInterval is defined in config.go
func NewDefaultConfig() Config {
return Config{
-62
View File
@@ -13,10 +13,8 @@ import (
"github.com/prometheus/client_golang/prometheus"
consensusCore "github.com/luxfi/node/consensus/engine/core"
"github.com/luxfi/ids"
"github.com/luxfi/log"
"github.com/luxfi/node/network/p2p"
"github.com/luxfi/node/network/p2p/gossip"
"github.com/luxfi/evm/core"
@@ -37,65 +35,6 @@ var (
_ eth.PushGossiper = (*EthPushGossiper)(nil)
)
<<<<<<< HEAD:plugin/evm/gossip.go
func newTxGossipHandler[T gossip.Gossipable](
log log.Logger,
marshaller gossip.Marshaller[T],
mempool gossip.Set[T],
metrics gossip.Metrics,
maxMessageSize int,
throttlingPeriod time.Duration,
throttlingLimit int,
validators p2p.ValidatorSet,
) txGossipHandler {
// push gossip messages can be handled from any peer
handler := gossip.NewHandler(
log,
marshaller,
mempool,
metrics,
maxMessageSize,
)
// pull gossip requests are filtered by validators and are throttled
// to prevent spamming
validatorHandler := p2p.NewValidatorHandler(
p2p.NewThrottlerHandler(
handler,
p2p.NewSlidingWindowThrottler(throttlingPeriod, throttlingLimit),
log,
),
validators,
log,
)
return txGossipHandler{
appGossipHandler: handler,
appRequestHandler: validatorHandler,
}
}
type txGossipHandler struct {
appGossipHandler p2p.Handler
appRequestHandler p2p.Handler
}
func (t txGossipHandler) AppGossip(ctx context.Context, nodeID ids.NodeID, gossipBytes []byte) {
t.appGossipHandler.AppGossip(ctx, nodeID, gossipBytes)
}
func (t txGossipHandler) AppRequest(ctx context.Context, nodeID ids.NodeID, deadline time.Time, requestBytes []byte) ([]byte, *consensusCore.AppError) {
return t.appRequestHandler.AppRequest(ctx, nodeID, deadline, requestBytes)
}
func (t txGossipHandler) CrossChainAppRequest(ctx context.Context, chainID ids.ID, deadline time.Time, requestBytes []byte) ([]byte, error) {
// Cross-chain requests not supported for tx gossip
return nil, fmt.Errorf("cross-chain requests not supported")
}
func NewGossipEthTxPool(mempool *txpool.TxPool, registerer prometheus.Registerer) (*GossipEthTxPool, error) {
bloom, err := gossip.NewBloomFilter(registerer, "eth_tx_bloom_filter", config.TxGossipBloomMinTargetElements, config.TxGossipBloomTargetFalsePositiveRate, config.TxGossipBloomResetFalsePositiveRate)
=======
func NewGossipEthTxPool(mempool *txpool.TxPool, registerer prometheus.Registerer) (*GossipEthTxPool, error) {
bloom, err := gossip.NewBloomFilter(
registerer,
@@ -104,7 +43,6 @@ func NewGossipEthTxPool(mempool *txpool.TxPool, registerer prometheus.Registerer
config.TxGossipBloomTargetFalsePositiveRate,
config.TxGossipBloomResetFalsePositiveRate,
)
>>>>>>> upstream/master:plugin/evm/eth_gossiper.go
if err != nil {
return nil, fmt.Errorf("failed to initialize bloom filter: %w", err)
}
-230
View File
@@ -1,230 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"context"
"fmt"
"sync"
"sync/atomic"
"time"
"github.com/prometheus/client_golang/prometheus"
consensusCore "github.com/luxfi/node/consensus/engine/core"
"github.com/luxfi/ids"
"github.com/luxfi/log"
"github.com/luxfi/node/network/p2p"
"github.com/luxfi/node/network/p2p/gossip"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/core/txpool"
"github.com/luxfi/evm/eth"
"github.com/luxfi/evm/plugin/evm/config"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/types"
)
const pendingTxsBuffer = 10
var (
_ p2p.Handler = (*txGossipHandler)(nil)
_ gossip.Gossipable = (*GossipEthTx)(nil)
_ gossip.Marshaller[*GossipEthTx] = (*GossipEthTxMarshaller)(nil)
_ gossip.Set[*GossipEthTx] = (*GossipEthTxPool)(nil)
_ eth.PushGossiper = (*EthPushGossiper)(nil)
)
func newTxGossipHandler[T gossip.Gossipable](
log log.Logger,
marshaller gossip.Marshaller[T],
mempool gossip.Set[T],
metrics gossip.Metrics,
maxMessageSize int,
throttlingPeriod time.Duration,
throttlingLimit int,
validators p2p.ValidatorSet,
) txGossipHandler {
// push gossip messages can be handled from any peer
handler := gossip.NewHandler(
log,
marshaller,
mempool,
metrics,
maxMessageSize,
)
// pull gossip requests are filtered by validators and are throttled
// to prevent spamming
validatorHandler := p2p.NewValidatorHandler(
p2p.NewThrottlerHandler(
handler,
p2p.NewSlidingWindowThrottler(throttlingPeriod, throttlingLimit),
log,
),
validators,
log,
)
return txGossipHandler{
appGossipHandler: handler,
appRequestHandler: validatorHandler,
}
}
type txGossipHandler struct {
appGossipHandler p2p.Handler
appRequestHandler p2p.Handler
}
func (t txGossipHandler) AppGossip(ctx context.Context, nodeID ids.NodeID, gossipBytes []byte) {
t.appGossipHandler.AppGossip(ctx, nodeID, gossipBytes)
}
func (t txGossipHandler) AppRequest(ctx context.Context, nodeID ids.NodeID, deadline time.Time, requestBytes []byte) ([]byte, *consensusCore.AppError) {
return t.appRequestHandler.AppRequest(ctx, nodeID, deadline, requestBytes)
}
func (t txGossipHandler) CrossChainAppRequest(ctx context.Context, chainID ids.ID, deadline time.Time, requestBytes []byte) ([]byte, error) {
// Cross-chain requests not supported for tx gossip
return nil, fmt.Errorf("cross-chain requests not supported")
}
func NewGossipEthTxPool(mempool *txpool.TxPool, registerer prometheus.Registerer) (*GossipEthTxPool, error) {
bloom, err := gossip.NewBloomFilter(registerer, "eth_tx_bloom_filter", config.TxGossipBloomMinTargetElements, config.TxGossipBloomTargetFalsePositiveRate, config.TxGossipBloomResetFalsePositiveRate)
if err != nil {
return nil, fmt.Errorf("failed to initialize bloom filter: %w", err)
}
return &GossipEthTxPool{
mempool: mempool,
pendingTxs: make(chan core.NewTxsEvent, pendingTxsBuffer),
bloom: bloom,
}, nil
}
type GossipEthTxPool struct {
mempool *txpool.TxPool
pendingTxs chan core.NewTxsEvent
bloom *gossip.BloomFilter
lock sync.RWMutex
// subscribed is set to true when the gossip subscription is active
// mostly used for testing
subscribed atomic.Bool
}
// IsSubscribed returns whether or not the gossip subscription is active.
func (g *GossipEthTxPool) IsSubscribed() bool {
return g.subscribed.Load()
}
func (g *GossipEthTxPool) Subscribe(ctx context.Context) {
sub := g.mempool.SubscribeTransactions(g.pendingTxs, false)
if sub == nil {
log.Warn("failed to subscribe to new txs event")
return
}
g.subscribed.CompareAndSwap(false, true)
defer func() {
sub.Unsubscribe()
g.subscribed.CompareAndSwap(true, false)
}()
for {
select {
case <-ctx.Done():
log.Debug("shutting down subscription")
return
case pendingTxs := <-g.pendingTxs:
g.lock.Lock()
optimalElements := (g.mempool.PendingSize(txpool.PendingFilter{}) + len(pendingTxs.Txs)) * config.TxGossipBloomChurnMultiplier
for _, pendingTx := range pendingTxs.Txs {
tx := &GossipEthTx{Tx: pendingTx}
g.bloom.Add(tx)
reset, err := gossip.ResetBloomFilterIfNeeded(g.bloom, optimalElements)
if err != nil {
log.Error("failed to reset bloom filter", "err", err)
continue
}
if reset {
log.Debug("resetting bloom filter", "reason", "reached max filled ratio")
g.mempool.IteratePending(func(tx *types.Transaction) bool {
g.bloom.Add(&GossipEthTx{Tx: tx})
return true
})
}
}
g.lock.Unlock()
}
}
}
// Add enqueues the transaction to the mempool. Subscribe should be called
// to receive an event if tx is actually added to the mempool or not.
func (g *GossipEthTxPool) Add(tx *GossipEthTx) error {
return g.mempool.Add([]*types.Transaction{tx.Tx}, false, false)[0]
}
// Has should just return whether or not the [txID] is still in the mempool,
// not whether it is in the mempool AND pending.
func (g *GossipEthTxPool) Has(txID ids.ID) bool {
return g.mempool.Has(common.Hash(txID))
}
func (g *GossipEthTxPool) Iterate(f func(tx *GossipEthTx) bool) {
g.mempool.IteratePending(func(tx *types.Transaction) bool {
return f(&GossipEthTx{Tx: tx})
})
}
func (g *GossipEthTxPool) GetFilter() ([]byte, []byte) {
g.lock.RLock()
defer g.lock.RUnlock()
return g.bloom.Marshal()
}
type GossipEthTxMarshaller struct{}
func (g GossipEthTxMarshaller) MarshalGossip(tx *GossipEthTx) ([]byte, error) {
return tx.Tx.MarshalBinary()
}
func (g GossipEthTxMarshaller) UnmarshalGossip(bytes []byte) (*GossipEthTx, error) {
tx := &GossipEthTx{
Tx: &types.Transaction{},
}
return tx, tx.Tx.UnmarshalBinary(bytes)
}
type GossipEthTx struct {
Tx *types.Transaction
}
func (tx *GossipEthTx) GossipID() ids.ID {
return ids.ID(tx.Tx.Hash())
}
// EthPushGossiper is used by the ETH backend to push transactions issued over
// the RPC and added to the mempool to peers.
type EthPushGossiper struct {
vm *VM
}
func (e *EthPushGossiper) Add(tx *types.Transaction) {
// eth.Backend is initialized before the [ethTxPushGossiper] is created, so
// we just ignore any gossip requests until it is set.
ethTxPushGossiper := e.vm.ethTxPushGossiper.Get()
if ethTxPushGossiper == nil {
return
}
ethTxPushGossiper.Add(&GossipEthTx{tx})
}
+21 -18
View File
@@ -5,21 +5,20 @@ package gossip
import (
"context"
"fmt"
"time"
"github.com/luxfi/node/ids"
"github.com/luxfi/ids"
"github.com/luxfi/node/network/p2p"
"github.com/luxfi/node/network/p2p/gossip"
"github.com/luxfi/node/snow/engine/common"
"github.com/luxfi/node/utils/logging"
"github.com/luxfi/consensus/engine/core"
"github.com/luxfi/log"
"github.com/prometheus/client_golang/prometheus"
)
var _ p2p.Handler = (*txGossipHandler)(nil)
func NewTxGossipHandler[T gossip.Gossipable](
log logging.Logger,
logger log.Logger,
marshaller gossip.Marshaller[T],
mempool gossip.Set[T],
metrics gossip.Metrics,
@@ -32,32 +31,31 @@ func NewTxGossipHandler[T gossip.Gossipable](
) (*txGossipHandler, error) {
// push gossip messages can be handled from any peer
handler := gossip.NewHandler(
log,
logger,
marshaller,
mempool,
metrics,
maxMessageSize,
)
throttledHandler, err := p2p.NewDynamicThrottlerHandler(
log,
handler,
validators,
// Create a sliding window throttler for rate limiting
throttler := p2p.NewSlidingWindowThrottler(
throttlingPeriod,
requestsPerPeer,
registerer,
namespace,
int(requestsPerPeer), // Convert float64 to int for limit
)
throttledHandler := p2p.NewThrottlerHandler(
handler,
throttler,
logger,
)
if err != nil {
return nil, fmt.Errorf("failed to initialize throttler handler: %w", err)
}
// pull gossip requests are filtered by validators and are throttled
// to prevent spamming
validatorHandler := p2p.NewValidatorHandler(
throttledHandler,
validators,
log,
logger,
)
return &txGossipHandler{
@@ -75,6 +73,11 @@ func (t *txGossipHandler) AppGossip(ctx context.Context, nodeID ids.NodeID, goss
t.appGossipHandler.AppGossip(ctx, nodeID, gossipBytes)
}
func (t *txGossipHandler) AppRequest(ctx context.Context, nodeID ids.NodeID, deadline time.Time, requestBytes []byte) ([]byte, *common.AppError) {
func (t *txGossipHandler) AppRequest(ctx context.Context, nodeID ids.NodeID, deadline time.Time, requestBytes []byte) ([]byte, *core.AppError) {
return t.appRequestHandler.AppRequest(ctx, nodeID, deadline, requestBytes)
}
func (t *txGossipHandler) CrossChainAppRequest(ctx context.Context, chainID ids.ID, deadline time.Time, requestBytes []byte) ([]byte, error) {
// Cross-chain requests are not supported for transaction gossip
return nil, nil
}
+1 -1
View File
@@ -14,7 +14,7 @@ import (
"time"
"github.com/luxfi/ids"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
"github.com/luxfi/crypto"
"github.com/luxfi/geth/common"
+17 -5
View File
@@ -8,7 +8,7 @@ import (
"fmt"
"time"
"github.com/luxfi/node/consensus/engine/core"
"github.com/luxfi/consensus/engine/core"
"github.com/luxfi/ids"
"github.com/luxfi/node/network/p2p"
"github.com/luxfi/node/network/p2p/lp118"
@@ -16,11 +16,11 @@ import (
// lp118HandlerAdapter adapts lp118.Handler to p2p.Handler
type lp118HandlerAdapter struct {
handler *lp118.Handler
handler lp118.Handler
}
// newLP118HandlerAdapter creates a new adapter
func newLP118HandlerAdapter(handler *lp118.Handler) p2p.Handler {
func newLP118HandlerAdapter(handler lp118.Handler) p2p.Handler {
return &lp118HandlerAdapter{
handler: handler,
}
@@ -33,8 +33,20 @@ func (a *lp118HandlerAdapter) AppGossip(ctx context.Context, nodeID ids.NodeID,
// AppRequest handles request messages
func (a *lp118HandlerAdapter) AppRequest(ctx context.Context, nodeID ids.NodeID, deadline time.Time, requestBytes []byte) ([]byte, *core.AppError) {
// Forward to lp118 handler - it already returns *core.AppError
return a.handler.AppRequest(ctx, nodeID, deadline, requestBytes)
// Forward to lp118 handler
resp, err := a.handler.AppRequest(ctx, nodeID, deadline, requestBytes)
if err != nil {
// Convert error to AppError if it's not already
if appErr, ok := err.(*core.AppError); ok {
return nil, appErr
}
// Create a new AppError from the error
return nil, &core.AppError{
Code: -1,
Message: err.Error(),
}
}
return resp, nil
}
// CrossChainAppRequest handles cross-chain requests
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"github.com/luxfi/evm/plugin/evm/client"
"github.com/luxfi/ids"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
)
type ValidatorsAPI struct {
+1 -1
View File
@@ -22,7 +22,7 @@ import (
"github.com/luxfi/database/prefixdb"
"github.com/luxfi/ids"
"github.com/luxfi/node/upgrade/upgradetest"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
"github.com/luxfi/crypto"
"github.com/luxfi/crypto/secp256k1"
+2 -2
View File
@@ -7,9 +7,9 @@ import (
"context"
"testing"
consensusSet "github.com/luxfi/consensus/utils/set"
"github.com/luxfi/ids"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
consensusSet "github.com/luxfi/consensus/utils/set"
)
// TestSender is a test implementation of the Sender interface
+1 -1
View File
@@ -20,7 +20,7 @@ import (
"github.com/luxfi/node/proto/pb/sdk"
"github.com/luxfi/node/upgrade/upgradetest"
agoUtils "github.com/luxfi/node/utils"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
+7 -7
View File
@@ -12,7 +12,7 @@ import (
"github.com/luxfi/database"
"github.com/luxfi/ids"
"github.com/luxfi/consensus"
luxuptime "github.com/luxfi/node/consensus/uptime"
luxuptime "github.com/luxfi/consensus/uptime"
luxvalidators "github.com/luxfi/consensus/validators"
"github.com/luxfi/node/utils/timer/mockable"
validators "github.com/luxfi/evm/plugin/evm/validators/state"
@@ -126,11 +126,11 @@ func (m *manager) sync(ctx context.Context) error {
}
// Convert validator set format for loadValidators
convertedValidatorSet := make(map[ids.ID]*luxvalidators.GetCurrentValidatorOutput)
convertedValidatorSet := make(map[ids.ID]*luxvalidators.GetValidatorOutput)
for nodeID, weight := range currentValidatorSet {
// Create a simple validator output - use a unique ID based on NodeID
validationID := ids.ID(nodeID.Bytes())
convertedValidatorSet[validationID] = &luxvalidators.GetCurrentValidatorOutput{
convertedValidatorSet[validationID] = &luxvalidators.GetValidatorOutput{
NodeID: nodeID,
Weight: weight,
}
@@ -152,7 +152,7 @@ func (m *manager) sync(ctx context.Context) error {
}
// loadValidators loads the [validators] into the validator state [validatorState]
func loadValidators(validatorState stateinterfaces.State, newValidators map[ids.ID]*luxvalidators.GetCurrentValidatorOutput) error {
func loadValidators(validatorState stateinterfaces.State, newValidators map[ids.ID]*luxvalidators.GetValidatorOutput) error {
currentValidationIDs := validatorState.GetValidationIDs()
// first check if we need to delete any existing validators
for vID := range currentValidationIDs {
@@ -169,9 +169,9 @@ func loadValidators(validatorState stateinterfaces.State, newValidators map[ids.
ValidationID: newVID,
NodeID: newVdr.NodeID,
Weight: newVdr.Weight,
StartTimestamp: newVdr.StartTime,
IsActive: newVdr.IsActive,
IsL1Validator: newVdr.IsL1Validator,
StartTimestamp: 0, // Default value since GetValidatorOutput doesn't have StartTime
IsActive: true, // Default to active for current validators
IsL1Validator: false, // Default to false, can be updated based on your requirements
}
if currentValidationIDs.Contains(newVID) {
if err := validatorState.UpdateValidator(currentVdr); err != nil {
+33 -46
View File
@@ -29,25 +29,24 @@ func TestLoadNewValidators(t *testing.T) {
}
tests := []struct {
name string
initialValidators map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput
newValidators map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput
initialValidators map[ids.ID]*luxdvalidators.GetValidatorOutput
newValidators map[ids.ID]*luxdvalidators.GetValidatorOutput
registerMockListenerCalls func(*interfaces.MockStateCallbackListener)
expectedLoadErr error
}{
{
name: "before empty/after empty",
initialValidators: map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput{},
newValidators: map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput{},
initialValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{},
newValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{},
registerMockListenerCalls: func(*interfaces.MockStateCallbackListener) {},
},
{
name: "before empty/after one",
initialValidators: map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput{},
newValidators: map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput{
initialValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{},
newValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
testValidationIDs[0]: {
NodeID: testNodeIDs[0],
IsActive: true,
StartTime: 0,
Weight: 1,
},
},
registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) {
@@ -56,14 +55,13 @@ func TestLoadNewValidators(t *testing.T) {
},
{
name: "before one/after empty",
initialValidators: map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput{
initialValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
testValidationIDs[0]: {
NodeID: testNodeIDs[0],
IsActive: true,
StartTime: 0,
Weight: 1,
},
},
newValidators: map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput{},
newValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{},
registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) {
// initial validator will trigger first
mock.EXPECT().OnValidatorAdded(testValidationIDs[0], testNodeIDs[0], uint64(0), true).Times(1)
@@ -73,18 +71,16 @@ func TestLoadNewValidators(t *testing.T) {
},
{
name: "no change",
initialValidators: map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput{
initialValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
testValidationIDs[0]: {
NodeID: testNodeIDs[0],
IsActive: true,
StartTime: 0,
Weight: 1,
},
},
newValidators: map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput{
newValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
testValidationIDs[0]: {
NodeID: testNodeIDs[0],
IsActive: true,
StartTime: 0,
Weight: 1,
},
},
registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) {
@@ -93,50 +89,42 @@ func TestLoadNewValidators(t *testing.T) {
},
{
name: "status and weight change and new one",
initialValidators: map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput{
initialValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
testValidationIDs[0]: {
NodeID: testNodeIDs[0],
IsActive: true,
StartTime: 0,
Weight: 1,
},
},
newValidators: map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput{
newValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
testValidationIDs[0]: {
NodeID: testNodeIDs[0],
IsActive: false,
StartTime: 0,
Weight: 2,
},
testValidationIDs[1]: {
NodeID: testNodeIDs[1],
IsActive: true,
StartTime: 0,
Weight: 1,
},
},
registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) {
// initial validator will trigger first
mock.EXPECT().OnValidatorAdded(testValidationIDs[0], testNodeIDs[0], uint64(0), true).Times(1)
// then it will be updated
mock.EXPECT().OnValidatorStatusUpdated(testValidationIDs[0], testNodeIDs[0], false).Times(1)
// Weight change happens but status stays the same (true) since we default to active
// new validator will be added
mock.EXPECT().OnValidatorAdded(testValidationIDs[1], testNodeIDs[1], uint64(0), true).Times(1)
},
},
{
name: "renew validation ID",
initialValidators: map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput{
initialValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
testValidationIDs[0]: {
NodeID: testNodeIDs[0],
IsActive: true,
StartTime: 0,
Weight: 1,
},
},
newValidators: map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput{
newValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
testValidationIDs[1]: {
NodeID: testNodeIDs[0],
IsActive: true,
StartTime: 0,
Weight: 1,
},
},
registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) {
@@ -150,18 +138,16 @@ func TestLoadNewValidators(t *testing.T) {
},
{
name: "renew node ID",
initialValidators: map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput{
initialValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
testValidationIDs[0]: {
NodeID: testNodeIDs[0],
IsActive: true,
StartTime: 0,
Weight: 1,
},
},
newValidators: map[ids.ID]*luxdvalidators.GetCurrentValidatorOutput{
newValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
testValidationIDs[0]: {
NodeID: testNodeIDs[1],
IsActive: true,
StartTime: 0,
Weight: 1,
},
},
expectedLoadErr: state.ErrImmutableField,
@@ -186,9 +172,9 @@ func TestLoadNewValidators(t *testing.T) {
ValidationID: vID,
NodeID: validator.NodeID,
Weight: validator.Weight,
StartTimestamp: validator.StartTime,
IsActive: validator.IsActive,
IsL1Validator: validator.IsL1Validator,
StartTimestamp: 0, // Default value since GetValidatorOutput doesn't have StartTime
IsActive: true, // Default to active
IsL1Validator: false, // Default value
}))
}
// enable mock listener
@@ -211,9 +197,10 @@ func TestLoadNewValidators(t *testing.T) {
require.NoError(err)
require.Equal(validator.NodeID, v.NodeID)
require.Equal(validator.Weight, v.Weight)
require.Equal(validator.StartTime, v.StartTimestamp)
require.Equal(validator.IsActive, v.IsActive)
require.Equal(validator.IsL1Validator, v.IsL1Validator)
// Default values are used for fields not in GetValidatorOutput
require.Equal(uint64(0), v.StartTimestamp)
require.Equal(true, v.IsActive)
require.Equal(false, v.IsL1Validator)
}
})
}
@@ -6,7 +6,7 @@ package interfaces
import (
"github.com/luxfi/ids"
"github.com/luxfi/consensus/uptime"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
)
type StateReader interface {
+16 -5
View File
@@ -10,7 +10,7 @@ import (
luxdb "github.com/luxfi/database"
"github.com/luxfi/ids"
"github.com/luxfi/consensus/uptime"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
"github.com/luxfi/evm/plugin/evm/validators/state/interfaces"
)
@@ -67,20 +67,29 @@ func NewState(db luxdb.Database) (interfaces.State, error) {
// GetUptime returns the uptime of the validator with the given nodeID
func (s *state) GetUptime(
nodeID ids.NodeID,
) (time.Duration, time.Time, error) {
subnetID ids.ID,
) (time.Duration, time.Duration, error) {
// Note: For subnet-specific validators, we currently ignore the subnetID
// as this state tracks validators per subnet already
data, err := s.getData(nodeID)
if err != nil {
return 0, time.Time{}, err
return 0, 0, err
}
return data.UpDuration, data.getLastUpdated(), nil
// Return uptime and time since last update
lastUpdated := data.getLastUpdated()
timeSinceLastUpdate := time.Since(lastUpdated)
return data.UpDuration, timeSinceLastUpdate, nil
}
// SetUptime sets the uptime of the validator with the given nodeID
func (s *state) SetUptime(
nodeID ids.NodeID,
subnetID ids.ID,
upDuration time.Duration,
lastUpdated time.Time,
) error {
// Note: For subnet-specific validators, we currently ignore the subnetID
// as this state tracks validators per subnet already
data, err := s.getData(nodeID)
if err != nil {
return err
@@ -93,7 +102,9 @@ func (s *state) SetUptime(
}
// GetStartTime returns the start time of the validator with the given nodeID
func (s *state) GetStartTime(nodeID ids.NodeID) (time.Time, error) {
func (s *state) GetStartTime(nodeID ids.NodeID, subnetID ids.ID) (time.Time, error) {
// Note: For subnet-specific validators, we currently ignore the subnetID
// as this state tracks validators per subnet already
data, err := s.getData(nodeID)
if err != nil {
return time.Time{}, err
+17 -12
View File
@@ -27,12 +27,13 @@ func TestState(t *testing.T) {
// get non-existent uptime
nodeID := ids.GenerateTestNodeID()
vID := ids.GenerateTestID()
_, _, err = state.GetUptime(nodeID)
subnetID := ids.GenerateTestID() // Test subnet ID
_, _, err = state.GetUptime(nodeID, subnetID)
require.ErrorIs(err, database.ErrNotFound)
// set non-existent uptime
startTime := time.Now()
err = state.SetUptime(nodeID, 1, startTime)
err = state.SetUptime(nodeID, subnetID, 1, startTime)
require.ErrorIs(err, database.ErrNotFound)
// add new validator
@@ -56,20 +57,22 @@ func TestState(t *testing.T) {
require.ErrorIs(err, ErrAlreadyExists)
// get uptime
uptime, lastUpdated, err := state.GetUptime(nodeID)
uptime, timeSinceLastUpdate, err := state.GetUptime(nodeID, subnetID)
require.NoError(err)
require.Equal(time.Duration(0), uptime)
require.Equal(startTime.Unix(), lastUpdated.Unix())
// timeSinceLastUpdate should be very small since we just added the validator
require.Less(timeSinceLastUpdate, time.Minute)
// set uptime
newUptime := 2 * time.Minute
newLastUpdated := lastUpdated.Add(time.Hour)
require.NoError(state.SetUptime(nodeID, newUptime, newLastUpdated))
newLastUpdated := time.Now()
require.NoError(state.SetUptime(nodeID, subnetID, newUptime, newLastUpdated))
// get new uptime
uptime, lastUpdated, err = state.GetUptime(nodeID)
uptime, timeSinceLastUpdate, err = state.GetUptime(nodeID, subnetID)
require.NoError(err)
require.Equal(newUptime, uptime)
require.Equal(newLastUpdated, lastUpdated)
// timeSinceLastUpdate should be very small since we just set it
require.Less(timeSinceLastUpdate, time.Second)
// set status
vdr.IsActive = false
@@ -113,7 +116,7 @@ func TestState(t *testing.T) {
require.NoError(state.DeleteValidator(vID))
// get deleted uptime
_, _, err = state.GetUptime(nodeID)
_, _, err = state.GetUptime(nodeID, subnetID)
require.ErrorIs(err, database.ErrNotFound)
}
@@ -128,6 +131,7 @@ func TestWriteValidator(t *testing.T) {
// load uptime
nodeID := ids.GenerateTestNodeID()
vID := ids.GenerateTestID()
subnetID := ids.GenerateTestID() // Test subnet ID
startTime := time.Now()
require.NoError(state.AddValidator(interfaces.Validator{
ValidationID: vID,
@@ -145,7 +149,7 @@ func TestWriteValidator(t *testing.T) {
// set uptime
newUptime := 2 * time.Minute
newLastUpdated := startTime.Add(time.Hour)
require.NoError(state.SetUptime(nodeID, newUptime, newLastUpdated))
require.NoError(state.SetUptime(nodeID, subnetID, newUptime, newLastUpdated))
require.NoError(state.WriteState())
// refresh state, should load from DB
@@ -153,10 +157,11 @@ func TestWriteValidator(t *testing.T) {
require.NoError(err)
// get uptime
uptime, lastUpdated, err := state.GetUptime(nodeID)
uptime, timeSinceLastUpdate, err := state.GetUptime(nodeID, subnetID)
require.NoError(err)
require.Equal(newUptime, uptime)
require.Equal(newLastUpdated.Unix(), lastUpdated.Unix())
// timeSinceLastUpdate should be small but accounting for db reload
require.Less(timeSinceLastUpdate, time.Minute)
// delete
require.NoError(state.DeleteValidator(vID))
@@ -11,7 +11,7 @@ import (
"github.com/luxfi/ids"
"github.com/luxfi/consensus/uptime"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
)
var errPausedDisconnect = errors.New("paused node cannot be disconnected")
@@ -8,7 +8,7 @@ import (
"time"
"github.com/luxfi/ids"
"github.com/luxfi/node/consensus/uptime"
"github.com/luxfi/consensus/uptime"
"github.com/luxfi/node/utils/timer/mockable"
"github.com/luxfi/evm/plugin/evm/validators/uptime/interfaces"
"github.com/stretchr/testify/require"
+80 -52
View File
@@ -33,6 +33,7 @@ import (
"github.com/luxfi/evm/eth"
"github.com/luxfi/evm/eth/ethconfig"
"github.com/luxfi/evm/plugin/evm/message"
gossipHandler "github.com/luxfi/evm/plugin/evm/gossip"
subnetevmprometheus "github.com/luxfi/evm/metrics/prometheus"
"github.com/luxfi/evm/miner"
"github.com/luxfi/evm/network"
@@ -80,9 +81,10 @@ import (
"github.com/luxfi/consensus/engine/chain/block"
consensusmockable "github.com/luxfi/consensus/utils/timer/mockable"
nodeblock "github.com/luxfi/node/consensus/engine/chain/block"
nodechain "github.com/luxfi/node/consensus/chain"
nodeConsensus "github.com/luxfi/node/consensus"
nodeblock "github.com/luxfi/consensus/engine/chain/block"
nodechain "github.com/luxfi/consensus/protocol/chain"
nodeConsensus "github.com/luxfi/consensus"
consensusInterfaces "github.com/luxfi/consensus/interfaces"
"github.com/luxfi/database/versiondb"
"github.com/luxfi/ids"
"github.com/luxfi/node/codec"
@@ -94,8 +96,8 @@ import (
"github.com/luxfi/node/version"
nodeChain "github.com/luxfi/node/vms/components/chain"
commonEng "github.com/luxfi/node/consensus/engine/core"
"github.com/luxfi/node/utils/set"
commonEng "github.com/luxfi/consensus/core"
"github.com/luxfi/math/set"
"github.com/luxfi/consensus/core/appsender"
"github.com/luxfi/database"
@@ -223,12 +225,7 @@ type appSenderWrapper struct {
}
func (w *appSenderWrapper) SendAppRequest(ctx context.Context, nodeIDs set.Set[ids.NodeID], requestID uint32, appRequestBytes []byte) error {
// Convert node set to consensus set
consensusSet := make(map[ids.NodeID]struct{})
for nodeID := range nodeIDs {
consensusSet[nodeID] = struct{}{}
}
return w.appSender.SendAppRequest(ctx, consensusSet, requestID, appRequestBytes)
return w.appSender.SendAppRequest(ctx, nodeIDs, requestID, appRequestBytes)
}
func (w *appSenderWrapper) SendAppResponse(ctx context.Context, nodeID ids.NodeID, requestID uint32, appResponseBytes []byte) error {
@@ -239,22 +236,12 @@ func (w *appSenderWrapper) SendAppError(ctx context.Context, nodeID ids.NodeID,
return w.appSender.SendAppError(ctx, nodeID, requestID, errorCode, errorMessage)
}
func (w *appSenderWrapper) SendAppGossip(ctx context.Context, config commonEng.SendConfig, appGossipBytes []byte) error {
// Convert SendConfig.NodeIDs (node set) to consensus set
consensusSet := make(map[ids.NodeID]struct{})
for nodeID := range config.NodeIDs {
consensusSet[nodeID] = struct{}{}
}
return w.appSender.SendAppGossip(ctx, consensusSet, appGossipBytes)
func (w *appSenderWrapper) SendAppGossip(ctx context.Context, nodeIDs set.Set[ids.NodeID], appGossipBytes []byte) error {
return w.appSender.SendAppGossip(ctx, nodeIDs, appGossipBytes)
}
func (w *appSenderWrapper) SendAppGossipSpecific(ctx context.Context, nodeIDs set.Set[ids.NodeID], appGossipBytes []byte) error {
// Convert node set to consensus set
consensusSet := make(map[ids.NodeID]struct{})
for nodeID := range nodeIDs {
consensusSet[nodeID] = struct{}{}
}
return w.appSender.SendAppGossipSpecific(ctx, consensusSet, appGossipBytes)
return w.appSender.SendAppGossipSpecific(ctx, nodeIDs, appGossipBytes)
}
// SendCrossChainAppError implements node's AppSender interface
@@ -374,18 +361,46 @@ type VM struct {
}
// ParseBlock implements nodeblock.ChainVM interface
func (vm *VM) ParseBlock(ctx context.Context, b []byte) (nodechain.Block, error) {
func (vm *VM) ParseBlock(ctx context.Context, b []byte) (nodeblock.Block, error) {
// Call the embedded State's ParseBlock and convert the result
blk, err := vm.State.ParseBlock(ctx, b)
if err != nil {
return nil, err
}
// Cast to the node's Block interface
return blk.(nodechain.Block), nil
// Adapt the consensus block to node block interface
return NewBlockAdapter(blk.(nodechain.Block)), nil
}
// Initialize implements the chain.ChainVM interface
// Initialize implements the chain.ChainVM interface with generic interface{} parameters
func (vm *VM) Initialize(
ctx context.Context,
chainCtx interface{},
dbManager interface{},
genesisBytes []byte,
upgradeBytes []byte,
configBytes []byte,
toEngine interface{},
fxs []interface{},
appSender interface{},
) error {
// Convert interface{} parameters to strongly typed ones
typedChainCtx := chainCtx.(*nodeConsensus.Context)
typedDB := dbManager.(database.Database)
// Convert fxs from []interface{} to []*commonEng.Fx
typedFxs := make([]*commonEng.Fx, len(fxs))
for i, fx := range fxs {
typedFxs[i] = fx.(*commonEng.Fx)
}
// Convert appSender interface to typed AppSender
typedAppSender := appSender.(commonEng.AppSender)
return vm.initializeInternal(ctx, typedChainCtx, typedDB, genesisBytes, upgradeBytes, configBytes, typedFxs, typedAppSender)
}
// initializeInternal contains the actual initialization logic with strongly typed parameters
func (vm *VM) initializeInternal(
ctx context.Context,
chainCtx *nodeConsensus.Context,
db database.Database,
@@ -424,8 +439,9 @@ func (vm *VM) Initialize(
}
vm.chainAlias = alias
// Use chainCtx.Log directly
contextLogger := chainCtx.Log
// Create a logger since consensus Context doesn't have Log field
// TODO: Integrate with proper logging from consensus package
contextLogger := log.New()
logWriter := newLoggerWriter(contextLogger)
subnetEVMLogger, err := subnetevmlog.InitLogger(vm.chainAlias, vm.config.LogLevel, vm.config.LogJSONFormat, logWriter)
if err != nil {
@@ -642,7 +658,7 @@ func (vm *VM) Initialize(
// Only create warp backend if we have a signer
if warpAdapter != nil {
vm.warpBackend, err = warp.NewBackend(
chainCtx.NetworkID,
chainCtx.QuantumID, // Use QuantumID as network ID from consensus Context
chainCtx.ChainID,
warpAdapter,
&warpBlockClient{vm: vm}, // Wrapper that implements warp.BlockClient
@@ -983,17 +999,17 @@ func (vm *VM) initChainState(lastAcceptedBlock *types.Block) error {
return nil
}
func (vm *VM) SetState(_ context.Context, state nodeConsensus.State) error {
func (vm *VM) SetState(_ context.Context, state consensusInterfaces.State) error {
vm.vmLock.Lock()
defer vm.vmLock.Unlock()
switch state {
case nodeConsensus.StateSyncing:
case consensusInterfaces.StateSyncing:
vm.bootstrapped.Set(false)
return nil
case nodeConsensus.Bootstrapping:
case consensusInterfaces.Bootstrapping:
return vm.onBootstrapStarted()
case nodeConsensus.NormalOp:
case consensusInterfaces.NormalOp:
return vm.onNormalOperationsStarted()
default:
return fmt.Errorf("unknown state: %v", state)
@@ -1113,16 +1129,22 @@ func (vm *VM) onNormalOperationsStarted() error {
if vm.ethTxGossipHandler == nil {
// Get logger from context for gossip handler
// Use VM's logger instead of consensus logger
vm.ethTxGossipHandler = newTxGossipHandler[*GossipEthTx](
vm.logger,
handler, err := gossipHandler.NewTxGossipHandler[*GossipEthTx](
log.Root(),
ethTxGossipMarshaller,
ethTxPool,
ethTxGossipMetrics,
config.TxGossipTargetMessageSize,
config.TxGossipThrottlingPeriod,
config.TxGossipThrottlingLimit,
float64(config.TxGossipThrottlingLimit),
p2pValidators,
vm.sdkMetrics,
ethTxGossipNamespace,
)
if err != nil {
return fmt.Errorf("failed to create tx gossip handler: %w", err)
}
vm.ethTxGossipHandler = handler
}
if err := vm.Network.AddHandler(TxGossipHandlerID, vm.ethTxGossipHandler); err != nil {
@@ -1193,11 +1215,12 @@ func (vm *VM) WaitForEvent(ctx context.Context) (commonEng.Message, error) {
if builder == nil {
select {
case <-ctx.Done():
return commonEng.Message(0), ctx.Err()
return commonEng.Message{}, ctx.Err()
case <-vm.stateSyncDone:
return commonEng.StateSyncDone, nil
// Return empty message to indicate state sync is done
return commonEng.Message{}, nil
case <-vm.shutdownChan:
return commonEng.Message(0), errShuttingDownVM
return commonEng.Message{}, errShuttingDownVM
}
}
@@ -1243,16 +1266,17 @@ func (vm *VM) Shutdown(context.Context) error {
}
// BuildBlock implements the ChainVM interface
func (vm *VM) BuildBlock(ctx context.Context) (nodechain.Block, error) {
func (vm *VM) BuildBlock(ctx context.Context) (nodeblock.Block, error) {
blk, err := vm.buildBlock(ctx)
if err != nil {
return nil, err
}
return blk.(*Block), nil
// Adapt the consensus block to node block interface
return NewBlockAdapter(blk.(*Block)), nil
}
// BuildBlockWithContext implements the BuildBlockWithContextChainVM interface
func (vm *VM) BuildBlockWithContext(ctx context.Context, proposerVMBlockCtx *nodeblock.Context) (nodechain.Block, error) {
func (vm *VM) BuildBlockWithContext(ctx context.Context, proposerVMBlockCtx *nodeblock.Context) (nodeblock.Block, error) {
// Convert node context to consensus context
var consensusCtx *block.Context
if proposerVMBlockCtx != nil {
@@ -1264,7 +1288,8 @@ func (vm *VM) BuildBlockWithContext(ctx context.Context, proposerVMBlockCtx *nod
if err != nil {
return nil, err
}
return blk.(*Block), nil
// Adapt the consensus block to node block interface
return NewBlockAdapter(blk.(*Block)), nil
}
// buildBlock builds a block to be wrapped by ChainState
@@ -1345,13 +1370,13 @@ func (vm *VM) ParseEthBlock(b []byte) (*types.Block, error) {
// getBlock attempts to retrieve block [id] from the VM to be wrapped
// by ChainState.
// GetBlock implements the ChainVM interface
func (vm *VM) GetBlock(ctx context.Context, id ids.ID) (nodechain.Block, error) {
func (vm *VM) GetBlock(ctx context.Context, id ids.ID) (nodeblock.Block, error) {
blk, err := vm.getBlock(ctx, id)
if err != nil {
return nil, err
}
// Cast to node's Block interface
return blk.(nodechain.Block), nil
// Adapt the consensus block to node block interface
return NewBlockAdapter(blk.(nodechain.Block)), nil
}
func (vm *VM) getBlock(_ context.Context, id ids.ID) (block.Block, error) {
@@ -1367,7 +1392,7 @@ func (vm *VM) getBlock(_ context.Context, id ids.ID) (block.Block, error) {
// GetAcceptedBlock attempts to retrieve block [blkID] from the VM. This method
// only returns accepted blocks.
func (vm *VM) GetAcceptedBlock(ctx context.Context, blkID ids.ID) (nodeChain.Block, error) {
func (vm *VM) GetAcceptedBlock(ctx context.Context, blkID ids.ID) (nodechain.Block, error) {
// First verify the block is accepted
ethBlock := vm.blockChain.GetBlockByHash(common.BytesToHash(blkID[:]))
if ethBlock == nil {
@@ -1685,8 +1710,11 @@ func (vm *VM) Disconnected(ctx context.Context, nodeID ids.NodeID) error {
// AppRequestFailed implements the VM interface
func (vm *VM) AppRequestFailed(ctx context.Context, nodeID ids.NodeID, requestID uint32, appErr *commonEng.AppError) error {
// Pass the error directly to the network (both use the same AppError type now)
return vm.Network.AppRequestFailed(ctx, nodeID, requestID, appErr)
// The Network interface doesn't expose AppRequestFailed directly
// We need to handle this at the VM level by logging the error
log.Debug("AppRequestFailed", "nodeID", nodeID, "requestID", requestID, "error", appErr)
// The network's response handler will handle the timeout internally
return nil
}
// CrossChainAppRequestFailed implements the VM interface
@@ -1774,7 +1802,7 @@ func (w *warpVerifierAdapter) Verify(ctx context.Context, msg *nodeWarp.Unsigned
// Convert node warp message to consensus warp message
luxMsg := &luxWarp.UnsignedMessage{
NetworkID: msg.NetworkID,
SourceChainID: msg.SourceChainID[:],
SourceChainID: msg.SourceChainID,
Payload: msg.Payload,
}
+2 -3
View File
@@ -24,7 +24,6 @@ import (
"github.com/luxfi/log"
metrics "github.com/luxfi/metric"
"github.com/luxfi/node/utils/constants"
"github.com/luxfi/node/utils/logging"
)
const (
@@ -69,7 +68,7 @@ func (vm *VM) initializeDBs(avaDB luxdatabase.Database) error {
}
log.Info("Using standalone database for the chain state", "DatabaseConfig", dbConfig)
// Create a logger adapter for newStandaloneDatabase
logger := logging.NoLog{}
logger := log.NoLog{}
// Pass nil for metrics as it's optional
db, err = newStandaloneDatabase(dbConfig, nil, logger)
if err != nil {
@@ -201,7 +200,7 @@ func inspectDB(db luxdatabase.Database, label string) error {
return nil
}
func newStandaloneDatabase(dbConfig DatabaseConfig, gatherer metrics.MultiGatherer, logger logging.Logger) (luxdatabase.Database, error) {
func newStandaloneDatabase(dbConfig DatabaseConfig, gatherer metrics.MultiGatherer, logger log.Logger) (luxdatabase.Database, error) {
dbPath := filepath.Join(dbConfig.Path, dbConfig.Name)
// Use log.Root() instead of trying to adapt the logger
+1 -1
View File
@@ -61,7 +61,7 @@ import (
protocolchain "github.com/luxfi/consensus/protocol/chain"
luxdconstants "github.com/luxfi/node/utils/constants"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
)
var (
+1 -1
View File
@@ -28,7 +28,7 @@ import (
"github.com/luxfi/ids"
"github.com/luxfi/node/upgrade"
"github.com/luxfi/node/upgrade/upgradetest"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
+1 -1
View File
@@ -17,7 +17,7 @@ import (
"github.com/luxfi/evm/utils/utilstest"
"github.com/luxfi/ids"
"github.com/luxfi/node/upgrade/upgradetest"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
+1 -1
View File
@@ -44,7 +44,7 @@ import (
"github.com/luxfi/node/upgrade/upgradetest"
luxdUtils "github.com/luxfi/node/utils"
"github.com/luxfi/node/utils/constants"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
luxWarp "github.com/luxfi/warp"
"github.com/luxfi/warp/payload"
"github.com/stretchr/testify/require"
+10
View File
@@ -58,6 +58,16 @@ func (b *warpConsensusBlockWrapper) Parent() ids.ID {
return b.block.Parent()
}
// ParentID returns the parent block's ID (consensus/chain.Block interface)
func (b *warpConsensusBlockWrapper) ParentID() ids.ID {
return b.block.ParentID()
}
// Status returns the block's status (consensus/chain.Block interface)
func (b *warpConsensusBlockWrapper) Status() uint8 {
return b.block.Status()
}
// Accept implements consensus/chain.Block interface
func (b *warpConsensusBlockWrapper) Accept(ctx context.Context) error {
// Block is already accepted (we only return accepted blocks)
@@ -10,6 +10,7 @@ import (
ethtypes "github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/core/vm"
"github.com/luxfi/evm/core/extstate"
"github.com/luxfi/evm/core/state"
"github.com/luxfi/evm/precompile/allowlist"
"github.com/luxfi/evm/precompile/modules"
"github.com/luxfi/evm/precompile/precompileconfig"
@@ -40,7 +41,7 @@ func AllowListTests(t testing.TB, module modules.Module) map[string]precompilete
SuppliedGas: allowlist.ModifyAllowListGasCost + allowlist.AllowListEventGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, state *extstate.StateDB) {
AfterHook: func(t testing.TB, state *state.StateDB) {
res := allowlist.GetAllowListStatus(state, contractAddress, TestNoRoleAddr)
require.Equal(t, allowlist.AdminRole, res)
// Check logs are stored in state
@@ -60,7 +61,7 @@ func AllowListTests(t testing.TB, module modules.Module) map[string]precompilete
SuppliedGas: allowlist.ModifyAllowListGasCost + allowlist.AllowListEventGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, state *extstate.StateDB) {
AfterHook: func(t testing.TB, state *state.StateDB) {
res := allowlist.GetAllowListStatus(state, contractAddress, TestNoRoleAddr)
require.Equal(t, allowlist.EnabledRole, res)
// Check logs are stored in state
@@ -80,7 +81,7 @@ func AllowListTests(t testing.TB, module modules.Module) map[string]precompilete
SuppliedGas: allowlist.ModifyAllowListGasCost + allowlist.AllowListEventGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, state *extstate.StateDB) {
AfterHook: func(t testing.TB, state *state.StateDB) {
res := allowlist.GetAllowListStatus(state, contractAddress, TestEnabledAddr)
require.Equal(t, allowlist.NoRole, res)
// Check logs are stored in state
@@ -273,7 +274,7 @@ func AllowListTests(t testing.TB, module modules.Module) map[string]precompilete
},
SuppliedGas: allowlist.ModifyAllowListGasCost + allowlist.AllowListEventGasCost,
ReadOnly: false,
AfterHook: func(t testing.TB, state *extstate.StateDB) {
AfterHook: func(t testing.TB, state *state.StateDB) {
res := allowlist.GetAllowListStatus(state, contractAddress, TestNoRoleAddr)
require.Equal(t, allowlist.ManagerRole, res)
// Check logs are stored in state
@@ -294,7 +295,7 @@ func AllowListTests(t testing.TB, module modules.Module) map[string]precompilete
ReadOnly: false,
ExpectedRes: []byte{},
ExpectedErr: "",
AfterHook: func(t testing.TB, state *extstate.StateDB) {
AfterHook: func(t testing.TB, state *state.StateDB) {
res := allowlist.GetAllowListStatus(state, contractAddress, TestNoRoleAddr)
require.Equal(t, allowlist.NoRole, res)
// Check logs are stored in state
@@ -315,7 +316,7 @@ func AllowListTests(t testing.TB, module modules.Module) map[string]precompilete
ReadOnly: false,
ExpectedRes: []byte{},
ExpectedErr: "",
AfterHook: func(t testing.TB, state *extstate.StateDB) {
AfterHook: func(t testing.TB, state *state.StateDB) {
res := allowlist.GetAllowListStatus(state, contractAddress, TestNoRoleAddr)
require.Equal(t, allowlist.EnabledRole, res)
@@ -398,7 +399,7 @@ func AllowListTests(t testing.TB, module modules.Module) map[string]precompilete
SuppliedGas: allowlist.ModifyAllowListGasCost + allowlist.AllowListEventGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, state *extstate.StateDB) {
AfterHook: func(t testing.TB, state *state.StateDB) {
res := allowlist.GetAllowListStatus(state, contractAddress, TestNoRoleAddr)
require.Equal(t, allowlist.NoRole, res)
@@ -548,7 +549,7 @@ func AllowListTests(t testing.TB, module modules.Module) map[string]precompilete
),
SuppliedGas: 0,
ReadOnly: false,
AfterHook: func(t testing.TB, state *extstate.StateDB) {
AfterHook: func(t testing.TB, state *state.StateDB) {
require.Equal(t, allowlist.AdminRole, allowlist.GetAllowListStatus(state, contractAddress, TestNoRoleAddr))
require.Equal(t, allowlist.AdminRole, allowlist.GetAllowListStatus(state, contractAddress, TestEnabledAddr))
},
@@ -562,7 +563,7 @@ func AllowListTests(t testing.TB, module modules.Module) map[string]precompilete
),
SuppliedGas: 0,
ReadOnly: false,
AfterHook: func(t testing.TB, state *extstate.StateDB) {
AfterHook: func(t testing.TB, state *state.StateDB) {
require.Equal(t, allowlist.ManagerRole, allowlist.GetAllowListStatus(state, contractAddress, TestNoRoleAddr))
require.Equal(t, allowlist.ManagerRole, allowlist.GetAllowListStatus(state, contractAddress, TestEnabledAddr))
},
@@ -576,7 +577,7 @@ func AllowListTests(t testing.TB, module modules.Module) map[string]precompilete
),
SuppliedGas: 0,
ReadOnly: false,
AfterHook: func(t testing.TB, state *extstate.StateDB) {
AfterHook: func(t testing.TB, state *state.StateDB) {
require.Equal(t, allowlist.EnabledRole, allowlist.GetAllowListStatus(state, contractAddress, TestAdminAddr))
require.Equal(t, allowlist.EnabledRole, allowlist.GetAllowListStatus(state, contractAddress, TestNoRoleAddr))
},
@@ -597,7 +598,7 @@ func AllowListTests(t testing.TB, module modules.Module) map[string]precompilete
SuppliedGas: allowlist.ModifyAllowListGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, stateDB *extstate.StateDB) {
AfterHook: func(t testing.TB, stateDB *state.StateDB) {
// Check no logs are stored in state
logs := stateDB.Logs()
require.Empty(t, logs)
@@ -619,7 +620,7 @@ func AllowListTests(t testing.TB, module modules.Module) map[string]precompilete
SuppliedGas: allowlist.ModifyAllowListGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, stateDB *extstate.StateDB) {
AfterHook: func(t testing.TB, stateDB *state.StateDB) {
// Check no logs are stored in state
logs := stateDB.Logs()
require.Empty(t, logs)
@@ -641,7 +642,7 @@ func AllowListTests(t testing.TB, module modules.Module) map[string]precompilete
SuppliedGas: allowlist.ModifyAllowListGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, stateDB *extstate.StateDB) {
AfterHook: func(t testing.TB, stateDB *state.StateDB) {
// Check no logs are stored in state
logs := stateDB.Logs()
require.Empty(t, logs)
@@ -652,9 +653,11 @@ func AllowListTests(t testing.TB, module modules.Module) map[string]precompilete
// SetDefaultRoles returns a BeforeHook that sets roles TestAdminAddr and TestEnabledAddr
// to have the AdminRole and EnabledRole respectively.
func SetDefaultRoles(contractAddress common.Address) func(t testing.TB, state *extstate.StateDB) {
return func(t testing.TB, state *extstate.StateDB) {
adapter := extstate.NewPrecompileAdapter(state)
func SetDefaultRoles(contractAddress common.Address) func(t testing.TB, stateDB *state.StateDB) {
return func(t testing.TB, stateDB *state.StateDB) {
// Create an ExtState wrapper and then get the adapter
extState := extstate.New(stateDB)
adapter := extstate.NewPrecompileAdapter(extState)
allowlist.SetAllowListRole(adapter, contractAddress, TestAdminAddr, allowlist.AdminRole)
allowlist.SetAllowListRole(adapter, contractAddress, TestManagerAddr, allowlist.ManagerRole)
allowlist.SetAllowListRole(adapter, contractAddress, TestEnabledAddr, allowlist.EnabledRole)
+1 -1
View File
@@ -267,7 +267,7 @@ func sendWarpMessage(accessibleState contract.AccessibleState, caller common.Add
}
unsignedWarpMessage, err := warp.NewUnsignedMessage(
consensus.GetNetworkID(ctx),
sourceChainID[:],
sourceChainID,
addressedPayload.Bytes(),
)
if err != nil {
+1 -1
View File
@@ -11,7 +11,7 @@ import (
"github.com/luxfi/consensus"
"github.com/luxfi/ids"
agoUtils "github.com/luxfi/node/utils"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
luxWarp "github.com/luxfi/warp"
"github.com/luxfi/warp/payload"
"github.com/luxfi/geth/common"
@@ -7,7 +7,7 @@ import (
"fmt"
"math"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
"github.com/luxfi/warp"
"github.com/luxfi/warp/payload"
"github.com/luxfi/geth/common"
@@ -8,12 +8,12 @@ import (
"math"
"testing"
"github.com/luxfi/node/ids"
"github.com/luxfi/node/snow/validators"
"github.com/luxfi/node/snow/validators/validatorsmock"
"github.com/luxfi/ids"
"github.com/luxfi/consensus/validators"
"github.com/luxfi/consensus/validators/validatorsmock"
"github.com/luxfi/node/utils/constants"
"github.com/luxfi/node/utils/crypto/bls"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
+10 -8
View File
@@ -1,19 +1,19 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package testutils
package precompiletest
import (
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/tracing"
"github.com/holiman/uint256"
"github.com/luxfi/evm/core/extstate"
"github.com/luxfi/evm/core/state"
"github.com/luxfi/evm/precompile/contract"
)
// StateDBAdapter wraps *extstate.StateDB to implement contract.StateDB
// StateDBAdapter wraps *state.StateDB to implement contract.StateDB
type StateDBAdapter struct {
*extstate.StateDB
*state.StateDB
}
// AddBalance adapts the AddBalance method to match the contract.StateDB interface
@@ -35,10 +35,12 @@ func (s *StateDBAdapter) SetState(address common.Address, key, value common.Hash
// GetPredicateStorageSlots implements the contract.StateDB interface
func (s *StateDBAdapter) GetPredicateStorageSlots(address common.Address, index int) ([]byte, bool) {
return s.StateDB.GetPredicateStorageSlots(address, index)
// StateDB doesn't have this method, so we return nil, false
// This should be overridden in test implementations if needed
return nil, false
}
// WrapStateDB wraps an *extstate.StateDB to implement contract.StateDB
func WrapStateDB(state *extstate.StateDB) contract.StateDB {
return &StateDBAdapter{StateDB: state}
// WrapStateDB wraps an *state.StateDB to implement contract.StateDB
func WrapStateDB(stateDB *state.StateDB) contract.StateDB {
return &StateDBAdapter{StateDB: stateDB}
}
+10 -9
View File
@@ -41,13 +41,13 @@ type PrecompileTest struct {
// If nil, Configure will not be called.
Config precompileconfig.Config
// BeforeHook is called before the precompile is called.
BeforeHook func(t testing.TB, state *extstate.StateDB)
BeforeHook func(t testing.TB, state *state.StateDB)
// Predicates that the precompile should have access to.
Predicates [][]byte
// SetupBlockContext sets the expected calls on MockBlockContext for the test execution.
SetupBlockContext func(*contract.MockBlockContext)
// AfterHook is called after the precompile is called.
AfterHook func(t testing.TB, state *extstate.StateDB)
AfterHook func(t testing.TB, state *state.StateDB)
// ExpectedRes is the expected raw byte result returned by the precompile
ExpectedRes []byte
// ExpectedErr is the expected error returned by the precompile
@@ -84,7 +84,7 @@ func (test PrecompileTest) Run(t *testing.T, module modules.Module) {
}
if test.AfterHook != nil {
test.AfterHook(t, state.StateDB)
test.AfterHook(t, state.underlyingStateDB)
}
}
@@ -95,7 +95,7 @@ func (test PrecompileTest) setup(t testing.TB, module modules.Module, state *tes
ctrl := gomock.NewController(t)
if test.BeforeHook != nil {
test.BeforeHook(t, state.StateDB)
test.BeforeHook(t, state.underlyingStateDB)
}
if test.ChainConfigFn == nil {
@@ -156,8 +156,8 @@ func RunPrecompileTests(t *testing.T, module modules.Module, contractTests map[s
// testStateDB allows for mocking the predicate storage slots without calling
// Prepare on the statedb.
type testStateDB struct {
*extstate.PrecompileStateDBAdapter
contract.StateDB
underlyingStateDB *state.StateDB // Keep reference to underlying StateDB for tests
predicateStorageSlots map[common.Address][][]byte
}
@@ -175,9 +175,10 @@ func newTestStateDB(t testing.TB, predicateStorageSlots map[common.Address][][]b
db := rawdb.NewMemoryDatabase()
statedb, err := state.New(common.Hash{}, state.NewDatabase(db), nil)
require.NoError(t, err)
extStateDB := extstate.New(statedb)
extState := extstate.New(statedb)
return &testStateDB{
PrecompileStateDBAdapter: extstate.NewPrecompileAdapter(extStateDB),
predicateStorageSlots: predicateStorageSlots,
StateDB: extstate.NewPrecompileAdapter(extState),
underlyingStateDB: statedb,
predicateStorageSlots: predicateStorageSlots,
}
}
+9 -7
View File
@@ -7,13 +7,13 @@ import (
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/tracing"
"github.com/holiman/uint256"
"github.com/luxfi/evm/core/extstate"
"github.com/luxfi/evm/core/state"
"github.com/luxfi/evm/precompile/contract"
)
// StateDBAdapter wraps *extstate.StateDB to implement contract.StateDB
// StateDBAdapter wraps *state.StateDB to implement contract.StateDB
type StateDBAdapter struct {
*extstate.StateDB
*state.StateDB
}
// AddBalance adapts the AddBalance method to match the contract.StateDB interface
@@ -35,10 +35,12 @@ func (s *StateDBAdapter) SetState(address common.Address, key, value common.Hash
// GetPredicateStorageSlots implements the contract.StateDB interface
func (s *StateDBAdapter) GetPredicateStorageSlots(address common.Address, index int) ([]byte, bool) {
return s.StateDB.GetPredicateStorageSlots(address, index)
// StateDB doesn't have this method, so we return nil, false
// This should be overridden in test implementations if needed
return nil, false
}
// WrapStateDB wraps an *extstate.StateDB to implement contract.StateDB
func WrapStateDB(state *extstate.StateDB) contract.StateDB {
return &StateDBAdapter{StateDB: state}
// WrapStateDB wraps an *state.StateDB to implement contract.StateDB
func WrapStateDB(stateDB *state.StateDB) contract.StateDB {
return &StateDBAdapter{StateDB: stateDB}
}
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"sync"
"github.com/luxfi/ids"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/ethdb"
-6
View File
@@ -1,6 +0,0 @@
FROM scratch AS execution
# Copy config artifacts from the build path. For simplicity, artifacts
# are built outside of the docker image.
COPY ./build/antithesis/docker-compose.yml /
COPY ./build/antithesis/volumes /volumes
-35
View File
@@ -1,35 +0,0 @@
# BUILDER_IMAGE_TAG should identify the builder image
ARG BUILDER_IMAGE_TAG
# LUXD_NODE_IMAGE needs to identify an existing luxd node image and should include the tag
ARG LUXD_NODE_IMAGE
# ============= Compilation Stage ================
FROM antithesis-evm-builder:$BUILDER_IMAGE_TAG AS builder
# The builder workdir will vary between instrumented and non-instrumented builders
ARG BUILDER_WORKDIR
WORKDIR $BUILDER_WORKDIR
# Build the VM
RUN ./scripts/build.sh /build/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy
# ============= Cleanup Stage ================
FROM $LUXD_NODE_IMAGE AS execution
# Copy identifying information into the container. This will replace
# the luxd commit hash in the base image.
COPY --from=builder /build/commit_hash.txt /luxd/build/commit_hash.txt
# Copy the antithesis dependencies into the container
COPY --from=builder /instrumented/symbols /symbols
# Configure the node with the location of the plugin
ENV LUXD_PLUGIN_DIR=/luxd/build/plugins
# Copy the executable into the container
COPY --from=builder $BUILDER_WORKDIR/build/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy\
$LUXD_PLUGIN_DIR/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy
# The node image's entrypoint will be reused.
-28
View File
@@ -1,28 +0,0 @@
# BUILDER_IMAGE_TAG should identify the builder image
ARG BUILDER_IMAGE_TAG
# LUXD_NODE_IMAGE needs to identify an existing luxd node image and should include the tag
ARG LUXD_NODE_IMAGE
# ============= Compilation Stage ================
FROM antithesis-evm-builder:$BUILDER_IMAGE_TAG AS builder
# The builder workdir will vary between instrumented and non-instrumented builders
ARG BUILDER_WORKDIR
WORKDIR $BUILDER_WORKDIR
# Build the workload
RUN ./scripts/build_antithesis_workload.sh
# ============= Cleanup Stage ================
# Base the workflow on the node image to support bootstrap testing
FROM $LUXD_NODE_IMAGE AS execution
# The builder workdir will vary between instrumented and non-instrumented builders
ARG BUILDER_WORKDIR
# Copy the executable into the container
COPY --from=builder $BUILDER_WORKDIR/build/workload ./workload
CMD [ "./workload" ]
-11
View File
@@ -1,11 +0,0 @@
# Antithesis Testing
This package supports testing with
[Antithesis](https://antithesis.com/docs/introduction/introduction.html),
a SaaS offering that enables deployment of distributed systems (such
as Lux) to a deterministic and simulated environment that
enables discovery and reproduction of anomalous behavior.
See luxd's
[documentation](https://github.com/luxfi/luxd/blob/master/tests/antithesis/README.md)
for more details.
-41
View File
@@ -1,41 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//go:build antithesis
// +build antithesis
package main
import (
"log"
"os"
"path/filepath"
"github.com/luxfi/node/tests/antithesis"
"github.com/luxfi/node/tests/fixture/tmpnet"
"github.com/luxfi/evm/tests/utils"
)
const baseImageName = "antithesis-evm"
// Creates docker-compose.yml and its associated volumes in the target path.
func main() {
// Assume the working directory is the root of the repository
cwd, err := os.Getwd()
if err != nil {
log.Fatalf("failed to get current working directory: %s", err)
}
genesisPath := filepath.Join(cwd, "tests/load/genesis/genesis.json")
// Create a network with a evm subnet
network := tmpnet.LocalNetworkOrPanic()
network.Subnets = []*tmpnet.Subnet{
utils.NewTmpnetSubnet("evm", genesisPath, utils.DefaultChainConfig, network.Nodes...),
}
if err := antithesis.GenerateComposeConfig(network, baseImageName); err != nil {
log.Fatalf("failed to generate compose config: %v", err)
}
}
-216
View File
@@ -1,216 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//go:build antithesis
// +build antithesis
package main
import (
"context"
"crypto/ecdsa"
"crypto/rand"
"fmt"
"math/big"
"path/filepath"
"time"
"github.com/antithesishq/antithesis-sdk-go/assert"
"github.com/antithesishq/antithesis-sdk-go/lifecycle"
"github.com/luxfi/geth/common"
"github.com/luxfi/crypto"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"github.com/luxfi/ids"
"github.com/luxfi/node/tests/antithesis"
"github.com/luxfi/node/tests/fixture/tmpnet"
"github.com/luxfi/node/utils/logging"
"github.com/luxfi/geth/core/types"
ethparams "github.com/luxfi/geth/params"
"github.com/luxfi/evm/accounts/abi/bind"
"github.com/luxfi/evm/ethclient"
"github.com/luxfi/evm/tests"
"github.com/luxfi/evm/tests/utils"
ago_tests "github.com/luxfi/node/tests"
timerpkg "github.com/luxfi/node/utils/timer"
)
const NumKeys = 5
func main() {
logger := ago_tests.NewDefaultLogger("")
tc := ago_tests.NewTestContext(logger)
defer tc.Cleanup()
require := require.New(tc)
c := antithesis.NewConfigWithSubnets(
tc,
// TODO(marun) Centralize network configuration for all test types
utils.NewTmpnetNetwork(
"antithesis-evm",
nil,
tmpnet.FlagsMap{},
),
func(nodes ...*tmpnet.Node) []*tmpnet.Subnet {
repoRootPath := tests.GetRepoRootPath("tests/antithesis")
genesisPath := filepath.Join(repoRootPath, "tests/load/genesis/genesis.json")
return []*tmpnet.Subnet{
utils.NewTmpnetSubnet("evm", genesisPath, utils.DefaultChainConfig, nodes...),
}
},
)
ctx := ago_tests.DefaultNotifyContext(c.Duration, tc.DeferCleanup)
require.Len(c.ChainIDs, 1)
logger.Info("Starting testing",
zap.Strings("chainIDs", c.ChainIDs),
)
chainID, err := ids.FromString(c.ChainIDs[0])
require.NoError(err, "failed to parse chainID")
genesisClient, err := ethclient.Dial(getChainURI(c.URIs[0], chainID.String()))
require.NoError(err, "failed to dial chain")
genesisKey := tmpnet.HardhatKey.ToECDSA()
genesisWorkload := &workload{
id: 0,
log: ago_tests.NewDefaultLogger(fmt.Sprintf("worker %d", 0)),
client: genesisClient,
key: genesisKey,
uris: c.URIs,
}
workloads := make([]*workload, NumKeys)
workloads[0] = genesisWorkload
initialAmount := uint64(1_000_000_000_000_000)
for i := 1; i < NumKeys; i++ {
key, err := crypto.ToECDSA(crypto.Keccak256([]byte{uint8(i)}))
require.NoError(err, "failed to generate key")
require.NoError(transferFunds(ctx, genesisClient, genesisKey, crypto.PubkeyToAddress(key.PublicKey), initialAmount, logger))
client, err := ethclient.Dial(getChainURI(c.URIs[i%len(c.URIs)], chainID.String()))
require.NoError(err, "failed to dial chain")
workloads[i] = &workload{
id: i,
log: ago_tests.NewDefaultLogger(fmt.Sprintf("worker %d", i)),
client: client,
key: key,
uris: c.URIs,
}
}
lifecycle.SetupComplete(map[string]any{
"msg": "initialized workers",
"numWorkers": NumKeys,
})
for _, w := range workloads[1:] {
go w.run(ctx)
}
genesisWorkload.run(ctx)
}
type workload struct {
id int
client ethclient.Client
log logging.Logger
key *ecdsa.PrivateKey
uris []string
}
func (w *workload) run(ctx context.Context) {
timer := timerpkg.StoppedTimer()
tc := ago_tests.NewTestContext(w.log)
defer tc.Cleanup()
require := require.New(tc)
balance, err := w.client.BalanceAt(ctx, crypto.PubkeyToAddress(w.key.PublicKey), nil)
require.NoError(err, "failed to fetch balance")
assert.Reachable("worker starting", map[string]any{
"worker": w.id,
"balance": balance,
})
// TODO(marun) What should this value be?
txAmount := uint64(10000)
for {
// TODO(marun) Exercise a wider variety of transactions
recipientEthAddress := crypto.PubkeyToAddress(w.key.PublicKey)
err := transferFunds(ctx, w.client, w.key, recipientEthAddress, txAmount, w.log)
if err != nil {
// Log the error and continue since the problem may be
// transient. require.NoError is only for errors that should stop
// execution.
w.log.Info("failed to transfer funds",
zap.Error(err),
)
}
val, err := rand.Int(rand.Reader, big.NewInt(int64(time.Second)))
require.NoError(err, "failed to read randomness")
timer.Reset(time.Duration(val.Int64()))
select {
case <-ctx.Done():
return
case <-timer.C:
}
}
}
func getChainURI(nodeURI string, blockchainID string) string {
return fmt.Sprintf("%s/ext/bc/%s/rpc", nodeURI, blockchainID)
}
func transferFunds(ctx context.Context, client ethclient.Client, key *ecdsa.PrivateKey, recipientAddress common.Address, txAmount uint64, log logging.Logger) error {
chainID, err := client.ChainID(ctx)
if err != nil {
return fmt.Errorf("failed to fetch chainID: %w", err)
}
acceptedNonce, err := client.AcceptedNonceAt(ctx, crypto.PubkeyToAddress(key.PublicKey))
if err != nil {
return fmt.Errorf("failed to fetch accepted nonce: %w", err)
}
gasTipCap, err := client.SuggestGasTipCap(ctx)
if err != nil {
return fmt.Errorf("failed to fetch suggested gas tip: %w", err)
}
gasFeeCap, err := client.EstimateBaseFee(ctx)
if err != nil {
return fmt.Errorf("failed to fetch estimated base fee: %w", err)
}
signer := types.LatestSignerForChainID(chainID)
tx, err := types.SignNewTx(key, signer, &types.DynamicFeeTx{
ChainID: chainID,
Nonce: acceptedNonce,
GasTipCap: gasTipCap,
GasFeeCap: gasFeeCap,
Gas: ethparams.TxGas,
To: &recipientAddress,
Value: big.NewInt(int64(txAmount)),
})
if err != nil {
return fmt.Errorf("failed to format transaction: %w", err)
}
log.Info("sending transaction", zap.Stringer("txID", tx.Hash()), zap.Uint64("nonce", acceptedNonce))
err = client.SendTransaction(ctx, tx)
if err != nil {
return fmt.Errorf("failed to send transaction: %w", err)
}
log.Info("waiting for acceptance of transaction", zap.Stringer("txID", tx.Hash()))
if _, err := bind.WaitMined(ctx, client, tx); err != nil {
return fmt.Errorf("failed to wait for receipt: %v", err)
}
log.Info("confirmed acceptance of transaction", zap.Stringer("txID", tx.Hash()))
return nil
}
+1 -1
View File
@@ -20,7 +20,7 @@ import (
"github.com/luxfi/ids"
"github.com/luxfi/node/tests/fixture/e2e"
"github.com/luxfi/node/tests/fixture/tmpnet"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
"github.com/luxfi/evm/tests"
"github.com/luxfi/evm/tests/utils"
+3 -3
View File
@@ -49,7 +49,7 @@ import (
ethparams "github.com/luxfi/geth/params"
"github.com/luxfi/geth/rlp"
"github.com/luxfi/geth/triedb"
"github.com/luxfi/evm/consensus/misc/eip4844"
"github.com/luxfi/geth/consensus/misc/eip4844"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/core/state"
"github.com/luxfi/evm/core/state/snapshot"
@@ -316,7 +316,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
context.Difficulty = big.NewInt(0)
}
if config.IsCancun(new(big.Int), block.Time()) && t.json.Env.ExcessBlobGas != nil {
context.BlobBaseFee = eip4844.CalcBlobFee(*t.json.Env.ExcessBlobGas)
context.BlobBaseFee = eip4844.CalcBlobFee(config, block.Header())
}
evm := vm.NewEVM(context, state.StateDB, config, vmconfig)
evm.SetTxContext(txContext)
@@ -500,7 +500,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, snapshotter bo
sdb := state.NewDatabaseWithNodeDB(db, triedb)
statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
for addr, a := range accounts {
statedb.SetCode(addr, a.Code)
statedb.SetCode(addr, a.Code, tracing.CodeChangeGenesis)
statedb.SetNonce(addr, a.Nonce, tracing.NonceChangeGenesis)
statedb.SetBalance(addr, uint256.MustFromBig(a.Balance), tracing.BalanceIncreaseGenesisBalance)
for k, v := range a.Storage {
+5 -5
View File
@@ -18,7 +18,7 @@ import (
"github.com/luxfi/node/genesis"
"github.com/luxfi/ids"
"github.com/luxfi/node/vms/secp256k1fx"
wallet "github.com/luxfi/node/wallet/subnet/primary"
"github.com/luxfi/node/wallet/net/primary"
"github.com/luxfi/log"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/plugin/evm"
@@ -117,12 +117,12 @@ func CreateNewSubnet(ctx context.Context, genesisFilePath string) string {
// MakeWallet fetches the available UTXOs owned by [kc] on the network
// that [LocalAPIURI] is hosting.
walletConfig := &wallet.WalletConfig{
walletConfig := &primary.WalletConfig{
URI: DefaultLocalNodeURI,
LUXKeychain: kc,
EthKeychain: kc,
}
wallet, err := wallet.MakeWallet(ctx, walletConfig)
wallet, err := primary.MakeWallet(ctx, walletConfig)
require.NoError(err)
pWallet := wallet.P()
@@ -141,7 +141,7 @@ func CreateNewSubnet(ctx context.Context, genesisFilePath string) string {
require.NoError(err)
log.Info("Creating new subnet")
createSubnetTx, err := pWallet.IssueCreateSubnetTx(owner)
createNetTx, err := pWallet.IssueCreateNetTx(owner)
require.NoError(err)
genesis := &core.Genesis{}
@@ -149,7 +149,7 @@ func CreateNewSubnet(ctx context.Context, genesisFilePath string) string {
log.Info("Creating new Subnet-EVM blockchain", "genesis", genesis)
createChainTx, err := pWallet.IssueCreateChainTx(
createSubnetTx.ID(),
createNetTx.ID(),
genesisBytes,
evm.ID,
nil,
+1 -1
View File
@@ -11,7 +11,7 @@ import (
"github.com/luxfi/ids"
cryptoBLS "github.com/luxfi/crypto/bls"
"github.com/luxfi/node/utils/set"
"github.com/luxfi/math/set"
luxWarp "github.com/luxfi/warp"
"github.com/luxfi/evm/precompile/contracts/warp"
)
+1 -1
View File
@@ -36,7 +36,7 @@ func (f *apiFetcher) GetSignature(ctx context.Context, nodeID ids.NodeID, unsign
}
switch p := parsedPayload.(type) {
case *payload.AddressedCall:
msgID, _ := ids.ToID(unsignedWarpMessage.ID())
msgID := unsignedWarpMessage.ID()
signatureBytes, err = client.GetMessageSignature(ctx, msgID)
case *payload.Hash:
blockID, _ := ids.ToID(p.Hash)
-273
View File
@@ -1,273 +0,0 @@
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package firewood
import (
"errors"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/crypto"
"github.com/luxfi/geth/ethdb"
"github.com/luxfi/geth/log"
"github.com/luxfi/geth/rlp"
"github.com/luxfi/geth/trie"
"github.com/luxfi/geth/trie/trienode"
"github.com/luxfi/geth/triedb/database"
)
// AccountTrie implements state.Trie for managing account states.
// There are a couple caveats to the current implementation:
// 1. `Commit` is not used as expected in the state package. The `StorageTrie` doesn't return
// values, and we thus rely on the `AccountTrie`.
// 2. The `Hash` method actually creates the proposal, since Firewood cannot calculate
// the hash of the trie without committing it. It is immediately dropped, and this
// can likely be optimized.
type AccountTrie struct {
fw *Database
parentRoot common.Hash
root common.Hash
reader database.Reader
dirtyKeys map[string][]byte // Store dirty changes
updateKeys [][]byte
updateValues [][]byte
hasChanges bool
}
func NewAccountTrie(root common.Hash, db *Database) (*AccountTrie, error) {
reader, err := db.Reader(root)
if err != nil {
return nil, err
}
return &AccountTrie{
fw: db,
parentRoot: root,
reader: reader,
dirtyKeys: make(map[string][]byte),
hasChanges: true, // Start with hasChanges true to allow computing the proposal hash
}, nil
}
// GetAccount implements state.Trie.
func (a *AccountTrie) GetAccount(addr common.Address) (*types.StateAccount, error) {
key := crypto.Keccak256Hash(addr.Bytes()).Bytes()
// First check if there's a pending update for this account
keyStr := string(key)
if updateValue, exists := a.dirtyKeys[keyStr]; exists {
// If the value is empty, it indicates deletion
// Invariant: All encoded values have length > 0
if len(updateValue) == 0 {
return nil, nil
}
// Decode and return the updated account
account := new(types.StateAccount)
err := rlp.DecodeBytes(updateValue, account)
return account, err
}
// No pending update found, read from the underlying reader
accountBytes, err := a.reader.Node(common.Hash{}, key, common.Hash{})
if err != nil {
return nil, err
}
if accountBytes == nil {
return nil, nil
}
// Decode the account node
account := new(types.StateAccount)
err = rlp.DecodeBytes(accountBytes, account)
return account, err
}
// GetStorage implements state.Trie.
func (a *AccountTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
// If the account has been deleted, we should return nil
accountKey := crypto.Keccak256Hash(addr.Bytes()).Bytes()
if val, exists := a.dirtyKeys[string(accountKey)]; exists && len(val) == 0 {
return nil, nil
}
var combinedKey [2 * common.HashLength]byte
storageKey := crypto.Keccak256Hash(key).Bytes()
copy(combinedKey[:common.HashLength], accountKey)
copy(combinedKey[common.HashLength:], storageKey)
// Check if there's a pending update for this storage slot
keyStr := string(combinedKey[:])
if updateValue, exists := a.dirtyKeys[keyStr]; exists {
// If the value is empty, it indicates deletion
if len(updateValue) == 0 {
return nil, nil
}
// Decode and return the updated storage value
_, decoded, _, err := rlp.Split(updateValue)
return decoded, err
}
// No pending update found, read from the underlying reader
storageBytes, err := a.reader.Node(common.Hash{}, combinedKey[:], common.Hash{})
if err != nil || storageBytes == nil {
return nil, err
}
// Decode the storage value
_, decoded, _, err := rlp.Split(storageBytes)
return decoded, err
}
// UpdateAccount implements state.Trie.
func (a *AccountTrie) UpdateAccount(addr common.Address, account *types.StateAccount) error {
// Queue the keys and values for later commit
key := crypto.Keccak256Hash(addr.Bytes()).Bytes()
data, err := rlp.EncodeToBytes(account)
if err != nil {
return err
}
a.dirtyKeys[string(key)] = data
a.updateKeys = append(a.updateKeys, key)
a.updateValues = append(a.updateValues, data)
a.hasChanges = true // Mark that there are changes to commit
return nil
}
// UpdateStorage implements state.Trie.
func (a *AccountTrie) UpdateStorage(addr common.Address, key []byte, value []byte) error {
var combinedKey [2 * common.HashLength]byte
accountKey := crypto.Keccak256Hash(addr.Bytes()).Bytes()
storageKey := crypto.Keccak256Hash(key).Bytes()
copy(combinedKey[:common.HashLength], accountKey)
copy(combinedKey[common.HashLength:], storageKey)
data, err := rlp.EncodeToBytes(value)
if err != nil {
return err
}
// Queue the keys and values for later commit
a.dirtyKeys[string(combinedKey[:])] = data
a.updateKeys = append(a.updateKeys, combinedKey[:])
a.updateValues = append(a.updateValues, data)
a.hasChanges = true // Mark that there are changes to commit
return nil
}
// DeleteAccount implements state.Trie.
func (a *AccountTrie) DeleteAccount(addr common.Address) error {
key := crypto.Keccak256Hash(addr.Bytes()).Bytes()
// Queue the key for deletion
a.dirtyKeys[string(key)] = []byte{}
a.updateKeys = append(a.updateKeys, key)
a.updateValues = append(a.updateValues, []byte{}) // Empty value indicates deletion
a.hasChanges = true // Mark that there are changes to commit
return nil
}
// DeleteStorage implements state.Trie.
func (a *AccountTrie) DeleteStorage(addr common.Address, key []byte) error {
var combinedKey [2 * common.HashLength]byte
accountKey := crypto.Keccak256Hash(addr.Bytes()).Bytes()
storageKey := crypto.Keccak256Hash(key).Bytes()
copy(combinedKey[:common.HashLength], accountKey)
copy(combinedKey[common.HashLength:], storageKey)
// Queue the key for deletion
a.dirtyKeys[string(combinedKey[:])] = []byte{}
a.updateKeys = append(a.updateKeys, combinedKey[:])
a.updateValues = append(a.updateValues, []byte{}) // Empty value indicates deletion
a.hasChanges = true // Mark that there are changes to commit
return nil
}
// Hash implements state.Trie.
func (a *AccountTrie) Hash() common.Hash {
hash, err := a.hash()
if err != nil {
log.Error("Failed to hash account trie", "error", err)
return common.Hash{}
}
return hash
}
func (a *AccountTrie) hash() (common.Hash, error) {
// If we haven't already hashed, we need to do so.
if a.hasChanges {
root, err := a.fw.getProposalHash(a.parentRoot, a.updateKeys, a.updateValues)
if err != nil {
return common.Hash{}, err
}
a.root = root
a.hasChanges = false // Avoid re-hashing until next update
}
return a.root, nil
}
// Commit implements state.Trie.
func (a *AccountTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) {
// Get the hash of the trie.
hash, err := a.hash()
if err != nil {
return common.Hash{}, nil, err
}
// Create the NodeSet. This will be sent to `triedb.Update` later.
nodeset := trienode.NewNodeSet(a.parentRoot)
for i, key := range a.updateKeys {
nodeset.AddNode(key, &trienode.Node{
Blob: a.updateValues[i],
})
}
return hash, nodeset, nil
}
// UpdateContractCode implements state.Trie.
// Contract code is controlled by rawdb, so we don't need to do anything here.
func (*AccountTrie) UpdateContractCode(_ common.Address, _ common.Hash, _ []byte) error {
return nil
}
// GetKey implements state.Trie.
func (*AccountTrie) GetKey(_ []byte) []byte {
return nil // Not implemented, as this is only used in APIs
}
// NodeIterator implements state.Trie.
func (*AccountTrie) NodeIterator(_ []byte) (trie.NodeIterator, error) {
return nil, errors.New("NodeIterator not implemented for Firewood")
}
// Prove implements state.Trie.
func (*AccountTrie) Prove(_ []byte, _ ethdb.KeyValueWriter) error {
return errors.New("Prove not implemented for Firewood")
}
func (a *AccountTrie) Copy() *AccountTrie {
// Create a new AccountTrie with the same root and reader
newTrie := &AccountTrie{
fw: a.fw,
parentRoot: a.parentRoot,
root: a.root,
reader: a.reader, // Share the same reader
hasChanges: a.hasChanges,
dirtyKeys: make(map[string][]byte, len(a.dirtyKeys)),
updateKeys: make([][]byte, len(a.updateKeys)),
updateValues: make([][]byte, len(a.updateValues)),
}
// Deep copy dirtyKeys map
for k, v := range a.dirtyKeys {
newTrie.dirtyKeys[k] = append([]byte{}, v...)
}
// Deep copy updateKeys and updateValues slices
for i := range a.updateKeys {
newTrie.updateKeys[i] = append([]byte{}, a.updateKeys[i]...)
newTrie.updateValues[i] = append([]byte{}, a.updateValues[i]...)
}
return newTrie
}
-593
View File
@@ -1,593 +0,0 @@
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package firewood
import (
"errors"
"fmt"
"os"
"path/filepath"
"sync"
"time"
// "github.com/luxfi/firewood-go-ethhash/ffi"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/ethdb"
"github.com/luxfi/geth/libevm/stateconf"
"github.com/luxfi/geth/log"
"github.com/luxfi/geth/metrics"
"github.com/luxfi/geth/trie/trienode"
"github.com/luxfi/geth/trie/triestate"
"github.com/luxfi/geth/triedb"
"github.com/luxfi/geth/triedb/database"
)
var (
_ proposable = (*ffi.Database)(nil)
_ proposable = (*ffi.Proposal)(nil)
// FFI triedb operation metrics
ffiProposeCount = metrics.GetOrRegisterCounter("firewood/triedb/propose/count", nil)
ffiProposeTimer = metrics.GetOrRegisterCounter("firewood/triedb/propose/time", nil)
ffiCommitCount = metrics.GetOrRegisterCounter("firewood/triedb/commit/count", nil)
ffiCommitTimer = metrics.GetOrRegisterCounter("firewood/triedb/commit/time", nil)
ffiCleanupTimer = metrics.GetOrRegisterCounter("firewood/triedb/cleanup/time", nil)
ffiOutstandingProposals = metrics.GetOrRegisterGauge("firewood/triedb/propose/outstanding", nil)
// FFI Trie operation metrics
ffiHashCount = metrics.GetOrRegisterCounter("firewood/triedb/hash/count", nil)
ffiHashTimer = metrics.GetOrRegisterCounter("firewood/triedb/hash/time", nil)
ffiReadCount = metrics.GetOrRegisterCounter("firewood/triedb/read/count", nil)
ffiReadTimer = metrics.GetOrRegisterCounter("firewood/triedb/read/time", nil)
)
type proposable interface {
// Propose creates a new proposal from the current state with the given keys and values.
Propose(keys, values [][]byte) (*ffi.Proposal, error)
}
// ProposalContext represents a proposal in the Firewood database.
// This tracks all outstanding proposals to allow dereferencing upon commit.
type ProposalContext struct {
Proposal *ffi.Proposal
Hashes map[common.Hash]struct{} // All corresponding block hashes
Root common.Hash
Block uint64
Parent *ProposalContext
Children []*ProposalContext
}
type Config struct {
FilePath string
CleanCacheSize int // Size of the clean cache in bytes
FreeListCacheEntries uint // Number of free list entries to cache
Revisions uint
ReadCacheStrategy ffi.CacheStrategy
}
// Note that `FilePath` is not specificied, and must always be set by the user.
var Defaults = &Config{
CleanCacheSize: 1024 * 1024, // 1MB
FreeListCacheEntries: 40_000,
Revisions: 100,
ReadCacheStrategy: ffi.CacheAllReads,
}
func (c Config) BackendConstructor(_ ethdb.Database) triedb.DBOverride {
return New(&c)
}
type Database struct {
fwDisk *ffi.Database // The underlying Firewood database, used for storing proposals and revisions.
proposalLock sync.RWMutex
// proposalMap provides O(1) access by state root to all proposals stored in the proposalTree
proposalMap map[common.Hash][]*ProposalContext
// The proposal tree tracks the structure of the current proposals, and which proposals are children of which.
// This is used to ensure that we can dereference proposals correctly and commit the correct ones
// in the case of duplicate state roots.
// The root of the tree is stored here, and represents the top-most layer on disk.
proposalTree *ProposalContext
}
// New creates a new Firewood database with the given disk database and configuration.
// Any error during creation will cause the program to exit.
func New(config *Config) *Database {
if config == nil {
config = Defaults
}
fwConfig, err := validatePath(config)
if err != nil {
log.Crit("firewood: error validating config", "error", err)
}
fw, err := ffi.New(config.FilePath, fwConfig)
if err != nil {
log.Crit("firewood: error creating firewood database", "error", err)
}
currentRoot, err := fw.Root()
if err != nil {
log.Crit("firewood: error getting current root", "error", err)
}
return &Database{
fwDisk: fw,
proposalMap: make(map[common.Hash][]*ProposalContext),
proposalTree: &ProposalContext{
Root: common.Hash(currentRoot),
},
}
}
func validatePath(trieConfig *Config) (*ffi.Config, error) {
if trieConfig.FilePath == "" {
return nil, errors.New("firewood database file path must be set")
}
// Check that the directory exists
dir := filepath.Dir(trieConfig.FilePath)
_, err := os.Stat(dir)
if err != nil {
if !os.IsNotExist(err) {
return nil, fmt.Errorf("error checking database directory: %w", err)
}
log.Info("Database directory not found, creating", "path", dir)
if err := os.MkdirAll(dir, 0o755); err != nil {
return nil, fmt.Errorf("error creating database directory: %w", err)
}
}
// Create the Firewood config from the provided config.
config := &ffi.Config{
NodeCacheEntries: uint(trieConfig.CleanCacheSize) / 256, // TODO: estimate 256 bytes per node
FreeListCacheEntries: trieConfig.FreeListCacheEntries,
Revisions: trieConfig.Revisions,
ReadCacheStrategy: trieConfig.ReadCacheStrategy,
}
return config, nil
}
// Scheme returns the scheme of the database.
// This is only used in some API calls
// and in StateDB to avoid iterating through deleted storage tries.
// WARNING: If cherry-picking anything from upstream that uses this,
// it must be overwritten to use something like:
// `_, ok := db.(*Database); if !ok { return "" }`
// to recognize the Firewood database.
func (*Database) Scheme() string {
return rawdb.HashScheme
}
// Initialized checks whether a non-empty genesis block has been written.
func (db *Database) Initialized(_ common.Hash) bool {
rootBytes, err := db.fwDisk.Root()
if err != nil {
log.Error("firewood: error getting current root", "error", err)
return false
}
root := common.BytesToHash(rootBytes)
// If the current root isn't empty, then unless the database is empty, we have a genesis block recorded.
return root != types.EmptyRootHash
}
// Update takes a root and a set of keys-values and creates a new proposal.
// It will not be committed until the Commit method is called.
// This function should be called even if there are no changes to the state to ensure proper tracking of block hashes.
func (db *Database) Update(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, _ *triestate.Set, opts ...stateconf.TrieDBUpdateOption) error {
// We require block hashes to be provided for all blocks in production.
// However, many tests cannot reasonably provide a block hash for genesis, so we allow it to be omitted.
parentHash, hash, ok := stateconf.ExtractTrieDBUpdatePayload(opts...)
if !ok {
log.Error("firewood: no block hash provided for block %d", block)
}
// The rest of the operations except key-value arranging must occur with a lock
db.proposalLock.Lock()
defer db.proposalLock.Unlock()
// Check if this proposal already exists.
// During reorgs, we may have already created this proposal.
// Additionally, we may have already created this proposal with a different block hash.
if existingProposals, ok := db.proposalMap[root]; ok {
for _, existing := range existingProposals {
// If the block hash is already tracked, we can skip proposing this again.
if _, exists := existing.Hashes[hash]; exists {
log.Debug("firewood: proposal already exists", "root", root.Hex(), "parent", parentRoot.Hex(), "block", block, "hash", hash.Hex())
return nil
}
// We already have this proposal, but should create a new context with the correct hash.
// This solves the case of a unique block hash, but the same underlying proposal.
if _, exists := existing.Parent.Hashes[parentHash]; exists {
log.Debug("firewood: proposal already exists, updating hash", "root", root.Hex(), "parent", parentRoot.Hex(), "block", block, "hash", hash.Hex())
existing.Hashes[hash] = struct{}{}
return nil
}
}
}
keys, values := arrangeKeyValuePairs(nodes) // may return nil, nil if no changes
return db.propose(root, parentRoot, hash, parentHash, block, keys, values)
}
// propose creates a new proposal for every possible parent with the given keys and values.
// If the parent cannot be found, an error will be returned.
//
// To avoid having to create a new proposal for each valid state root, the block hashes are
// provided to ensure uniqueness. When this method is called, we can guarantee that the proposalContext
// must be created and tracked.
//
// Should only be accessed with the proposal lock held.
func (db *Database) propose(root common.Hash, parentRoot common.Hash, hash common.Hash, parentHash common.Hash, block uint64, keys [][]byte, values [][]byte) error {
// Find the parent proposal with the correct hash.
// We assume the number of proposals at a given root is small, so we can iterate through them.
for _, parentProposal := range db.proposalMap[parentRoot] {
// If we know this proposal cannot be the parent, we can skip it.
// Since the only possible block that won't have a parent hash is block 1,
// and that will always be proposed from the database root,
// we can guarantee that the parent hash will be present in one of the proposals.
if _, exists := parentProposal.Hashes[parentHash]; !exists {
continue
}
log.Debug("firewood: proposing from parent proposal", "parent", parentProposal.Root.Hex(), "root", root.Hex(), "height", block)
p, err := createProposal(parentProposal.Proposal, root, keys, values)
if err != nil {
return err
}
pCtx := &ProposalContext{
Proposal: p,
Hashes: map[common.Hash]struct{}{hash: {}},
Root: root,
Block: block,
Parent: parentProposal,
}
db.proposalMap[root] = append(db.proposalMap[root], pCtx)
parentProposal.Children = append(parentProposal.Children, pCtx)
return nil
}
// Since we were unable to find a parent proposal with the given parent hash,
// we must create a new proposal from the database root.
// We must avoid the case in which we are reexecuting blocks upon startup, and haven't yet stored the parent block.
if _, exists := db.proposalTree.Hashes[parentHash]; db.proposalTree.Block != 0 && !exists {
return fmt.Errorf("firewood: parent hash %s not found for block %s at height %d", parentHash.Hex(), hash.Hex(), block)
} else if db.proposalTree.Root != parentRoot {
return fmt.Errorf("firewood: parent root %s does not match proposal tree root %s for root %s at height %d", parentRoot.Hex(), db.proposalTree.Root.Hex(), root.Hex(), block)
}
log.Debug("firewood: proposing from database root", "root", root.Hex(), "height", block)
p, err := createProposal(db.fwDisk, root, keys, values)
if err != nil {
return err
}
pCtx := &ProposalContext{
Proposal: p,
Hashes: map[common.Hash]struct{}{hash: {}}, // This may be common.Hash{} for genesis blocks.
Root: root,
Block: block,
Parent: db.proposalTree,
}
db.proposalMap[root] = append(db.proposalMap[root], pCtx)
db.proposalTree.Children = append(db.proposalTree.Children, pCtx)
return nil
}
// Commit persists a proposal as a revision to the database.
//
// Any time this is called, we expect either:
// 1. The root is the same as the current root of the database (empty block during bootstrapping)
// 2. We have created a valid propsal with that root, and it is of height +1 above the proposal tree root.
// Additionally, this should be unique.
//
// Afterward, we know that no other proposal at this height can be committed, so we can dereference all
// children in the the other branches of the proposal tree.
func (db *Database) Commit(root common.Hash, report bool) (err error) {
// We need to lock the proposal tree to prevent concurrent writes.
var pCtx *ProposalContext
db.proposalLock.Lock()
defer db.proposalLock.Unlock()
// On success, we should persist the genesis root as necessary, and dereference all children
// of the committed proposal.
defer func() {
// If we attempted to commit a proposal, but it failed, we must dereference its children.
if pCtx != nil {
db.cleanupCommittedProposal(pCtx)
}
}()
// Find the proposal with the given root.
for _, possible := range db.proposalMap[root] {
if possible.Parent.Root == db.proposalTree.Root && possible.Parent.Block == db.proposalTree.Block {
// We found the proposal with the correct parent.
if pCtx != nil {
// This should never happen, as we ensure that we don't create duplicate proposals in `propose`.
return fmt.Errorf("firewood: multiple proposals found for %s", root.Hex())
}
pCtx = possible
}
}
if pCtx == nil {
return fmt.Errorf("firewood: committable proposal not found for %s", root.Hex())
}
start := time.Now()
// Commit the proposal to the database.
if commitErr := pCtx.Proposal.Commit(); commitErr != nil {
return fmt.Errorf("firewood: error committing proposal %s: %w", root.Hex(), commitErr)
}
ffiCommitCount.Inc(1)
ffiCommitTimer.Inc(time.Since(start).Milliseconds())
ffiOutstandingProposals.Dec(1)
// Assert that the root of the database matches the committed proposal root.
currentRootBytes, err := db.fwDisk.Root()
if err != nil {
return fmt.Errorf("firewood: error getting current root after commit: %w", err)
}
currentRoot := common.BytesToHash(currentRootBytes)
if currentRoot != root {
return fmt.Errorf("firewood: current root %s does not match expected root %s", currentRoot.Hex(), root.Hex())
}
if report {
log.Info("Persisted proposal to firewood database", "root", root)
} else {
log.Debug("Persisted proposal to firewood database", "root", root)
}
return nil
}
// Size returns the storage size of diff layer nodes above the persistent disk
// layer and the dirty nodes buffered within the disk layer
// Only used for metrics and Commit intervals in APIs.
// This will be implemented in the firewood database eventually.
// Currently, Firewood stores all revisions in disk and proposals in memory.
func (*Database) Size() (common.StorageSize, common.StorageSize) {
return 0, 0
}
// This isn't called anywhere in subnet-evm
func (*Database) Reference(common.Hash, common.Hash) {
log.Error("firewood: Reference not implemented")
}
// Dereference drops a proposal from the database.
// This function is no-op because unused proposals are dereferenced when no longer valid.
// We cannot dereference at this call. Consider the following case:
// Chain 1 has root A and root C
// Chain 2 has root B and root C
// We commit root A, and immediately dereference root B and its child.
// Root C is Rejected, (which is intended to be 2C) but there's now only one record of root C in the proposal map.
// Thus, we recognize the single root C as the only proposal, and dereference it.
func (*Database) Dereference(common.Hash) {
}
// Firewood does not support this.
func (*Database) Cap(common.StorageSize) error {
return nil
}
func (db *Database) Close() error {
db.proposalLock.Lock()
defer db.proposalLock.Unlock()
// We don't need to explicitly dereference the proposals, since they will be cleaned up
// within the firewood close method.
db.proposalMap = nil
db.proposalTree.Children = nil
// Close the database
return db.fwDisk.Close()
}
// createProposal creates a new proposal from the given layer
// If there are no changes, it will return nil.
func createProposal(layer proposable, root common.Hash, keys, values [][]byte) (p *ffi.Proposal, err error) {
// If there's an error after creating the proposal, we must drop it.
defer func() {
if err != nil && p != nil {
if dropErr := p.Drop(); dropErr != nil {
// We should still return the original error.
log.Error("firewood: error dropping proposal after error", "root", root.Hex(), "error", dropErr)
}
p = nil
}
}()
if len(keys) != len(values) {
return nil, fmt.Errorf("firewood: keys and values must have the same length, got %d keys and %d values", len(keys), len(values))
}
start := time.Now()
p, err = layer.Propose(keys, values)
if err != nil {
return nil, fmt.Errorf("firewood: unable to create proposal for root %s: %w", root.Hex(), err)
}
ffiProposeCount.Inc(1)
ffiProposeTimer.Inc(time.Since(start).Milliseconds())
ffiOutstandingProposals.Inc(1)
currentRootBytes, err := p.Root()
if err != nil {
return nil, fmt.Errorf("firewood: error getting root of proposal %s: %w", root, err)
}
currentRoot := common.BytesToHash(currentRootBytes)
if root != currentRoot {
return nil, fmt.Errorf("firewood: proposed root %s does not match expected root %s", currentRoot.Hex(), root.Hex())
}
// Store the proposal context.
return p, nil
}
// cleanupCommittedProposal dereferences the proposal and removes it from the proposal map.
// It also recursively dereferences all children of the proposal.
func (db *Database) cleanupCommittedProposal(pCtx *ProposalContext) {
start := time.Now()
oldChildren := db.proposalTree.Children
db.proposalTree = pCtx
db.proposalTree.Parent = nil
db.removeProposalFromMap(pCtx)
for _, childCtx := range oldChildren {
// Don't dereference the recently commit proposal.
if childCtx != pCtx {
db.dereference(childCtx)
}
}
ffiCleanupTimer.Inc(time.Since(start).Milliseconds())
}
// Internally removes all references of the proposal from the database.
// Should only be accessed with the proposal lock held.
// Consumer must not be iterating the proposal map at this root.
func (db *Database) dereference(pCtx *ProposalContext) {
// Base case: if there are children, we need to dereference them as well.
for _, child := range pCtx.Children {
db.dereference(child)
}
pCtx.Children = nil
// Remove the proposal from the map.
db.removeProposalFromMap(pCtx)
// Drop the proposal in the backend.
if err := pCtx.Proposal.Drop(); err != nil {
log.Error("firewood: error dropping proposal", "root", pCtx.Root.Hex(), "error", err)
}
ffiOutstandingProposals.Dec(1)
}
// removeProposalFromMap removes the proposal from the proposal map.
// The proposal lock must be held when calling this function.
func (db *Database) removeProposalFromMap(pCtx *ProposalContext) {
rootList := db.proposalMap[pCtx.Root]
for i, p := range rootList {
if p == pCtx { // pointer comparison - guaranteed to be unique
rootList[i] = rootList[len(rootList)-1]
rootList[len(rootList)-1] = nil
rootList = rootList[:len(rootList)-1]
break
}
}
if len(rootList) == 0 {
delete(db.proposalMap, pCtx.Root)
} else {
db.proposalMap[pCtx.Root] = rootList
}
}
// Reader retrieves a node reader belonging to the given state root.
// An error will be returned if the requested state is not available.
func (db *Database) Reader(root common.Hash) (database.Reader, error) {
if _, err := db.fwDisk.GetFromRoot(root.Bytes(), []byte{}); err != nil {
return nil, fmt.Errorf("firewood: unable to retrieve from root %s: %w", root.Hex(), err)
}
return &reader{db: db, root: root}, nil
}
// reader is a state reader of Database which implements the Reader interface.
type reader struct {
db *Database
root common.Hash // The root of the state this reader is reading.
}
// Node retrieves the trie node with the given node hash. No error will be
// returned if the node is not found.
func (reader *reader) Node(_ common.Hash, path []byte, _ common.Hash) ([]byte, error) {
// This function relies on Firewood's internal locking to ensure concurrent reads are safe.
// This is safe even if a proposal is being committed concurrently.
start := time.Now()
result, err := reader.db.fwDisk.GetFromRoot(reader.root.Bytes(), path)
if metrics.EnabledExpensive {
ffiReadCount.Inc(1)
ffiReadTimer.Inc(time.Since(start).Milliseconds())
}
return result, err
}
// getProposalHash calculates the hash if the set of keys and values are
// proposed from the given parent root.
func (db *Database) getProposalHash(parentRoot common.Hash, keys, values [][]byte) (common.Hash, error) {
// This function only reads from existing tracked proposals, so we can use a read lock.
db.proposalLock.RLock()
defer db.proposalLock.RUnlock()
var (
p *ffi.Proposal
err error
)
start := time.Now()
if db.proposalTree.Root == parentRoot {
// Propose from the database root.
p, err = db.fwDisk.Propose(keys, values)
if err != nil {
return common.Hash{}, fmt.Errorf("firewood: error proposing from root %s: %w", parentRoot.Hex(), err)
}
} else {
// Find any proposal with the given parent root.
// Since we are only using the proposal to find the root hash,
// we can use the first proposal found.
proposals, ok := db.proposalMap[parentRoot]
if !ok || len(proposals) == 0 {
return common.Hash{}, fmt.Errorf("firewood: no proposal found for parent root %s", parentRoot.Hex())
}
rootProposal := proposals[0].Proposal
p, err = rootProposal.Propose(keys, values)
if err != nil {
return common.Hash{}, fmt.Errorf("firewood: error proposing from parent proposal %s: %w", parentRoot.Hex(), err)
}
}
ffiHashCount.Inc(1)
ffiHashTimer.Inc(time.Since(start).Milliseconds())
// We succesffuly created a proposal, so we must drop it after use.
defer p.Drop()
rootBytes, err := p.Root()
if err != nil {
return common.Hash{}, err
}
return common.BytesToHash(rootBytes), nil
}
func arrangeKeyValuePairs(nodes *trienode.MergedNodeSet) ([][]byte, [][]byte) {
if nodes == nil {
return nil, nil // No changes to propose
}
// Create key-value pairs for the nodes in bytes.
var (
acctKeys [][]byte
acctValues [][]byte
storageKeys [][]byte
storageValues [][]byte
)
flattenedNodes := nodes.Flatten()
for _, nodeset := range flattenedNodes {
for str, node := range nodeset {
if len(str) == common.HashLength {
// This is an account node.
acctKeys = append(acctKeys, []byte(str))
acctValues = append(acctValues, node.Blob)
} else {
storageKeys = append(storageKeys, []byte(str))
storageValues = append(storageValues, node.Blob)
}
}
}
// We need to do all storage operations first, so prefix-deletion works for accounts.
keys := append(storageKeys, acctKeys...)
values := append(storageValues, acctValues...)
return keys, values
}
-41
View File
@@ -1,41 +0,0 @@
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package firewood
import (
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/trie/trienode"
)
type StorageTrie struct {
*AccountTrie
storageRoot common.Hash
}
// `NewStorageTrie` returns a wrapper around an `AccountTrie` since Firewood
// does not require a separate storage trie. All changes are managed by the account trie.
func NewStorageTrie(accountTrie *AccountTrie, storageRoot common.Hash) (*StorageTrie, error) {
return &StorageTrie{
AccountTrie: accountTrie,
storageRoot: storageRoot,
}, nil
}
// Actual commit is handled by the account trie.
// Return the old storage root as if there was no change - we don't want to use the
// actual account trie hash and nodeset here.
func (s *StorageTrie) Commit(bool) (common.Hash, *trienode.NodeSet, error) {
return s.storageRoot, nil, nil
}
// Firewood doesn't require tracking storage roots inside of an account.
func (s *StorageTrie) Hash() common.Hash {
return s.storageRoot // only used in statedb to populate a `StateAccount`
}
// Copy should never be called on a storage trie, as it is just a wrapper around the account trie.
// Each storage trie should be re-opened with the account trie separately.
func (*StorageTrie) Copy() *StorageTrie {
return nil
}
+8
View File
@@ -0,0 +1,8 @@
package firewood
// Minimal stub for firewood package
type Database interface{}
func New() Database {
return nil
}
+5 -5
View File
@@ -105,20 +105,20 @@ func (h *testHasher) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, e
continue
}
if len(val) == 0 {
set.AddNode(hash.Bytes(), trienode.NewDeleted())
set.AddNode(hash.Bytes(), trienode.NewDeletedWithPrev(h.cleans[hash]))
} else {
h := crypto.Keccak256Hash(val)
set.AddNode(hash.Bytes(), trienode.New(common.BytesToHash(h[:]), val))
valHash := crypto.Keccak256Hash(val)
set.AddNode(hash.Bytes(), trienode.NewNodeWithPrev(common.BytesToHash(valHash[:]), val, h.cleans[hash]))
}
}
root, blob := hash(nodes)
// Include the dirty root node as well.
if root != types.EmptyRootHash && root != h.root {
set.AddNode(nil, trienode.New(root, blob))
set.AddNode(nil, trienode.NewNodeWithPrev(root, blob, nil))
}
if root == types.EmptyRootHash && h.root != types.EmptyRootHash {
set.AddNode(nil, trienode.NewDeleted())
set.AddNode(nil, trienode.NewDeletedWithPrev(nil))
}
return root, set, nil
}
+14 -12
View File
@@ -9,7 +9,7 @@ import (
"github.com/luxfi/ids"
"github.com/luxfi/consensus"
"github.com/luxfi/node/consensus/consensustest"
"github.com/luxfi/consensus/consensustest"
"github.com/luxfi/consensus/validators"
"github.com/luxfi/consensus/validators/validatorstest"
"github.com/luxfi/node/utils/constants"
@@ -24,6 +24,7 @@ type testValidatorState struct {
}
func (t *testValidatorState) GetCurrentHeight() (uint64, error) {
// Call GetCurrentHeight with a background context
return t.State.GetCurrentHeight(context.Background())
}
@@ -47,7 +48,7 @@ func (t *testValidatorState) GetValidatorSet(height uint64, subnetID ids.ID) (ma
}
func (t *testValidatorState) GetSubnetID(chainID ids.ID) (ids.ID, error) {
return t.State.GetSubnetID(context.Background(), chainID)
return t.State.GetSubnetID(chainID)
}
// @TODO: This should eventually be replaced by a more robust solution, or alternatively, the presence of nil
@@ -57,7 +58,7 @@ func NewTestValidatorState() consensus.ValidatorState {
GetCurrentHeightF: func(context.Context) (uint64, error) {
return 0, nil
},
GetSubnetIDF: func(_ context.Context, chainID ids.ID) (ids.ID, error) {
GetSubnetIDF: func(chainID ids.ID) (ids.ID, error) {
// For testing, all chains belong to the primary network
if chainID == constants.PlatformChainID || chainID == SubnetEVMTestChainID {
return constants.PrimaryNetworkID, nil
@@ -68,9 +69,6 @@ func NewTestValidatorState() consensus.ValidatorState {
GetValidatorSetF: func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
return map[ids.NodeID]*validators.GetValidatorOutput{}, nil
},
GetCurrentValidatorSetF: func(context.Context, ids.ID) (map[ids.ID]*validators.GetCurrentValidatorOutput, uint64, error) {
return map[ids.ID]*validators.GetCurrentValidatorOutput{}, 0, nil
},
}
return &testValidatorState{State: state}
@@ -100,16 +98,20 @@ func NewTestValidatorStateFromBase(baseState *validatorstest.State) consensus.Va
// that includes the GetValidatorSetF function, which is required by many tests.
func NewTestConsensusContext(t testing.TB) context.Context {
consensusCtx := consensustest.Context(t, SubnetEVMTestChainID)
// Use consensus.WithValidatorState to add validator state to context
consensusCtx = consensus.WithValidatorState(consensusCtx, NewTestValidatorState())
return consensusCtx
// Create a standard context and add the consensus context to it
ctx := context.Background()
ctx = consensus.WithContext(ctx, consensusCtx)
// Add validator state to the context
return consensus.WithValidatorState(ctx, NewTestValidatorState())
}
// NewTestConsensusContextWithChainID returns a context.Context with validator state properly configured for testing
// with a specific chain ID. This is provided for backward compatibility when a specific chain ID is needed.
func NewTestConsensusContextWithChainID(t testing.TB, chainID ids.ID) context.Context {
consensusCtx := consensustest.Context(t, chainID)
// Use consensus.WithValidatorState to add validator state to context
consensusCtx = consensus.WithValidatorState(consensusCtx, NewTestValidatorState())
return consensusCtx
// Create a standard context and add the consensus context to it
ctx := context.Background()
ctx = consensus.WithContext(ctx, consensusCtx)
// Add validator state to the context
return consensus.WithValidatorState(ctx, NewTestValidatorState())
}
+13 -10
View File
@@ -4,9 +4,9 @@
package utilstest
import (
"context"
"testing"
"github.com/luxfi/consensus"
"github.com/luxfi/ids"
"github.com/stretchr/testify/require"
)
@@ -14,20 +14,23 @@ import (
func TestNewTestConsensusContext(t *testing.T) {
// Test that NewTestConsensusContext creates a context with validator state
consensusCtx := NewTestConsensusContext(t)
require.NotNil(t, consensusCtx.ValidatorState)
// Test that the validator state has the required functions
validatorState := consensusCtx.ValidatorState
// Extract validator state from context using consensus.GetValidatorState
validatorState := consensus.GetValidatorState(consensusCtx)
require.NotNil(t, validatorState)
// Test that we can call GetValidatorSetF without panicking
validators, err := validatorState.GetValidatorSet(context.TODO(), 0, ids.Empty)
// Test that we can call GetValidatorSet without panicking
validators, err := validatorState.GetValidatorSet(0, ids.Empty)
require.NoError(t, err)
require.NotNil(t, validators)
// Test that we can call GetCurrentValidatorSetF without panicking
currentValidators, height, err := validatorState.GetCurrentValidatorSet(context.TODO(), ids.Empty)
// Test GetCurrentHeight
height, err := validatorState.GetCurrentHeight()
require.NoError(t, err)
require.NotNil(t, currentValidators)
require.Equal(t, uint64(0), height)
// Test GetSubnetID
subnetID, err := validatorState.GetSubnetID(SubnetEVMTestChainID)
require.NoError(t, err)
require.Equal(t, ids.Empty, subnetID) // Default returns empty ID
}
+9 -17
View File
@@ -4,7 +4,6 @@
package warp
import (
"bytes"
"context"
"errors"
"fmt"
@@ -103,9 +102,8 @@ func (b *backend) initOffChainMessages(offchainMessages [][]byte) error {
return fmt.Errorf("wrong network ID at index %d", i)
}
// Compare source chain IDs as bytes
sourceChainIDBytes := b.sourceChainID[:]
if !bytes.Equal(unsignedMsg.SourceChainID, sourceChainIDBytes) {
// Compare source chain IDs
if unsignedMsg.SourceChainID != b.sourceChainID {
return fmt.Errorf("wrong source chain ID at index %d", i)
}
@@ -113,8 +111,7 @@ func (b *backend) initOffChainMessages(offchainMessages [][]byte) error {
if err != nil {
return fmt.Errorf("%w at index %d as AddressedCall: %w", errParsingOffChainMessage, i, err)
}
messageIDBytes := unsignedMsg.ID()
messageID, _ := ids.ToID(messageIDBytes)
messageID := unsignedMsg.ID()
b.offchainAddressedCallMsgs[messageID.String()] = unsignedMsg
}
@@ -122,14 +119,13 @@ func (b *backend) initOffChainMessages(offchainMessages [][]byte) error {
}
func (b *backend) AddMessage(unsignedMessage *luxWarp.UnsignedMessage) error {
messageIDBytes := unsignedMessage.ID()
messageID, _ := ids.ToID(messageIDBytes)
messageID := unsignedMessage.ID()
log.Debug("Adding warp message to backend", "messageID", messageID)
// In the case when a node restarts, and possibly changes its bls key, the cache gets emptied but the database does not.
// So to avoid having incorrect signatures saved in the database after a bls key change, we save the full message in the database.
// Whereas for the cache, after the node restart, the cache would be emptied so we can directly save the signatures.
if err := b.db.Put(messageIDBytes, unsignedMessage.Bytes()); err != nil {
if err := b.db.Put(messageID[:], unsignedMessage.Bytes()); err != nil {
return fmt.Errorf("failed to put warp signature in db: %w", err)
}
@@ -140,8 +136,7 @@ func (b *backend) AddMessage(unsignedMessage *luxWarp.UnsignedMessage) error {
}
func (b *backend) GetMessageSignature(ctx context.Context, unsignedMessage *luxWarp.UnsignedMessage) ([]byte, error) {
messageIDBytes := unsignedMessage.ID()
messageID, _ := ids.ToID(messageIDBytes)
messageID := unsignedMessage.ID()
log.Debug("Getting warp message from backend", "messageID", messageID)
if sig, ok := b.signatureCache.Get(messageID); ok {
@@ -162,14 +157,12 @@ func (b *backend) GetBlockSignature(ctx context.Context, blockID ids.ID) ([]byte
return nil, fmt.Errorf("failed to create new block hash payload: %w", err)
}
sourceChainIDBytes := b.sourceChainID[:]
unsignedMessage, err := luxWarp.NewUnsignedMessage(b.networkID, sourceChainIDBytes, blockHashPayload.Bytes())
unsignedMessage, err := luxWarp.NewUnsignedMessage(b.networkID, b.sourceChainID, blockHashPayload.Bytes())
if err != nil {
return nil, fmt.Errorf("failed to create new unsigned warp message: %w", err)
}
messageIDBytes := unsignedMessage.ID()
messageID, _ := ids.ToID(messageIDBytes)
messageID := unsignedMessage.ID()
if sig, ok := b.signatureCache.Get(messageID); ok {
return sig, nil
}
@@ -213,8 +206,7 @@ func (b *backend) signMessage(unsignedMessage *luxWarp.UnsignedMessage) ([]byte,
// For now, return empty signature
sig := make([]byte, 64)
messageIDBytes := unsignedMessage.ID()
messageID, _ := ids.ToID(messageIDBytes)
messageID := unsignedMessage.ID()
b.signatureCache.Put(messageID, sig)
return sig, nil
}
+2 -2
View File
@@ -32,7 +32,7 @@ func init() {
if err != nil {
panic(err)
}
testUnsignedMessage, err = luxWarp.NewUnsignedMessage(networkID, sourceChainID[:], testAddressedCallPayload.Bytes())
testUnsignedMessage, err = luxWarp.NewUnsignedMessage(networkID, sourceChainID, testAddressedCallPayload.Bytes())
if err != nil {
panic(err)
}
@@ -97,7 +97,7 @@ func TestGetBlockSignature(t *testing.T) {
blockHashPayload, err := payload.NewHash(blkID[:])
require.NoError(err)
unsignedMessage, err := luxWarp.NewUnsignedMessage(networkID, sourceChainID[:], blockHashPayload.Bytes())
unsignedMessage, err := luxWarp.NewUnsignedMessage(networkID, sourceChainID, blockHashPayload.Bytes())
require.NoError(err)
msgBytes := unsignedMessage.Bytes()
expectedSigBytes, err := localSigner.Sign(msgBytes)
+1 -1
View File
@@ -82,7 +82,7 @@ func (a *API) GetBlockAggregateSignature(ctx context.Context, blockID ids.ID, qu
return nil, err
}
chainID := consensus.GetChainID(a.chainContext)
unsignedMessage, err := luxWarp.NewUnsignedMessage(consensus.GetNetworkID(a.chainContext), chainID[:], blockHashPayload.Bytes())
unsignedMessage, err := luxWarp.NewUnsignedMessage(consensus.GetNetworkID(a.chainContext), chainID, blockHashPayload.Bytes())
if err != nil {
return nil, err
}
+1 -2
View File
@@ -24,8 +24,7 @@ const (
// Verify verifies the signature of the message
// It also implements the lp118.Verifier interface
func (b *backend) Verify(ctx context.Context, unsignedMessage *luxWarp.UnsignedMessage, _ []byte) error {
messageIDBytes := unsignedMessage.ID()
messageID, _ := ids.ToID(messageIDBytes)
messageID := unsignedMessage.ID()
// Known on-chain messages should be signed
if _, err := b.GetMessage(messageID); err == nil {
return nil