Exemple #1
0
// FileContractTerminationPayoutID returns the ID of a file contract
// termination payout, given the index of the payout in the termination. The
// ID is calculated by hashing the concatenation of the
// FileContractTerminationPayout Specifier, the ID of the file contract being
// terminated, and the payout index.
func (fcid FileContractID) FileContractTerminationPayoutID(i int) SiacoinOutputID {
	return SiacoinOutputID(crypto.HashAll(
		SpecifierFileContractTerminationPayout,
		fcid,
		i,
	))
}
Exemple #2
0
// StorageProofOutputID returns the ID of an output created by a file
// contract, given the status of the storage proof. The ID is calculating by
// hashing the concatenation of the StorageProofOutput Specifier, the ID of
// the file contract that the proof is for, a boolean indicating whether the
// proof was valid (true) or missed (false), and the index of the output
// within the file contract.
func (fcid FileContractID) StorageProofOutputID(proofStatus ProofStatus, i uint64) SiacoinOutputID {
	return SiacoinOutputID(crypto.HashAll(
		SpecifierStorageProofOutput,
		fcid,
		proofStatus,
		i,
	))
}
Exemple #3
0
// generateSpendableKey creates the keys and unlock conditions a given index of a
// seed.
func generateSpendableKey(seed modules.Seed, index uint64) spendableKey {
	// Generate the keys and unlock conditions.
	entropy := crypto.HashAll(seed, index)
	sk, pk := crypto.GenerateKeyPairDeterministic(entropy)
	return spendableKey{
		UnlockConditions: generateUnlockConditions(pk),
		SecretKeys:       []crypto.SecretKey{sk},
	}
}
Exemple #4
0
// ID returns the id of a transaction, which is taken by marshalling all of the
// fields except for the signatures and taking the hash of the result.
func (t Transaction) ID() crypto.Hash {
	return crypto.HashAll(
		t.SiacoinInputs,
		t.SiacoinOutputs,
		t.FileContracts,
		t.FileContractRevisions,
		t.StorageProofs,
		t.SiafundInputs,
		t.SiafundOutputs,
		t.MinerFees,
		t.ArbitraryData,
	)
}
Exemple #5
0
// SiafundOutputID returns the ID of a SiafundOutput at the given index, which
// is calculated by hashing the concatenation of the SiafundOutput Specifier,
// all of the fields in the transaction (except the signatures), and output
// index.
func (t Transaction) SiafundOutputID(i int) SiafundOutputID {
	return SiafundOutputID(crypto.HashAll(
		SpecifierSiafundOutput,
		t.SiacoinInputs,
		t.SiacoinOutputs,
		t.FileContracts,
		t.FileContractRevisions,
		t.StorageProofs,
		t.SiafundInputs,
		t.SiafundOutputs,
		t.MinerFees,
		t.ArbitraryData,
		i,
	))
}
Exemple #6
0
// removeFileContractRevisions removes all of the file contract revisions of a
// transaction from the unconfirmed consensus set.
func (tp *TransactionPool) removeFileContractRevisions(t types.Transaction) {
	for _, fcr := range t.FileContractRevisions {
		// Sanity check - the corresponding file contract should be in the
		// reference set.
		referenceID := crypto.HashAll(fcr.ParentID, fcr.NewRevisionNumber)
		if build.DEBUG {
			_, exists := tp.referenceFileContractRevisions[referenceID]
			if !exists {
				panic("cannot locate file contract to delete storage proof transaction")
			}
		}

		tp.fileContracts[fcr.ParentID] = tp.referenceFileContractRevisions[referenceID]
		delete(tp.referenceFileContractRevisions, referenceID)
	}
}
// storageProofSegment returns the index of the segment that needs to be proven
// exists in a file contract.
func (cs *ConsensusSet) storageProofSegment(fcid types.FileContractID) (index uint64, err error) {
	err = cs.db.View(func(tx *bolt.Tx) error {
		// Check that the parent file contract exists.
		fcBucket := tx.Bucket(FileContracts)
		fcBytes := fcBucket.Get(fcid[:])
		if fcBytes == nil {
			return ErrUnrecognizedFileContractID
		}

		// Decode the file contract.
		var fc types.FileContract
		err := encoding.Unmarshal(fcBytes, &fc)
		if build.DEBUG && err != nil {
			panic(err)
		}

		// Get the trigger block id.
		blockPath := tx.Bucket(BlockPath)
		triggerHeight := fc.WindowStart - 1
		if triggerHeight > types.BlockHeight(blockPath.Stats().KeyN) {
			return ErrUnfinishedFileContract
		}
		var triggerID types.BlockID
		copy(triggerID[:], blockPath.Get(encoding.EncUint64(uint64(triggerHeight))))

		// Get the index by appending the file contract ID to the trigger block and
		// taking the hash, then converting the hash to a numerical value and
		// modding it against the number of segments in the file. The result is a
		// random number in range [0, numSegments]. The probability is very
		// slightly weighted towards the beginning of the file, but because the
		// size difference between the number of segments and the random number
		// being modded, the difference is too small to make any practical
		// difference.
		seed := crypto.HashAll(triggerID, fcid)
		numSegments := int64(crypto.CalculateLeaves(fc.FileSize))
		seedInt := new(big.Int).SetBytes(seed[:])
		index = seedInt.Mod(seedInt, big.NewInt(numSegments)).Uint64()
		return nil
	})
	if err != nil {
		return 0, err
	}
	return index, nil
}
Exemple #8
0
// TestBlockHeader checks that BlockHeader returns the correct value, and that
// the hash is consistent with the old method for obtaining the hash.
func TestBlockHeader(t *testing.T) {
	var b Block
	b.ParentID[1] = 1
	b.Nonce[2] = 2
	b.Timestamp = 3
	b.MinerPayouts = []SiacoinOutput{{Value: NewCurrency64(4)}}
	b.Transactions = []Transaction{{ArbitraryData: [][]byte{[]byte{'5'}}}}

	id1 := b.ID()
	id2 := BlockID(crypto.HashBytes(encoding.Marshal(b.Header())))
	id3 := BlockID(crypto.HashAll(
		b.ParentID,
		b.Nonce,
		b.Timestamp,
		b.MerkleRoot(),
	))

	if id1 != id2 || id2 != id3 || id3 != id1 {
		t.Error("Methods for getting block id don't return the same results")
	}
}
Exemple #9
0
// uidEncryptionKey creates an encryption key that is used to decrypt a
// specific key file.
func uidEncryptionKey(masterKey crypto.TwofishKey, uid UniqueID) crypto.TwofishKey {
	return crypto.TwofishKey(crypto.HashAll(masterKey, uid))
}
Exemple #10
0
// deriveKey derives the key used to encrypt and decrypt a specific file piece.
func deriveKey(masterKey crypto.TwofishKey, chunkIndex, pieceIndex uint64) crypto.TwofishKey {
	return crypto.TwofishKey(crypto.HashAll(masterKey, chunkIndex, pieceIndex))
}
Exemple #11
0
// MinerPayoutID returns the ID of the miner payout at the given index, which
// is calculated by hashing the concatenation of the BlockID and the payout
// index.
func (b Block) MinerPayoutID(i uint64) SiacoinOutputID {
	return SiacoinOutputID(crypto.HashAll(
		b.ID(),
		i,
	))
}
Exemple #12
0
// CalculateWalletTransactionID is a helper function for determining the id of
// a wallet transaction.
func CalculateWalletTransactionID(tid types.TransactionID, oid types.OutputID) WalletTransactionID {
	return WalletTransactionID(crypto.HashAll(tid, oid))
}
Exemple #13
0
// negotiateContract establishes a connection to a host and negotiates an
// initial file contract according to the terms of the host.
func (hu *hostUploader) negotiateContract(filesize uint64, duration types.BlockHeight, renterAddress types.UnlockHash) error {
	conn, err := net.DialTimeout("tcp", string(hu.settings.IPAddress), 15*time.Second)
	if err != nil {
		return err
	}
	defer conn.Close()
	conn.SetDeadline(time.Now().Add(30 * time.Second))

	// inital calculations before connecting to host
	lockID := hu.renter.mu.RLock()
	height := hu.renter.blockHeight
	hu.renter.mu.RUnlock(lockID)

	renterCost := hu.settings.Price.Mul(types.NewCurrency64(filesize)).Mul(types.NewCurrency64(uint64(duration)))
	renterCost = renterCost.MulFloat(1.05) // extra buffer to guarantee we won't run out of money during revision
	payout := renterCost                   // no collateral

	// write rpcID
	if err := encoding.WriteObject(conn, modules.RPCUpload); err != nil {
		return errors.New("couldn't initiate RPC: " + err.Error())
	}

	// read host key
	// TODO: need to save this?
	var hostPublicKey types.SiaPublicKey
	if err := encoding.ReadObject(conn, &hostPublicKey, 256); err != nil {
		return errors.New("couldn't read host's public key: " + err.Error())
	}

	// create our own key by combining the renter entropy with the host key
	entropy := crypto.HashAll(hu.renter.entropy, hostPublicKey)
	ourSK, ourPK := crypto.StdKeyGen.GenerateDeterministic(entropy)
	ourPublicKey := types.SiaPublicKey{
		Algorithm: types.SignatureEd25519,
		Key:       ourPK[:],
	}
	hu.secretKey = ourSK // used to sign future revisions

	// send our public key
	if err := encoding.WriteObject(conn, ourPublicKey); err != nil {
		return errors.New("couldn't send our public key: " + err.Error())
	}

	// create unlock conditions
	hu.unlockConditions = types.UnlockConditions{
		PublicKeys:         []types.SiaPublicKey{ourPublicKey, hostPublicKey},
		SignaturesRequired: 2,
	}

	// create file contract
	fc := types.FileContract{
		FileSize:       0,
		FileMerkleRoot: crypto.Hash{}, // no proof possible without data
		WindowStart:    height + duration,
		WindowEnd:      height + duration + hu.settings.WindowSize,
		Payout:         payout,
		UnlockHash:     hu.unlockConditions.UnlockHash(),
		RevisionNumber: 0,
	}
	// outputs need account for tax
	fc.ValidProofOutputs = []types.SiacoinOutput{
		{Value: renterCost.Sub(types.Tax(hu.renter.blockHeight, fc.Payout)), UnlockHash: renterAddress},
		{Value: types.ZeroCurrency, UnlockHash: hu.settings.UnlockHash}, // no collateral
	}
	fc.MissedProofOutputs = []types.SiacoinOutput{
		// same as above
		fc.ValidProofOutputs[0],
		// goes to the void, not the renter
		{Value: types.ZeroCurrency, UnlockHash: types.UnlockHash{}},
	}

	// build transaction containing fc
	txnBuilder := hu.renter.wallet.StartTransaction()
	err = txnBuilder.FundSiacoins(fc.Payout)
	if err != nil {
		return err
	}
	txnBuilder.AddFileContract(fc)
	txn, parents := txnBuilder.View()
	txnSet := append(parents, txn)

	// calculate contract ID
	fcid := txn.FileContractID(0) // TODO: is it actually 0?

	// send txn
	if err := encoding.WriteObject(conn, txnSet); err != nil {
		txnBuilder.Drop()
		return errors.New("couldn't send our proposed contract: " + err.Error())
	}

	// read back acceptance
	var response string
	if err := encoding.ReadObject(conn, &response, 128); err != nil {
		txnBuilder.Drop()
		return errors.New("couldn't read the host's response to our proposed contract: " + err.Error())
	}
	if response != modules.AcceptResponse {
		txnBuilder.Drop()
		return errors.New("host rejected proposed contract: " + response)
	}

	// read back txn with host collateral.
	var hostTxnSet []types.Transaction
	if err := encoding.ReadObject(conn, &hostTxnSet, types.BlockSizeLimit); err != nil {
		txnBuilder.Drop()
		return errors.New("couldn't read the host's updated contract: " + err.Error())
	}

	// check that txn is okay. For now, no collateral will be added, so the
	// transaction sets should be identical.
	if len(hostTxnSet) != len(txnSet) {
		txnBuilder.Drop()
		return errors.New("host sent bad collateral transaction")
	}
	for i := range hostTxnSet {
		if hostTxnSet[i].ID() != txnSet[i].ID() {
			txnBuilder.Drop()
			return errors.New("host sent bad collateral transaction")
		}
	}

	// sign the txn and resend
	// NOTE: for now, we are assuming that the transaction has not changed
	// since we sent it. Otherwise, the txnBuilder would have to be updated
	// with whatever fields were added by the host.
	signedTxnSet, err := txnBuilder.Sign(true)
	if err != nil {
		txnBuilder.Drop()
		return err
	}
	if err := encoding.WriteObject(conn, signedTxnSet); err != nil {
		txnBuilder.Drop()
		return errors.New("couldn't send the contract signed by us: " + err.Error())
	}

	// read signed txn from host
	var signedHostTxnSet []types.Transaction
	if err := encoding.ReadObject(conn, &signedHostTxnSet, types.BlockSizeLimit); err != nil {
		txnBuilder.Drop()
		return errors.New("couldn't read the contract signed by the host: " + err.Error())
	}

	// submit to blockchain
	err = hu.renter.tpool.AcceptTransactionSet(signedHostTxnSet)
	if err == modules.ErrDuplicateTransactionSet {
		// this can happen if the renter is uploading to itself
		err = nil
	}
	if err != nil {
		txnBuilder.Drop()
		return err
	}

	// create initial fileContract object
	hu.contract = fileContract{
		ID:          fcid,
		IP:          hu.settings.IPAddress,
		WindowStart: fc.WindowStart,
	}

	lockID = hu.renter.mu.Lock()
	hu.renter.contracts[fcid] = fc
	hu.renter.mu.Unlock(lockID)

	return nil
}
Exemple #14
0
// unlockKey creates a wallet unlocking key from the input master key.
func unlockKey(masterKey crypto.TwofishKey) crypto.TwofishKey {
	return crypto.TwofishKey(crypto.HashAll(masterKey, unlockModifier))
}
Exemple #15
0
// sectorID returns the id that should be used when referring to a sector.
// There are lots of sectors, and to minimize their footprint a reduced size
// hash is used. Hashes are typically 256 bits to provide collision resistance
// against an attacker that is able to peform an obscene number of trials per
// second on each of an obscene number of machines. Potential collisions for
// sectors are limited because hosts have secret data that the attacker does
// not know which is used to salt the transformation of a sector hash to a
// sectorID. As a result, an attacker is limited in the number of times they
// can try to cause a collision - one random shot every time they upload a
// sector, and the attacker has limited ability to learn of the success of the
// attempt. Uploads are very slow; even on fast machines there will be less
// than 1000 per second. It is therefore safe to reduce the security from
// 256 bits to 96 bits, which has a collision resistance of 2^48. A reasonable
// upper bound for the number of sectors on a host is 2^32, corresponding with
// 16 PB of data.
//
// 12 bytes can be represented as a filepath using 16 base64 characters. This
// keeps the filesize small and therefore limits the amount of load placed on
// the filesystem when trying to manage hundreds of thousands or even tens of
// millions of sectors in a single folder.
func (sm *StorageManager) sectorID(sectorRootBytes []byte) []byte {
	saltedRoot := crypto.HashAll(sectorRootBytes, sm.sectorSalt)
	id := make([]byte, base64.RawURLEncoding.EncodedLen(12))
	base64.RawURLEncoding.Encode(id, saltedRoot[:12])
	return id
}
Exemple #16
0
// seedFileEncryptionKey creates an encryption key that is used to decrypt a
// specific key file.
func seedFileEncryptionKey(masterKey crypto.TwofishKey, sfuid SeedFileUID) crypto.TwofishKey {
	return crypto.TwofishKey(crypto.HashAll(masterKey, seedModifier, sfuid))
}