60 lines
2.2 KiB
Go
60 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"testing"
|
|
)
|
|
|
|
// R-1176 (2026-10-21): the off-site apply-bridge waits only until the FLOOR is known (offsiteapply.AwaitSettle), and
|
|
// then may mint a repository password unless the box has recorded that the hub holds the household's sealed package
|
|
// (R-241's mint guard reads that record). Both facts arrive in the same report ACK, so inside OnPushResponse the
|
|
// escrow must be recorded BEFORE updater.SetFloor makes the floor known — otherwise a bridge poll landing between the
|
|
// two mints a fresh key on a rebuilt box and the household's history is set aside on its first night.
|
|
// Walks main.go's AST with comments dropped, so a commented-out call cannot satisfy it.
|
|
// COMPANION RED-PROOF: move escrowConfirmer.Reconcile below updater.SetFloor → this fails.
|
|
func TestR1176_EscrowIsRecordedBeforeTheFloorIsKnown(t *testing.T) {
|
|
fset := token.NewFileSet()
|
|
f, err := parser.ParseFile(fset, "main.go", nil, 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var escrowPos, floorPos token.Pos
|
|
ast.Inspect(f, func(n ast.Node) bool {
|
|
as, ok := n.(*ast.AssignStmt)
|
|
if !ok || len(as.Lhs) != 1 || len(as.Rhs) != 1 {
|
|
return true
|
|
}
|
|
sel, ok := as.Lhs[0].(*ast.SelectorExpr)
|
|
if !ok || sel.Sel.Name != "OnPushResponse" {
|
|
return true
|
|
}
|
|
ast.Inspect(as.Rhs[0], func(m ast.Node) bool {
|
|
call, ok := m.(*ast.CallExpr)
|
|
if !ok {
|
|
return true
|
|
}
|
|
if s, ok := call.Fun.(*ast.SelectorExpr); ok {
|
|
if x, ok := s.X.(*ast.Ident); ok {
|
|
switch {
|
|
case x.Name == "escrowConfirmer" && s.Sel.Name == "Reconcile" && escrowPos == 0:
|
|
escrowPos = call.Pos()
|
|
case x.Name == "updater" && s.Sel.Name == "SetFloor" && floorPos == 0:
|
|
floorPos = call.Pos()
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
return true
|
|
})
|
|
if escrowPos == 0 || floorPos == 0 {
|
|
t.Fatalf("OnPushResponse no longer calls both escrowConfirmer.Reconcile (%v) and updater.SetFloor (%v) — the order is unasserted", escrowPos, floorPos)
|
|
}
|
|
if escrowPos > floorPos {
|
|
t.Fatalf("escrowConfirmer.Reconcile (%s) runs AFTER updater.SetFloor (%s): a rebuilt box can mint a fresh key before it learns the hub holds the household's package",
|
|
fset.Position(escrowPos), fset.Position(floorPos))
|
|
}
|
|
}
|