mirror of
https://github.com/luxfi/evm.git
synced 2026-09-24 00:27:44 +00:00
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:
@@ -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
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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 })
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/luxfi/geth/common"
|
||||
)
|
||||
|
||||
const defaultCommitInterval = 4096
|
||||
// defaultCommitInterval is defined in config.go
|
||||
|
||||
func NewDefaultConfig() Config {
|
||||
return Config{
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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" ]
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package firewood
|
||||
|
||||
// Minimal stub for firewood package
|
||||
type Database interface{}
|
||||
|
||||
func New() Database {
|
||||
return nil
|
||||
}
|
||||
@@ -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
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user