Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add proof output IDs #135

Merged
merged 4 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 4 additions & 4 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,26 +186,26 @@ func (c *Client) AddressBalance(address types.Address) (resp AddressBalanceRespo
}

// Contract returns the file contract with the specified ID.
func (c *Client) Contract(id types.FileContractID) (resp explorer.FileContract, err error) {
func (c *Client) Contract(id types.FileContractID) (resp explorer.EnhancedFileContract, err error) {
err = c.c.GET(fmt.Sprintf("/contracts/%s", id), &resp)
return
}

// Contracts returns the transactions with the specified IDs.
func (c *Client) Contracts(ids []types.FileContractID) (resp []explorer.FileContract, err error) {
func (c *Client) Contracts(ids []types.FileContractID) (resp []explorer.EnhancedFileContract, err error) {
err = c.c.POST("/contracts", ids, &resp)
return
}

// ContractsKey returns the contracts for a particular ed25519 key.
func (c *Client) ContractsKey(key types.PublicKey) (resp []explorer.FileContract, err error) {
func (c *Client) ContractsKey(key types.PublicKey) (resp []explorer.EnhancedFileContract, err error) {
err = c.c.GET(fmt.Sprintf("/pubkey/%s/contracts", key), &resp)
return
}

// ContractRevisions returns all the revisions of the contract with the
// specified ID.
func (c *Client) ContractRevisions(id types.FileContractID) (resp []explorer.FileContract, err error) {
func (c *Client) ContractRevisions(id types.FileContractID) (resp []explorer.EnhancedFileContract, err error) {
err = c.c.GET(fmt.Sprintf("/contracts/%s/revisions", id), &resp)
return
}
Expand Down
6 changes: 3 additions & 3 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ type (
UnspentSiacoinOutputs(address types.Address, offset, limit uint64) ([]explorer.SiacoinOutput, error)
UnspentSiafundOutputs(address types.Address, offset, limit uint64) ([]explorer.SiafundOutput, error)
AddressEvents(address types.Address, offset, limit uint64) (events []explorer.Event, err error)
Contracts(ids []types.FileContractID) (result []explorer.FileContract, err error)
ContractsKey(key types.PublicKey) (result []explorer.FileContract, err error)
ContractRevisions(id types.FileContractID) (result []explorer.FileContract, err error)
Contracts(ids []types.FileContractID) (result []explorer.EnhancedFileContract, err error)
ContractsKey(key types.PublicKey) (result []explorer.EnhancedFileContract, err error)
ContractRevisions(id types.FileContractID) (result []explorer.EnhancedFileContract, err error)
Search(id types.Hash256) (explorer.SearchType, error)

Hosts(pks []types.PublicKey) ([]explorer.Host, error)
Expand Down
12 changes: 6 additions & 6 deletions explorer/explorer.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ type Store interface {
UnspentSiafundOutputs(address types.Address, offset, limit uint64) ([]SiafundOutput, error)
AddressEvents(address types.Address, offset, limit uint64) (events []Event, err error)
Balance(address types.Address) (sc types.Currency, immatureSC types.Currency, sf uint64, err error)
Contracts(ids []types.FileContractID) (result []FileContract, err error)
ContractsKey(key types.PublicKey) (result []FileContract, err error)
ContractRevisions(id types.FileContractID) (result []FileContract, err error)
Contracts(ids []types.FileContractID) (result []EnhancedFileContract, err error)
ContractsKey(key types.PublicKey) (result []EnhancedFileContract, err error)
ContractRevisions(id types.FileContractID) (result []EnhancedFileContract, err error)
SiacoinElements(ids []types.SiacoinOutputID) (result []SiacoinOutput, err error)
SiafundElements(ids []types.SiafundOutputID) (result []SiafundOutput, err error)

Expand Down Expand Up @@ -247,18 +247,18 @@ func (e *Explorer) Balance(address types.Address) (sc types.Currency, immatureSC
}

// Contracts returns the contracts with the specified IDs.
func (e *Explorer) Contracts(ids []types.FileContractID) (result []FileContract, err error) {
func (e *Explorer) Contracts(ids []types.FileContractID) (result []EnhancedFileContract, err error) {
return e.s.Contracts(ids)
}

// ContractsKey returns the contracts for a particular ed25519 key.
func (e *Explorer) ContractsKey(key types.PublicKey) (result []FileContract, err error) {
func (e *Explorer) ContractsKey(key types.PublicKey) (result []EnhancedFileContract, err error) {
return e.s.ContractsKey(key)
}

// ContractRevisions returns all the revisions of the contract with the
// specified ID.
func (e *Explorer) ContractRevisions(id types.FileContractID) (result []FileContract, err error) {
func (e *Explorer) ContractRevisions(id types.FileContractID) (result []EnhancedFileContract, err error) {
return e.s.ContractRevisions(id)
}

Expand Down
39 changes: 34 additions & 5 deletions explorer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,38 @@
types.SiafundElement
}

// A FileContract is a types.FileContractElement with added fields for
// resolved/valid state.
type ContractSiacoinOutput struct {

Check failure on line 126 in explorer/types.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

exported: exported type ContractSiacoinOutput should have comment or be unexported (revive)

Check failure on line 126 in explorer/types.go

View workflow job for this annotation

GitHub Actions / test / test (1.23, ubuntu-latest)

exported: exported type ContractSiacoinOutput should have comment or be unexported (revive)
ID types.SiacoinOutputID `json:"id"`
types.SiacoinOutput
}

// A FileContract is a storage agreement between a renter and a host. It
// contains a bidirectional payment channel that resolves as either "valid" or
chris124567 marked this conversation as resolved.
Show resolved Hide resolved
// "missed" depending on whether a valid StorageProof is submitted for the
// contract.
type FileContract struct {
Filesize uint64 `json:"filesize"`
FileMerkleRoot types.Hash256 `json:"fileMerkleRoot"`
WindowStart uint64 `json:"windowStart"`
WindowEnd uint64 `json:"windowEnd"`
Payout types.Currency `json:"payout"`
ValidProofOutputs []ContractSiacoinOutput `json:"validProofOutputs"`
MissedProofOutputs []ContractSiacoinOutput `json:"missedProofOutputs"`
UnlockHash types.Hash256 `json:"unlockHash"`
RevisionNumber uint64 `json:"revisionNumber"`
}

// A FileContractElement is a record of a FileContract within the state
// accumulator.
type FileContractElement struct {
chris124567 marked this conversation as resolved.
Show resolved Hide resolved
ID types.FileContractID `json:"id"`
StateElement types.StateElement `json:"stateElement"`
FileContract FileContract `json:"fileContract"`
}

// A EnhancedFileContract is a FileContractElement with added fields for
// resolved/valid state, and when the transaction was confirmed and proved.
type EnhancedFileContract struct {
chris124567 marked this conversation as resolved.
Show resolved Hide resolved
Resolved bool `json:"resolved"`
Valid bool `json:"valid"`

Expand All @@ -137,7 +166,7 @@
ProofIndex *types.ChainIndex `json:"proofIndex"`
ProofTransactionID *types.TransactionID `json:"proofTransactionID"`

types.FileContractElement
FileContractElement
}

// A FileContractRevision is a FileContract with extra fields for revision
Expand All @@ -146,7 +175,7 @@
ParentID types.FileContractID `json:"parentID"`
UnlockConditions types.UnlockConditions `json:"unlockConditions"`

FileContract
EnhancedFileContract
}

// A Transaction is a transaction that uses the wrapped types above.
Expand All @@ -156,7 +185,7 @@
SiacoinOutputs []SiacoinOutput `json:"siacoinOutputs,omitempty"`
SiafundInputs []SiafundInput `json:"siafundInputs,omitempty"`
SiafundOutputs []SiafundOutput `json:"siafundOutputs,omitempty"`
FileContracts []FileContract `json:"fileContracts,omitempty"`
FileContracts []EnhancedFileContract `json:"fileContracts,omitempty"`
FileContractRevisions []FileContractRevision `json:"fileContractRevisions,omitempty"`
StorageProofs []types.StorageProof `json:"storageProofs,omitempty"`
MinerFees []types.Currency `json:"minerFees,omitempty"`
Expand Down
2 changes: 1 addition & 1 deletion internal/testutil/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func CheckV2ChainIndices(t *testing.T, db explorer.Store, txnID types.Transactio

// CheckFC checks the retrieved file contract with the source file contract in
// addition to checking the resolved and valid fields.
func CheckFC(t *testing.T, revision, resolved, valid bool, expected types.FileContract, got explorer.FileContract) {
func CheckFC(t *testing.T, revision, resolved, valid bool, expected types.FileContract, got explorer.EnhancedFileContract) {
t.Helper()

Equal(t, "resolved state", resolved, got.Resolved)
Expand Down
8 changes: 4 additions & 4 deletions persist/sqlite/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -790,13 +790,13 @@ func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []expl
}
defer revisionStmt.Close()

validOutputsStmt, err := tx.Prepare(`INSERT INTO file_contract_valid_proof_outputs(contract_id, contract_order, address, value) VALUES (?, ?, ?, ?) ON CONFLICT DO NOTHING`)
validOutputsStmt, err := tx.Prepare(`INSERT INTO file_contract_valid_proof_outputs(contract_id, contract_order, id, address, value) VALUES (?, ?, ?, ?, ?) ON CONFLICT DO NOTHING`)
if err != nil {
return nil, fmt.Errorf("addFileContracts: failed to prepare valid proof outputs statement: %w", err)
}
defer validOutputsStmt.Close()

missedOutputsStmt, err := tx.Prepare(`INSERT INTO file_contract_missed_proof_outputs(contract_id, contract_order, address, value) VALUES (?, ?, ?, ?) ON CONFLICT DO NOTHING`)
missedOutputsStmt, err := tx.Prepare(`INSERT INTO file_contract_missed_proof_outputs(contract_id, contract_order, id, address, value) VALUES (?, ?, ?, ?, ?) ON CONFLICT DO NOTHING`)
if err != nil {
return nil, fmt.Errorf("addFileContracts: failed to prepare missed proof outputs statement: %w", err)
}
Expand Down Expand Up @@ -861,12 +861,12 @@ func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []expl
}

for i, sco := range fc.ValidProofOutputs {
if _, err := validOutputsStmt.Exec(dbID, i, encode(sco.Address), encode(sco.Value)); err != nil {
if _, err := validOutputsStmt.Exec(dbID, i, encode(fcID.ValidOutputID(i)), encode(sco.Address), encode(sco.Value)); err != nil {
return fmt.Errorf("updateFileContractElements: failed to execute valid proof outputs statement: %w", err)
}
}
for i, sco := range fc.MissedProofOutputs {
if _, err := missedOutputsStmt.Exec(dbID, i, encode(sco.Address), encode(sco.Value)); err != nil {
if _, err := missedOutputsStmt.Exec(dbID, i, encode(fcID.MissedOutputID(i)), encode(sco.Address), encode(sco.Value)); err != nil {
return fmt.Errorf("updateFileContractElements: failed to execute missed proof outputs statement: %w", err)
}
}
Expand Down
36 changes: 27 additions & 9 deletions persist/sqlite/consensus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,34 @@ func CheckChainIndices(t *testing.T, db explorer.Store, txnID types.TransactionI
}

// CheckFCRevisions checks that the revision numbers for the file contracts match.
func CheckFCRevisions(t *testing.T, confirmationIndex types.ChainIndex, confirmationTransactionID types.TransactionID, valid, missed []types.SiacoinOutput, revisionNumbers []uint64, fcs []explorer.FileContract) {
func CheckFCRevisions(t *testing.T, confirmationIndex types.ChainIndex, confirmationTransactionID types.TransactionID, valid, missed []types.SiacoinOutput, revisionNumbers []uint64, fcs []explorer.EnhancedFileContract) {
t.Helper()

testutil.Equal(t, "number of revisions", len(revisionNumbers), len(fcs))
for i := range revisionNumbers {
testutil.Equal(t, "revision number", revisionNumbers[i], fcs[i].FileContract.RevisionNumber)
testutil.Equal(t, "confirmation index", confirmationIndex, *fcs[i].ConfirmationIndex)
testutil.Equal(t, "confirmation transaction ID", confirmationTransactionID, *fcs[i].ConfirmationTransactionID)
testutil.Equal(t, "valid proof outputs", valid, fcs[i].FileContract.ValidProofOutputs)
testutil.Equal(t, "missed proof outputs", missed, fcs[i].FileContract.MissedProofOutputs)
testutil.Equal(t, "revision number", revisionNumbers[i], fcs[i].FileContract.RevisionNumber)

testutil.Equal(t, "valid proof outputs", len(valid), len(fcs[i].FileContract.ValidProofOutputs))
for j := range valid {
expected := valid[j]
got := fcs[i].FileContract.ValidProofOutputs[j]

testutil.Equal(t, "id", fcs[i].ID.ValidOutputID(j), got.ID)
testutil.Equal(t, "value", expected.Value, got.Value)
testutil.Equal(t, "address", expected.Address, got.Address)
}

testutil.Equal(t, "missed proof outputs", len(missed), len(fcs[i].FileContract.MissedProofOutputs))
for j := range missed {
expected := missed[j]
got := fcs[i].FileContract.MissedProofOutputs[j]

testutil.Equal(t, "id", fcs[i].ID.MissedOutputID(j), got.ID)
testutil.Equal(t, "value", expected.Value, got.Value)
testutil.Equal(t, "address", expected.Address, got.Address)
}
}
}

Expand Down Expand Up @@ -697,7 +715,7 @@ func TestFileContract(t *testing.T) {
testutil.Equal(t, "confirmation index", prevTip, *fcr.ConfirmationIndex)
testutil.Equal(t, "confirmation transaction ID", txn.ID(), *fcr.ConfirmationTransactionID)

testutil.CheckFC(t, false, false, false, fc, fcr.FileContract)
testutil.CheckFC(t, false, false, false, fc, fcr.EnhancedFileContract)
}

for i := cm.Tip().Height; i < windowEnd; i++ {
Expand Down Expand Up @@ -920,7 +938,7 @@ func TestEphemeralFileContract(t *testing.T) {
testutil.Equal(t, "parent id", txn.FileContractID(0), fcr.ParentID)
testutil.Equal(t, "unlock conditions", uc, fcr.UnlockConditions)

testutil.CheckFC(t, true, false, false, revisedFC1, fcr.FileContract)
testutil.CheckFC(t, true, false, false, revisedFC1, fcr.EnhancedFileContract)
}

revisedFC2 := revisedFC1
Expand Down Expand Up @@ -1002,7 +1020,7 @@ func TestEphemeralFileContract(t *testing.T) {
fcr := txns[0].FileContractRevisions[0]
testutil.Equal(t, "parent id", txn.FileContractID(0), fcr.ParentID)
testutil.Equal(t, "unlock conditions", uc, fcr.UnlockConditions)
testutil.CheckFC(t, true, false, false, revisedFC2, fcr.FileContract)
testutil.CheckFC(t, true, false, false, revisedFC2, fcr.EnhancedFileContract)
}

{
Expand All @@ -1016,7 +1034,7 @@ func TestEphemeralFileContract(t *testing.T) {
fcr := txns[0].FileContractRevisions[0]
testutil.Equal(t, "parent id", txn.FileContractID(0), fcr.ParentID)
testutil.Equal(t, "unlock conditions", uc, fcr.UnlockConditions)
testutil.CheckFC(t, true, false, false, revisedFC3, fcr.FileContract)
testutil.CheckFC(t, true, false, false, revisedFC3, fcr.EnhancedFileContract)
}
}

Expand Down Expand Up @@ -2330,7 +2348,7 @@ func TestMultipleReorgFileContract(t *testing.T) {
testutil.Equal(t, "parent id", txn.FileContractID(0), fcr.ParentID)
testutil.Equal(t, "unlock conditions", uc, fcr.UnlockConditions)

testutil.CheckFC(t, false, false, false, revFC, fcr.FileContract)
testutil.CheckFC(t, false, false, false, revFC, fcr.EnhancedFileContract)
}

{
Expand Down
18 changes: 9 additions & 9 deletions persist/sqlite/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func encodedIDs(ids []types.FileContractID) []any {
return result
}

func scanFileContract(s scanner) (contractID int64, fc explorer.FileContract, err error) {
func scanFileContract(s scanner) (contractID int64, fc explorer.EnhancedFileContract, err error) {
var confirmationIndex, proofIndex types.ChainIndex
var confirmationTransactionID, proofTransactionID types.TransactionID
err = s.Scan(&contractID, decode(&fc.ID), decode(&fc.StateElement.LeafIndex), &fc.Resolved, &fc.Valid, decode(&fc.TransactionID), decodeNull(&confirmationIndex), decodeNull(&confirmationTransactionID), decodeNull(&proofIndex), decodeNull(&proofTransactionID), decode(&fc.FileContract.Filesize), decode(&fc.FileContract.FileMerkleRoot), decode(&fc.FileContract.WindowStart), decode(&fc.FileContract.WindowEnd), decode(&fc.FileContract.Payout), decode(&fc.FileContract.UnlockHash), decode(&fc.FileContract.RevisionNumber))
Expand All @@ -37,7 +37,7 @@ func scanFileContract(s scanner) (contractID int64, fc explorer.FileContract, er
}

// Contracts implements explorer.Store.
func (s *Store) Contracts(ids []types.FileContractID) (result []explorer.FileContract, err error) {
func (s *Store) Contracts(ids []types.FileContractID) (result []explorer.EnhancedFileContract, err error) {
err = s.transaction(func(tx *txn) error {
query := `SELECT fc1.id, fc1.contract_id, fc1.leaf_index, fc1.resolved, fc1.valid, fc1.transaction_id, rev.confirmation_index, rev.confirmation_transaction_id, rev.proof_index, rev.proof_transaction_id, fc1.filesize, fc1.file_merkle_root, fc1.window_start, fc1.window_end, fc1.payout, fc1.unlock_hash, fc1.revision_number
FROM file_contract_elements fc1
Expand All @@ -50,10 +50,10 @@ func (s *Store) Contracts(ids []types.FileContractID) (result []explorer.FileCon
defer rows.Close()

var contractIDs []int64
idContract := make(map[int64]explorer.FileContract)
idContract := make(map[int64]explorer.EnhancedFileContract)
for rows.Next() {
var contractID int64
var fc explorer.FileContract
var fc explorer.EnhancedFileContract

contractID, fc, err := scanFileContract(rows)
if err != nil {
Expand Down Expand Up @@ -82,7 +82,7 @@ func (s *Store) Contracts(ids []types.FileContractID) (result []explorer.FileCon
}

// ContractRevisions implements explorer.Store.
func (s *Store) ContractRevisions(id types.FileContractID) (revisions []explorer.FileContract, err error) {
func (s *Store) ContractRevisions(id types.FileContractID) (revisions []explorer.EnhancedFileContract, err error) {
err = s.transaction(func(tx *txn) error {
query := `SELECT fc.id, fc.contract_id, fc.leaf_index, fc.resolved, fc.valid, fc.transaction_id, rev.confirmation_index, rev.confirmation_transaction_id, rev.proof_index, rev.proof_transaction_id, fc.filesize, fc.file_merkle_root, fc.window_start, fc.window_end, fc.payout, fc.unlock_hash, fc.revision_number
FROM file_contract_elements fc
Expand All @@ -98,7 +98,7 @@ func (s *Store) ContractRevisions(id types.FileContractID) (revisions []explorer
// fetch revisions
type fce struct {
ID int64
FileContract explorer.FileContract
FileContract explorer.EnhancedFileContract
}
var fces []fce
var contractIDs []int64
Expand All @@ -119,7 +119,7 @@ func (s *Store) ContractRevisions(id types.FileContractID) (revisions []explorer
}

// merge outputs into revisions
revisions = make([]explorer.FileContract, len(fces))
revisions = make([]explorer.EnhancedFileContract, len(fces))
for i, revision := range fces {
output, found := proofOutputs[revision.ID]
if !found {
Expand All @@ -140,7 +140,7 @@ func (s *Store) ContractRevisions(id types.FileContractID) (revisions []explorer
}

// ContractsKey implements explorer.Store.
func (s *Store) ContractsKey(key types.PublicKey) (result []explorer.FileContract, err error) {
func (s *Store) ContractsKey(key types.PublicKey) (result []explorer.EnhancedFileContract, err error) {
err = s.transaction(func(tx *txn) error {
query := `SELECT fc1.id, fc1.contract_id, fc1.leaf_index, fc1.resolved, fc1.valid, fc1.transaction_id, rev.confirmation_index, rev.confirmation_transaction_id, rev.proof_index, rev.proof_transaction_id, fc1.filesize, fc1.file_merkle_root, fc1.window_start, fc1.window_end, fc1.payout, fc1.unlock_hash, fc1.revision_number
FROM file_contract_elements fc1
Expand All @@ -153,7 +153,7 @@ func (s *Store) ContractsKey(key types.PublicKey) (result []explorer.FileContrac
defer rows.Close()

var contractIDs []int64
idContract := make(map[int64]explorer.FileContract)
idContract := make(map[int64]explorer.EnhancedFileContract)
for rows.Next() {
contractID, fc, err := scanFileContract(rows)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions persist/sqlite/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ CREATE TABLE last_contract_revision (
CREATE TABLE file_contract_valid_proof_outputs (
contract_id INTEGER REFERENCES file_contract_elements(id) ON DELETE CASCADE NOT NULL,
contract_order INTEGER NOT NULL,
id BLOB NOT NULL,
address BLOB NOT NULL,
value BLOB NOT NULL,
UNIQUE(contract_id, contract_order)
Expand All @@ -126,6 +127,7 @@ CREATE INDEX file_contract_valid_proof_outputs_contract_id_index ON file_contrac
CREATE TABLE file_contract_missed_proof_outputs (
contract_id INTEGER REFERENCES file_contract_elements(id) ON DELETE CASCADE NOT NULL,
contract_order INTEGER NOT NULL,
id BLOB NOT NULL,
address BLOB NOT NULL,
value BLOB NOT NULL,
UNIQUE(contract_id, contract_order)
Expand Down
Loading
Loading