mirror of
https://github.com/luxfi/evm.git
synced 2026-09-24 00:27:44 +00:00
evm: FIX native-MINT bug in precompile StateDB bridge + precompile v0.5.44
CRITICAL: stateDBBridge.SubBalance type-asserts the concrete adapter else SILENTLY no-ops; the internal contract.StateDB had AddBalance but no SubBalance and stateDBAdapter only implemented AddBalance → a precompile debiting native value (DEX withdraw) CREDITED the caller while debiting NOTHING = native mint (caught live: vault 100000, wallet +100000). The DEX is the first precompile to debit native value so the path was never exercised. Added stateDBAdapter.SubBalance + regression guard (core/precompile_statedb_subbalance_test.go); zero 'SubBalance fallback' warnings. Bump precompile v0.5.43→v0.5.44 (PoolManager pure-ingress custody).
This commit is contained in:
@@ -168,6 +168,20 @@ func (s *stateDBAdapter) AddBalance(addr common.Address, amount *uint256.Int) {
|
||||
s.stateDB.AddBalance(addr, amount, tracing.BalanceChangeUnspecified)
|
||||
}
|
||||
|
||||
// SubBalance forwards to the underlying vm.StateDB with the FULL external
|
||||
// signature (addr, amount, reason) -> prev. The registry's stateDBBridge type-
|
||||
// asserts the internal contract.StateDB for exactly this method so that a
|
||||
// precompile which moves native value (e.g. the DEX 0x9010 custody vault: a
|
||||
// withdraw debits the vault before releasing to the caller) actually subtracts
|
||||
// the balance instead of hitting the bridge's no-op fallback. AddBalance is the
|
||||
// narrow internal pair; SubBalance is provided here so the pair is complete on
|
||||
// the concrete adapter without widening the internal interface (which has many
|
||||
// implementers/mocks). Without this the bridge logged "SubBalance fallback used"
|
||||
// and silently MINTED native value (vault unchanged, caller credited).
|
||||
func (s *stateDBAdapter) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int {
|
||||
return s.stateDB.SubBalance(addr, amount, reason)
|
||||
}
|
||||
|
||||
func (s *stateDBAdapter) CreateAccount(addr common.Address) {
|
||||
s.stateDB.CreateAccount(addr)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/luxfi/geth/common"
|
||||
"github.com/luxfi/geth/core/tracing"
|
||||
)
|
||||
|
||||
// precompile_statedb_subbalance_test.go pins the fix for a LATENT bug: the
|
||||
// precompile registry's stateDBBridge.SubBalance type-asserts the internal
|
||||
// contract.StateDB for the EXTERNAL SubBalance signature
|
||||
//
|
||||
// SubBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) uint256.Int
|
||||
//
|
||||
// and falls back to a NO-OP (logging "SubBalance fallback used") when the
|
||||
// assertion fails. The concrete adapter the EVM hands precompiles is
|
||||
// *stateDBAdapter; if it does not implement that EXACT signature, a precompile
|
||||
// that debits native value — e.g. the DEX 0x9010 custody vault, whose withdraw
|
||||
// debits the vault before releasing to the caller — would silently MINT (caller
|
||||
// credited, vault never debited). The fix added SubBalance to *stateDBAdapter so
|
||||
// the bridge's assertion succeeds and the fallback is unreachable.
|
||||
|
||||
// subBalancer is the EXACT interface precompile/registry/bridge.go type-asserts.
|
||||
// Keep it byte-identical so this test fails if the bridge's expected signature or
|
||||
// the adapter's method ever drifts.
|
||||
type subBalancer interface {
|
||||
SubBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) uint256.Int
|
||||
}
|
||||
|
||||
// TestStateDBAdapter_SatisfiesSubBalancer is the regression guard: *stateDBAdapter
|
||||
// MUST satisfy the subBalancer interface the registry bridge type-asserts. If this
|
||||
// fails to compile or asserts false, the bridge would hit its mint fallback for
|
||||
// every precompile native-value debit (the DEX withdraw vault-release).
|
||||
func TestStateDBAdapter_SatisfiesSubBalancer(t *testing.T) {
|
||||
// Compile-time assertion (the primary guard against signature drift).
|
||||
var _ subBalancer = (*stateDBAdapter)(nil)
|
||||
|
||||
// Runtime assertion mirroring exactly what the bridge does:
|
||||
// if sb, ok := s.internal.(subBalancer); ok { return sb.SubBalance(...) }
|
||||
var adapter contractStateDBSurface = (*stateDBAdapter)(nil)
|
||||
if _, ok := interface{}(adapter).(subBalancer); !ok {
|
||||
t.Fatal("*stateDBAdapter does NOT satisfy subBalancer — the registry bridge would hit the mint fallback for native-value debits")
|
||||
}
|
||||
// A non-nil *uint256.Int sanity reference (keeps the import meaningful and the
|
||||
// signature exercised in source).
|
||||
_ = uint256.NewInt(0)
|
||||
}
|
||||
|
||||
// contractStateDBSurface is the minimal surface we hold the adapter as for the
|
||||
// runtime assertion — it is satisfied by *stateDBAdapter (which implements the
|
||||
// full internal contract.StateDB). Held as an interface value so the type
|
||||
// assertion to subBalancer is a genuine runtime check, not a compile-time tautology.
|
||||
type contractStateDBSurface interface {
|
||||
GetBalance(common.Address) *uint256.Int
|
||||
AddBalance(common.Address, *uint256.Int)
|
||||
}
|
||||
@@ -32,7 +32,7 @@ require (
|
||||
github.com/luxfi/metric v1.5.8
|
||||
github.com/luxfi/p2p v1.21.1
|
||||
github.com/luxfi/pq v1.0.3
|
||||
github.com/luxfi/precompile v0.5.43
|
||||
github.com/luxfi/precompile v0.5.44
|
||||
github.com/luxfi/runtime v1.1.1
|
||||
github.com/luxfi/sys v0.1.0
|
||||
github.com/luxfi/timer v1.0.2
|
||||
|
||||
@@ -278,8 +278,8 @@ github.com/luxfi/p2p v1.21.1 h1:gmz1JMDhzHIL3dQlhwIDvR4OlFuhNVfnWUl/ipYhAIo=
|
||||
github.com/luxfi/p2p v1.21.1/go.mod h1:SsNPR5fPGWWNem9plGWhSmRqyDoysJ3kPAN0zG0g3iw=
|
||||
github.com/luxfi/pq v1.0.3 h1:pFlQm1+5FuKTDUh2y/23bXWkN4I2Rc5iuxJypwDFFMs=
|
||||
github.com/luxfi/pq v1.0.3/go.mod h1:8bppZcRElfrVt0n3nYCZW3iX1TvhvzNbdjNdK1irgIE=
|
||||
github.com/luxfi/precompile v0.5.43 h1:FaijObCK1+z1CppNIFG6i9/0C/R+IUE9VNcA5WMRx9A=
|
||||
github.com/luxfi/precompile v0.5.43/go.mod h1:FYfq9CT8XRmlpjFAtblHAMcpnjByuoM+YpKq/FF9QVk=
|
||||
github.com/luxfi/precompile v0.5.44 h1:r5g98nYTwHX0v7mOpico8Lksc/MInSHEaK/ST+QwKfc=
|
||||
github.com/luxfi/precompile v0.5.44/go.mod h1:FYfq9CT8XRmlpjFAtblHAMcpnjByuoM+YpKq/FF9QVk=
|
||||
github.com/luxfi/proto v1.3.4 h1:dNrdQ/414Pe9otIekVUmQ9QLqemaMFK43H4N3DIbuzQ=
|
||||
github.com/luxfi/proto v1.3.4/go.mod h1:S80KrTzISCktptit/pWLzk6nHgagjJS86qbQL6gHutE=
|
||||
github.com/luxfi/resource v0.0.1 h1:mTh+ICWSy548GTUSSyx7V/X5dV18oEwxZeQEYGJQhD4=
|
||||
|
||||
Reference in New Issue
Block a user