Commit effd343c authored by David Vorick's avatar David Vorick

Merge branch 'acid-bug' into 'master'

Check WAL for revision in renew

See merge request !3193
parents 310d49be 3e303d13
Pipeline #28994408 failed with stages
in 21 minutes and 57 seconds
......@@ -181,20 +181,9 @@ func (cs *ContractSet) NewDownloader(host modules.HostDBEntry, id types.FileCont
}
}()
conn, closeChan, err := initiateRevisionLoop(host, contract, modules.RPCDownload, cancel, cs.rl)
if IsRevisionMismatch(err) && len(sc.unappliedTxns) > 0 {
// we have desynced from the host. If we have unapplied updates from the
// WAL, try applying them.
conn, closeChan, err = initiateRevisionLoop(host, sc.unappliedHeader(), modules.RPCDownload, cancel, cs.rl)
if err != nil {
return nil, err
}
// applying the updates was successful; commit them to disk
if err := sc.commitTxns(); err != nil {
return nil, err
}
} else if err != nil {
return nil, err
conn, closeChan, err := initiateRevisionLoop(host, sc, modules.RPCDownload, cancel, cs.rl)
if err != nil {
return nil, errors.AddContext(err, "failed to initiate revision loop")
}
// if we succeeded, we can safely discard the unappliedTxns
for _, txn := range sc.unappliedTxns {
......
......@@ -190,20 +190,9 @@ func (cs *ContractSet) NewEditor(host modules.HostDBEntry, id types.FileContract
}
}()
conn, closeChan, err := initiateRevisionLoop(host, contract, modules.RPCReviseContract, cancel, cs.rl)
if IsRevisionMismatch(err) && len(sc.unappliedTxns) > 0 {
// we have desynced from the host. If we have unapplied updates from the
// WAL, try applying them.
conn, closeChan, err = initiateRevisionLoop(host, sc.unappliedHeader(), modules.RPCReviseContract, cancel, cs.rl)
if err != nil {
return nil, err
}
// applying the updates was successful; commit them to disk
if err := sc.commitTxns(); err != nil {
return nil, err
}
} else if err != nil {
return nil, err
conn, closeChan, err := initiateRevisionLoop(host, sc, modules.RPCReviseContract, cancel, cs.rl)
if err != nil {
return nil, errors.AddContext(err, "failed to initiate revision loop")
}
// if we succeeded, we can safely discard the unappliedTxns
for _, txn := range sc.unappliedTxns {
......@@ -226,7 +215,7 @@ func (cs *ContractSet) NewEditor(host modules.HostDBEntry, id types.FileContract
// initiateRevisionLoop initiates either the editor or downloader loop with
// host, depending on which rpc was passed.
func initiateRevisionLoop(host modules.HostDBEntry, contract contractHeader, rpc types.Specifier, cancel <-chan struct{}, rl *ratelimit.RateLimit) (net.Conn, chan struct{}, error) {
func initiateRevisionLoop(host modules.HostDBEntry, contract *SafeContract, rpc types.Specifier, cancel <-chan struct{}, rl *ratelimit.RateLimit) (net.Conn, chan struct{}, error) {
c, err := (&net.Dialer{
Cancel: cancel,
Timeout: 45 * time.Second, // TODO: Constant
......
package proto
import (
"errors"
"net"
"time"
......@@ -10,6 +9,7 @@ import (
"gitlab.com/NebulousLabs/Sia/encoding"
"gitlab.com/NebulousLabs/Sia/modules"
"gitlab.com/NebulousLabs/Sia/types"
"gitlab.com/NebulousLabs/errors"
)
// extendDeadline is a helper function for extending the connection timeout.
......@@ -68,9 +68,9 @@ func verifySettings(conn net.Conn, host modules.HostDBEntry) (modules.HostDBEntr
// verifyRecentRevision confirms that the host and contractor agree upon the current
// state of the contract being revised.
func verifyRecentRevision(conn net.Conn, contract contractHeader, hostVersion string) error {
func verifyRecentRevision(conn net.Conn, contract *SafeContract, hostVersion string) error {
// send contract ID
if err := encoding.WriteObject(conn, contract.ID()); err != nil {
if err := encoding.WriteObject(conn, contract.header.ID()); err != nil {
return errors.New("couldn't send contract ID: " + err.Error())
}
// read challenge
......@@ -82,7 +82,7 @@ func verifyRecentRevision(conn net.Conn, contract contractHeader, hostVersion st
crypto.SecureWipe(challenge[:16])
}
// sign and return
sig := crypto.SignHash(challenge, contract.SecretKey)
sig := crypto.SignHash(challenge, contract.header.SecretKey)
if err := encoding.WriteObject(conn, sig); err != nil {
return errors.New("couldn't send challenge response: " + err.Error())
}
......@@ -101,16 +101,24 @@ func verifyRecentRevision(conn net.Conn, contract contractHeader, hostVersion st
}
// Check that the unlock hashes match; if they do not, something is
// seriously wrong. Otherwise, check that the revision numbers match.
ourRev := contract.LastRevision()
ourRev := contract.header.LastRevision()
if lastRevision.UnlockConditions.UnlockHash() != ourRev.UnlockConditions.UnlockHash() {
return errors.New("unlock conditions do not match")
} else if lastRevision.NewRevisionNumber != ourRev.NewRevisionNumber {
return &recentRevisionError{ourRev.NewRevisionNumber, lastRevision.NewRevisionNumber}
// If the revision number doesn't match try to commit potential
// unapplied transactions and check again.
if err := contract.commitTxns(); err != nil {
return errors.AddContext(err, "failed to commit transactions")
}
ourRev = contract.header.LastRevision()
if lastRevision.NewRevisionNumber != ourRev.NewRevisionNumber {
return &recentRevisionError{ourRev.NewRevisionNumber, lastRevision.NewRevisionNumber}
}
}
// NOTE: we can fake the blockheight here because it doesn't affect
// verification; it just needs to be above the fork height and below the
// contract expiration (which was checked earlier).
return modules.VerifyFileContractRevisionTransactionSignatures(lastRevision, hostSignatures, contract.EndHeight()-1)
return modules.VerifyFileContractRevisionTransactionSignatures(lastRevision, hostSignatures, contract.header.EndHeight()-1)
}
// negotiateRevision sends a revision and actions to the host for approval,
......
......@@ -134,11 +134,11 @@ func (cs *ContractSet) Renew(oldContract *SafeContract, params ContractParams, t
return modules.RenterContract{}, errors.New("couldn't initiate RPC: " + err.Error())
}
// verify that both parties are renewing the same contract
if err = verifyRecentRevision(conn, contract, host.Version); err != nil {
// don't add context; want to preserve the original error type so that
// callers can check using IsRevisionMismatch
err = verifyRecentRevision(conn, oldContract, host.Version)
if err != nil {
return modules.RenterContract{}, err
}
// verify the host's settings and confirm its identity
host, err = verifySettings(conn, host)
if err != nil {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment