Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions api/authority_staking_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import (
func TestDailyAuditSpansAuthorityRotationWithSignedStakeOperations(t *testing.T) {
var retired crypto.ValidatorPrivKey
db, s, _, _ := makeDailyAuditBenchmarkFixtureWithOptions(t, 8, 0, 0, 1, 0, 0, 0, false, true, &retired)
defer db.Close()
defer func() {
if err := db.Close(); err != nil {
t.Errorf("close authority staking fixture: %v", err)
}
}()
rec := httptest.NewRecorder()
s.restLPoDAuditDaily(rec, httptest.NewRequest(http.MethodGet, "/?date=2024-01-02", nil))
var result struct {
Expand Down Expand Up @@ -79,7 +83,11 @@ func TestDailyAuditSpansAuthorityRotationWithSignedStakeOperations(t *testing.T)
func TestRotatedLocalStakingUsesCollateralIdentity(t *testing.T) {
var retired crypto.ValidatorPrivKey
db, s, _, _ := makeDailyAuditBenchmarkFixtureWithOptions(t, 8, 0, 0, 1, 0, 0, 0, false, true, &retired)
defer db.Close()
defer func() {
if err := db.Close(); err != nil {
t.Errorf("close authority staking fixture: %v", err)
}
}()
identity := s.validatorAuthorityFork.Identity
got, err := s.localValidatorIdentity(true)
if err != nil || !got.Equals(identity) {
Expand Down
1 change: 1 addition & 0 deletions api/genesis_vesting.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/aperod/aperod/store"
)

// SetGenesisVestingConfig sets the activation height for genesis vesting.
func (s *Server) SetGenesisVestingConfig(height uint64) { s.genesisVestingHeight = height }

// Public execution evidence comes from the committed reserve ledger, never
Expand Down
8 changes: 6 additions & 2 deletions api/genesis_vesting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ func TestGenesisVestingCanonicalPaymentRestartAndPruning(t *testing.T) {
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { db.Close() })
t.Cleanup(func() {
if err := db.Close(); err != nil {
t.Errorf("close reopened genesis vesting database: %v", err)
}
})
commit(deadline+1, false)
c, err = db.LoadLPoDCheckpoint()
if err != nil || c.GenesisVesting.Paid != amount {
Expand Down Expand Up @@ -179,7 +183,7 @@ func TestGenesisVestingCanonicalPaymentRestartAndPruning(t *testing.T) {
raw, err := db.GetRawBlockByHeight(b.Header.Height)
var saved core.Block
if err != nil || json.Unmarshal(raw, &saved) != nil || len(saved.Txs) != 2 {
t.Fatal("annual funding/payment evidence was pruned")
t.Fatalf("annual funding/payment evidence at height %d was pruned: txs=%d err=%v", b.Header.Height, len(saved.Txs), err)
}
}
// Canonical tip selection after a reorg selects its reserve checkpoint,
Expand Down
4 changes: 3 additions & 1 deletion api/genesis_vesting_wallet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ func testNativeReserveReceiptAndSpend(t *testing.T, amount uint64) {
tx := core.Transaction{Version: core.TxVersionLPoDPayout}
tx.Outputs = append(tx.Outputs, out)
b := &core.Block{Header: core.BlockHeader{Height: i}, Txs: []core.Transaction{tx}}
utxos.ApplyBlock(b)
if err := utxos.ApplyBlock(b); err != nil {
t.Fatal(err)
}
if i == 1 {
u := utxos.Get(tx.Hash(), 0)
hs, err := crypto.ScanForOutput(owner.View.Private, owner.Spend.Public, out.TxPubKey, out.OneTimePub)
Expand Down
6 changes: 5 additions & 1 deletion api/lpod_daily_jobs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,11 @@ func TestDailyJobStorageBudgetAndRetry(t *testing.T) {
func TestDailyJobAdvancingCertificateOracleDoesNotResetCursor(t *testing.T) {
const intervalBlocks = 4 * dailyBatchBlocks
db, s, _, _ := makeDailyAuditBenchmarkFixtureWithTail(t, intervalBlocks, 1, 128, 4, 0, 4, intervalBlocks)
defer db.Close()
defer func() {
if err := db.Close(); err != nil {
t.Errorf("close daily audit fixture: %v", err)
}
}()
certificate, err := db.LoadFinalityCertificate()
if err != nil {
t.Fatal(err)
Expand Down
18 changes: 14 additions & 4 deletions api/lpod_earnings_outputs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,9 @@ func seedSyntheticEarningsRows(
}
if err := iter.Error(); err != nil {
iter.Release()
raw.Close()
if closeErr := raw.Close(); closeErr != nil {
t.Errorf("close synthetic earnings index: %v", closeErr)
}
t.Fatalf("scan synthetic earnings index: %v", err)
}
iter.Release()
Expand All @@ -304,14 +306,18 @@ func seedSyntheticEarningsRows(
key = append(key, row.TxHash[:]...)
value, err := json.Marshal(row)
if err != nil {
raw.Close()
if closeErr := raw.Close(); closeErr != nil {
t.Errorf("close synthetic earnings index: %v", closeErr)
}
t.Fatalf("marshal synthetic earnings row %d for %s: %v", i, owner, err)
}
batch.Put(key, value)
}
}
if err := raw.Write(batch, nil); err != nil {
raw.Close()
if closeErr := raw.Close(); closeErr != nil {
t.Errorf("close synthetic earnings index: %v", closeErr)
}
t.Fatalf("write synthetic earnings rows: %v", err)
}
if err := raw.Close(); err != nil {
Expand All @@ -322,7 +328,11 @@ func seedSyntheticEarningsRows(
if err != nil {
t.Fatalf("reopen synthetic store after seeding: %v", err)
}
t.Cleanup(func() { reopened.Close() })
t.Cleanup(func() {
if err := reopened.Close(); err != nil {
t.Errorf("close reopened synthetic store: %v", err)
}
})
return reopened
}

Expand Down
31 changes: 22 additions & 9 deletions api/lpod_positions.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@ type lpodWalletPosition struct {
ID string `json:"id"`
Vault string `json:"vault"`
Principal string `json:"principal_napro"`
Original string `json:"original_principal_napro"`
Returned string `json:"returned_napro"`
Due string `json:"due_napro"`
PendingDue string `json:"pending_due_napro"`
NextPayoutNS string `json:"next_payout_ns,omitempty"`
Nonce string `json:"nonce"`
DepositJSON string `json:"deposit_action_json"`
EffectiveVault string `json:"effective_vault"`
RouteHeight string `json:"route_height"`
AutoReturned bool `json:"auto_returned"`
TopUpCount int `json:"top_up_count"`
Beneficiary string `json:"beneficiary"`
}

Expand All @@ -40,10 +44,11 @@ func lpodWalletProjection(c *store.LPoDCheckpoint, address crypto.Address) ([]lp
if p.Deposit.Beneficiary != address {
continue
}
if p.Withdrawn > p.Deposit.Amount {
principalTotal, err := store.LPoDPositionPrincipal(p)
if err != nil || p.Withdrawn > principalTotal {
return nil, 0, fmt.Errorf("invalid principal")
}
principal := p.Deposit.Amount - p.Withdrawn
principal := principalTotal - p.Withdrawn
if p.Returned {
principal = 0
}
Expand All @@ -58,8 +63,11 @@ func lpodWalletProjection(c *store.LPoDCheckpoint, address crypto.Address) ([]lp
effective := store.LPoDEffectiveVault(p)
rows = append(rows, lpodWalletPosition{
ID: id, Vault: fmt.Sprintf("%x", effective[:]), EffectiveVault: fmt.Sprintf("%x", effective[:]),
Principal: strconv.FormatUint(principal, 10), Returned: strconv.FormatUint(p.Withdrawn, 10),
Due: strconv.FormatUint(p.Due, 10), Nonce: strconv.FormatUint(p.Nonce, 10),
Principal: strconv.FormatUint(principal, 10), Original: strconv.FormatUint(principalTotal, 10),
Returned: strconv.FormatUint(p.Withdrawn, 10),
Due: strconv.FormatUint(p.Due, 10), Nonce: strconv.FormatUint(p.Nonce, 10),
PendingDue: strconv.FormatUint(p.PendingDue, 10), NextPayoutNS: strconv.FormatInt(p.NextPayoutNS, 10),
TopUpCount: len(p.TopUps),
DepositJSON: string(action), RouteHeight: strconv.FormatUint(p.RouteHeight, 10), AutoReturned: p.AutoReturned,
Beneficiary: string(p.Deposit.Beneficiary),
})
Expand All @@ -75,10 +83,11 @@ func lpodVaultProjection(c *store.LPoDCheckpoint, vault crypto.Point32) ([]lpodW
if store.LPoDEffectiveVault(p) != vault {
continue
}
if p.Withdrawn > p.Deposit.Amount {
principalTotal, err := store.LPoDPositionPrincipal(p)
if err != nil || p.Withdrawn > principalTotal {
return nil, 0, fmt.Errorf("invalid principal")
}
principal := p.Deposit.Amount - p.Withdrawn
principal := principalTotal - p.Withdrawn
if p.Returned {
principal = 0
}
Expand All @@ -91,8 +100,10 @@ func lpodVaultProjection(c *store.LPoDCheckpoint, vault crypto.Point32) ([]lpodW
return nil, 0, err
}
rows = append(rows, lpodWalletPosition{ID: id, Vault: fmt.Sprintf("%x", vault[:]), EffectiveVault: fmt.Sprintf("%x", vault[:]),
Principal: strconv.FormatUint(principal, 10), Returned: strconv.FormatUint(p.Withdrawn, 10), Due: strconv.FormatUint(p.Due, 10),
Nonce: strconv.FormatUint(p.Nonce, 10), DepositJSON: string(action), RouteHeight: strconv.FormatUint(p.RouteHeight, 10),
Principal: strconv.FormatUint(principal, 10), Original: strconv.FormatUint(principalTotal, 10),
Returned: strconv.FormatUint(p.Withdrawn, 10), Due: strconv.FormatUint(p.Due, 10),
PendingDue: strconv.FormatUint(p.PendingDue, 10), NextPayoutNS: strconv.FormatInt(p.NextPayoutNS, 10),
TopUpCount: len(p.TopUps), Nonce: strconv.FormatUint(p.Nonce, 10), DepositJSON: string(action), RouteHeight: strconv.FormatUint(p.RouteHeight, 10),
AutoReturned: p.AutoReturned, Beneficiary: string(p.Deposit.Beneficiary)})
}
sort.Slice(rows, func(i, j int) bool { return rows[i].ID < rows[j].ID })
Expand Down Expand Up @@ -126,7 +137,7 @@ func (s *Server) restLPoDPositions(w http.ResponseWriter, r *http.Request) {
}
out := map[string]interface{}{"version": 1, "state": "disabled", "address": address,
"reserved_napro": nil, "positions": nil, "checkpoint_hash": nil, "finalized_height": nil,
"wallet_mutations_supported": false}
"wallet_mutations_supported": false, "daily_payout_version": 0, "daily_topups_supported": false}
defer func() { writeJSON(w, 200, out) }()
if s.lpodMigration == nil {
return
Expand Down Expand Up @@ -198,6 +209,8 @@ func (s *Server) restLPoDPositions(w http.ResponseWriter, r *http.Request) {
}
out["state"] = "active"
out["wallet_mutations_supported"] = true
out["daily_payout_version"] = c.DailyPayoutVersion
out["daily_topups_supported"] = c.DailyPayoutVersion > 0
out["chain_anchor"] = fmt.Sprintf("%x", a.Genesis[:])
out["vaults"] = vaults
out["positions"] = rows
Expand Down
160 changes: 160 additions & 0 deletions api/lpod_public_docs_status_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package api

import (
"os"
"path/filepath"
"regexp"
"strings"
"testing"
)

var (
currentMainnetStatusMarker = regexp.MustCompile(`(?i)\b(?:public network status|current public network|mainnet status|current public mainnet|mainnet lpod v3)\b`)
nonCurrentLpodScope = regexp.MustCompile(`(?i)\b(?:historical|rehearsal|new network|another chain|default-disabled|repository/config)\b`)
staleLpodActivationClaim = regexp.MustCompile(`(?i)(?:\b(?:if|when|once)\s+lpod(?:\s+v3)?\b.{0,120}\b(?:activated|enabled|funded)\b|\blpod(?:\s+v3)?\b.{0,120}\b(?:will be activated|will activate|has not(?: yet)? been activated|(?:is|was) not(?: yet)? active|not yet active|remains inactive|awaits activation|activation is planned)\b)`)
futureLpodVersion = regexp.MustCompile(`(?i)\blpod\s+v[4-9][0-9]*\b`)
currentLpodVersion = regexp.MustCompile(`(?i)\blpod\s+v3\b`)
)

func isStaleCurrentLpodClaim(text string) bool {
if futureLpodVersion.MatchString(text) && !currentLpodVersion.MatchString(text) {
return false
}
if nonCurrentLpodScope.MatchString(text) && !currentMainnetStatusMarker.MatchString(text) {
return false
}
return staleLpodActivationClaim.MatchString(text)
}

func findPublishedDocsRoot(t *testing.T) string {
t.Helper()
workingDir, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
for dir := workingDir; ; dir = filepath.Dir(dir) {
if exists(filepath.Join(dir, "README.md")) &&
exists(filepath.Join(dir, "LPOD.md")) &&
exists(filepath.Join(dir, "deploy", "README-public.md")) {
return dir
}
parent := filepath.Dir(dir)
if parent == dir {
t.Skip("published root documents are not present in this monorepo checkout")
}
}
}

func exists(path string) bool {
_, err := os.Stat(path)
return err == nil
}

func paragraphAtMarker(document, marker string) (string, bool) {
index := strings.Index(document, marker)
if index < 0 {
return "", false
}
start := strings.LastIndex(document[:index], "\n\n") + 2
if end := strings.Index(document[index:], "\n\n"); end >= 0 {
return document[start : index+end], true
}
return document[start:], true
}

func assertCurrentLpodStatus(t *testing.T, document, marker string) {
t.Helper()
paragraph, ok := paragraphAtMarker(document, marker)
if !ok {
t.Fatalf("current-status marker %q is missing", marker)
}
if !strings.Contains(strings.ToLower(paragraph), "lpod v3") {
t.Errorf("current-status paragraph %q does not name LPoD v3", marker)
}
normalizedHeight := strings.NewReplacer(",", "").Replace(
strings.Join(strings.Fields(paragraph), ""),
)
if !strings.Contains(normalizedHeight, "2493218") {
t.Errorf("current-status paragraph %q does not include canonical activation height 2,493,218", marker)
}
if !regexp.MustCompile(`(?i)\b(?:active|activated|funded|complete)\b`).MatchString(paragraph) {
t.Errorf("current-status paragraph %q does not say the mainnet deployment is active and funded", marker)
}
if isStaleCurrentLpodClaim(paragraph) {
t.Errorf("current-status paragraph %q reverted to a future-activation claim: %s", marker, paragraph)
}
}

func TestPublishedLpodDocumentsDescribeCurrentMainnetStatus(t *testing.T) {
root := findPublishedDocsRoot(t)
documents := []struct {
path string
markers []string
}{
{
path: filepath.Join(root, "README.md"),
markers: []string{"LPoD v3 is active on Aperod from canonical height", "**Current public network:**"},
},
{
path: filepath.Join(root, "LPOD.md"),
markers: []string{"Mainnet status: LPoD v3", "Mainnet LPoD v3 funding and activation are complete:"},
},
{
path: filepath.Join(root, "deploy", "README-public.md"),
markers: []string{"The current public mainnet also has funded, active LPoD v3"},
},
}

for _, doc := range documents {
content, err := os.ReadFile(doc.path)
if err != nil {
t.Fatalf("read published document %s: %v", doc.path, err)
}
for _, marker := range doc.markers {
t.Run(filepath.Base(doc.path)+"/"+marker, func(t *testing.T) {
assertCurrentLpodStatus(t, string(content), marker)
})
}
}
}

func TestLpodStatusRegressionScopePreservesHistoricalAndOperationalStates(t *testing.T) {
tests := []struct {
name string
text string
want bool
}{
{
name: "unscoped future activation is stale",
text: "If LPoD v3 is activated and funded, the pool will distribute rewards.",
want: true,
},
{
name: "historical report",
text: "Historical report: LPoD v3 was not active at that checkpoint.",
want: false,
},
{
name: "new network default-disabled",
text: "New network repository/config default-disabled: LPoD v3 will be activated only with its own authenticated witness.",
want: false,
},
{
name: "future protocol version",
text: "LPoD v4 will be activated in a future version.",
want: false,
},
{
name: "truthful API state",
text: "The LPoD v3 API response state can be pending, disabled, or unavailable.",
want: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if got := isStaleCurrentLpodClaim(test.text); got != test.want {
t.Fatalf("isStaleCurrentLpodClaim(%q) = %t, want %t", test.text, got, test.want)
}
})
}
}
6 changes: 5 additions & 1 deletion api/rest_address_history_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ func TestREST_AddressTxsPagesCanonicalDiskHistoryBeyondReportedHeight(t *testing
if err != nil {
t.Fatalf("store.Open: %v", err)
}
defer db.Close()
defer func() {
if err := db.Close(); err != nil {
t.Errorf("close address history database: %v", err)
}
}()

priv, validatorPub, err := crypto.GenerateValidatorKey()
if err != nil {
Expand Down
Loading
Loading