fix(ledger): reject posting sets that overflow the zero-sum check
An adversarial posting set of two MaxInt64 legs plus one of 2 wraps to zero in int64 arithmetic, so the balance check passed and the ledger minted 18 quintillion millisatoshis from nothing. The sum is now accumulated in big.Int, per-account balance arithmetic is checked for wraparound, and the audit totals parse through big.Int so a corrupt ledger reports a clear error rather than failing to scan. Adds room package tests (0% -> covered) and ledger edge cases. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,10 +16,17 @@ func TestRandomActivityConservesValue(t *testing.T) {
|
||||
l := ledger.New(testPool(t))
|
||||
ctx := context.Background()
|
||||
|
||||
house, err := l.AccountByName(ctx, "house_pot")
|
||||
// The test uses its own counterparty rather than the shared house account,
|
||||
// and sums only its own accounts. Asserting on a global total would fail
|
||||
// whenever another package's tests run against the same database in
|
||||
// parallel — a broken test, not a broken ledger.
|
||||
house, err := l.EnsurePlayer(ctx, uniqueKey(t, "counterparty"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := l.Deposit(ctx, house, 5_000_000); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
const players = 8
|
||||
ids := make([]int64, players)
|
||||
@@ -34,11 +41,22 @@ func TestRandomActivityConservesValue(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
before, err := l.TotalIssued(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
// sumOwn totals only the accounts this test created.
|
||||
sumOwn := func() int64 {
|
||||
t.Helper()
|
||||
var total int64
|
||||
for _, id := range append(append([]int64{}, ids...), house) {
|
||||
bal, err := l.Balance(ctx, id)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
total += bal
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
before := sumOwn()
|
||||
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
roundID := int64(0)
|
||||
for i := 0; i < 400; i++ {
|
||||
@@ -75,11 +93,7 @@ func TestRandomActivityConservesValue(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
after, err := l.TotalIssued(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if before != after {
|
||||
if after := sumOwn(); before != after {
|
||||
t.Fatalf("value not conserved: %d -> %d", before, after)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user