github.com/NebulousLabs/Sia@v1.3.7/modules/renter/contractor/negotiate_test.go (about)

     1  package contractor
     2  
     3  import (
     4  	"path/filepath"
     5  	"testing"
     6  
     7  	"github.com/NebulousLabs/Sia/build"
     8  	"github.com/NebulousLabs/Sia/crypto"
     9  	"github.com/NebulousLabs/Sia/modules"
    10  	"github.com/NebulousLabs/Sia/modules/consensus"
    11  	"github.com/NebulousLabs/Sia/modules/gateway"
    12  	"github.com/NebulousLabs/Sia/modules/miner"
    13  	"github.com/NebulousLabs/Sia/modules/renter/hostdb"
    14  	"github.com/NebulousLabs/Sia/modules/transactionpool"
    15  	modWallet "github.com/NebulousLabs/Sia/modules/wallet" // name conflicts with type
    16  	"github.com/NebulousLabs/Sia/types"
    17  )
    18  
    19  // contractorTester contains all of the modules that are used while testing the contractor.
    20  type contractorTester struct {
    21  	cs        modules.ConsensusSet
    22  	gateway   modules.Gateway
    23  	miner     modules.TestMiner
    24  	tpool     modules.TransactionPool
    25  	wallet    modules.Wallet
    26  	walletKey crypto.TwofishKey
    27  	hdb       hostDB
    28  
    29  	contractor *Contractor
    30  }
    31  
    32  // Close shuts down the contractor tester.
    33  func (rt *contractorTester) Close() error {
    34  	errs := []error{
    35  		rt.gateway.Close(),
    36  		rt.cs.Close(),
    37  		rt.tpool.Close(),
    38  		rt.miner.Close(),
    39  		rt.wallet.Close(),
    40  	}
    41  	return build.JoinErrors(errs, ": ")
    42  }
    43  
    44  // newContractorTester creates a ready-to-use contractor tester with money in the
    45  // wallet.
    46  func newContractorTester(name string) (*contractorTester, error) {
    47  	// Create the modules.
    48  	testdir := build.TempDir("contractor", name)
    49  	g, err := gateway.New("localhost:0", false, filepath.Join(testdir, modules.GatewayDir))
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	cs, err := consensus.New(g, false, filepath.Join(testdir, modules.ConsensusDir))
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir))
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	w, err := modWallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir))
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	key := crypto.GenerateTwofishKey()
    66  	_, err = w.Encrypt(key)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  	err = w.Unlock(key)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	hdb, err := hostdb.New(g, cs, filepath.Join(testdir, modules.RenterDir))
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir))
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	c, err := New(cs, w, tp, hdb, filepath.Join(testdir, modules.RenterDir))
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	// Assemble all pieces into a contractor tester.
    88  	ct := &contractorTester{
    89  		cs:      cs,
    90  		gateway: g,
    91  		miner:   m,
    92  		tpool:   tp,
    93  		wallet:  w,
    94  		hdb:     hdb,
    95  
    96  		contractor: c,
    97  	}
    98  
    99  	// Mine blocks until there is money in the wallet.
   100  	for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ {
   101  		_, err := ct.miner.AddBlock()
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  	}
   106  	return ct, nil
   107  }
   108  
   109  func TestNegotiateContract(t *testing.T) {
   110  	if testing.Short() {
   111  		t.SkipNow()
   112  	}
   113  	t.Parallel()
   114  	ct, err := newContractorTester(t.Name())
   115  	if err != nil {
   116  		t.Fatal(err)
   117  	}
   118  	defer ct.Close()
   119  
   120  	payout := types.NewCurrency64(1e16)
   121  
   122  	fc := types.FileContract{
   123  		FileSize:       0,
   124  		FileMerkleRoot: crypto.Hash{}, // no proof possible without data
   125  		WindowStart:    100,
   126  		WindowEnd:      1000,
   127  		Payout:         payout,
   128  		ValidProofOutputs: []types.SiacoinOutput{
   129  			{Value: types.PostTax(ct.contractor.blockHeight, payout), UnlockHash: types.UnlockHash{}},
   130  			{Value: types.ZeroCurrency, UnlockHash: types.UnlockHash{}},
   131  		},
   132  		MissedProofOutputs: []types.SiacoinOutput{
   133  			// same as above
   134  			{Value: types.PostTax(ct.contractor.blockHeight, payout), UnlockHash: types.UnlockHash{}},
   135  			// goes to the void, not the hostdb
   136  			{Value: types.ZeroCurrency, UnlockHash: types.UnlockHash{}},
   137  		},
   138  		UnlockHash:     types.UnlockHash{},
   139  		RevisionNumber: 0,
   140  	}
   141  
   142  	txnBuilder, err := ct.wallet.StartTransaction()
   143  	if err != nil {
   144  		t.Fatal(err)
   145  	}
   146  	err = txnBuilder.FundSiacoins(fc.Payout)
   147  	if err != nil {
   148  		t.Fatal(err)
   149  	}
   150  	txnBuilder.AddFileContract(fc)
   151  	signedTxnSet, err := txnBuilder.Sign(true)
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  
   156  	err = ct.tpool.AcceptTransactionSet(signedTxnSet)
   157  	if err != nil {
   158  		t.Fatal(err)
   159  	}
   160  
   161  }
   162  
   163  func TestReviseContract(t *testing.T) {
   164  	if testing.Short() {
   165  		t.SkipNow()
   166  	}
   167  	t.Parallel()
   168  	ct, err := newContractorTester(t.Name())
   169  	if err != nil {
   170  		t.Fatal(err)
   171  	}
   172  	defer ct.Close()
   173  
   174  	// get an address
   175  	ourAddr, err := ct.wallet.NextAddress()
   176  	if err != nil {
   177  		t.Fatal(err)
   178  	}
   179  
   180  	// generate keys
   181  	sk, pk := crypto.GenerateKeyPair()
   182  	renterPubKey := types.SiaPublicKey{
   183  		Algorithm: types.SignatureEd25519,
   184  		Key:       pk[:],
   185  	}
   186  
   187  	uc := types.UnlockConditions{
   188  		PublicKeys:         []types.SiaPublicKey{renterPubKey, renterPubKey},
   189  		SignaturesRequired: 1,
   190  	}
   191  
   192  	// create file contract
   193  	payout := types.NewCurrency64(1e16)
   194  
   195  	fc := types.FileContract{
   196  		FileSize:       0,
   197  		FileMerkleRoot: crypto.Hash{}, // no proof possible without data
   198  		WindowStart:    100,
   199  		WindowEnd:      1000,
   200  		Payout:         payout,
   201  		UnlockHash:     uc.UnlockHash(),
   202  		RevisionNumber: 0,
   203  	}
   204  	// outputs need account for tax
   205  	fc.ValidProofOutputs = []types.SiacoinOutput{
   206  		{Value: types.PostTax(ct.contractor.blockHeight, payout), UnlockHash: ourAddr.UnlockHash()},
   207  		{Value: types.ZeroCurrency, UnlockHash: types.UnlockHash{}}, // no collateral
   208  	}
   209  	fc.MissedProofOutputs = []types.SiacoinOutput{
   210  		// same as above
   211  		fc.ValidProofOutputs[0],
   212  		// goes to the void, not the hostdb
   213  		{Value: types.ZeroCurrency, UnlockHash: types.UnlockHash{}},
   214  	}
   215  
   216  	txnBuilder, err := ct.wallet.StartTransaction()
   217  	if err != nil {
   218  		t.Fatal(err)
   219  	}
   220  	err = txnBuilder.FundSiacoins(fc.Payout)
   221  	if err != nil {
   222  		t.Fatal(err)
   223  	}
   224  	txnBuilder.AddFileContract(fc)
   225  	signedTxnSet, err := txnBuilder.Sign(true)
   226  	if err != nil {
   227  		t.Fatal(err)
   228  	}
   229  
   230  	// submit contract
   231  	err = ct.tpool.AcceptTransactionSet(signedTxnSet)
   232  	if err != nil {
   233  		t.Fatal(err)
   234  	}
   235  
   236  	// create revision
   237  	fcid := signedTxnSet[len(signedTxnSet)-1].FileContractID(0)
   238  	rev := types.FileContractRevision{
   239  		ParentID:              fcid,
   240  		UnlockConditions:      uc,
   241  		NewFileSize:           10,
   242  		NewWindowStart:        100,
   243  		NewWindowEnd:          1000,
   244  		NewRevisionNumber:     1,
   245  		NewValidProofOutputs:  fc.ValidProofOutputs,
   246  		NewMissedProofOutputs: fc.MissedProofOutputs,
   247  	}
   248  
   249  	// create transaction containing the revision
   250  	signedTxn := types.Transaction{
   251  		FileContractRevisions: []types.FileContractRevision{rev},
   252  		TransactionSignatures: []types.TransactionSignature{{
   253  			ParentID:       crypto.Hash(fcid),
   254  			CoveredFields:  types.CoveredFields{FileContractRevisions: []uint64{0}},
   255  			PublicKeyIndex: 0, // hostdb key is always first -- see negotiateContract
   256  		}},
   257  	}
   258  
   259  	// sign the transaction
   260  	encodedSig := crypto.SignHash(signedTxn.SigHash(0), sk)
   261  	signedTxn.TransactionSignatures[0].Signature = encodedSig[:]
   262  
   263  	err = signedTxn.StandaloneValid(ct.contractor.blockHeight)
   264  	if err != nil {
   265  		t.Fatal(err)
   266  	}
   267  
   268  	// submit revision
   269  	err = ct.tpool.AcceptTransactionSet([]types.Transaction{signedTxn})
   270  	if err != nil {
   271  		t.Fatal(err)
   272  	}
   273  }