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