diff --git a/consensus/state.go b/consensus/state.go index 5cd9b17c..0d82f2ab 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -620,6 +620,9 @@ func (ms *MidState) siafundElement(ts V1TransactionSupplement, id types.SiafundO } func (ms *MidState) fileContractElement(ts V1TransactionSupplement, id types.FileContractID) (types.FileContractElement, bool) { + if rev, ok := ms.revs[types.Hash256(id)]; ok { + return *rev, true + } if i, ok := ms.created[types.Hash256(id)]; ok { return ms.fces[i], true } diff --git a/consensus/validation.go b/consensus/validation.go index 40b8d6ac..8004f705 100644 --- a/consensus/validation.go +++ b/consensus/validation.go @@ -238,11 +238,15 @@ func validateSiafunds(ms *MidState, txn types.Transaction, ts V1TransactionSuppl } func validateFileContracts(ms *MidState, txn types.Transaction, ts V1TransactionSupplement) error { + v2RequireHeight := ms.base.Network.HardforkV2.RequireHeight + for i, fc := range txn.FileContracts { if fc.WindowStart < ms.base.childHeight() { return fmt.Errorf("file contract %v has window that starts in the past", i) } else if fc.WindowEnd <= fc.WindowStart { return fmt.Errorf("file contract %v has window that ends before it begins", i) + } else if fc.WindowStart >= v2RequireHeight { + return fmt.Errorf("file contract %v has window that starts after v2 hardfork", i) } var validSum, missedSum types.Currency for _, output := range fc.ValidProofOutputs { @@ -265,6 +269,8 @@ func validateFileContracts(ms *MidState, txn types.Transaction, ts V1Transaction return fmt.Errorf("file contract revision %v has window that starts in the past", i) } else if fcr.FileContract.WindowEnd <= fcr.FileContract.WindowStart { return fmt.Errorf("file contract revision %v has window that ends before it begins", i) + } else if fcr.WindowStart >= v2RequireHeight { + return fmt.Errorf("file contract %v has window that starts after v2 hardfork", i) } else if txid, ok := ms.spent(types.Hash256(fcr.ParentID)); ok { return fmt.Errorf("file contract revision %v conflicts with previous proof or revision (in %v)", i, txid) } diff --git a/consensus/validation_test.go b/consensus/validation_test.go index 40f37946..52908b9c 100644 --- a/consensus/validation_test.go +++ b/consensus/validation_test.go @@ -365,6 +365,9 @@ func TestValidateBlock(t *testing.T) { t.Fatal(err) } + // clear signatures to avoid false positives + validBlock.Transactions[0].Signatures = nil + // tests with correct signatures { tests := []struct { @@ -658,6 +661,32 @@ func TestValidateBlock(t *testing.T) { txn.FileContractRevisions = append(txn.FileContractRevisions, newRevision) }, }, + { + "misordered revisions", + func(b *types.Block) { + newRevision := b.Transactions[0].FileContractRevisions[0] + newRevision.RevisionNumber = 99 + + b.Transactions = append(b.Transactions, types.Transaction{ + FileContractRevisions: []types.FileContractRevision{newRevision}, + }) + + // set the initial revision number to be higher than the new + // revision + b.Transactions[0].FileContractRevisions[0].RevisionNumber = 100 + }, + }, + { + "duplicate revisions in same block", + func(b *types.Block) { + txn := &b.Transactions[0] + newRevision := txn.FileContractRevisions[0] + + b.Transactions = append(b.Transactions, types.Transaction{ + FileContractRevisions: []types.FileContractRevision{newRevision}, + }) + }, + }, { "double-spent siacoin input", func(b *types.Block) { @@ -685,11 +714,15 @@ func TestValidateBlock(t *testing.T) { for _, test := range tests { corruptBlock := deepCopyBlock(validBlock) test.corrupt(&corruptBlock) - signTxn(&corruptBlock.Transactions[0]) + for i := range corruptBlock.Transactions { + signTxn(&corruptBlock.Transactions[i]) + } findBlockNonce(cs, &corruptBlock) if err := ValidateBlock(cs, corruptBlock, db.supplementTipBlock(corruptBlock)); err == nil { t.Fatalf("accepted block with %v", test.desc) + } else { + t.Log(test.desc, err) } } } @@ -758,6 +791,9 @@ func TestValidateBlock(t *testing.T) { } for _, test := range tests { corruptBlock := deepCopyBlock(validBlock) + for i := range corruptBlock.Transactions { + signTxn(&corruptBlock.Transactions[i]) + } test.corrupt(&corruptBlock) findBlockNonce(cs, &corruptBlock)