From f207a3be474f55045188d132c3d366671a3d44a7 Mon Sep 17 00:00:00 2001 From: Christopher Tarry Date: Mon, 25 Mar 2024 16:06:56 -0400 Subject: [PATCH 1/8] refactor signing and gift address code --- persist/sqlite/consensus_test.go | 91 +++++++++++++------------------- 1 file changed, 38 insertions(+), 53 deletions(-) diff --git a/persist/sqlite/consensus_test.go b/persist/sqlite/consensus_test.go index 5311f479..3bc4b523 100644 --- a/persist/sqlite/consensus_test.go +++ b/persist/sqlite/consensus_test.go @@ -14,9 +14,7 @@ import ( "go.uber.org/zap/zaptest" ) -const giftSF = 10000 - -func testV1Network(giftAddr types.Address) (*consensus.Network, types.Block) { +func testV1Network(giftAddr types.Address, sc types.Currency, sf uint64) (*consensus.Network, types.Block) { // use a modified version of Zen n, genesisBlock := chain.TestnetZen() n.InitialTarget = types.BlockID{0xFF} @@ -28,12 +26,19 @@ func testV1Network(giftAddr types.Address) (*consensus.Network, types.Block) { n.HardforkFoundation.Height = 1 n.HardforkV2.AllowHeight = 1000 n.HardforkV2.RequireHeight = 1000 - genesisBlock.Transactions = []types.Transaction{{ - SiafundOutputs: []types.SiafundOutput{{ + genesisBlock.Transactions = []types.Transaction{{}} + if sf > 0 { + genesisBlock.Transactions[0].SiafundOutputs = []types.SiafundOutput{{ + Address: giftAddr, + Value: sf, + }} + } + if sc.Cmp(types.ZeroCurrency) == 1 { + genesisBlock.Transactions[0].SiacoinOutputs = []types.SiacoinOutput{{ Address: giftAddr, - Value: giftSF, - }}, - }} + Value: sc, + }} + } return n, genesisBlock } @@ -83,6 +88,24 @@ func mineV2Block(state consensus.State, txns []types.V2Transaction, minerAddr ty return b } +func signTxn(cs consensus.State, pk types.PrivateKey, txn *types.Transaction) { + appendSig := func(key types.PrivateKey, pubkeyIndex uint64, parentID types.Hash256) { + sig := key.SignHash(cs.WholeSigHash(*txn, parentID, pubkeyIndex, 0, nil)) + txn.Signatures = append(txn.Signatures, types.TransactionSignature{ + ParentID: parentID, + CoveredFields: types.CoveredFields{WholeTransaction: true}, + PublicKeyIndex: pubkeyIndex, + Signature: sig[:], + }) + } + for i := range txn.SiacoinInputs { + appendSig(pk, 0, types.Hash256(txn.SiacoinInputs[i].ParentID)) + } + for i := range txn.SiafundInputs { + appendSig(pk, 0, types.Hash256(txn.SiafundInputs[i].ParentID)) + } +} + func TestBalance(t *testing.T) { log := zaptest.NewLogger(t) dir := t.TempDir() @@ -98,7 +121,7 @@ func TestBalance(t *testing.T) { } defer bdb.Close() - network, genesisBlock := testV1Network(types.VoidAddress) + network, genesisBlock := testV1Network(types.VoidAddress, types.ZeroCurrency, 0) store, genesisState, err := chain.NewDBStore(bdb, network, genesisBlock) if err != nil { @@ -177,17 +200,8 @@ func TestBalance(t *testing.T) { {Address: addr1, Value: types.Siacoins(100)}, {Address: addr2, Value: utxos[0].SiacoinOutput.Value.Sub(types.Siacoins(100))}, }, - Signatures: []types.TransactionSignature{ - { - ParentID: utxos[0].ID, - PublicKeyIndex: 0, - CoveredFields: types.CoveredFields{WholeTransaction: true}, - }, - }, } - parentSigHash := cm.TipState().WholeSigHash(parentTxn, utxos[0].ID, 0, 0, nil) - parentSig := pk1.SignHash(parentSigHash) - parentTxn.Signatures[0].Signature = parentSig[:] + signTxn(cm.TipState(), pk1, &parentTxn) // In the same block, have addr1 send the 100 SC it still has left to // addr3 @@ -202,17 +216,8 @@ func TestBalance(t *testing.T) { SiacoinOutputs: []types.SiacoinOutput{ {Address: addr3, Value: types.Siacoins(100)}, }, - Signatures: []types.TransactionSignature{ - { - ParentID: types.Hash256(outputID), - PublicKeyIndex: 0, - CoveredFields: types.CoveredFields{WholeTransaction: true}, - }, - }, } - sigHash := cm.TipState().WholeSigHash(txn, types.Hash256(outputID), 0, 0, nil) - sig := pk1.SignHash(sigHash) - txn.Signatures[0].Signature = sig[:] + signTxn(cm.TipState(), pk1, &txn) if err := cm.AddBlocks([]types.Block{mineBlock(cm.TipState(), []types.Transaction{parentTxn, txn}, types.VoidAddress)}); err != nil { t.Fatal(err) @@ -247,7 +252,8 @@ func TestSendTransactions(t *testing.T) { pk3 := types.GeneratePrivateKey() addr3 := types.StandardUnlockHash(pk3.PublicKey()) - network, genesisBlock := testV1Network(addr1) + const giftSF = 10000 + network, genesisBlock := testV1Network(addr1, types.ZeroCurrency, giftSF) store, genesisState, err := chain.NewDBStore(bdb, network, genesisBlock) if err != nil { @@ -392,30 +398,9 @@ func TestSendTransactions(t *testing.T) { {Address: addr3, Value: 2}, {Address: addr1, Value: addr1SFs}, }, - Signatures: []types.TransactionSignature{ - { - ParentID: scOutputID, - PublicKeyIndex: 0, - CoveredFields: types.CoveredFields{WholeTransaction: true}, - }, - { - ParentID: types.Hash256(sfOutputID), - PublicKeyIndex: 0, - CoveredFields: types.CoveredFields{WholeTransaction: true}, - }, - }, } - { - parentSigHash := cm.TipState().WholeSigHash(parentTxn, scOutputID, 0, 0, nil) - parentSig := pk1.SignHash(parentSigHash) - parentTxn.Signatures[0].Signature = parentSig[:] - } - { - parentSigHash := cm.TipState().WholeSigHash(parentTxn, types.Hash256(sfOutputID), 0, 0, nil) - parentSig := pk1.SignHash(parentSigHash) - parentTxn.Signatures[1].Signature = parentSig[:] - } + signTxn(cm.TipState(), pk1, &parentTxn) scOutputID = types.Hash256(parentTxn.SiacoinOutputID(2)) sfOutputID = parentTxn.SiafundOutputID(2) @@ -534,7 +519,7 @@ func TestTip(t *testing.T) { } defer bdb.Close() - network, genesisBlock := testV1Network(types.VoidAddress) + network, genesisBlock := testV1Network(types.VoidAddress, types.ZeroCurrency, 0) store, genesisState, err := chain.NewDBStore(bdb, network, genesisBlock) if err != nil { From f7c4018edd0412b4608b5d8e4360215a6f573b61 Mon Sep 17 00:00:00 2001 From: Christopher Tarry Date: Mon, 25 Mar 2024 17:00:41 -0400 Subject: [PATCH 2/8] add file contract test --- persist/sqlite/consensus_test.go | 134 +++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/persist/sqlite/consensus_test.go b/persist/sqlite/consensus_test.go index 3bc4b523..3edbd2bf 100644 --- a/persist/sqlite/consensus_test.go +++ b/persist/sqlite/consensus_test.go @@ -1,6 +1,7 @@ package sqlite_test import ( + "math/bits" "path/filepath" "reflect" "testing" @@ -558,3 +559,136 @@ func TestTip(t *testing.T) { } } } + +// copied from rhp/v2 to avoid import cycle +func prepareContractFormation(renterPubKey types.PublicKey, hostKey types.PublicKey, renterPayout, hostCollateral types.Currency, endHeight uint64, windowSize uint64, refundAddr types.Address) types.FileContract { + taxAdjustedPayout := func(target types.Currency) types.Currency { + guess := target.Mul64(1000).Div64(961) + mod64 := func(c types.Currency, v uint64) types.Currency { + var r uint64 + if c.Hi < v { + _, r = bits.Div64(c.Hi, c.Lo, v) + } else { + _, r = bits.Div64(0, c.Hi, v) + _, r = bits.Div64(r, c.Lo, v) + } + return types.NewCurrency64(r) + } + sfc := (consensus.State{}).SiafundCount() + tm := mod64(target, sfc) + gm := mod64(guess, sfc) + if gm.Cmp(tm) < 0 { + guess = guess.Sub(types.NewCurrency64(sfc)) + } + return guess.Add(tm).Sub(gm) + } + uc := types.UnlockConditions{ + PublicKeys: []types.UnlockKey{ + {Algorithm: types.SpecifierEd25519, Key: renterPubKey[:]}, + {Algorithm: types.SpecifierEd25519, Key: hostKey[:]}, + }, + SignaturesRequired: 2, + } + hostPayout := hostCollateral + payout := taxAdjustedPayout(renterPayout.Add(hostPayout)) + return types.FileContract{ + Filesize: 0, + FileMerkleRoot: types.Hash256{}, + WindowStart: endHeight, + WindowEnd: endHeight + windowSize, + Payout: payout, + UnlockHash: types.Hash256(uc.UnlockHash()), + RevisionNumber: 0, + ValidProofOutputs: []types.SiacoinOutput{ + {Value: renterPayout, Address: refundAddr}, + {Value: hostPayout, Address: types.VoidAddress}, + }, + MissedProofOutputs: []types.SiacoinOutput{ + {Value: renterPayout, Address: refundAddr}, + {Value: hostPayout, Address: types.VoidAddress}, + {Value: types.ZeroCurrency, Address: types.VoidAddress}, + }, + } +} + +func TestFileContract(t *testing.T) { + log := zaptest.NewLogger(t) + dir := t.TempDir() + + db, err := sqlite.OpenDatabase(filepath.Join(dir, "explored.sqlite3"), log.Named("sqlite3")) + if err != nil { + t.Fatal(err) + } + defer db.Close() + + bdb, err := coreutils.OpenBoltChainDB(filepath.Join(dir, "consensus.db")) + if err != nil { + t.Fatal(err) + } + defer bdb.Close() + + pk1 := types.GeneratePrivateKey() + addr1 := types.StandardUnlockHash(pk1.PublicKey()) + + renterPrivateKey := types.GeneratePrivateKey() + renterPublicKey := renterPrivateKey.PublicKey() + + hostPrivateKey := types.GeneratePrivateKey() + hostPublicKey := hostPrivateKey.PublicKey() + + giftSC := types.Siacoins(1000) + network, genesisBlock := testV1Network(addr1, giftSC, 0) + store, genesisState, err := chain.NewDBStore(bdb, network, genesisBlock) + if err != nil { + t.Fatal(err) + } + defer store.Close() + + cm := chain.NewManager(store, genesisState) + // if err := cm.AddSubscriber(db, types.ChainIndex{}); err != nil { + // t.Fatal(err) + // } + + scOutputID := genesisBlock.Transactions[0].SiacoinOutputID(0) + unlockConditions := types.StandardUnlockConditions(pk1.PublicKey()) + + signTxn := func(cs consensus.State, txn *types.Transaction) { + appendSig := func(key types.PrivateKey, pubkeyIndex uint64, parentID types.Hash256) { + sig := key.SignHash(cs.WholeSigHash(*txn, parentID, pubkeyIndex, 0, nil)) + txn.Signatures = append(txn.Signatures, types.TransactionSignature{ + ParentID: parentID, + CoveredFields: types.CoveredFields{WholeTransaction: true}, + PublicKeyIndex: pubkeyIndex, + Signature: sig[:], + }) + } + for i := range txn.SiacoinInputs { + appendSig(pk1, 0, types.Hash256(txn.SiacoinInputs[i].ParentID)) + } + for i := range txn.SiafundInputs { + appendSig(pk1, 0, types.Hash256(txn.SiafundInputs[i].ParentID)) + } + for i := range txn.FileContractRevisions { + appendSig(renterPrivateKey, 0, types.Hash256(txn.FileContractRevisions[i].ParentID)) + appendSig(hostPrivateKey, 1, types.Hash256(txn.FileContractRevisions[i].ParentID)) + } + } + + fc := prepareContractFormation(renterPublicKey, hostPublicKey, types.Siacoins(1), types.Siacoins(1), cm.Tip().Height+1, 100, types.VoidAddress) + txn := types.Transaction{ + SiacoinInputs: []types.SiacoinInput{{ + ParentID: scOutputID, + UnlockConditions: unlockConditions, + }}, + SiacoinOutputs: []types.SiacoinOutput{{ + Address: addr1, + Value: giftSC.Sub(fc.Payout), + }}, + FileContracts: []types.FileContract{fc}, + } + signTxn(cm.TipState(), &txn) + + if err := cm.AddBlocks([]types.Block{mineBlock(cm.TipState(), []types.Transaction{txn}, types.VoidAddress)}); err != nil { + t.Fatal(err) + } +} From 3b47a3039902d9989dde007569ed44cf5b795012 Mon Sep 17 00:00:00 2001 From: Christopher Tarry Date: Tue, 26 Mar 2024 19:35:46 -0400 Subject: [PATCH 3/8] fix matured outputs height calculation bug --- persist/sqlite/consensus.go | 2 -- persist/sqlite/consensus_test.go | 10 +++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/persist/sqlite/consensus.go b/persist/sqlite/consensus.go index a4d358be..2f5b2ebc 100644 --- a/persist/sqlite/consensus.go +++ b/persist/sqlite/consensus.go @@ -371,8 +371,6 @@ func (s *Store) updateMaturedBalances(dbTxn txn, update consensusUpdate, height _, isRevert := update.(*chain.RevertUpdate) if isRevert { height++ - } else { - height-- } rows, err := dbTxn.Query(`SELECT address, value diff --git a/persist/sqlite/consensus_test.go b/persist/sqlite/consensus_test.go index 3edbd2bf..d011fbf8 100644 --- a/persist/sqlite/consensus_test.go +++ b/persist/sqlite/consensus_test.go @@ -161,7 +161,7 @@ func TestBalance(t *testing.T) { addr3 := types.StandardUnlockHash(pk3.PublicKey()) expectedPayout := cm.TipState().BlockReward() - maturityHeight := cm.TipState().MaturityHeight() + 1 + maturityHeight := cm.TipState().MaturityHeight() // Mine a block sending the payout to addr1 if err := cm.AddBlocks([]types.Block{mineBlock(cm.TipState(), nil, addr1)}); err != nil { @@ -336,7 +336,7 @@ func TestSendTransactions(t *testing.T) { } expectedPayout := cm.TipState().BlockReward() - maturityHeight := cm.TipState().MaturityHeight() + 1 + maturityHeight := cm.TipState().MaturityHeight() // Mine a block sending the payout to the addr1 if err := cm.AddBlocks([]types.Block{mineBlock(cm.TipState(), nil, addr1)}); err != nil { @@ -645,9 +645,9 @@ func TestFileContract(t *testing.T) { defer store.Close() cm := chain.NewManager(store, genesisState) - // if err := cm.AddSubscriber(db, types.ChainIndex{}); err != nil { - // t.Fatal(err) - // } + if err := cm.AddSubscriber(db, types.ChainIndex{}); err != nil { + t.Fatal(err) + } scOutputID := genesisBlock.Transactions[0].SiacoinOutputID(0) unlockConditions := types.StandardUnlockConditions(pk1.PublicKey()) From 211fd670261db45854f289a695adcb997caa5bc4 Mon Sep 17 00:00:00 2001 From: Christopher Tarry Date: Tue, 26 Mar 2024 21:43:09 -0400 Subject: [PATCH 4/8] test contract retrieved from database --- persist/sqlite/consensus_test.go | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/persist/sqlite/consensus_test.go b/persist/sqlite/consensus_test.go index d011fbf8..c97929df 100644 --- a/persist/sqlite/consensus_test.go +++ b/persist/sqlite/consensus_test.go @@ -107,6 +107,12 @@ func signTxn(cs consensus.State, pk types.PrivateKey, txn *types.Transaction) { } } +func check(t *testing.T, desc string, expect, got any) { + if expect != got { + t.Fatalf("expected %v %s, got %v", expect, desc, got) + } +} + func TestBalance(t *testing.T) { log := zaptest.NewLogger(t) dir := t.TempDir() @@ -691,4 +697,31 @@ func TestFileContract(t *testing.T) { if err := cm.AddBlocks([]types.Block{mineBlock(cm.TipState(), []types.Transaction{txn}, types.VoidAddress)}); err != nil { t.Fatal(err) } + + dbFCs, err := db.Contracts([]types.FileContractID{txn.FileContractID(0)}) + if err != nil { + t.Fatal(err) + } + check(t, "fcs", 1, len(dbFCs)) + + dbFC := dbFCs[0] + check(t, "resolved state", false, dbFC.Resolved) + check(t, "valid state", true, dbFC.Valid) + check(t, "filesize", fc.Filesize, dbFC.Filesize) + check(t, "file merkle root", fc.FileMerkleRoot, dbFC.FileMerkleRoot) + check(t, "window start", fc.WindowStart, dbFC.WindowStart) + check(t, "window end", fc.WindowEnd, dbFC.WindowEnd) + check(t, "payout", fc.Payout, dbFC.Payout) + check(t, "unlock hash", fc.UnlockHash, dbFC.UnlockHash) + check(t, "revision number", fc.RevisionNumber, dbFC.RevisionNumber) + check(t, "valid proof outputs", len(fc.ValidProofOutputs), len(dbFC.ValidProofOutputs)) + for i := range fc.ValidProofOutputs { + check(t, "valid proof output address", fc.ValidProofOutputs[i].Address, dbFC.ValidProofOutputs[i].Address) + check(t, "valid proof output value", fc.ValidProofOutputs[i].Value, dbFC.ValidProofOutputs[i].Value) + } + check(t, "missed proof outputs", len(fc.MissedProofOutputs), len(dbFC.MissedProofOutputs)) + for i := range fc.MissedProofOutputs { + check(t, "missed proof output address", fc.MissedProofOutputs[i].Address, dbFC.MissedProofOutputs[i].Address) + check(t, "missed proof output value", fc.MissedProofOutputs[i].Value, dbFC.MissedProofOutputs[i].Value) + } } From 715ff5cc23923e7226f1e9117b67cbee1e384c1a Mon Sep 17 00:00:00 2001 From: Christopher Tarry Date: Wed, 27 Mar 2024 12:48:17 -0400 Subject: [PATCH 5/8] use check function to compare values --- persist/sqlite/consensus_test.go | 191 ++++++++++++------------------- 1 file changed, 75 insertions(+), 116 deletions(-) diff --git a/persist/sqlite/consensus_test.go b/persist/sqlite/consensus_test.go index c97929df..4919acda 100644 --- a/persist/sqlite/consensus_test.go +++ b/persist/sqlite/consensus_test.go @@ -147,13 +147,10 @@ func TestBalance(t *testing.T) { sc, immatureSC, sf, err := db.Balance(addr) if err != nil { t.Fatal(err) - } else if sc != expectSC { - t.Fatalf("expected %v siacoins, got %v", expectSC, sc) - } else if immatureSC != expectImmatureSC { - t.Fatalf("expected %v immature siacoins, got %v", expectImmatureSC, immatureSC) - } else if sf != expectSF { - t.Fatalf("expected %d siafunds, got %d", expectSF, sf) } + check(t, "siacoins", expectSC, sc) + check(t, "immature siacoins", expectImmatureSC, immatureSC) + check(t, "siafunds", expectSF, sf) } // Generate three addresses: addr1, addr2, addr3 @@ -178,11 +175,10 @@ func TestBalance(t *testing.T) { utxos, err := db.UnspentSiacoinOutputs(addr1, 100, 0) if err != nil { t.Fatal(err) - } else if len(utxos) != 1 { - t.Fatalf("expected 1 utxo, got %d", len(utxos)) - } else if utxos[0].SiacoinOutput.Value != expectedPayout { - t.Fatalf("expected value %v, got %v", expectedPayout, utxos[0].SiacoinOutput.Value) } + check(t, "utxos", 1, len(utxos)) + check(t, "value", expectedPayout, utxos[0].SiacoinOutput.Value) + check(t, "source", explorer.SourceMinerPayout, utxos[0].Source) // Mine until the payout matures for i := cm.TipState().Index.Height; i < maturityHeight; i++ { @@ -279,65 +275,51 @@ func TestSendTransactions(t *testing.T) { sc, immatureSC, sf, err := db.Balance(addr) if err != nil { t.Fatal(err) - } else if sc != expectSC { - t.Fatalf("expected %v siacoins, got %v", expectSC, sc) - } else if immatureSC != expectImmatureSC { - t.Fatalf("expected %v immature siacoins, got %v", expectImmatureSC, immatureSC) - } else if sf != expectSF { - t.Fatalf("expected %d siafunds, got %d", expectSF, sf) } + check(t, "siacoins", expectSC, sc) + check(t, "immature siacoins", expectImmatureSC, immatureSC) + check(t, "siafunds", expectSF, sf) } checkTransaction := func(expectTxn types.Transaction, gotTxn explorer.Transaction) { - if len(expectTxn.SiacoinInputs) != len(gotTxn.SiacoinInputs) { - t.Fatalf("expected %d siacoin inputs, got %d", len(expectTxn.SiacoinInputs), len(gotTxn.SiacoinInputs)) - } else if len(expectTxn.SiacoinOutputs) != len(gotTxn.SiacoinOutputs) { - t.Fatalf("expected %d siacoin outputs, got %d", len(expectTxn.SiacoinOutputs), len(gotTxn.SiacoinOutputs)) - } else if len(expectTxn.SiafundInputs) != len(gotTxn.SiafundInputs) { - t.Fatalf("expected %d siafund inputs, got %d", len(expectTxn.SiafundInputs), len(gotTxn.SiafundInputs)) - } else if len(expectTxn.SiafundOutputs) != len(gotTxn.SiafundOutputs) { - t.Fatalf("expected %d siafund outputs, got %d", len(expectTxn.SiafundOutputs), len(gotTxn.SiafundOutputs)) - } + check(t, "siacoin inputs", len(expectTxn.SiacoinInputs), len(gotTxn.SiacoinInputs)) + check(t, "siacoin outputs", len(expectTxn.SiacoinOutputs), len(gotTxn.SiacoinOutputs)) + check(t, "siafund inputs", len(expectTxn.SiafundInputs), len(gotTxn.SiafundInputs)) + check(t, "siafund outputs", len(expectTxn.SiafundOutputs), len(gotTxn.SiafundOutputs)) for i := range expectTxn.SiacoinInputs { expectSci := expectTxn.SiacoinInputs[i] gotSci := gotTxn.SiacoinInputs[i] - if expectSci.ParentID != gotSci.ParentID { - t.Fatalf("expected parent ID %v, got %v", expectSci.ParentID, gotSci.ParentID) - } else if !reflect.DeepEqual(expectSci.UnlockConditions, gotSci.UnlockConditions) { + + check(t, "parent ID", expectSci.ParentID, gotSci.ParentID) + if !reflect.DeepEqual(expectSci.UnlockConditions, gotSci.UnlockConditions) { t.Fatalf("expected unlock conditions %v, got %v", expectSci.UnlockConditions, gotSci.UnlockConditions) } } for i := range expectTxn.SiacoinOutputs { expectSco := expectTxn.SiacoinOutputs[i] gotSco := gotTxn.SiacoinOutputs[i].SiacoinOutput - if expectSco.Address != gotSco.Address { - t.Fatalf("expected address %v, got %v", expectSco.Address, gotSco.Address) - } else if expectSco.Value != gotSco.Value { - t.Fatalf("expected value %v, got %v", expectSco.Value, gotSco.Value) - } else if gotTxn.SiacoinOutputs[i].Source != explorer.SourceTransaction { - t.Fatalf("expected source %v, got %v", explorer.SourceTransaction, gotTxn.SiacoinOutputs[i].Source) - } + + check(t, "address", expectSco.Address, gotSco.Address) + check(t, "value", expectSco.Value, gotSco.Value) + check(t, "source", explorer.SourceTransaction, gotTxn.SiacoinOutputs[i].Source) } for i := range expectTxn.SiafundInputs { expectSfi := expectTxn.SiafundInputs[i] gotSfi := gotTxn.SiafundInputs[i] - if expectSfi.ParentID != gotSfi.ParentID { - t.Fatalf("expected parent ID %v, got %v", expectSfi.ParentID, gotSfi.ParentID) - } else if expectSfi.ClaimAddress != gotSfi.ClaimAddress { - t.Fatalf("expected claim address %v, got %v", expectSfi.ClaimAddress, gotSfi.ClaimAddress) - } else if !reflect.DeepEqual(expectSfi.UnlockConditions, gotSfi.UnlockConditions) { + + check(t, "parent ID", expectSfi.ParentID, gotSfi.ParentID) + check(t, "claim address", expectSfi.ClaimAddress, gotSfi.ClaimAddress) + if !reflect.DeepEqual(expectSfi.UnlockConditions, gotSfi.UnlockConditions) { t.Fatalf("expected unlock conditions %v, got %v", expectSfi.UnlockConditions, gotSfi.UnlockConditions) } } for i := range expectTxn.SiafundOutputs { expectSfo := expectTxn.SiafundOutputs[i] gotSfo := gotTxn.SiafundOutputs[i].SiafundOutput - if expectSfo.Address != gotSfo.Address { - t.Fatalf("expected address %v, got %v", expectSfo.Address, gotSfo.Address) - } else if expectSfo.Value != gotSfo.Value { - t.Fatalf("expected value %v, got %v", expectSfo.Value, gotSfo.Value) - } + + check(t, "address", expectSfo.Address, gotSfo.Address) + check(t, "value", expectSfo.Value, gotSfo.Value) } } @@ -366,13 +348,10 @@ func TestSendTransactions(t *testing.T) { utxos, err := db.UnspentSiacoinOutputs(addr1, n, 0) if err != nil { t.Fatal(err) - } else if len(utxos) != 1 { - t.Fatalf("expected 1 utxo, got %d", len(utxos)) - } else if utxos[0].SiacoinOutput.Value != expectedPayout { - t.Fatalf("expected value %v, got %v", expectedPayout, utxos[0].SiacoinOutput.Value) - } else if utxos[0].Source != explorer.SourceMinerPayout { - t.Fatalf("expected source %v, got %v", explorer.SourceMinerPayout, utxos[0].Source) } + check(t, "utxos", 1, len(utxos)) + check(t, "value", expectedPayout, utxos[0].SiacoinOutput.Value) + check(t, "source", explorer.SourceMinerPayout, utxos[0].Source) sfOutputID := genesisBlock.Transactions[0].SiafundOutputID(0) scOutputID := utxos[0].ID @@ -426,25 +405,16 @@ func TestSendTransactions(t *testing.T) { block, err := db.Block(b.ID()) if err != nil { t.Fatal(err) - } else if len(b.Transactions) != len(block.Transactions) { - t.Fatalf("expected %d transactions, got %d", len(b.Transactions), len(block.Transactions)) - } else if b.Nonce != block.Nonce { - t.Fatalf("expected nonce %d, got %d", b.Nonce, block.Nonce) - } else if b.Timestamp != block.Timestamp { - t.Fatalf("expected timestamp %d, got %d", b.Timestamp.Unix(), block.Timestamp.Unix()) - } else if len(b.MinerPayouts) != len(block.MinerPayouts) { - t.Fatalf("expected %d miner payouts, got %d", len(b.MinerPayouts), len(block.MinerPayouts)) - } else if len(b.Transactions) != len(block.Transactions) { - t.Fatalf("expected %d transactions, got %d", len(b.Transactions), len(block.Transactions)) } + check(t, "transactions", len(b.Transactions), len(block.Transactions)) + check(t, "miner payouts", len(b.MinerPayouts), len(block.MinerPayouts)) + check(t, "nonce", b.Nonce, block.Nonce) + check(t, "timestamp", b.Timestamp, block.Timestamp) // Ensure the miner payouts in the block match for i := range b.MinerPayouts { - if b.MinerPayouts[i].Address != block.MinerPayouts[i].SiacoinOutput.Address { - t.Fatalf("expected address %v, got %v", b.MinerPayouts[i].Address, block.MinerPayouts[i].SiacoinOutput.Address) - } else if b.MinerPayouts[i].Value != block.MinerPayouts[i].SiacoinOutput.Value { - t.Fatalf("expected value %v, got %v", b.MinerPayouts[i].Value, block.MinerPayouts[i].SiacoinOutput.Value) - } + check(t, "address", b.MinerPayouts[i].Address, b.MinerPayouts[i].Address) + check(t, "value", b.MinerPayouts[i].Value, b.MinerPayouts[i].Value) } // Ensure the transactions in the block and retrieved separately match @@ -455,9 +425,8 @@ func TestSendTransactions(t *testing.T) { txns, err := db.Transactions([]types.TransactionID{b.Transactions[i].ID()}) if err != nil { t.Fatal(err) - } else if len(txns) != 1 { - t.Fatal("failed to get transaction") } + check(t, "transactions", 1, len(txns)) checkTransaction(b.Transactions[i], txns[0]) } @@ -485,27 +454,17 @@ func TestSendTransactions(t *testing.T) { t.Fatal(err) } - if e.sc != len(sc) { - t.Fatalf("expected %d siacoin utxos, got %d", e.sc, len(sc)) - } else if e.sf != len(sf) { - t.Fatalf("expected %d siafund utxos, got %d", e.sf, len(sf)) - } + check(t, "sc utxos", e.sc, len(sc)) + check(t, "sf utxos", e.sf, len(sf)) for _, sco := range sc { - if e.addr != sco.SiacoinOutput.Address { - t.Fatalf("expected address %v, got %v", e.addr, sco.SiacoinOutput.Address) - } else if e.scValue != sco.SiacoinOutput.Value { - t.Fatalf("expected value %v, got %v", e.scValue, sco.SiacoinOutput.Value) - } else if explorer.SourceTransaction != sco.Source { - t.Fatalf("expected source %v, got %v", explorer.SourceTransaction, sco.Source) - } + check(t, "address", e.addr, sco.SiacoinOutput.Address) + check(t, "value", e.scValue, sco.SiacoinOutput.Value) + check(t, "source", explorer.SourceTransaction, sco.Source) } for _, sfo := range sf { - if e.addr != sfo.SiafundOutput.Address { - t.Fatalf("expected address %v, got %v", e.addr, sfo.SiafundOutput.Address) - } else if e.sfValue != sfo.SiafundOutput.Value { - t.Fatalf("expected value %v, got %v", e.sfValue, sfo.SiafundOutput.Value) - } + check(t, "address", e.addr, sfo.SiafundOutput.Address) + check(t, "value", e.sfValue, sfo.SiafundOutput.Value) } } } @@ -550,9 +509,7 @@ func TestTip(t *testing.T) { if err != nil { t.Fatal(err) } - if cm.Tip() != tip { - t.Fatal("tip mismatch") - } + check(t, "tip", cm.Tip(), tip) } for i := 0; i < n; i++ { @@ -658,9 +615,9 @@ func TestFileContract(t *testing.T) { scOutputID := genesisBlock.Transactions[0].SiacoinOutputID(0) unlockConditions := types.StandardUnlockConditions(pk1.PublicKey()) - signTxn := func(cs consensus.State, txn *types.Transaction) { + signTxn := func(txn *types.Transaction) { appendSig := func(key types.PrivateKey, pubkeyIndex uint64, parentID types.Hash256) { - sig := key.SignHash(cs.WholeSigHash(*txn, parentID, pubkeyIndex, 0, nil)) + sig := key.SignHash(cm.TipState().WholeSigHash(*txn, parentID, pubkeyIndex, 0, nil)) txn.Signatures = append(txn.Signatures, types.TransactionSignature{ ParentID: parentID, CoveredFields: types.CoveredFields{WholeTransaction: true}, @@ -692,36 +649,38 @@ func TestFileContract(t *testing.T) { }}, FileContracts: []types.FileContract{fc}, } - signTxn(cm.TipState(), &txn) + signTxn(&txn) if err := cm.AddBlocks([]types.Block{mineBlock(cm.TipState(), []types.Transaction{txn}, types.VoidAddress)}); err != nil { t.Fatal(err) } - dbFCs, err := db.Contracts([]types.FileContractID{txn.FileContractID(0)}) - if err != nil { - t.Fatal(err) - } - check(t, "fcs", 1, len(dbFCs)) - - dbFC := dbFCs[0] - check(t, "resolved state", false, dbFC.Resolved) - check(t, "valid state", true, dbFC.Valid) - check(t, "filesize", fc.Filesize, dbFC.Filesize) - check(t, "file merkle root", fc.FileMerkleRoot, dbFC.FileMerkleRoot) - check(t, "window start", fc.WindowStart, dbFC.WindowStart) - check(t, "window end", fc.WindowEnd, dbFC.WindowEnd) - check(t, "payout", fc.Payout, dbFC.Payout) - check(t, "unlock hash", fc.UnlockHash, dbFC.UnlockHash) - check(t, "revision number", fc.RevisionNumber, dbFC.RevisionNumber) - check(t, "valid proof outputs", len(fc.ValidProofOutputs), len(dbFC.ValidProofOutputs)) - for i := range fc.ValidProofOutputs { - check(t, "valid proof output address", fc.ValidProofOutputs[i].Address, dbFC.ValidProofOutputs[i].Address) - check(t, "valid proof output value", fc.ValidProofOutputs[i].Value, dbFC.ValidProofOutputs[i].Value) - } - check(t, "missed proof outputs", len(fc.MissedProofOutputs), len(dbFC.MissedProofOutputs)) - for i := range fc.MissedProofOutputs { - check(t, "missed proof output address", fc.MissedProofOutputs[i].Address, dbFC.MissedProofOutputs[i].Address) - check(t, "missed proof output value", fc.MissedProofOutputs[i].Value, dbFC.MissedProofOutputs[i].Value) + { + dbFCs, err := db.Contracts([]types.FileContractID{txn.FileContractID(0)}) + if err != nil { + t.Fatal(err) + } + check(t, "fcs", 1, len(dbFCs)) + + dbFC := dbFCs[0] + check(t, "resolved state", false, dbFC.Resolved) + check(t, "valid state", true, dbFC.Valid) + check(t, "filesize", fc.Filesize, dbFC.Filesize) + check(t, "file merkle root", fc.FileMerkleRoot, dbFC.FileMerkleRoot) + check(t, "window start", fc.WindowStart, dbFC.WindowStart) + check(t, "window end", fc.WindowEnd, dbFC.WindowEnd) + check(t, "payout", fc.Payout, dbFC.Payout) + check(t, "unlock hash", fc.UnlockHash, dbFC.UnlockHash) + check(t, "revision number", fc.RevisionNumber, dbFC.RevisionNumber) + check(t, "valid proof outputs", len(fc.ValidProofOutputs), len(dbFC.ValidProofOutputs)) + for i := range fc.ValidProofOutputs { + check(t, "valid proof output address", fc.ValidProofOutputs[i].Address, dbFC.ValidProofOutputs[i].Address) + check(t, "valid proof output value", fc.ValidProofOutputs[i].Value, dbFC.ValidProofOutputs[i].Value) + } + check(t, "missed proof outputs", len(fc.MissedProofOutputs), len(dbFC.MissedProofOutputs)) + for i := range fc.MissedProofOutputs { + check(t, "missed proof output address", fc.MissedProofOutputs[i].Address, dbFC.MissedProofOutputs[i].Address) + check(t, "missed proof output value", fc.MissedProofOutputs[i].Value, dbFC.MissedProofOutputs[i].Value) + } } } From 258efc782af8ec2456ab4d332ab0d9e7b6a2fa9e Mon Sep 17 00:00:00 2001 From: Christopher Tarry Date: Wed, 27 Mar 2024 13:48:45 -0400 Subject: [PATCH 6/8] check retrieved file contract from blocks and transactions --- persist/sqlite/consensus_test.go | 58 ++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/persist/sqlite/consensus_test.go b/persist/sqlite/consensus_test.go index 4919acda..513d1bc9 100644 --- a/persist/sqlite/consensus_test.go +++ b/persist/sqlite/consensus_test.go @@ -637,6 +637,28 @@ func TestFileContract(t *testing.T) { } } + checkFC := func(expected types.FileContract, got explorer.FileContract) { + check(t, "resolved state", false, got.Resolved) + check(t, "valid state", true, got.Valid) + check(t, "filesize", expected.Filesize, got.Filesize) + check(t, "file merkle root", expected.FileMerkleRoot, got.FileMerkleRoot) + check(t, "window start", expected.WindowStart, got.WindowStart) + check(t, "window end", expected.WindowEnd, got.WindowEnd) + check(t, "payout", expected.Payout, got.Payout) + check(t, "unlock hash", expected.UnlockHash, got.UnlockHash) + check(t, "revision number", expected.RevisionNumber, got.RevisionNumber) + check(t, "valid proof outputs", len(expected.ValidProofOutputs), len(got.ValidProofOutputs)) + for i := range expected.ValidProofOutputs { + check(t, "valid proof output address", expected.ValidProofOutputs[i].Address, got.ValidProofOutputs[i].Address) + check(t, "valid proof output value", expected.ValidProofOutputs[i].Value, got.ValidProofOutputs[i].Value) + } + check(t, "missed proof outputs", len(expected.MissedProofOutputs), len(got.MissedProofOutputs)) + for i := range expected.MissedProofOutputs { + check(t, "missed proof output address", expected.MissedProofOutputs[i].Address, got.MissedProofOutputs[i].Address) + check(t, "missed proof output value", expected.MissedProofOutputs[i].Value, got.MissedProofOutputs[i].Value) + } + } + fc := prepareContractFormation(renterPublicKey, hostPublicKey, types.Siacoins(1), types.Siacoins(1), cm.Tip().Height+1, 100, types.VoidAddress) txn := types.Transaction{ SiacoinInputs: []types.SiacoinInput{{ @@ -661,26 +683,26 @@ func TestFileContract(t *testing.T) { t.Fatal(err) } check(t, "fcs", 1, len(dbFCs)) + checkFC(fc, dbFCs[0]) + } - dbFC := dbFCs[0] - check(t, "resolved state", false, dbFC.Resolved) - check(t, "valid state", true, dbFC.Valid) - check(t, "filesize", fc.Filesize, dbFC.Filesize) - check(t, "file merkle root", fc.FileMerkleRoot, dbFC.FileMerkleRoot) - check(t, "window start", fc.WindowStart, dbFC.WindowStart) - check(t, "window end", fc.WindowEnd, dbFC.WindowEnd) - check(t, "payout", fc.Payout, dbFC.Payout) - check(t, "unlock hash", fc.UnlockHash, dbFC.UnlockHash) - check(t, "revision number", fc.RevisionNumber, dbFC.RevisionNumber) - check(t, "valid proof outputs", len(fc.ValidProofOutputs), len(dbFC.ValidProofOutputs)) - for i := range fc.ValidProofOutputs { - check(t, "valid proof output address", fc.ValidProofOutputs[i].Address, dbFC.ValidProofOutputs[i].Address) - check(t, "valid proof output value", fc.ValidProofOutputs[i].Value, dbFC.ValidProofOutputs[i].Value) + { + block, err := db.Block(cm.Tip().ID) + if err != nil { + t.Fatal(err) } - check(t, "missed proof outputs", len(fc.MissedProofOutputs), len(dbFC.MissedProofOutputs)) - for i := range fc.MissedProofOutputs { - check(t, "missed proof output address", fc.MissedProofOutputs[i].Address, dbFC.MissedProofOutputs[i].Address) - check(t, "missed proof output value", fc.MissedProofOutputs[i].Value, dbFC.MissedProofOutputs[i].Value) + check(t, "transactions", 1, len(block.Transactions)) + check(t, "file contracts", 1, len(block.Transactions[0].FileContracts)) + checkFC(fc, block.Transactions[0].FileContracts[0]) + } + + { + txns, err := db.Transactions([]types.TransactionID{txn.ID()}) + if err != nil { + t.Fatal(err) } + check(t, "transactions", 1, len(txns)) + check(t, "file contracts", 1, len(txns[0].FileContracts)) + checkFC(fc, txns[0].FileContracts[0]) } } From d425c3f42a3b289819e07045f0b643265f778cc1 Mon Sep 17 00:00:00 2001 From: Christopher Tarry Date: Wed, 27 Mar 2024 14:45:35 -0400 Subject: [PATCH 7/8] test revision --- persist/sqlite/consensus.go | 2 +- persist/sqlite/consensus_test.go | 66 ++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/persist/sqlite/consensus.go b/persist/sqlite/consensus.go index 2f5b2ebc..ec7e0bd7 100644 --- a/persist/sqlite/consensus.go +++ b/persist/sqlite/consensus.go @@ -191,7 +191,7 @@ func (s *Store) addFileContractRevisions(dbTxn txn, id int64, txn types.Transact return errors.New("addFileContractRevisions: dbID not in map") } - if _, err := stmt.Exec(id, i, dbID, dbEncode(fcr.UnlockConditions), dbEncode(fcr.UnlockHash)); err != nil { + if _, err := stmt.Exec(id, i, dbID, dbEncode(fcr.ParentID), dbEncode(fcr.UnlockConditions)); err != nil { return fmt.Errorf("addFileContractRevisions: failed to execute statement: %w", err) } diff --git a/persist/sqlite/consensus_test.go b/persist/sqlite/consensus_test.go index 513d1bc9..f0dcae33 100644 --- a/persist/sqlite/consensus_test.go +++ b/persist/sqlite/consensus_test.go @@ -108,7 +108,7 @@ func signTxn(cs consensus.State, pk types.PrivateKey, txn *types.Transaction) { } func check(t *testing.T, desc string, expect, got any) { - if expect != got { + if !reflect.DeepEqual(expect, got) { t.Fatalf("expected %v %s, got %v", expect, desc, got) } } @@ -292,9 +292,7 @@ func TestSendTransactions(t *testing.T) { gotSci := gotTxn.SiacoinInputs[i] check(t, "parent ID", expectSci.ParentID, gotSci.ParentID) - if !reflect.DeepEqual(expectSci.UnlockConditions, gotSci.UnlockConditions) { - t.Fatalf("expected unlock conditions %v, got %v", expectSci.UnlockConditions, gotSci.UnlockConditions) - } + check(t, "unlock conditions", expectSci.UnlockConditions, gotSci.UnlockConditions) } for i := range expectTxn.SiacoinOutputs { expectSco := expectTxn.SiacoinOutputs[i] @@ -310,9 +308,7 @@ func TestSendTransactions(t *testing.T) { check(t, "parent ID", expectSfi.ParentID, gotSfi.ParentID) check(t, "claim address", expectSfi.ClaimAddress, gotSfi.ClaimAddress) - if !reflect.DeepEqual(expectSfi.UnlockConditions, gotSfi.UnlockConditions) { - t.Fatalf("expected unlock conditions %v, got %v", expectSfi.UnlockConditions, gotSfi.UnlockConditions) - } + check(t, "unlock conditions", expectSfi.UnlockConditions, gotSfi.UnlockConditions) } for i := range expectTxn.SiafundOutputs { expectSfo := expectTxn.SiafundOutputs[i] @@ -547,8 +543,8 @@ func prepareContractFormation(renterPubKey types.PublicKey, hostKey types.Public } uc := types.UnlockConditions{ PublicKeys: []types.UnlockKey{ - {Algorithm: types.SpecifierEd25519, Key: renterPubKey[:]}, - {Algorithm: types.SpecifierEd25519, Key: hostKey[:]}, + renterPubKey.UnlockKey(), + hostKey.UnlockKey(), }, SignaturesRequired: 2, } @@ -659,7 +655,7 @@ func TestFileContract(t *testing.T) { } } - fc := prepareContractFormation(renterPublicKey, hostPublicKey, types.Siacoins(1), types.Siacoins(1), cm.Tip().Height+1, 100, types.VoidAddress) + fc := prepareContractFormation(renterPublicKey, hostPublicKey, types.Siacoins(1), types.Siacoins(1), cm.Tip().Height+10, 100, types.VoidAddress) txn := types.Transaction{ SiacoinInputs: []types.SiacoinInput{{ ParentID: scOutputID, @@ -687,22 +683,58 @@ func TestFileContract(t *testing.T) { } { - block, err := db.Block(cm.Tip().ID) + txns, err := db.Transactions([]types.TransactionID{txn.ID()}) if err != nil { t.Fatal(err) } - check(t, "transactions", 1, len(block.Transactions)) - check(t, "file contracts", 1, len(block.Transactions[0].FileContracts)) - checkFC(fc, block.Transactions[0].FileContracts[0]) + check(t, "transactions", 1, len(txns)) + check(t, "file contracts", 1, len(txns[0].FileContracts)) + checkFC(fc, txns[0].FileContracts[0]) + } + + uc := types.UnlockConditions{ + PublicKeys: []types.UnlockKey{ + renterPublicKey.UnlockKey(), + hostPublicKey.UnlockKey(), + }, + SignaturesRequired: 2, } + fc.RevisionNumber++ + reviseTxn := types.Transaction{ + FileContractRevisions: []types.FileContractRevision{{ + ParentID: txn.FileContractID(0), + UnlockConditions: uc, + FileContract: fc, + }}, + } + signTxn(&reviseTxn) + if err := cm.AddBlocks([]types.Block{mineBlock(cm.TipState(), []types.Transaction{reviseTxn}, types.VoidAddress)}); err != nil { + t.Fatal(err) + } + + // Explorer.Contracts should return latest revision { - txns, err := db.Transactions([]types.TransactionID{txn.ID()}) + dbFCs, err := db.Contracts([]types.FileContractID{txn.FileContractID(0)}) + if err != nil { + t.Fatal(err) + } + check(t, "fcs", 1, len(dbFCs)) + checkFC(fc, dbFCs[0]) + } + + { + txns, err := db.Transactions([]types.TransactionID{reviseTxn.ID()}) if err != nil { t.Fatal(err) } check(t, "transactions", 1, len(txns)) - check(t, "file contracts", 1, len(txns[0].FileContracts)) - checkFC(fc, txns[0].FileContracts[0]) + check(t, "file contracts", 1, len(txns[0].FileContractRevisions)) + + fcr := txns[0].FileContractRevisions[0] + check(t, "parent id", txn.FileContractID(0), fcr.ParentID) + check(t, "unlock conditions", uc, fcr.UnlockConditions) + + checkFC(fc, fcr.FileContract) } } From 5d0bf011e06113ccd5c770cd0a39a842dfdafad7 Mon Sep 17 00:00:00 2001 From: Christopher Tarry Date: Wed, 27 Mar 2024 15:43:55 -0400 Subject: [PATCH 8/8] test contract resolution and fix bug when updating resolved/valid status of file contract --- persist/sqlite/consensus.go | 14 ++++------ persist/sqlite/consensus_test.go | 46 ++++++++++++++++++++++---------- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/persist/sqlite/consensus.go b/persist/sqlite/consensus.go index ec7e0bd7..5a7d4d1c 100644 --- a/persist/sqlite/consensus.go +++ b/persist/sqlite/consensus.go @@ -544,7 +544,8 @@ func (s *Store) addFileContractElements(dbTxn txn, bid types.BlockID, update con stmt, err := dbTxn.Prepare(`INSERT INTO file_contract_elements(block_id, contract_id, leaf_index, merkle_proof, resolved, valid, filesize, file_merkle_root, window_start, window_end, payout, unlock_hash, revision_number) VALUES (?, ?, ?, ?, FALSE, TRUE, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT (contract_id, revision_number) - DO UPDATE SET resolved = ? AND valid = ?`) + DO UPDATE SET resolved = ?, valid = ? + RETURNING id;`) if err != nil { return nil, fmt.Errorf("addFileContractElements: failed to prepare file_contract_elements statement: %w", err) } @@ -552,7 +553,7 @@ func (s *Store) addFileContractElements(dbTxn txn, bid types.BlockID, update con revisionStmt, err := dbTxn.Prepare(`INSERT INTO last_contract_revision(contract_id, contract_element_id) VALUES (?, ?) - ON CONFLICT + ON CONFLICT (contract_id) DO UPDATE SET contract_element_id = ?`) if err != nil { return nil, fmt.Errorf("addFileContractElements: failed to prepare last_contract_revision statement: %w", err) @@ -570,18 +571,13 @@ func (s *Store) addFileContractElements(dbTxn txn, bid types.BlockID, update con fc = &rev.FileContract } - result, err := stmt.Exec(dbEncode(bid), dbEncode(fce.StateElement.ID), dbEncode(fce.StateElement.LeafIndex), dbEncode(fce.StateElement.MerkleProof), fc.Filesize, dbEncode(fc.FileMerkleRoot), fc.WindowStart, fc.WindowEnd, dbEncode(fc.Payout), dbEncode(fc.UnlockHash), fc.RevisionNumber, resolved, valid) + var dbID int64 + err := stmt.QueryRow(dbEncode(bid), dbEncode(fce.StateElement.ID), dbEncode(fce.StateElement.LeafIndex), dbEncode(fce.StateElement.MerkleProof), fc.Filesize, dbEncode(fc.FileMerkleRoot), fc.WindowStart, fc.WindowEnd, dbEncode(fc.Payout), dbEncode(fc.UnlockHash), fc.RevisionNumber, resolved, valid).Scan(&dbID) if err != nil { updateErr = fmt.Errorf("addFileContractElements: failed to execute file_contract_elements statement: %w", err) return } - dbID, err := result.LastInsertId() - if err != nil { - updateErr = fmt.Errorf("addFileContractElements: failed to get last insert ID: %w", err) - return - } - if _, err := revisionStmt.Exec(dbEncode(fce.StateElement.ID), dbID, dbID); err != nil { updateErr = fmt.Errorf("addFileContractElements: failed to update last revision number: %w", err) return diff --git a/persist/sqlite/consensus_test.go b/persist/sqlite/consensus_test.go index f0dcae33..a86634d5 100644 --- a/persist/sqlite/consensus_test.go +++ b/persist/sqlite/consensus_test.go @@ -181,7 +181,7 @@ func TestBalance(t *testing.T) { check(t, "source", explorer.SourceMinerPayout, utxos[0].Source) // Mine until the payout matures - for i := cm.TipState().Index.Height; i < maturityHeight; i++ { + for i := cm.Tip().Height; i < maturityHeight; i++ { checkBalance(addr1, types.ZeroCurrency, expectedPayout, 0) if err := cm.AddBlocks([]types.Block{mineBlock(cm.TipState(), nil, types.VoidAddress)}); err != nil { t.Fatal(err) @@ -328,7 +328,7 @@ func TestSendTransactions(t *testing.T) { } // Mine until the payout matures - for i := cm.TipState().Index.Height; i < maturityHeight; i++ { + for i := cm.Tip().Height; i < maturityHeight; i++ { if err := cm.AddBlocks([]types.Block{mineBlock(cm.TipState(), nil, types.VoidAddress)}); err != nil { t.Fatal(err) } @@ -496,7 +496,7 @@ func TestTip(t *testing.T) { } const n = 100 - for i := cm.TipState().Index.Height; i < n; i++ { + for i := cm.Tip().Height; i < n; i++ { if err := cm.AddBlocks([]types.Block{mineBlock(cm.TipState(), nil, types.VoidAddress)}); err != nil { t.Fatal(err) } @@ -633,9 +633,9 @@ func TestFileContract(t *testing.T) { } } - checkFC := func(expected types.FileContract, got explorer.FileContract) { - check(t, "resolved state", false, got.Resolved) - check(t, "valid state", true, got.Valid) + checkFC := func(resolved, valid bool, expected types.FileContract, got explorer.FileContract) { + check(t, "resolved state", resolved, got.Resolved) + check(t, "valid state", valid, got.Valid) check(t, "filesize", expected.Filesize, got.Filesize) check(t, "file merkle root", expected.FileMerkleRoot, got.FileMerkleRoot) check(t, "window start", expected.WindowStart, got.WindowStart) @@ -655,7 +655,9 @@ func TestFileContract(t *testing.T) { } } - fc := prepareContractFormation(renterPublicKey, hostPublicKey, types.Siacoins(1), types.Siacoins(1), cm.Tip().Height+10, 100, types.VoidAddress) + windowStart := cm.Tip().Height + 10 + windowEnd := windowStart + 10 + fc := prepareContractFormation(renterPublicKey, hostPublicKey, types.Siacoins(1), types.Siacoins(1), windowStart, windowEnd, types.VoidAddress) txn := types.Transaction{ SiacoinInputs: []types.SiacoinInput{{ ParentID: scOutputID, @@ -667,6 +669,7 @@ func TestFileContract(t *testing.T) { }}, FileContracts: []types.FileContract{fc}, } + fcID := txn.FileContractID(0) signTxn(&txn) if err := cm.AddBlocks([]types.Block{mineBlock(cm.TipState(), []types.Transaction{txn}, types.VoidAddress)}); err != nil { @@ -674,12 +677,12 @@ func TestFileContract(t *testing.T) { } { - dbFCs, err := db.Contracts([]types.FileContractID{txn.FileContractID(0)}) + dbFCs, err := db.Contracts([]types.FileContractID{fcID}) if err != nil { t.Fatal(err) } check(t, "fcs", 1, len(dbFCs)) - checkFC(fc, dbFCs[0]) + checkFC(false, true, fc, dbFCs[0]) } { @@ -689,7 +692,7 @@ func TestFileContract(t *testing.T) { } check(t, "transactions", 1, len(txns)) check(t, "file contracts", 1, len(txns[0].FileContracts)) - checkFC(fc, txns[0].FileContracts[0]) + checkFC(false, true, fc, txns[0].FileContracts[0]) } uc := types.UnlockConditions{ @@ -702,7 +705,7 @@ func TestFileContract(t *testing.T) { fc.RevisionNumber++ reviseTxn := types.Transaction{ FileContractRevisions: []types.FileContractRevision{{ - ParentID: txn.FileContractID(0), + ParentID: fcID, UnlockConditions: uc, FileContract: fc, }}, @@ -715,12 +718,12 @@ func TestFileContract(t *testing.T) { // Explorer.Contracts should return latest revision { - dbFCs, err := db.Contracts([]types.FileContractID{txn.FileContractID(0)}) + dbFCs, err := db.Contracts([]types.FileContractID{fcID}) if err != nil { t.Fatal(err) } check(t, "fcs", 1, len(dbFCs)) - checkFC(fc, dbFCs[0]) + checkFC(false, true, fc, dbFCs[0]) } { @@ -735,6 +738,21 @@ func TestFileContract(t *testing.T) { check(t, "parent id", txn.FileContractID(0), fcr.ParentID) check(t, "unlock conditions", uc, fcr.UnlockConditions) - checkFC(fc, fcr.FileContract) + checkFC(false, true, fc, fcr.FileContract) + } + + for i := cm.Tip().Height; i < windowEnd+10; i++ { + if err := cm.AddBlocks([]types.Block{mineBlock(cm.TipState(), nil, types.VoidAddress)}); err != nil { + t.Fatal(err) + } + } + + { + dbFCs, err := db.Contracts([]types.FileContractID{fcID}) + if err != nil { + t.Fatal(err) + } + check(t, "fcs", 1, len(dbFCs)) + checkFC(true, false, fc, dbFCs[0]) } }