github.com/ZuluSpl0it/Sia@v1.3.7/modules/negotiate.go (about)

     1  package modules
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io"
     7  	"time"
     8  
     9  	"github.com/NebulousLabs/Sia/build"
    10  	"github.com/NebulousLabs/Sia/crypto"
    11  	"github.com/NebulousLabs/Sia/encoding"
    12  	"github.com/NebulousLabs/Sia/types"
    13  )
    14  
    15  const (
    16  	// AcceptResponse is the response given to an RPC call to indicate
    17  	// acceptance, i.e. that the sender wishes to continue communication.
    18  	AcceptResponse = "accept"
    19  
    20  	// StopResponse is the response given to an RPC call to indicate graceful
    21  	// termination, i.e. that the sender wishes to cease communication, but
    22  	// not due to an error.
    23  	StopResponse = "stop"
    24  )
    25  
    26  const (
    27  	// NegotiateDownloadTime defines the amount of time that the renter and
    28  	// host have to negotiate a download request batch. The time is set high
    29  	// enough that two nodes behind Tor have a reasonable chance of completing
    30  	// the negotiation.
    31  	NegotiateDownloadTime = 600 * time.Second
    32  
    33  	// NegotiateFileContractRevisionTime defines the minimum amount of time
    34  	// that the renter and host have to negotiate a file contract revision. The
    35  	// time is set high enough that a full 4MB can be piped through a
    36  	// connection that is running over Tor.
    37  	NegotiateFileContractRevisionTime = 600 * time.Second
    38  
    39  	// NegotiateFileContractTime defines the amount of time that the renter and
    40  	// host have to negotiate a file contract. The time is set high enough that
    41  	// a node behind Tor has a reasonable chance at making the multiple
    42  	// required round trips to complete the negotiation.
    43  	NegotiateFileContractTime = 360 * time.Second
    44  
    45  	// NegotiateMaxDownloadActionRequestSize defines the maximum size that a
    46  	// download request can be. Note, this is not a max size for the data that
    47  	// can be requested, but instead is a max size for the definition of the
    48  	// data being requested.
    49  	NegotiateMaxDownloadActionRequestSize = 50e3
    50  
    51  	// NegotiateMaxErrorSize indicates the maximum number of bytes that can be
    52  	// used to encode an error being sent during negotiation.
    53  	NegotiateMaxErrorSize = 256
    54  
    55  	// NegotiateMaxFileContractRevisionSize specifies the maximum size that a
    56  	// file contract revision is allowed to have when being sent over the wire
    57  	// during negotiation.
    58  	NegotiateMaxFileContractRevisionSize = 3e3
    59  
    60  	// NegotiateMaxFileContractSetLen determines the maximum allowed size of a
    61  	// transaction set that can be sent when trying to negotiate a file
    62  	// contract. The transaction set will contain all of the unconfirmed
    63  	// dependencies of the file contract, meaning that it can be quite large.
    64  	// The transaction pool's size limit for transaction sets has been chosen
    65  	// as a reasonable guideline for determining what is too large.
    66  	NegotiateMaxFileContractSetLen = TransactionSetSizeLimit - 1e3
    67  
    68  	// NegotiateMaxHostExternalSettingsLen is the maximum allowed size of an
    69  	// encoded HostExternalSettings.
    70  	NegotiateMaxHostExternalSettingsLen = 16000
    71  
    72  	// NegotiateMaxSiaPubkeySize defines the maximum size that a SiaPubkey is
    73  	// allowed to be when being sent over the wire during negotiation.
    74  	NegotiateMaxSiaPubkeySize = 1e3
    75  
    76  	// NegotiateMaxTransactionSignatureSize defines the maximum size that a
    77  	// transaction signature is allowed to be when being sent over the wire
    78  	// during negotiation.
    79  	NegotiateMaxTransactionSignatureSize = 2e3
    80  
    81  	// NegotiateMaxTransactionSignaturesSize defines the maximum size that a
    82  	// transaction signature slice is allowed to be when being sent over the
    83  	// wire during negotiation.
    84  	NegotiateMaxTransactionSignaturesSize = 5e3
    85  
    86  	// NegotiateRecentRevisionTime establishes the minimum amount of time that
    87  	// the connection deadline is expected to be set to when a recent file
    88  	// contract revision is being requested from the host. The deadline is long
    89  	// enough that the connection should be successful even if both parties are
    90  	// running Tor.
    91  	NegotiateRecentRevisionTime = 120 * time.Second
    92  
    93  	// NegotiateRenewContractTime defines the minimum amount of time that the
    94  	// renter and host have to negotiate a final contract renewal. The time is
    95  	// high enough that the negotiation can occur over a Tor connection, and
    96  	// that both the host and the renter can have time to process large Merkle
    97  	// tree calculations that may be involved with renewing a file contract.
    98  	NegotiateRenewContractTime = 600 * time.Second
    99  )
   100  
   101  var (
   102  	// NegotiateSettingsTime establishes the minimum amount of time that the
   103  	// connection deadline is expected to be set to when settings are being
   104  	// requested from the host. The deadline is long enough that the connection
   105  	// should be successful even if both parties are on Tor.
   106  	NegotiateSettingsTime = build.Select(build.Var{
   107  		Dev:      120 * time.Second,
   108  		Standard: 120 * time.Second,
   109  		Testing:  3 * time.Second,
   110  	}).(time.Duration)
   111  )
   112  
   113  var (
   114  	// ActionDelete is the specifier for a RevisionAction that deletes a
   115  	// sector.
   116  	ActionDelete = types.Specifier{'D', 'e', 'l', 'e', 't', 'e'}
   117  
   118  	// ActionInsert is the specifier for a RevisionAction that inserts a
   119  	// sector.
   120  	ActionInsert = types.Specifier{'I', 'n', 's', 'e', 'r', 't'}
   121  
   122  	// ActionModify is the specifier for a RevisionAction that modifies sector
   123  	// data.
   124  	ActionModify = types.Specifier{'M', 'o', 'd', 'i', 'f', 'y'}
   125  
   126  	// ErrAnnNotAnnouncement indicates that the provided host announcement does
   127  	// not use a recognized specifier, indicating that it's either not a host
   128  	// announcement or it's not a recognized version of a host announcement.
   129  	ErrAnnNotAnnouncement = errors.New("provided data does not form a recognized host announcement")
   130  
   131  	// ErrAnnUnrecognizedSignature is returned when the signature in a host
   132  	// announcement is not a type of signature that is recognized.
   133  	ErrAnnUnrecognizedSignature = errors.New("the signature provided in the host announcement is not recognized")
   134  
   135  	// ErrRevisionCoveredFields is returned if there is a covered fields object
   136  	// in a transaction signature which has the 'WholeTransaction' field set to
   137  	// true, meaning that miner fees cannot be added to the transaction without
   138  	// invalidating the signature.
   139  	ErrRevisionCoveredFields = errors.New("file contract revision transaction signature does not allow miner fees to be added")
   140  
   141  	// ErrRevisionSigCount is returned when a file contract revision has the
   142  	// wrong number of transaction signatures.
   143  	ErrRevisionSigCount = errors.New("file contract revision has the wrong number of transaction signatures")
   144  
   145  	// ErrStopResponse is the error returned by ReadNegotiationAcceptance when
   146  	// it reads the StopResponse string.
   147  	ErrStopResponse = errors.New("sender wishes to stop communicating")
   148  
   149  	// PrefixHostAnnouncement is used to indicate that a transaction's
   150  	// Arbitrary Data field contains a host announcement. The encoded
   151  	// announcement will follow this prefix.
   152  	PrefixHostAnnouncement = types.Specifier{'H', 'o', 's', 't', 'A', 'n', 'n', 'o', 'u', 'n', 'c', 'e', 'm', 'e', 'n', 't'}
   153  
   154  	// RPCDownload is the specifier for downloading a file from a host.
   155  	RPCDownload = types.Specifier{'D', 'o', 'w', 'n', 'l', 'o', 'a', 'd', 2}
   156  
   157  	// RPCFormContract is the specifier for forming a contract with a host.
   158  	RPCFormContract = types.Specifier{'F', 'o', 'r', 'm', 'C', 'o', 'n', 't', 'r', 'a', 'c', 't', 2}
   159  
   160  	// RPCRenewContract is the specifier to renewing an existing contract.
   161  	RPCRenewContract = types.Specifier{'R', 'e', 'n', 'e', 'w', 'C', 'o', 'n', 't', 'r', 'a', 'c', 't', 2}
   162  
   163  	// RPCReviseContract is the specifier for revising an existing file
   164  	// contract.
   165  	RPCReviseContract = types.Specifier{'R', 'e', 'v', 'i', 's', 'e', 'C', 'o', 'n', 't', 'r', 'a', 'c', 't', 2}
   166  
   167  	// RPCSettings is the specifier for requesting settings from the host.
   168  	RPCSettings = types.Specifier{'S', 'e', 't', 't', 'i', 'n', 'g', 's', 2}
   169  
   170  	// SectorSize defines how large a sector should be in bytes. The sector
   171  	// size needs to be a power of two to be compatible with package
   172  	// merkletree. 4MB has been chosen for the live network because large
   173  	// sectors significantly reduce the tracking overhead experienced by the
   174  	// renter and the host.
   175  	SectorSize = build.Select(build.Var{
   176  		Dev:      uint64(1 << 18), // 256 KiB
   177  		Standard: uint64(1 << 22), // 4 MiB
   178  		Testing:  uint64(1 << 12), // 4 KiB
   179  	}).(uint64)
   180  )
   181  
   182  type (
   183  	// A DownloadAction is a description of a download that the renter would
   184  	// like to make. The MerkleRoot indicates the root of the sector, the
   185  	// offset indicates what portion of the sector is being downloaded, and the
   186  	// length indicates how many bytes should be grabbed starting from the
   187  	// offset.
   188  	DownloadAction struct {
   189  		MerkleRoot crypto.Hash
   190  		Offset     uint64
   191  		Length     uint64
   192  	}
   193  
   194  	// HostAnnouncement is an announcement by the host that appears in the
   195  	// blockchain. 'Specifier' is always 'PrefixHostAnnouncement'. The
   196  	// announcement is always followed by a signature from the public key of
   197  	// the whole announcement.
   198  	HostAnnouncement struct {
   199  		Specifier  types.Specifier
   200  		NetAddress NetAddress
   201  		PublicKey  types.SiaPublicKey
   202  	}
   203  
   204  	// HostExternalSettings are the parameters advertised by the host. These
   205  	// are the values that the renter will request from the host in order to
   206  	// build its database.
   207  	HostExternalSettings struct {
   208  		// MaxBatchSize indicates the maximum size in bytes that a batch is
   209  		// allowed to be. A batch is an array of revision actions; each
   210  		// revision action can have a different number of bytes, depending on
   211  		// the action, so the number of revision actions allowed depends on the
   212  		// sizes of each.
   213  		AcceptingContracts   bool              `json:"acceptingcontracts"`
   214  		MaxDownloadBatchSize uint64            `json:"maxdownloadbatchsize"`
   215  		MaxDuration          types.BlockHeight `json:"maxduration"`
   216  		MaxReviseBatchSize   uint64            `json:"maxrevisebatchsize"`
   217  		NetAddress           NetAddress        `json:"netaddress"`
   218  		RemainingStorage     uint64            `json:"remainingstorage"`
   219  		SectorSize           uint64            `json:"sectorsize"`
   220  		TotalStorage         uint64            `json:"totalstorage"`
   221  		UnlockHash           types.UnlockHash  `json:"unlockhash"`
   222  		WindowSize           types.BlockHeight `json:"windowsize"`
   223  
   224  		// Collateral is the amount of collateral that the host will put up for
   225  		// storage in 'bytes per block', as an assurance to the renter that the
   226  		// host really is committed to keeping the file. But, because the file
   227  		// contract is created with no data available, this does leave the host
   228  		// exposed to an attack by a wealthy renter whereby the renter causes
   229  		// the host to lockup in-advance a bunch of funds that the renter then
   230  		// never uses, meaning the host will not have collateral for other
   231  		// clients.
   232  		//
   233  		// MaxCollateral indicates the maximum number of coins that a host is
   234  		// willing to put into a file contract.
   235  		Collateral    types.Currency `json:"collateral"`
   236  		MaxCollateral types.Currency `json:"maxcollateral"`
   237  
   238  		// ContractPrice is the number of coins that the renter needs to pay to
   239  		// the host just to open a file contract with them. Generally, the
   240  		// price is only to cover the siacoin fees that the host will suffer
   241  		// when submitting the file contract revision and storage proof to the
   242  		// blockchain.
   243  		//
   244  		// The storage price is the cost per-byte-per-block in hastings of
   245  		// storing data on the host.
   246  		//
   247  		// 'Download' bandwidth price is the cost per byte of downloading data
   248  		// from the host.
   249  		//
   250  		// 'Upload' bandwidth price is the cost per byte of uploading data to
   251  		// the host.
   252  		ContractPrice          types.Currency `json:"contractprice"`
   253  		DownloadBandwidthPrice types.Currency `json:"downloadbandwidthprice"`
   254  		StoragePrice           types.Currency `json:"storageprice"`
   255  		UploadBandwidthPrice   types.Currency `json:"uploadbandwidthprice"`
   256  
   257  		// Because the host has a public key, and settings are signed, and
   258  		// because settings may be MITM'd, settings need a revision number so
   259  		// that a renter can compare multiple sets of settings and determine
   260  		// which is the most recent.
   261  		RevisionNumber uint64 `json:"revisionnumber"`
   262  		Version        string `json:"version"`
   263  	}
   264  
   265  	// A RevisionAction is a description of an edit to be performed on a file
   266  	// contract. Three types are allowed, 'ActionDelete', 'ActionInsert', and
   267  	// 'ActionModify'. ActionDelete just takes a sector index, indicating which
   268  	// sector is going to be deleted. ActionInsert takes a sector index, and a
   269  	// full sector of data, indicating that a sector at the index should be
   270  	// inserted with the provided data. 'Modify' revises the sector at the
   271  	// given index, rewriting it with the provided data starting from the
   272  	// 'offset' within the sector.
   273  	//
   274  	// Modify could be simulated with an insert and a delete, however an insert
   275  	// requires a full sector to be uploaded, and a modify can be just a few
   276  	// kb, which can be significantly faster.
   277  	RevisionAction struct {
   278  		Type        types.Specifier
   279  		SectorIndex uint64
   280  		Offset      uint64
   281  		Data        []byte
   282  	}
   283  )
   284  
   285  // ReadNegotiationAcceptance reads an accept/reject response from r (usually a
   286  // net.Conn). If the response is not AcceptResponse, ReadNegotiationAcceptance
   287  // returns the response as an error. If the response is StopResponse,
   288  // ErrStopResponse is returned, allowing for direct error comparison.
   289  //
   290  // Note that since errors returned by ReadNegotiationAcceptance are newly
   291  // allocated, they cannot be compared to other errors in the traditional
   292  // fashion.
   293  func ReadNegotiationAcceptance(r io.Reader) error {
   294  	var resp string
   295  	err := encoding.ReadObject(r, &resp, NegotiateMaxErrorSize)
   296  	if err != nil {
   297  		return err
   298  	}
   299  	switch resp {
   300  	case AcceptResponse:
   301  		return nil
   302  	case StopResponse:
   303  		return ErrStopResponse
   304  	default:
   305  		return errors.New(resp)
   306  	}
   307  }
   308  
   309  // WriteNegotiationAcceptance writes the 'accept' response to w (usually a
   310  // net.Conn).
   311  func WriteNegotiationAcceptance(w io.Writer) error {
   312  	return encoding.WriteObject(w, AcceptResponse)
   313  }
   314  
   315  // WriteNegotiationRejection will write a rejection response to w (usually a
   316  // net.Conn) and return the input error. If the write fails, the write error
   317  // is joined with the input error.
   318  func WriteNegotiationRejection(w io.Writer, err error) error {
   319  	writeErr := encoding.WriteObject(w, err.Error())
   320  	if writeErr != nil {
   321  		return build.JoinErrors([]error{err, writeErr}, "; ")
   322  	}
   323  	return err
   324  }
   325  
   326  // WriteNegotiationStop writes the 'stop' response to w (usually a
   327  // net.Conn).
   328  func WriteNegotiationStop(w io.Writer) error {
   329  	return encoding.WriteObject(w, StopResponse)
   330  }
   331  
   332  // CreateAnnouncement will take a host announcement and encode it, returning
   333  // the exact []byte that should be added to the arbitrary data of a
   334  // transaction.
   335  func CreateAnnouncement(addr NetAddress, pk types.SiaPublicKey, sk crypto.SecretKey) (signedAnnouncement []byte, err error) {
   336  	if err := addr.IsValid(); err != nil {
   337  		return nil, err
   338  	}
   339  
   340  	// Create the HostAnnouncement and marshal it.
   341  	annBytes := encoding.Marshal(HostAnnouncement{
   342  		Specifier:  PrefixHostAnnouncement,
   343  		NetAddress: addr,
   344  		PublicKey:  pk,
   345  	})
   346  
   347  	// Create a signature for the announcement.
   348  	annHash := crypto.HashBytes(annBytes)
   349  	sig := crypto.SignHash(annHash, sk)
   350  	// Return the signed announcement.
   351  	return append(annBytes, sig[:]...), nil
   352  }
   353  
   354  // DecodeAnnouncement decodes announcement bytes into a host announcement,
   355  // verifying the prefix and the signature.
   356  func DecodeAnnouncement(fullAnnouncement []byte) (na NetAddress, spk types.SiaPublicKey, err error) {
   357  	// Read the first part of the announcement to get the intended host
   358  	// announcement.
   359  	var ha HostAnnouncement
   360  	dec := encoding.NewDecoder(bytes.NewReader(fullAnnouncement))
   361  	err = dec.Decode(&ha)
   362  	if err != nil {
   363  		return "", types.SiaPublicKey{}, err
   364  	}
   365  
   366  	// Check that the announcement was registered as a host announcement.
   367  	if ha.Specifier != PrefixHostAnnouncement {
   368  		return "", types.SiaPublicKey{}, ErrAnnNotAnnouncement
   369  	}
   370  	// Check that the public key is a recognized type of public key.
   371  	if ha.PublicKey.Algorithm != types.SignatureEd25519 {
   372  		return "", types.SiaPublicKey{}, ErrAnnUnrecognizedSignature
   373  	}
   374  
   375  	// Read the signature out of the reader.
   376  	var sig crypto.Signature
   377  	err = dec.Decode(&sig)
   378  	if err != nil {
   379  		return "", types.SiaPublicKey{}, err
   380  	}
   381  	// Verify the signature.
   382  	var pk crypto.PublicKey
   383  	copy(pk[:], ha.PublicKey.Key)
   384  	annHash := crypto.HashObject(ha)
   385  	err = crypto.VerifyHash(annHash, pk, sig)
   386  	if err != nil {
   387  		return "", types.SiaPublicKey{}, err
   388  	}
   389  	return ha.NetAddress, ha.PublicKey, nil
   390  }
   391  
   392  // VerifyFileContractRevisionTransactionSignatures checks that the signatures
   393  // on a file contract revision are valid and cover the right fields.
   394  func VerifyFileContractRevisionTransactionSignatures(fcr types.FileContractRevision, tsigs []types.TransactionSignature, height types.BlockHeight) error {
   395  	if len(tsigs) != 2 {
   396  		return ErrRevisionSigCount
   397  	}
   398  	for _, tsig := range tsigs {
   399  		// The transaction needs to be malleable so that miner fees can be
   400  		// added. If the whole transaction is covered, it is doomed to have no
   401  		// fees.
   402  		if tsig.CoveredFields.WholeTransaction {
   403  			return ErrRevisionCoveredFields
   404  		}
   405  	}
   406  	txn := types.Transaction{
   407  		FileContractRevisions: []types.FileContractRevision{fcr},
   408  		TransactionSignatures: tsigs,
   409  	}
   410  	// Check that the signatures verify. This will also check that the covered
   411  	// fields object is not over-aggressive, because if the object is pointing
   412  	// to elements that haven't been added to the transaction, verification
   413  	// will fail.
   414  	return txn.StandaloneValid(height)
   415  }