Files
geth/core/vm/precompile_env.go
zeekay 0d504ffba5 core/vm: per-tx precompile CallIndex for the DEX native atomic seam
PrecompileEnvironment.CallIndex() exposes each precompile invocation's ordinal
within the current tx (evm.precompileCallIndex, reset to 0 at tx start,
incremented once per NewPrecompileEnvironment — for every invocation, including
reverted sub-frames and STATICCALLs). It is a pure function of (tx, ordinal),
so it is identical across deterministic re-execution and distinct between two
invocations in one tx.

The DEX 0x9999 seam binds CallIndex into DeriveIntentID so two same-params swaps
in one tx get distinct, collision-free cross-chain object ids while replaying
byte-identically network-wide.
2026-06-21 00:19:25 -07:00

194 lines
5.2 KiB
Go

// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package vm
import (
"context"
"math/big"
"github.com/holiman/uint256"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/params"
)
// PrecompileEnvironment is the interface for precompile execution environment.
// This is a Lux extension for stateful precompiles that need access to
// the execution context.
type PrecompileEnvironment interface {
// BlockHeader returns the block header
BlockHeader() (*types.Header, error)
// Rules returns the chain rules
Rules() params.Rules
// BlockNumber returns the current block number
BlockNumber() *big.Int
// BlockTime returns the current block timestamp
BlockTime() uint64
// Addresses returns the caller and contract addresses
Addresses() PrecompileAddresses
// ReadOnly returns whether the environment is read-only
ReadOnly() bool
// ChainConfig returns the chain config
ChainConfig() *params.ChainConfig
// StateDB returns the state database
StateDB() StateDB
// ReadOnlyState returns a read-only state view
ReadOnlyState() StateDB
// UseGas deducts gas from the available gas
UseGas(gas uint64) bool
// Gas returns the available gas
Gas() uint64
// Call executes a call to another contract
Call(addr common.Address, input []byte, gas uint64, value *big.Int, opts ...CallOption) ([]byte, uint64, error)
// ConsensusContext returns the consensus context with chain ID/network ID for warp
ConsensusContext() context.Context
// CallIndex returns this precompile invocation's index within the current
// transaction (0-based, monotonic per tx). A stateful precompile uses it to
// derive a per-invocation content-addressed identity that is unique even when
// the same precompile is called multiple times in one transaction.
CallIndex() uint32
}
// CallOption is an option for Call
type CallOption func(*CallConfig)
// CallConfig holds call configuration
type CallConfig struct {
ProxyCaller bool
}
// WithUNSAFECallerAddressProxying returns an option to proxy caller address
func WithUNSAFECallerAddressProxying() CallOption {
return func(c *CallConfig) {
c.ProxyCaller = true
}
}
// PrecompileAddresses holds the caller and self addresses for a precompile call
type PrecompileAddresses struct {
Caller common.Address
Self common.Address
}
// evmPrecompileEnv implements PrecompileEnvironment using the EVM
type evmPrecompileEnv struct {
evm *EVM
caller common.Address
self common.Address
gas uint64
readOnly bool
// callIndex is this invocation's ordinal within the current tx, captured at
// construction (one increment of evm.precompileCallIndex per invocation).
callIndex uint32
}
// NewPrecompileEnvironment creates a PrecompileEnvironment from EVM context. It
// captures and advances the per-tx precompile call index so each invocation gets
// a distinct, monotonically increasing CallIndex() within the transaction.
func NewPrecompileEnvironment(evm *EVM, caller, self common.Address, gas uint64, readOnly bool) PrecompileEnvironment {
idx := evm.precompileCallIndex
evm.precompileCallIndex++
return &evmPrecompileEnv{
evm: evm,
caller: caller,
self: self,
gas: gas,
readOnly: readOnly,
callIndex: idx,
}
}
func (e *evmPrecompileEnv) BlockHeader() (*types.Header, error) {
return &types.Header{
Number: e.evm.Context.BlockNumber,
Time: e.evm.Context.Time,
Coinbase: e.evm.Context.Coinbase,
GasLimit: e.evm.Context.GasLimit,
BaseFee: e.evm.Context.BaseFee,
}, nil
}
func (e *evmPrecompileEnv) Rules() params.Rules {
return e.evm.chainRules
}
func (e *evmPrecompileEnv) BlockNumber() *big.Int {
return new(big.Int).Set(e.evm.Context.BlockNumber)
}
func (e *evmPrecompileEnv) BlockTime() uint64 {
return e.evm.Context.Time
}
func (e *evmPrecompileEnv) Addresses() PrecompileAddresses {
return PrecompileAddresses{
Caller: e.caller,
Self: e.self,
}
}
func (e *evmPrecompileEnv) ReadOnly() bool {
return e.readOnly
}
func (e *evmPrecompileEnv) ChainConfig() *params.ChainConfig {
return e.evm.chainConfig
}
func (e *evmPrecompileEnv) StateDB() StateDB {
return e.evm.StateDB
}
func (e *evmPrecompileEnv) ReadOnlyState() StateDB {
return e.evm.StateDB
}
func (e *evmPrecompileEnv) UseGas(gas uint64) bool {
if e.gas < gas {
return false
}
e.gas -= gas
return true
}
func (e *evmPrecompileEnv) Gas() uint64 {
return e.gas
}
func (e *evmPrecompileEnv) Call(addr common.Address, input []byte, gas uint64, value *big.Int, opts ...CallOption) ([]byte, uint64, error) {
// Handle call options
cfg := &CallConfig{}
for _, opt := range opts {
opt(cfg)
}
caller := e.self
if cfg.ProxyCaller {
caller = e.caller
}
// Convert *big.Int to *uint256.Int
var val *uint256.Int
if value != nil {
val = new(uint256.Int)
val.SetFromBig(value)
} else {
val = new(uint256.Int)
}
ret, remainingGas, err := e.evm.Call(caller, addr, input, gas, val)
return ret, remainingGas, err
}
func (e *evmPrecompileEnv) ConsensusContext() context.Context {
if e.evm.Context.ConsensusContext != nil {
return e.evm.Context.ConsensusContext
}
return context.Background()
}
func (e *evmPrecompileEnv) CallIndex() uint32 {
return e.callIndex
}