diff --git a/internal/testutil/check.go b/internal/testutil/check.go index 9f112dad..76d473a5 100644 --- a/internal/testutil/check.go +++ b/internal/testutil/check.go @@ -5,6 +5,7 @@ import ( "testing" "go.sia.tech/core/types" + "go.sia.tech/coreutils/chain" "go.sia.tech/explored/explorer" ) @@ -111,11 +112,25 @@ func CheckV2Transaction(t *testing.T, expectTxn types.V2Transaction, gotTxn expl Equal(t, "new foundation address", expectTxn.NewFoundationAddress, gotTxn.NewFoundationAddress) Equal(t, "miner fee", expectTxn.MinerFee, gotTxn.MinerFee) - Equal(t, "arbitrary data", len(expectTxn.ArbitraryData), len(gotTxn.ArbitraryData)) + Equal(t, "attestations", len(expectTxn.Attestations), len(gotTxn.Attestations)) + for i := range expectTxn.Attestations { + Equal(t, "attestation", expectTxn.Attestations[i], gotTxn.Attestations[i]) + } + Equal(t, "arbitrary data", len(expectTxn.ArbitraryData), len(gotTxn.ArbitraryData)) for i := range expectTxn.ArbitraryData { Equal(t, "arbitrary data value", expectTxn.ArbitraryData[i], gotTxn.ArbitraryData[i]) } + + j := 0 + for i := range expectTxn.Attestations { + var ha chain.HostAnnouncement + if !ha.FromAttestation(expectTxn.Attestations[i]) { + continue + } + Equal(t, "host announcement", ha, gotTxn.HostAnnouncements[j]) + j++ + } } // CheckV2ChainIndices checks that the chain indices that a v2 transaction was diff --git a/persist/sqlite/v2consensus_test.go b/persist/sqlite/v2consensus_test.go index f437f864..9f8d5df1 100644 --- a/persist/sqlite/v2consensus_test.go +++ b/persist/sqlite/v2consensus_test.go @@ -416,3 +416,69 @@ func TestV2FoundationAddress(t *testing.T) { testutil.CheckV2Transaction(t, txn1, dbTxns[0]) } } + +func TestV2Attestations(t *testing.T) { + log := zaptest.NewLogger(t) + dir := t.TempDir() + db, err := sqlite.OpenDatabase(filepath.Join(dir, "explored.sqlite3"), log.Named("sqlite3")) + if err != nil { + t.Fatal(err) + } + defer db.Close() + + bdb, err := coreutils.OpenBoltChainDB(filepath.Join(dir, "consensus.db")) + if err != nil { + t.Fatal(err) + } + defer bdb.Close() + + pk1 := types.GeneratePrivateKey() + + pk2 := types.GeneratePrivateKey() + + network, genesisBlock := ctestutil.V2Network() + network.HardforkV2.AllowHeight = 1 + network.HardforkV2.RequireHeight = 2 + + edb, _ := newConsensusDB(network, genesisBlock) + store, genesisState, err := chain.NewDBStore(bdb, network, genesisBlock) + if err != nil { + t.Fatal(err) + } + + cm := chain.NewManager(store, genesisState) + cs := cm.TipState() + + ha1 := chain.HostAnnouncement{ + PublicKey: pk1.PublicKey(), + NetAddress: "127.0.0.1:4444", + } + ha2 := chain.HostAnnouncement{ + PublicKey: pk2.PublicKey(), + NetAddress: "127.0.0.1:8888", + } + + otherAttestation := types.Attestation{ + PublicKey: pk1.PublicKey(), + Key: "hello", + Value: []byte("world"), + } + otherAttestation.Signature = pk1.SignHash(cs.AttestationSigHash(otherAttestation)) + + txn1 := types.V2Transaction{ + Attestations: []types.Attestation{ha1.ToAttestation(cs, pk1), otherAttestation, ha2.ToAttestation(cs, pk2)}, + } + + if err := cm.AddBlocks([]types.Block{testutil.MineV2Block(cs, []types.V2Transaction{txn1}, types.VoidAddress)}); err != nil { + t.Fatal(err) + } + v2SyncDB(t, edb, db, cm) + + { + dbTxns, err := db.V2Transactions([]types.TransactionID{txn1.ID()}) + if err != nil { + t.Fatal(err) + } + testutil.CheckV2Transaction(t, txn1, dbTxns[0]) + } +} diff --git a/persist/sqlite/v2transactions.go b/persist/sqlite/v2transactions.go index 22076bf0..4db95804 100644 --- a/persist/sqlite/v2transactions.go +++ b/persist/sqlite/v2transactions.go @@ -100,7 +100,7 @@ ORDER BY transaction_order ASC` for rows.Next() { var txnID int64 var attestation types.Attestation - if err := rows.Scan(&txnID, decode(attestation.PublicKey), &attestation.Key, &attestation.Value, decode(&attestation.Signature)); err != nil { + if err := rows.Scan(&txnID, decode(&attestation.PublicKey), &attestation.Key, &attestation.Value, decode(&attestation.Signature)); err != nil { return nil, fmt.Errorf("failed to scan attestation: %w", err) } result[txnID] = append(result[txnID], attestation) @@ -154,6 +154,11 @@ func getV2Transactions(tx *txn, idMap map[int64]transactionID) ([]explorer.V2Tra return nil, fmt.Errorf("getV2Transactions: failed to get other fields: %w", err) } + txnAttestations, err := v2TransactionAttestations(tx, dbIDs) + if err != nil { + return nil, fmt.Errorf("getV2Transactions: failed to get attestations: %w", err) + } + txnArbitraryData, err := v2TransactionArbitraryData(tx, dbIDs) if err != nil { return nil, fmt.Errorf("getV2Transactions: failed to get arbitrary data: %w", err) @@ -165,6 +170,7 @@ func getV2Transactions(tx *txn, idMap map[int64]transactionID) ([]explorer.V2Tra txn := explorer.V2Transaction{ ID: idMap[int64(order)].id, + Attestations: txnAttestations[dbID], ArbitraryData: txnArbitraryData[dbID], NewFoundationAddress: otherFields.newFoundationAddress, MinerFee: otherFields.minerFee,