diff --git a/explorer/types.go b/explorer/types.go index ad242bef..a0c929f8 100644 --- a/explorer/types.go +++ b/explorer/types.go @@ -209,20 +209,6 @@ type V2HostAnnouncement struct { chain.V2HostAnnouncement } -// V2FileContractResolutionType enumerates the types of file contract resolution. -type V2FileContractResolutionType interface { - isV2FileContractResolution() -} - -func (*V2FileContractFinalization) isV2FileContractResolution() {} -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"` @@ -235,35 +221,13 @@ type V2FileContractRenewal struct { 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"` + Parent V2FileContract `json:"parent"` + Type string `json:"string"` + Resolution any `json:"resolution"` } // A V2Transaction is a V2 transaction that uses the wrapped types above. diff --git a/internal/testutil/check.go b/internal/testutil/check.go index 733a1659..f0b58120 100644 --- a/internal/testutil/check.go +++ b/internal/testutil/check.go @@ -212,29 +212,35 @@ func CheckV2Transaction(t *testing.T, expectTxn types.V2Transaction, gotTxn expl CheckV2FC(t, v.FinalRevision, gotV.FinalRevision) CheckV2FC(t, v.NewContract, gotV.NewContract) + Equal(t, "type", "renewal", got.Type) 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 { + if gotV, ok := got.Resolution.(*types.V2StorageProof); !ok { t.Fatalf("expected V2StorageProof, got %v", reflect.TypeOf(got.Resolution)) } else { + Equal(t, "type", "storageProof", got.Type) 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 { + if gotV, ok := got.Resolution.(*types.V2FileContractFinalization); !ok { t.Fatalf("expected V2FileContractFinalization, got %v", reflect.TypeOf(got.Resolution)) } else { + Equal(t, "type", "finalization", got.Type) Equal(t, "finalization signature", types.Signature(*v), types.Signature(*gotV)) } case *types.V2FileContractExpiration: - if _, ok := got.Resolution.(*explorer.V2FileContractExpiration); !ok { + Equal(t, "type", "expiration", got.Type) + if _, ok := got.Resolution.(*types.V2FileContractExpiration); !ok { t.Fatalf("expected V2FileContractExpiration, got %v", reflect.TypeOf(got.Resolution)) } + default: + t.Fatalf("invalid resolution type: %v", reflect.TypeOf(got.Resolution)) } } diff --git a/persist/sqlite/v2transactions.go b/persist/sqlite/v2transactions.go index ddef3e48..4d290cfd 100644 --- a/persist/sqlite/v2transactions.go +++ b/persist/sqlite/v2transactions.go @@ -497,7 +497,9 @@ WHERE fc.id = ?`) return fmt.Errorf("failed to scan file contract: %w", err) } - var res explorer.V2FileContractResolutionType + fcr := explorer.V2FileContractResolution{ + Parent: parent, + } switch resolutionType { case 0: // V2FileContractRenewal renewal := &explorer.V2FileContractRenewal{ @@ -518,25 +520,28 @@ WHERE fc.id = ?`) return fmt.Errorf("failed to scan new contract: %w", err) } } - res = renewal + + fcr.Type = "renewal" + fcr.Resolution = renewal case 1: // V2StorageProof - proof := &explorer.V2StorageProof{ + proof := &types.V2StorageProof{ ProofIndex: storageProofProofIndex, Proof: storageProofProof, Leaf: [64]byte(storageProofLeaf), } - res = proof + + fcr.Type = "storageProof" + fcr.Resolution = proof case 2: // V2FileContractFinalization - res = (*explorer.V2FileContractFinalization)(&finalizationSignature) + fcr.Type = "finalization" + fcr.Resolution = (*types.V2FileContractFinalization)(&finalizationSignature) case 3: // V2FileContractExpiration - res = new(explorer.V2FileContractExpiration) + fcr.Type = "expiration" + fcr.Resolution = new(types.V2FileContractExpiration) } // Append the resolution to the transaction. - txns[i].FileContractResolutions = append(txns[i].FileContractResolutions, explorer.V2FileContractResolution{ - Parent: parent, - Resolution: res, - }) + txns[i].FileContractResolutions = append(txns[i].FileContractResolutions, fcr) } return nil }()