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 v2 file contract resolutions #136

Merged
merged 3 commits into from
Nov 15, 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
76 changes: 67 additions & 9 deletions explorer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ type V2FileContract struct {
ConfirmationIndex *types.ChainIndex `json:"confirmationIndex"`
ConfirmationTransactionID *types.TransactionID `json:"confirmationTransactionID"`

Resolution *types.V2FileContractResolutionType
ResolutionIndex *types.ChainIndex `json:"resolutionIndex"`
ResolutionTransactionID *types.TransactionID `json:"resolutionTransactionID"`

Expand All @@ -210,6 +209,63 @@ type V2HostAnnouncement struct {
chain.V2HostAnnouncement
}

// V2FileContractResolutionType enumerates the types of file contract resolution.
type V2FileContractResolutionType interface {
chris124567 marked this conversation as resolved.
Show resolved Hide resolved
isV2FileContractResolution()
}

func (*V2FileContractFinalization) isV2FileContractResolution() {}
ChrisSchinnerl marked this conversation as resolved.
Show resolved Hide resolved
func (*V2FileContractRenewal) isV2FileContractResolution() {}
func (*V2StorageProof) isV2FileContractResolution() {}
func (*V2FileContractExpiration) isV2FileContractResolution() {}

// A V2FileContractFinalization finalizes a contract, preventing further
// revisions and immediately creating its valid outputs.
type V2FileContractFinalization types.Signature

// A V2FileContractRenewal renews a file contract.
type V2FileContractRenewal struct {
FinalRevision V2FileContract `json:"finalRevision"`
NewContract V2FileContract `json:"newContract"`
RenterRollover types.Currency `json:"renterRollover"`
HostRollover types.Currency `json:"hostRollover"`

// signatures cover above fields
RenterSignature types.Signature `json:"renterSignature"`
HostSignature types.Signature `json:"hostSignature"`
}

// A V2StorageProof asserts the presence of a randomly-selected leaf within the
// Merkle tree of a V2FileContract's data.
type V2StorageProof struct {
// Selecting the leaf requires a source of unpredictable entropy; we use the
// ID of the block at the contract's ProofHeight. The storage proof thus
// includes a proof that this ID is the correct ancestor.
//
// During validation, it is imperative to check that ProofIndex.Height
// matches the ProofHeight field of the contract's final revision;
// otherwise, the prover could use any ProofIndex, giving them control over
// the leaf index.
ProofIndex types.ChainIndexElement

// The leaf is always 64 bytes, extended with zeros if necessary.
Leaf [64]byte
Proof []types.Hash256
}

// A V2FileContractExpiration resolves an expired contract. A contract is
// considered expired when its proof window has elapsed. If the contract is not
// storing any data, it will resolve as valid; otherwise, it resolves as missed.
type V2FileContractExpiration struct{}

// A V2FileContractResolution closes a v2 file contract's payment channel.
// There are four resolution types: renewwal, storage proof, finalization,
// and expiration.
type V2FileContractResolution struct {
Parent V2FileContract `json:"parent"`
Resolution V2FileContractResolutionType `json:"resolution"`
}

// A V2Transaction is a V2 transaction that uses the wrapped types above.
type V2Transaction struct {
ID types.TransactionID `json:"id"`
Expand All @@ -219,8 +275,9 @@ type V2Transaction struct {
SiafundInputs []types.V2SiafundInput `json:"siafundInputs,omitempty"`
SiafundOutputs []SiafundOutput `json:"siafundOutputs,omitempty"`

FileContracts []V2FileContract `json:"fileContracts,omitempty"`
FileContractRevisions []V2FileContractRevision `json:"fileContractRevisions,omitempty"`
FileContracts []V2FileContract `json:"fileContracts,omitempty"`
FileContractRevisions []V2FileContractRevision `json:"fileContractRevisions,omitempty"`
FileContractResolutions []V2FileContractResolution `json:"fileContractResolutions,omitempty"`

Attestations []types.Attestation `json:"attestations,omitempty"`
ArbitraryData []byte `json:"arbitraryData,omitempty"`
Expand All @@ -242,12 +299,13 @@ type V2BlockData struct {
// A Block is a block containing wrapped transactions and siacoin
// outputs for the miner payouts.
type Block struct {
Height uint64 `json:"height"`
ParentID types.BlockID `json:"parentID"`
Nonce uint64 `json:"nonce"`
Timestamp time.Time `json:"timestamp"`
MinerPayouts []SiacoinOutput `json:"minerPayouts"`
Transactions []Transaction `json:"transactions"`
Height uint64 `json:"height"`
ParentID types.BlockID `json:"parentID"`
Nonce uint64 `json:"nonce"`
Timestamp time.Time `json:"timestamp"`
ChainIndexElement types.ChainIndexElement `json:"chainIndexElement"`
MinerPayouts []SiacoinOutput `json:"minerPayouts"`
Transactions []Transaction `json:"transactions"`

V2 *V2BlockData `json:"v2,omitempty"`
}
Expand Down
6 changes: 4 additions & 2 deletions explorer/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ type (
// An UpdateState contains information relevant to the block being applied
// or reverted.
UpdateState struct {
Block types.Block
Block types.Block
ChainIndexElement types.ChainIndexElement

Events []Event
Metrics Metrics
Expand Down Expand Up @@ -211,7 +212,8 @@ func applyChainUpdate(tx UpdateTx, cau chain.ApplyUpdate) error {
events := AppliedEvents(cau.State, cau.Block, cau)

state := UpdateState{
Block: cau.Block,
Block: cau.Block,
ChainIndexElement: cau.ChainIndexElement(),

Events: events,
TreeUpdates: treeUpdates,
Expand Down
14 changes: 0 additions & 14 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,6 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.sia.tech/core v0.5.0 h1:feLC7DSCF+PhU157s/94106hFKyiGrGQ9HC3/dF/l7E=
go.sia.tech/core v0.5.0/go.mod h1:P3C1BWa/7J4XgdzWuaYHBvLo2RzZ0UBaJM4TG1GWB2g=
go.sia.tech/coreutils v0.5.0 h1:/xKxdw83iZy0jjLzI2NGHyG4azyjK5DJscxpkr6nIGQ=
go.sia.tech/coreutils v0.5.0/go.mod h1:VYM4FcmlhVrpDGvglLHjRW+gitoaxPNLvp5mL2quilo=
go.sia.tech/jape v0.12.1 h1:xr+o9V8FO8ScRqbSaqYf9bjj1UJ2eipZuNcI1nYousU=
go.sia.tech/jape v0.12.1/go.mod h1:wU+h6Wh5olDjkPXjF0tbZ1GDgoZ6VTi4naFw91yyWC4=
go.sia.tech/mux v1.3.0 h1:hgR34IEkqvfBKUJkAzGi31OADeW2y7D6Bmy/Jcbop9c=
go.sia.tech/mux v1.3.0/go.mod h1:I46++RD4beqA3cW9Xm9SwXbezwPqLvHhVs9HLpDtt58=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
Expand Down
4 changes: 3 additions & 1 deletion internal/testutil/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,9 @@ func SignV2TransactionWithContracts(cs consensus.State, pk, renterPK, hostPK typ
r.RenterSignature = renterPK.SignHash(cs.RenewalSigHash(*r))
r.HostSignature = hostPK.SignHash(cs.RenewalSigHash(*r))
case *types.V2FileContractFinalization:
*r = types.V2FileContractFinalization(renterPK.SignHash(cs.ContractSigHash(txn.FileContractResolutions[i].Parent.V2FileContract)))
finalRevision := txn.FileContractResolutions[i].Parent.V2FileContract
finalRevision.RevisionNumber = types.MaxRevisionNumber
*r = types.V2FileContractFinalization(renterPK.SignHash(cs.ContractSigHash(finalRevision)))
}
}
}
Expand Down
41 changes: 41 additions & 0 deletions internal/testutil/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,47 @@ func CheckV2Transaction(t *testing.T, expectTxn types.V2Transaction, gotTxn expl
CheckV2FC(t, expected.Revision, got.Revision)
}

Equal(t, "file contract resolutions", len(expectTxn.FileContractResolutions), len(gotTxn.FileContractResolutions))
for i := range expectTxn.FileContractResolutions {
expected := expectTxn.FileContractResolutions[i]
got := gotTxn.FileContractResolutions[i]

CheckV2FC(t, expected.Parent.V2FileContract, got.Parent)

switch v := expected.Resolution.(type) {
case *types.V2FileContractRenewal:
if gotV, ok := got.Resolution.(*explorer.V2FileContractRenewal); !ok {
t.Fatalf("expected V2FileContractRenewal, got %v", reflect.TypeOf(got.Resolution))
} else {
CheckV2FC(t, v.FinalRevision, gotV.FinalRevision)
CheckV2FC(t, v.NewContract, gotV.NewContract)

Equal(t, "renter rollover", v.RenterRollover, gotV.RenterRollover)
Equal(t, "host rollover", v.HostRollover, gotV.HostRollover)
Equal(t, "renter signature", v.RenterSignature, gotV.RenterSignature)
Equal(t, "host signature", v.HostSignature, gotV.HostSignature)
}
case *types.V2StorageProof:
if gotV, ok := got.Resolution.(*explorer.V2StorageProof); !ok {
t.Fatalf("expected V2StorageProof, got %v", reflect.TypeOf(got.Resolution))
} else {
Equal(t, "proof index", v.ProofIndex, gotV.ProofIndex)
Equal(t, "leaf", v.Leaf, gotV.Leaf)
Equal(t, "proof", v.Proof, gotV.Proof)
}
case *types.V2FileContractFinalization:
if gotV, ok := got.Resolution.(*explorer.V2FileContractFinalization); !ok {
t.Fatalf("expected V2FileContractFinalization, got %v", reflect.TypeOf(got.Resolution))
} else {
Equal(t, "finalization signature", types.Signature(*v), types.Signature(*gotV))
}
case *types.V2FileContractExpiration:
if _, ok := got.Resolution.(*explorer.V2FileContractExpiration); !ok {
t.Fatalf("expected V2FileContractExpiration, got %v", reflect.TypeOf(got.Resolution))
}
}
}

Equal(t, "attestations", len(expectTxn.Attestations), len(gotTxn.Attestations))
for i := range expectTxn.Attestations {
expected := expectTxn.Attestations[i]
Expand Down
2 changes: 1 addition & 1 deletion persist/sqlite/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func (s *Store) Block(id types.BlockID) (result explorer.Block, err error) {
err = s.transaction(func(tx *txn) error {
var v2Height uint64
var v2Commitment types.Hash256
err := tx.QueryRow(`SELECT parent_id, nonce, timestamp, height, v2_height, v2_commitment FROM blocks WHERE id = ?`, encode(id)).Scan(decode(&result.ParentID), decode(&result.Nonce), decode(&result.Timestamp), &result.Height, decodeNull(&v2Height), decodeNull(&v2Commitment))
err := tx.QueryRow(`SELECT parent_id, nonce, timestamp, height, chain_index_element, v2_height, v2_commitment FROM blocks WHERE id = ?`, encode(id)).Scan(decode(&result.ParentID), decode(&result.Nonce), decode(&result.Timestamp), &result.Height, decode(&result.ChainIndexElement), decodeNull(&v2Height), decodeNull(&v2Commitment))
if err != nil {
return fmt.Errorf("failed to get block: %w", err)
}
Expand Down
9 changes: 6 additions & 3 deletions persist/sqlite/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@ type updateTx struct {
tx *txn
}

func addBlock(tx *txn, b types.Block, height uint64) error {
func addBlock(tx *txn, b types.Block, cie types.ChainIndexElement, height uint64) error {
// nonce is encoded because database/sql doesn't support uint64 with high bit set
var v2Height any
var v2Commitment any
if b.V2 != nil {
v2Height = encode(b.V2.Height)
v2Commitment = encode(b.V2.Commitment)
}
_, err := tx.Exec("INSERT INTO blocks(id, height, parent_id, nonce, timestamp, v2_height, v2_commitment) VALUES (?, ?, ?, ?, ?, ?, ?);", encode(b.ID()), height, encode(b.ParentID), encode(b.Nonce), encode(b.Timestamp), v2Height, v2Commitment)
cie.StateElement.MerkleProof = nil
_, err := tx.Exec("INSERT INTO blocks(id, height, parent_id, nonce, timestamp, chain_index_element, v2_height, v2_commitment) VALUES (?, ?, ?, ?, ?, ?, ?, ?);", encode(b.ID()), height, encode(b.ParentID), encode(b.Nonce), encode(b.Timestamp), encode(cie), v2Height, v2Commitment)
return err
}

Expand Down Expand Up @@ -1018,7 +1019,7 @@ func (ut *updateTx) Metrics(height uint64) (explorer.Metrics, error) {
}

func (ut *updateTx) ApplyIndex(state explorer.UpdateState) error {
if err := addBlock(ut.tx, state.Block, state.Metrics.Index.Height); err != nil {
if err := addBlock(ut.tx, state.Block, state.ChainIndexElement, state.Metrics.Index.Height); err != nil {
return fmt.Errorf("ApplyIndex: failed to add block: %w", err)
} else if err := updateMaturedBalances(ut.tx, false, state.Metrics.Index.Height); err != nil {
return fmt.Errorf("ApplyIndex: failed to update matured balances: %w", err)
Expand Down Expand Up @@ -1106,6 +1107,8 @@ func (ut *updateTx) RevertIndex(state explorer.UpdateState) error {
return fmt.Errorf("RevertIndex: failed to update balances: %w", err)
} else if _, err := updateFileContractElements(ut.tx, true, state.Block, state.FileContractElements); err != nil {
return fmt.Errorf("RevertIndex: failed to update file contract state: %w", err)
} else if _, err := updateV2FileContractElements(ut.tx, true, state.Block, state.V2FileContractElements); err != nil {
return fmt.Errorf("ApplyIndex: failed to add v2 file contracts: %w", err)
} else if err := deleteBlock(ut.tx, state.Block.ID()); err != nil {
return fmt.Errorf("RevertIndex: failed to delete block: %w", err)
} else if err := updateStateTree(ut.tx, state.TreeUpdates); err != nil {
Expand Down
33 changes: 32 additions & 1 deletion persist/sqlite/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ CREATE TABLE blocks (
parent_id BLOB NOT NULL,
nonce BLOB NOT NULL,
timestamp INTEGER NOT NULL,
chain_index_element BLOB NOT NULL,
chris124567 marked this conversation as resolved.
Show resolved Hide resolved

v2_height INTEGER,
v2_commitment BLOB
Expand Down Expand Up @@ -327,6 +328,37 @@ CREATE TABLE v2_transaction_file_contract_revisions (
);
CREATE INDEX v2_transaction_file_contract_revisions_transaction_id_index ON v2_transaction_file_contract_revisions(transaction_id);

CREATE TABLE v2_transaction_file_contract_resolutions (
transaction_id INTEGER REFERENCES v2_transactions(id) ON DELETE CASCADE NOT NULL,
transaction_order INTEGER NOT NULL,
parent_contract_id INTEGER REFERENCES v2_file_contract_elements(id) ON DELETE CASCADE NOT NULL, -- add an index to all foreign keys
chris124567 marked this conversation as resolved.
Show resolved Hide resolved

-- V2FileContractRenewal = 0, V2StorageProof = 1, V2FileContractFinalization = 2, V2FileContractExpiration = 3
resolution_type INTEGER NOT NULL,

-- V2FileContractRenewal
renewal_final_revision_contract_id INTEGER REFERENCES v2_file_contract_elements(id) ON DELETE CASCADE,
renewal_new_contract_id INTEGER REFERENCES v2_file_contract_elements(id) ON DELETE CASCADE,
renewal_renter_rollover BLOB,
renewal_host_rollover BLOB,
renewal_renter_signature BLOB,
renewal_host_signature BLOB,

-- V2StorageProof
storage_proof_proof_index BLOB,
storage_proof_leaf BLOB,
storage_proof_proof BLOB,

-- V2FileContractFinalization
finalization_signature BLOB,

-- V2FileContractExpiration
-- no fields

UNIQUE(transaction_id, transaction_order)
);
CREATE INDEX v2_transaction_file_contract_resolutions_transaction_id_index ON v2_transaction_file_contract_resolutions(transaction_id);

CREATE TABLE v2_transaction_attestations (
transaction_id INTEGER REFERENCES v2_transactions(id) ON DELETE CASCADE NOT NULL,
transaction_order INTEGER NOT NULL,
Expand Down Expand Up @@ -423,7 +455,6 @@ CREATE TABLE v2_last_contract_revision (
confirmation_index BLOB,
confirmation_transaction_id BLOB REFERENCES v2_transactions(transaction_id),

resolution BLOB,
resolution_index BLOB,
resolution_transaction_id BLOB REFERENCES v2_transactions(transaction_id),

Expand Down
Loading
Loading