Skip to content

Commit

Permalink
add v2 outputs and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
chris124567 authored and ChrisSchinnerl committed Oct 23, 2024
1 parent 3fecb71 commit 63755f7
Show file tree
Hide file tree
Showing 6 changed files with 364 additions and 42 deletions.
8 changes: 5 additions & 3 deletions explorer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,11 @@ 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"`
ID types.TransactionID `json:"id"`
SiacoinOutputs []SiacoinOutput `json:"siacoinOutputs,omitempty"`
SiafundOutputs []SiafundOutput `json:"siafundOutputs,omitempty"`
Attestations []types.Attestation `json:"attestations,omitempty"`
ArbitraryData []byte `json:"arbitraryData,omitempty"`

NewFoundationAddress *types.Address `json:"newFoundationAddress,omitempty"`
MinerFee types.Currency `json:"minerFee"`
Expand Down
124 changes: 87 additions & 37 deletions internal/testutil/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

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

Expand Down Expand Up @@ -36,70 +37,75 @@ func CheckTransaction(t *testing.T, expectTxn types.Transaction, gotTxn explorer
t.Helper()

Equal(t, "siacoin inputs", len(expectTxn.SiacoinInputs), len(gotTxn.SiacoinInputs))
Equal(t, "siacoin outputs", len(expectTxn.SiacoinOutputs), len(gotTxn.SiacoinOutputs))
Equal(t, "siafund inputs", len(expectTxn.SiafundInputs), len(gotTxn.SiafundInputs))
Equal(t, "siafund outputs", len(expectTxn.SiafundOutputs), len(gotTxn.SiafundOutputs))
Equal(t, "arbitrary data", len(expectTxn.ArbitraryData), len(gotTxn.ArbitraryData))
Equal(t, "miner fees", len(expectTxn.MinerFees), len(gotTxn.MinerFees))
Equal(t, "signatures", len(expectTxn.Signatures), len(gotTxn.Signatures))

for i := range expectTxn.SiacoinInputs {
expectSci := expectTxn.SiacoinInputs[i]
gotSci := gotTxn.SiacoinInputs[i]
expected := expectTxn.SiacoinInputs[i]
got := gotTxn.SiacoinInputs[i]

if gotSci.Value == types.ZeroCurrency {
if got.Value == types.ZeroCurrency {
t.Fatal("invalid value")
}
Equal(t, "parent ID", expectSci.ParentID, gotSci.ParentID)
Equal(t, "unlock conditions", expectSci.UnlockConditions, gotSci.UnlockConditions)
Equal(t, "address", expectSci.UnlockConditions.UnlockHash(), gotSci.Address)
Equal(t, "parent ID", expected.ParentID, got.ParentID)
Equal(t, "unlock conditions", expected.UnlockConditions, got.UnlockConditions)
Equal(t, "address", expected.UnlockConditions.UnlockHash(), got.Address)
}

Equal(t, "siacoin outputs", len(expectTxn.SiacoinOutputs), len(gotTxn.SiacoinOutputs))
for i := range expectTxn.SiacoinOutputs {
expectSco := expectTxn.SiacoinOutputs[i]
gotSco := gotTxn.SiacoinOutputs[i].SiacoinOutput
expected := expectTxn.SiacoinOutputs[i]
got := gotTxn.SiacoinOutputs[i].SiacoinOutput

Equal(t, "address", expectSco.Address, gotSco.Address)
Equal(t, "value", expectSco.Value, gotSco.Value)
Equal(t, "address", expected.Address, got.Address)
Equal(t, "value", expected.Value, got.Value)
Equal(t, "source", explorer.SourceTransaction, gotTxn.SiacoinOutputs[i].Source)
}

Equal(t, "siafund inputs", len(expectTxn.SiafundInputs), len(gotTxn.SiafundInputs))
for i := range expectTxn.SiafundInputs {
expectSfi := expectTxn.SiafundInputs[i]
gotSfi := gotTxn.SiafundInputs[i]
expected := expectTxn.SiafundInputs[i]
got := gotTxn.SiafundInputs[i]

if gotSfi.Value == 0 {
if got.Value == 0 {
t.Fatal("invalid value")
}
Equal(t, "parent ID", expectSfi.ParentID, gotSfi.ParentID)
Equal(t, "claim address", expectSfi.ClaimAddress, gotSfi.ClaimAddress)
Equal(t, "unlock conditions", expectSfi.UnlockConditions, gotSfi.UnlockConditions)
Equal(t, "address", expectSfi.UnlockConditions.UnlockHash(), gotSfi.Address)
Equal(t, "parent ID", expected.ParentID, got.ParentID)
Equal(t, "claim address", expected.ClaimAddress, got.ClaimAddress)
Equal(t, "unlock conditions", expected.UnlockConditions, got.UnlockConditions)
Equal(t, "address", expected.UnlockConditions.UnlockHash(), got.Address)
}

Equal(t, "siafund outputs", len(expectTxn.SiafundOutputs), len(gotTxn.SiafundOutputs))
for i := range expectTxn.SiafundOutputs {
expectSfo := expectTxn.SiafundOutputs[i]
gotSfo := gotTxn.SiafundOutputs[i].SiafundOutput
expected := expectTxn.SiafundOutputs[i]
got := gotTxn.SiafundOutputs[i].SiafundOutput

Equal(t, "address", expectSfo.Address, gotSfo.Address)
Equal(t, "value", expectSfo.Value, gotSfo.Value)
Equal(t, "address", expected.Address, got.Address)
Equal(t, "value", expected.Value, got.Value)
}

Equal(t, "arbitrary data", len(expectTxn.ArbitraryData), len(gotTxn.ArbitraryData))
for i := range expectTxn.ArbitraryData {
Equal(t, "miner fee", expectTxn.ArbitraryData[i], gotTxn.ArbitraryData[i])
Equal(t, "arbitrary data", expectTxn.ArbitraryData[i], gotTxn.ArbitraryData[i])
}

Equal(t, "miner fees", len(expectTxn.MinerFees), len(gotTxn.MinerFees))
for i := range expectTxn.MinerFees {
Equal(t, "miner fee", expectTxn.MinerFees[i], gotTxn.MinerFees[i])
}

Equal(t, "signatures", len(expectTxn.Signatures), len(gotTxn.Signatures))
for i := range expectTxn.Signatures {
expectSig := expectTxn.Signatures[i]
gotSig := gotTxn.Signatures[i]
expected := expectTxn.Signatures[i]
got := gotTxn.Signatures[i]

Equal(t, "parent ID", expectSig.ParentID, gotSig.ParentID)
Equal(t, "public key index", expectSig.PublicKeyIndex, gotSig.PublicKeyIndex)
Equal(t, "timelock", expectSig.Timelock, gotSig.Timelock)
Equal(t, "signature", expectSig.Signature, gotSig.Signature)
Equal(t, "parent ID", expected.ParentID, got.ParentID)
Equal(t, "public key index", expected.PublicKeyIndex, got.PublicKeyIndex)
Equal(t, "timelock", expected.Timelock, got.Timelock)
Equal(t, "signature", expected.Signature, got.Signature)

// reflect.DeepCheck treats empty slices as different from nil
// slices so these will differ because the decoder is doing
// cf.X = make([]uint64, d.ReadPrefix()) and the prefix is 0
// testutil.Equal(t, "covered fields", expectSig.CoveredFields, gotSig.CoveredFields)
// testutil.Equal(t, "covered fields", expected.CoveredFields, got.CoveredFields)
}
}

Expand All @@ -111,8 +117,52 @@ 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, "siacoin outputs", len(expectTxn.SiacoinOutputs), len(gotTxn.SiacoinOutputs))
for i := range expectTxn.SiacoinOutputs {
expected := expectTxn.SiacoinOutputs[i]
got := gotTxn.SiacoinOutputs[i].SiacoinOutput

Equal(t, "address", expected.Address, got.Address)
Equal(t, "value", expected.Value, got.Value)
}

Equal(t, "siafund outputs", len(expectTxn.SiafundOutputs), len(gotTxn.SiafundOutputs))
for i := range expectTxn.SiafundOutputs {
expected := expectTxn.SiafundOutputs[i]
got := gotTxn.SiafundOutputs[i].SiafundOutput

Equal(t, "address", expected.Address, got.Address)
Equal(t, "value", expected.Value, got.Value)
}

Equal(t, "attestations", len(expectTxn.Attestations), len(gotTxn.Attestations))
for i := range expectTxn.Attestations {
expected := expectTxn.Attestations[i]
got := gotTxn.Attestations[i]

Equal(t, "public key", expected.PublicKey, got.PublicKey)
Equal(t, "key", expected.Key, got.Key)
Equal(t, "value", expected.Value, got.Value)
Equal(t, "signature", expected.Signature, got.Signature)
}

var hostAnnouncements []chain.HostAnnouncement
for _, attestation := range expectTxn.Attestations {
var ha chain.HostAnnouncement
if ha.FromAttestation(attestation) {
hostAnnouncements = append(hostAnnouncements, ha)
}
}
Equal(t, "host announcements", len(hostAnnouncements), len(gotTxn.HostAnnouncements))
for i := range hostAnnouncements {
expected := hostAnnouncements[i]
got := gotTxn.HostAnnouncements[i]

Equal(t, "net address", expected.NetAddress, got.NetAddress)
Equal(t, "public key", expected.PublicKey, got.PublicKey)
}

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])
}
Expand Down
16 changes: 16 additions & 0 deletions persist/sqlite/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,22 @@ 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_siacoin_outputs (
transaction_id INTEGER REFERENCES v2_transactions(id) ON DELETE CASCADE NOT NULL,
transaction_order INTEGER NOT NULL,
output_id INTEGER REFERENCES siacoin_elements(id) ON DELETE CASCADE NOT NULL,
UNIQUE(transaction_id, transaction_order)
);
CREATE INDEX v2_transaction_siacoin_outputs_transaction_id_index ON v2_transaction_siacoin_outputs(transaction_id);

CREATE TABLE v2_transaction_siafund_outputs (
transaction_id INTEGER REFERENCES v2_transactions(id) ON DELETE CASCADE NOT NULL,
transaction_order INTEGER NOT NULL,
output_id INTEGER REFERENCES siafund_elements(id) ON DELETE CASCADE NOT NULL, -- add an index to all foreign keys
UNIQUE(transaction_id, transaction_order)
);
CREATE INDEX v2_transaction_siafund_outputs_transaction_id_index ON v2_transaction_siafund_outputs(transaction_id);

CREATE TABLE v2_transaction_attestations (
transaction_id INTEGER REFERENCES transactions(id) ON DELETE CASCADE NOT NULL,
transaction_order INTEGER NOT NULL,
Expand Down
51 changes: 49 additions & 2 deletions persist/sqlite/v2consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sqlite

import (
"database/sql"
"errors"
"fmt"

"go.sia.tech/core/types"
Expand Down Expand Up @@ -61,15 +62,57 @@ func addV2Transactions(tx *txn, bid types.BlockID, txns []types.V2Transaction) (
return txnDBIds, nil
}

func addV2Attestations(tx *txn, id int64, txn types.V2Transaction) error {
func addV2SiacoinOutputs(tx *txn, txnID int64, txn types.V2Transaction, dbIDs map[types.SiacoinOutputID]int64) error {
stmt, err := tx.Prepare(`INSERT INTO v2_transaction_siacoin_outputs(transaction_id, transaction_order, output_id) VALUES (?, ?, ?)`)
if err != nil {
return fmt.Errorf("addV2SiacoinOutputs: failed to prepare statement: %w", err)
}
defer stmt.Close()

id := txn.ID()
for i := range txn.SiacoinOutputs {
dbID, ok := dbIDs[txn.SiacoinOutputID(id, i)]
if !ok {
return errors.New("addV2SiacoinOutputs: dbID not in map")
}

if _, err := stmt.Exec(txnID, i, dbID); err != nil {
return fmt.Errorf("addV2SiacoinOutputs: failed to execute statement: %w", err)
}
}
return nil
}

func addV2SiafundOutputs(tx *txn, txnID int64, txn types.V2Transaction, dbIDs map[types.SiafundOutputID]int64) error {
stmt, err := tx.Prepare(`INSERT INTO v2_transaction_siafund_outputs(transaction_id, transaction_order, output_id) VALUES (?, ?, ?)`)
if err != nil {
return fmt.Errorf("addV2SiafundOutputs: failed to prepare statement: %w", err)
}
defer stmt.Close()

id := txn.ID()
for i := range txn.SiafundOutputs {
dbID, ok := dbIDs[txn.SiafundOutputID(id, i)]
if !ok {
return errors.New("addV2SiafundOutputs: dbID not in map")
}

if _, err := stmt.Exec(txnID, i, dbID); err != nil {
return fmt.Errorf("addV2SiafundOutputs: failed to execute statement: %w", err)
}
}
return nil
}

func addV2Attestations(tx *txn, txnID 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 {
if _, err := stmt.Exec(txnID, i, encode(attestation.PublicKey), attestation.Key, attestation.Value, encode(attestation.Signature)); err != nil {
return fmt.Errorf("addV2Attestations: failed to execute statement: %w", err)
}
}
Expand All @@ -90,6 +133,10 @@ func addV2TransactionFields(tx *txn, txns []types.V2Transaction, scDBIds map[typ

if err := addV2Attestations(tx, dbID.id, txn); err != nil {
return fmt.Errorf("addV2TransactionFields: failed to add attestations: %w", err)
} else if err := addV2SiacoinOutputs(tx, dbID.id, txn, scDBIds); err != nil {
return fmt.Errorf("failed to add siacoin outputs: %w", err)
} else if err := addV2SiafundOutputs(tx, dbID.id, txn, sfDBIds); err != nil {
return fmt.Errorf("failed to add siafund outputs: %w", err)
}
}

Expand Down
Loading

0 comments on commit 63755f7

Please sign in to comment.