chain: keep only what something calls

Two pieces of the new package had no caller. Pool.Holds was the read side of a
claim set that Add already enforces, so nothing ever needed to ask; it is gone,
and the tests that used it now assert the same property through Add, which is
the behaviour that matters.

chain.Closed had no caller because M-Chain still declared its own
NoUserTxPolicy. It declares chain.Closed() now — one fewer copy of a policy
five chains state identically, and the one no-user-tx chain in this pass
exercises the value that describes it.
This commit is contained in:
2026-08-28 09:22:52 -07:00
parent 7ed6c6d644
commit f28e5d0e30
5 changed files with 21 additions and 46 deletions
-8
View File
@@ -114,14 +114,6 @@ func (p *Pool[T, K]) Drop(entries []T) {
}
}
// Holds reports whether a queued entry already claims c.
func (p *Pool[T, K]) Holds(c K) bool {
p.mu.Lock()
defer p.mu.Unlock()
_, ok := p.held[c]
return ok
}
// Len is how many entries are waiting.
func (p *Pool[T, K]) Len() int {
p.mu.Lock()
+4 -8
View File
@@ -50,8 +50,6 @@ func TestASecondClaimOnQueuedWorkIsRefused(t *testing.T) {
require.NoError(t, p.Add(at(1)))
require.ErrorIs(t, p.Add(at(1)), ErrHeld)
require.Equal(t, 1, p.Len())
require.True(t, p.Holds(ids.ID{1}))
require.False(t, p.Holds(ids.ID{2}))
}
func TestAFullPoolRefusesRatherThanGrows(t *testing.T) {
@@ -74,9 +72,8 @@ func TestAClaimDoesNotOutliveItsEntry(t *testing.T) {
p.Drop([]entry{at(1)})
require.Equal(t, []entry{at(2)}, p.Take(0))
require.False(t, p.Holds(ids.ID{1}), "the dropped entry holds nothing")
require.True(t, p.Holds(ids.ID{2}), "the one still queued does")
require.NoError(t, p.Add(at(1)), "and its claim can be made again")
require.NoError(t, p.Add(at(1)), "the dropped entry's claim can be made again")
require.ErrorIs(t, p.Add(at(2)), ErrHeld, "the one still queued still holds its own")
}
func TestDroppingNothingChangesNothing(t *testing.T) {
@@ -84,7 +81,7 @@ func TestDroppingNothingChangesNothing(t *testing.T) {
require.NoError(t, p.Add(at(1)))
p.Drop(nil)
require.Equal(t, 1, p.Len())
require.True(t, p.Holds(ids.ID{1}))
require.ErrorIs(t, p.Add(at(1)), ErrHeld)
}
func TestDroppingWhatWasNeverQueuedLeavesTheRestAlone(t *testing.T) {
@@ -92,7 +89,7 @@ func TestDroppingWhatWasNeverQueuedLeavesTheRestAlone(t *testing.T) {
require.NoError(t, p.Add(at(1)))
p.Drop([]entry{at(9)})
require.Equal(t, []entry{at(1)}, p.Take(0))
require.True(t, p.Holds(ids.ID{1}))
require.ErrorIs(t, p.Add(at(1)), ErrHeld)
}
func TestAddingTellsConsensusThereIsSomethingToBuild(t *testing.T) {
@@ -128,7 +125,6 @@ func TestConcurrentUseOfAPoolDoesNotRace(t *testing.T) {
default:
p.Take(4)
p.Len()
p.Holds(ids.ID{1})
}
}
}()
+9 -25
View File
@@ -7,29 +7,13 @@ import (
"github.com/luxfi/node/vms/types/fee"
)
// newFeePolicy returns the canonical M-Chain FeePolicy. M-Chain is a
// committee-driven service VM — keygen / signing requests come from
// validators via consensus, not a user mempool — so it declares the
// NoUserTxPolicy sentinel. See vms/types/fee/policy.go.
//
// Any tx that reaches the fee gate on a NoUserTx chain is a wiring
// bug; ValidateFee returns ErrChainAcceptsNoUserTxs.
func newFeePolicy() fee.Policy {
return fee.NoUserTxPolicy{}
}
// gateUserTx refuses every caller — M-Chain accepts no user txs. It is a
// committee-driven service VM: keygen and signing requests come from validators
// through consensus, not a user mempool. Any service entry that exposes itself
// as user-callable MUST route through this gate, so the refusal is explicit
// rather than a path nobody happened to write.
func (vm *VM) gateUserTx() error { return vm.fee.Admit(0) }
// gateUserTx refuses every caller — M-Chain accepts no user txs.
// Any service entry that exposes itself as user-callable MUST route
// through this gate so the refusal is explicit, not implicit.
func (vm *VM) gateUserTx() error {
if vm.feePolicy == nil {
return fee.ErrChainAcceptsNoUserTxs
}
// NoUserTxPolicy.ValidateFee ignores the args and always returns
// ErrChainAcceptsNoUserTxs.
return vm.feePolicy.ValidateFee(0, fee.NoUserTxPolicy{}.FeeAssetID())
}
// FeePolicy exposes the chain's declared fee policy for diagnostics
// and the boot-time Validate gate.
func (vm *VM) FeePolicy() fee.Policy { return vm.feePolicy }
// FeePolicy exposes the chain's declared fee policy for diagnostics and the
// boot-time Validate gate.
func (vm *VM) FeePolicy() fee.Policy { return vm.fee.Policy() }
+3 -2
View File
@@ -7,12 +7,13 @@ import (
"errors"
"testing"
"github.com/luxfi/chains/chain"
"github.com/luxfi/log"
"github.com/luxfi/node/vms/types/fee"
)
func TestThresholdVM_FeePolicy_IsNoUserTxSentinel(t *testing.T) {
v := &VM{log: log.NewNoOpLogger(), feePolicy: newFeePolicy()}
v := &VM{log: log.NewNoOpLogger(), fee: chain.Closed()}
if v.FeePolicy() == nil {
t.Fatal("FeePolicy() = nil; want NoUserTxPolicy")
}
@@ -25,7 +26,7 @@ func TestThresholdVM_FeePolicy_IsNoUserTxSentinel(t *testing.T) {
}
func TestThresholdVM_FeePolicy_RejectsAllUserTx(t *testing.T) {
v := &VM{log: log.NewNoOpLogger(), feePolicy: newFeePolicy()}
v := &VM{log: log.NewNoOpLogger(), fee: chain.Closed()}
if err := v.gateUserTx(); !errors.Is(err, fee.ErrChainAcceptsNoUserTxs) {
t.Fatalf("gateUserTx() = %v, want ErrChainAcceptsNoUserTxs", err)
}
+5 -3
View File
@@ -180,7 +180,9 @@ type VM struct {
// Fee policy. M-Chain is service-only (committee-driven, no
// user mempool) so this is the NoUserTxPolicy sentinel.
feePolicy fee.Policy
// fee is what this chain charges to admit a user transaction, which is
// nothing: M-Chain takes none.
fee chain.Fee
// mu guards State and the VM's own maps. State is deliberately not safe for
// concurrent use — a state machine with its own locking invites callers to
@@ -238,8 +240,8 @@ func (vm *VM) Initialize(
// Pin fee policy. M-Chain is service-only (committee-driven, no
// user mempool) so attach the NoUserTxPolicy sentinel.
// fee.Validate passes for the sentinel even with zero MinTxFee.
vm.feePolicy = newFeePolicy()
if err := fee.Validate(vm.feePolicy); err != nil {
vm.fee = chain.Closed()
if err := fee.Validate(vm.fee.Policy()); err != nil {
return fmt.Errorf("mpcvm: fee policy: %w", err)
}