Skip to content

Commit

Permalink
Merge pull request #119 from SiaFoundation/add-v2-attestations
Browse files Browse the repository at this point in the history
Add v2 attestations
  • Loading branch information
n8maninger authored Oct 22, 2024
2 parents 19f281a + a665cb0 commit 3fecb71
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 1 deletion.
1 change: 1 addition & 0 deletions explorer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ type Transaction struct {
// A V2Transaction is a v2 transaction that uses the wrapped types above.
type V2Transaction struct {
ID types.TransactionID `json:"id"`
Attestations []types.Attestation `json:"attestations,omitempty"`
ArbitraryData []byte `json:"arbitraryData,omitempty"`

NewFoundationAddress *types.Address `json:"newFoundationAddress,omitempty"`
Expand Down
11 changes: 11 additions & 0 deletions persist/sqlite/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,17 @@ CREATE TABLE v2_block_transactions (
CREATE INDEX v2_block_transactions_block_id_index ON v2_block_transactions(block_id);
CREATE INDEX v2_block_transactions_transaction_id_block_id ON v2_block_transactions(transaction_id, block_id);

CREATE TABLE v2_transaction_attestations (
transaction_id INTEGER REFERENCES transactions(id) ON DELETE CASCADE NOT NULL,
transaction_order INTEGER NOT NULL,
public_key BLOB NOT NULL,
key TEXT NOT NULL,
value BLOB NOT NULL,
signature BLOB NOT NULL,
UNIQUE(transaction_id, transaction_order)
);
CREATE INDEX v2_transaction_attestations_transaction_id_index ON v2_transaction_attestations(transaction_id);

CREATE TABLE state_tree (
row INTEGER NOT NULL,
column INTEGER NOT NULL,
Expand Down
19 changes: 19 additions & 0 deletions persist/sqlite/v2consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,21 @@ func addV2Transactions(tx *txn, bid types.BlockID, txns []types.V2Transaction) (
return txnDBIds, nil
}

func addV2Attestations(tx *txn, id int64, txn types.V2Transaction) error {
stmt, err := tx.Prepare(`INSERT INTO v2_transaction_attestations(transaction_id, transaction_order, public_key, key, value, signature) VALUES (?, ?, ?, ?, ?, ?)`)
if err != nil {
return fmt.Errorf("addV2Attestations: failed to prepare statement: %w", err)
}
defer stmt.Close()

for i, attestation := range txn.Attestations {
if _, err := stmt.Exec(id, i, encode(attestation.PublicKey), attestation.Key, attestation.Value, encode(attestation.Signature)); err != nil {
return fmt.Errorf("addV2Attestations: failed to execute statement: %w", err)
}
}
return nil
}

func addV2TransactionFields(tx *txn, txns []types.V2Transaction, scDBIds map[types.SiacoinOutputID]int64, sfDBIds map[types.SiafundOutputID]int64, fcDBIds map[explorer.DBFileContract]int64, v2TxnDBIds map[types.TransactionID]txnDBId) error {
for _, txn := range txns {
dbID, ok := v2TxnDBIds[txn.ID()]
Expand All @@ -72,6 +87,10 @@ func addV2TransactionFields(tx *txn, txns []types.V2Transaction, scDBIds map[typ
if dbID.exist {
continue
}

if err := addV2Attestations(tx, dbID.id, txn); err != nil {
return fmt.Errorf("addV2TransactionFields: failed to add attestations: %w", err)
}
}

return nil
Expand Down
45 changes: 45 additions & 0 deletions persist/sqlite/v2consensus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"go.sia.tech/core/consensus"
"go.sia.tech/core/types"
"go.sia.tech/coreutils/chain"
"go.sia.tech/explored/explorer"
"go.sia.tech/explored/internal/testutil"
)
Expand Down Expand Up @@ -175,3 +176,47 @@ func TestV2FoundationAddress(t *testing.T) {
testutil.CheckV2Transaction(t, txn1, dbTxns[0])
}
}

func TestV2Attestations(t *testing.T) {
pk1 := types.GeneratePrivateKey()
pk2 := types.GeneratePrivateKey()

_, _, cm, db := newStore(t, true, func(network *consensus.Network, genesisBlock types.Block) {
network.HardforkV2.AllowHeight = 1
network.HardforkV2.RequireHeight = 2
})
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)
}
syncDB(t, db, cm)

{
dbTxns, err := db.V2Transactions([]types.TransactionID{txn1.ID()})
if err != nil {
t.Fatal(err)
}
testutil.CheckV2Transaction(t, txn1, dbTxns[0])
}
}
47 changes: 46 additions & 1 deletion persist/sqlite/v2transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

"go.sia.tech/core/types"
"go.sia.tech/coreutils/chain"
"go.sia.tech/explored/explorer"
)

Expand Down Expand Up @@ -60,11 +61,22 @@ WHERE block_id = ? ORDER BY block_order ASC`, encode(blockID))
// getV2Transactions fetches v2 transactions in the correct order using
// prepared statements.
func getV2Transactions(tx *txn, ids []types.TransactionID) ([]explorer.V2Transaction, error) {
_, txns, err := getV2TransactionBase(tx, ids)
dbIDs, txns, err := getV2TransactionBase(tx, ids)
if err != nil {
return nil, fmt.Errorf("getV2Transactions: failed to get base transactions: %w", err)
} else if err := fillV2TransactionAttestations(tx, dbIDs, txns); err != nil {
return nil, fmt.Errorf("getV2Transactions: failed to get attestations: %w", err)
}

// add host announcements if we have any
for i := range txns {
for _, attestation := range txns[i].Attestations {
var ha chain.HostAnnouncement
if ha.FromAttestation(attestation) {
txns[i].HostAnnouncements = append(txns[i].HostAnnouncements, ha)
}
}
}
return txns, nil
}

Expand Down Expand Up @@ -96,6 +108,39 @@ func getV2TransactionBase(tx *txn, txnIDs []types.TransactionID) ([]int64, []exp
return dbIDs, txns, nil
}

// fillV2TransactionAttestations fills in the attestations for each
// transaction.
func fillV2TransactionAttestations(tx *txn, dbIDs []int64, txns []explorer.V2Transaction) error {
stmt, err := tx.Prepare(`SELECT public_key, key, value, signature FROM v2_transaction_attestations WHERE transaction_id = ? ORDER BY transaction_order`)
if err != nil {
return fmt.Errorf("failed to prepare attestations statement: %w", err)
}
defer stmt.Close()

for i, dbID := range dbIDs {
err := func() error {
rows, err := stmt.Query(dbID)
if err != nil {
return fmt.Errorf("failed to query attestations: %w", err)
}
defer rows.Close()

for rows.Next() {
var attestation types.Attestation
if err := rows.Scan(decode(&attestation.PublicKey), &attestation.Key, &attestation.Value, decode(&attestation.Signature)); err != nil {
return fmt.Errorf("failed to scan attestation: %w", err)
}
txns[i].Attestations = append(txns[i].Attestations, attestation)
}
return nil
}()
if err != nil {
return err
}
}
return nil
}

// V2Transactions implements explorer.Store.
func (s *Store) V2Transactions(ids []types.TransactionID) (results []explorer.V2Transaction, err error) {
err = s.transaction(func(tx *txn) error {
Expand Down

0 comments on commit 3fecb71

Please sign in to comment.