Skip to content

Commit

Permalink
Merge pull request #188 from SiaFoundation:revision-window
Browse files Browse the repository at this point in the history
consensus: Disallow contract revision after proof window opens
  • Loading branch information
n8maninger authored Aug 12, 2024
2 parents 9a56fba + ccf1fd7 commit 9dffffb
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 2 deletions.
7 changes: 5 additions & 2 deletions consensus/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,9 @@ func validateFileContracts(ms *MidState, txn types.Transaction, ts V1Transaction
parent, ok := ms.fileContractElement(ts, fcr.ParentID)
if !ok {
return fmt.Errorf("file contract revision %v revises nonexistent file contract %v", i, fcr.ParentID)
}
if fcr.FileContract.RevisionNumber <= parent.FileContract.RevisionNumber {
} else if parent.FileContract.WindowStart < ms.base.childHeight() {
return fmt.Errorf("file contract revision %v revises contract after its proof window has opened", i)
} else if fcr.FileContract.RevisionNumber <= parent.FileContract.RevisionNumber {
return fmt.Errorf("file contract revision %v does not have a higher revision number than its parent", i)
} else if types.Hash256(fcr.UnlockConditions.UnlockHash()) != parent.FileContract.UnlockHash {
return fmt.Errorf("file contract revision %v claims incorrect unlock conditions", i)
Expand Down Expand Up @@ -750,6 +751,8 @@ func validateV2FileContracts(ms *MidState, txn types.V2Transaction) error {
curOutputSum := cur.RenterOutput.Value.Add(cur.HostOutput.Value)
revOutputSum := rev.RenterOutput.Value.Add(rev.HostOutput.Value)
switch {
case cur.ProofHeight < ms.base.childHeight():
return fmt.Errorf("revises contract after its proof window has opened")
case rev.RevisionNumber <= cur.RevisionNumber:
return fmt.Errorf("does not increase revision number (%v -> %v)", cur.RevisionNumber, rev.RevisionNumber)
case !revOutputSum.Equals(curOutputSum):
Expand Down
53 changes: 53 additions & 0 deletions consensus/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1539,3 +1539,56 @@ func TestEarlyV2Transaction(t *testing.T) {
t.Fatalf("expected %q, got %q", exp, err)
}
}

func TestWindowRevision(t *testing.T) {
n, genesisBlock := testnet()
n.InitialTarget = types.BlockID{0xFF}

// create file contract with window that is already open
sk := types.NewPrivateKeyFromSeed(make([]byte, 32))
uc := types.StandardUnlockConditions(sk.PublicKey())
fc := types.FileContract{
WindowStart: 0,
WindowEnd: 3,
UnlockHash: types.Hash256(uc.UnlockHash()),
}
genesisBlock.Transactions = []types.Transaction{{
FileContracts: []types.FileContract{fc},
}}
db, cs := newConsensusDB(n, genesisBlock)

// attempt to extend the window
rev := fc
rev.WindowStart = 1
rev.RevisionNumber++
txn := types.Transaction{
FileContractRevisions: []types.FileContractRevision{{
ParentID: genesisBlock.Transactions[0].FileContractID(0),
UnlockConditions: uc,
FileContract: rev,
}},
Signatures: []types.TransactionSignature{{
ParentID: types.Hash256(genesisBlock.Transactions[0].FileContractID(0)),
PublicKeyIndex: 0,
Timelock: 0,
CoveredFields: types.CoveredFields{WholeTransaction: true},
}},
}
sig := sk.SignHash(cs.WholeSigHash(txn, txn.Signatures[0].ParentID, 0, 0, nil))
txn.Signatures[0].Signature = sig[:]

b := types.Block{
ParentID: genesisBlock.ID(),
Timestamp: types.CurrentTimestamp(),
MinerPayouts: []types.SiacoinOutput{{
Address: types.VoidAddress,
Value: cs.BlockReward(),
}},
Transactions: []types.Transaction{txn},
}

findBlockNonce(cs, &b)
if err := ValidateBlock(cs, b, db.supplementTipBlock(b)); err == nil || !strings.Contains(err.Error(), "proof window has opened") {
t.Fatal("expected error when extending window")
}
}

0 comments on commit 9dffffb

Please sign in to comment.