Exemple #1
0
// removeTailTransaction removes the most recent transaction from the pool. The
// most recent transaction is guaranteed not to have any dependents or
// children.
func (tp *TransactionPool) removeTailTransaction() {
	// Sanity check - the transaction list should not be empty if
	// removeTailTransaction has been called.
	if len(tp.transactionList) == 0 {
		if build.DEBUG {
			panic("calling removeTailTransaction when transaction list is empty")
		}
		return
	}

	// Grab the most recent transaction and remove it from the unconfirmed
	// consensus set piecemeal.
	t := tp.transactionList[len(tp.transactionList)-1]
	tp.removeSiacoinInputs(t)
	tp.removeSiacoinOutputs(t)
	tp.removeFileContracts(t)
	tp.removeFileContractRevisions(t)
	tp.removeStorageProofs(t)
	tp.removeSiafundInputs(t)
	tp.removeSiafundOutputs(t)

	// Sanity check - transaction hash should be in the list of transactions.
	if build.DEBUG {
		_, exists := tp.transactions[crypto.HashObject(t)]
		if !exists {
			panic("transaction not available in transaction list")
		}
	}

	// Remove the transaction from the transaction lists.
	delete(tp.transactions, crypto.HashObject(t))
	tp.transactionList = tp.transactionList[:len(tp.transactionList)-1]
	return
}
// TestIntegrationBlankEncryption probes the encryption process when the user
// supplies a blank encryption key during the encryption process.
func TestIntegrationBlankEncryption(t *testing.T) {
	// Create the wallet.
	wt, err := createBlankWalletTester("TestIntegrationBlankEncryption")
	if err != nil {
		t.Fatal(err)
	}
	defer wt.closeWt()
	// Encrypt the wallet using a blank key.
	seed, err := wt.wallet.Encrypt(crypto.TwofishKey{})
	if err != nil {
		t.Error(err)
	}

	// Try unlocking the wallet using a blank key.
	err = wt.wallet.Unlock(crypto.TwofishKey{})
	if err != modules.ErrBadEncryptionKey {
		t.Fatal(err)
	}
	// Try unlocking the wallet using the correct key.
	err = wt.wallet.Unlock(crypto.TwofishKey(crypto.HashObject(seed)))
	if err != nil {
		t.Fatal(err)
	}
	err = wt.wallet.Lock()
	if err != nil {
		t.Fatal(err)
	}
	postEncryptionTesting(wt.miner, wt.wallet, crypto.TwofishKey(crypto.HashObject(seed)))
}
Exemple #3
0
// encryptionKeys enumerates the possible encryption keys that can be derived
// from an input string.
func encryptionKeys(seedStr string) (validKeys []crypto.TwofishKey) {
	dicts := []mnemonics.DictionaryID{"english", "german", "japanese"}
	for _, dict := range dicts {
		seed, err := modules.StringToSeed(seedStr, dict)
		if err != nil {
			continue
		}
		validKeys = append(validKeys, crypto.TwofishKey(crypto.HashObject(seed)))
	}
	validKeys = append(validKeys, crypto.TwofishKey(crypto.HashObject(seedStr)))
	return validKeys
}
Exemple #4
0
// TestIntegrationMinerHeader checks that the header GET and POST calls are
// useful tools for mining blocks.
func TestIntegrationMinerHeader(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	st, err := createServerTester("TestIntegrationMinerHeader")
	if err != nil {
		t.Fatal(err)
	}
	defer st.server.Close()
	startingHeight := st.cs.Height()

	// Get a header that can be used for mining.
	resp, err := HttpGET("http://" + st.server.listener.Addr().String() + "/miner/header")
	if err != nil {
		t.Fatal(err)
	}
	defer resp.Body.Close()
	targetAndHeader, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatal(err)
	}

	// Twiddle the header bits until a block has been found.
	//
	// Note: this test treats the target as hardcoded, if the testing target is
	// changed, this test will also need to be changed.
	if types.RootTarget[0] != 128 {
		t.Fatal("test will fail because the testing constants have been unexpectedly changed")
	}
	var header [80]byte
	copy(header[:], targetAndHeader[32:])
	headerHash := crypto.HashObject(header)
	for headerHash[0] >= types.RootTarget[0] {
		header[35]++
		headerHash = crypto.HashObject(header)
	}

	// Submit the solved header through the api and check that the height of
	// the blockchain increases.
	resp, err = HttpPOST("http://"+st.server.listener.Addr().String()+"/miner/header", string(header[:]))
	if err != nil {
		t.Fatal(err)
	}
	defer resp.Body.Close()
	time.Sleep(500 * time.Millisecond)
	if st.cs.Height() != startingHeight+1 {
		t.Errorf("block height did not increase after trying to mine a block through the api, started at %v and ended at %v", startingHeight, st.cs.Height())
	}
}
Exemple #5
0
// acceptTransactionSet verifies that a transaction set is allowed to be in the
// transaction pool, and then adds it to the transaction pool.
func (tp *TransactionPool) acceptTransactionSet(ts []types.Transaction) error {
	if len(ts) == 0 {
		return errEmptySet
	}

	// Remove all transactions that have been confirmed in the transaction set.
	err := tp.db.Update(func(tx *bolt.Tx) error {
		oldTS := ts
		ts = []types.Transaction{}
		for _, txn := range oldTS {
			if !tp.transactionConfirmed(tx, txn.ID()) {
				ts = append(ts, txn)
			}
		}
		return nil
	})
	if err != nil {
		return err
	}
	// If no transactions remain, return a dublicate error.
	if len(ts) == 0 {
		return modules.ErrDuplicateTransactionSet
	}

	// Check the composition of the transaction set, including fees and
	// IsStandard rules.
	err = tp.checkTransactionSetComposition(ts)
	if err != nil {
		return err
	}

	// Check for conflicts with other transactions, which would indicate a
	// double-spend. Legal children of a transaction set will also trigger the
	// conflict-detector.
	oids := relatedObjectIDs(ts)
	var conflicts []TransactionSetID
	for _, oid := range oids {
		conflict, exists := tp.knownObjects[oid]
		if exists {
			conflicts = append(conflicts, conflict)
		}
	}
	if len(conflicts) > 0 {
		return tp.handleConflicts(ts, conflicts)
	}
	cc, err := tp.consensusSet.TryTransactionSet(ts)
	if err != nil {
		return modules.NewConsensusConflict(err.Error())
	}

	// Add the transaction set to the pool.
	setID := TransactionSetID(crypto.HashObject(ts))
	tp.transactionSets[setID] = ts
	for _, oid := range oids {
		tp.knownObjects[oid] = setID
	}
	tp.transactionSetDiffs[setID] = cc
	tp.transactionListSize += len(encoding.Marshal(ts))
	return nil
}
Exemple #6
0
// TestDeleteNonexistentSector checks that attempting to delete a storage
// sector that doesn't exist will fail with the appropriate error.
func TestDeleteNonexistentSector(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	st, err := createServerTester("TestDeleteNonexistentSector")
	if err != nil {
		t.Fatal(err)
	}
	defer st.server.Close()

	// These calls to delete imaginary sectors should fail for a few reasons:
	// - the given sector root strings are invalid
	// - the renter hasn't uploaded anything
	// - the host has no storage folders yet
	// Right now, the calls fail for the first reason. This test will report if that behavior changes.
	badHash := crypto.HashObject("fake object").String()
	err = st.stdPostAPI("/host/storage/sectors/delete/"+badHash, url.Values{})
	if err == nil || err.Error() != storagemanager.ErrSectorNotFound.Error() {
		t.Fatalf("expected error to be %v; got %v", storagemanager.ErrSectorNotFound, err)
	}
	wrongSize := "wrong size string"
	err = st.stdPostAPI("/host/storage/sectors/delete/"+wrongSize, url.Values{})
	if err == nil || err.Error() != crypto.ErrHashWrongLen.Error() {
		t.Fatalf("expected error to be %v; got %v", crypto.ErrHashWrongLen, err)
	}
}
// repeatCheckHelper recursively goes through nodes in the host map and adds
// them to the repeat maps.
func (hn *hostNode) repeatCheckHelper(ipMap, pkMap map[string]struct{}) error {
	ipStr := string(hn.hostEntry.NetAddress)
	pkStr := crypto.HashObject(hn.hostEntry.PublicKey).String()
	_, exists := ipMap[ipStr]
	if exists && hn.taken {
		return errors.New("found a duplicate ip address in the hostdb: " + ipStr)
	}
	_, exists = pkMap[pkStr]
	if exists && hn.taken {
		return errors.New("found a duplicate pubkey in the hostdb: " + ipStr + " " + pkStr)
	}
	if hn.taken {
		ipMap[ipStr] = struct{}{}
		pkMap[pkStr] = struct{}{}
	}

	if hn.left != nil {
		err := hn.left.repeatCheckHelper(ipMap, pkMap)
		if err != nil {
			return err
		}
	}
	if hn.right != nil {
		err := hn.right.repeatCheckHelper(ipMap, pkMap)
		if err != nil {
			return err
		}
	}
	return nil
}
Exemple #8
0
// GetHashInfo returns sufficient data about the hash that was
// provided to do more extensive lookups
func (e *Explorer) GetHashInfo(hash []byte) (interface{}, error) {
	if len(hash) < crypto.HashSize {
		return nil, errors.New("requested hash not long enough")
	}

	lockID := e.mu.RLock()
	defer e.mu.RUnlock(lockID)

	// Perform a lookup to tell which type of hash it is
	typeBytes, err := e.db.GetFromBucket("Hashes", hash[:crypto.HashSize])
	if err != nil {
		return nil, err
	}

	var hashType int
	err = encoding.Unmarshal(typeBytes, &hashType)
	if err != nil {
		return nil, err
	}

	switch hashType {
	case hashBlock:
		var id types.BlockID
		copy(id[:], hash[:])
		return e.db.getBlock(types.BlockID(id))
	case hashTransaction:
		var id crypto.Hash
		copy(id[:], hash[:])
		return e.db.getTransaction(id)
	case hashFilecontract:
		var id types.FileContractID
		copy(id[:], hash[:])
		return e.db.getFileContract(id)
	case hashCoinOutputID:
		var id types.SiacoinOutputID
		copy(id[:], hash[:])
		return e.db.getSiacoinOutput(id)
	case hashFundOutputID:
		var id types.SiafundOutputID
		copy(id[:], hash[:])
		return e.db.getSiafundOutput(id)
	case hashUnlockHash:
		// Check that the address is valid before doing a lookup
		if len(hash) != crypto.HashSize+types.UnlockHashChecksumSize {
			return nil, errors.New("address does not have a valid checksum")
		}
		var id types.UnlockHash
		copy(id[:], hash[:crypto.HashSize])
		uhChecksum := crypto.HashObject(id)

		givenChecksum := hash[crypto.HashSize : crypto.HashSize+types.UnlockHashChecksumSize]
		if !bytes.Equal(givenChecksum, uhChecksum[:types.UnlockHashChecksumSize]) {
			return nil, errors.New("address does not have a valid checksum")
		}

		return e.db.getAddressTransactions(id)
	default:
		return nil, errors.New("bad hash type")
	}
}
Exemple #9
0
// checkTransactionSetComposition checks if the transaction set is valid given
// the state of the pool. It does not check that each individual transaction
// would be legal in the next block, but does check things like miner fees and
// IsStandard.
func (tp *TransactionPool) checkTransactionSetComposition(ts []types.Transaction) error {
	// Check that the transaction set is not already known.
	setID := TransactionSetID(crypto.HashObject(ts))
	_, exists := tp.transactionSets[setID]
	if exists {
		return modules.ErrDuplicateTransactionSet
	}

	// Check that the transaction set has enough fees to justify adding it to
	// the transaction list.
	err := tp.checkMinerFees(ts)
	if err != nil {
		return err
	}

	// All checks after this are expensive.
	//
	// TODO: There is no DoS prevention mechanism in place to prevent repeated
	// expensive verifications of invalid transactions that are created on the
	// fly.

	// Check that all transactions follow 'Standard.md' guidelines.
	err = tp.IsStandardTransactionSet(ts)
	if err != nil {
		return err
	}
	return nil
}
Exemple #10
0
// DecodeAnnouncement decodes announcement bytes into a host announcement,
// verifying the prefix and the signature.
func DecodeAnnouncement(fullAnnouncement []byte) (na NetAddress, spk types.SiaPublicKey, err error) {
	// Read the first part of the announcement to get the intended host
	// announcement.
	var ha HostAnnouncement
	dec := encoding.NewDecoder(bytes.NewReader(fullAnnouncement))
	err = dec.Decode(&ha)
	if err != nil {
		return "", types.SiaPublicKey{}, err
	}

	// Check that the announcement was registered as a host announcement.
	if ha.Specifier != PrefixHostAnnouncement {
		return "", types.SiaPublicKey{}, ErrAnnNotAnnouncement
	}
	// Check that the public key is a recognized type of public key.
	if ha.PublicKey.Algorithm != types.SignatureEd25519 {
		return "", types.SiaPublicKey{}, ErrAnnUnrecognizedSignature
	}

	// Read the signature out of the reader.
	var sig crypto.Signature
	err = dec.Decode(&sig)
	if err != nil {
		return "", types.SiaPublicKey{}, err
	}
	// Verify the signature.
	var pk crypto.PublicKey
	copy(pk[:], ha.PublicKey.Key)
	annHash := crypto.HashObject(ha)
	err = crypto.VerifyHash(annHash, pk, sig)
	if err != nil {
		return "", types.SiaPublicKey{}, err
	}
	return ha.NetAddress, ha.PublicKey, nil
}
Exemple #11
0
// SeedToString converts a wallet seed to a human friendly string.
func SeedToString(seed Seed, did mnemonics.DictionaryID) (string, error) {
	fullChecksum := crypto.HashObject(seed)
	checksumSeed := append(seed[:], fullChecksum[:SeedChecksumSize]...)
	phrase, err := mnemonics.ToPhrase(checksumSeed, did)
	if err != nil {
		return "", err
	}
	return phrase.String(), nil
}
Exemple #12
0
// TestIntegrationBlocksMined checks that the BlocksMined function correctly
// indicates the number of real blocks and stale blocks that have been mined.
func TestIntegrationBlocksMined(t *testing.T) {
	mt, err := createMinerTester("TestIntegrationBlocksMined")
	if err != nil {
		t.Fatal(err)
	}

	// Get an unsolved header.
	unsolvedHeader, target, err := mt.miner.HeaderForWork()
	if err != nil {
		t.Fatal(err)
	}
	// Unsolve the header.
	for {
		unsolvedHeader.Nonce[0]++
		id := crypto.HashObject(unsolvedHeader)
		if bytes.Compare(target[:], id[:]) < 0 {
			break
		}
	}

	// Get two solved headers.
	header1, target, err := mt.miner.HeaderForWork()
	if err != nil {
		t.Fatal(err)
	}
	header1 = solveHeader(header1, target)
	header2, target, err := mt.miner.HeaderForWork()
	if err != nil {
		t.Fatal(err)
	}
	header2 = solveHeader(header2, target)

	// Submit the unsolved header followed by the two solved headers, this
	// should result in 1 real block mined and 1 stale block mined.
	err = mt.miner.SubmitHeader(unsolvedHeader)
	if err != modules.ErrBlockUnsolved {
		t.Fatal(err)
	}
	err = mt.miner.SubmitHeader(header1)
	if err != nil {
		t.Fatal(err)
	}
	err = mt.miner.SubmitHeader(header2)
	if err != modules.ErrNonExtendingBlock {
		t.Fatal(err)
	}

	goodBlocks, staleBlocks := mt.miner.BlocksMined()
	if goodBlocks != 1 {
		t.Error("expexting 1 good block")
	}
	if staleBlocks != 1 {
		t.Error(len(mt.miner.blocksFound))
		t.Error("expecting 1 stale block, got", staleBlocks)
	}
}
// solveHeader takes a block header as input and returns a solved block header
// as output.
func solveHeader(header types.BlockHeader, target types.Target) types.BlockHeader {
	// Solve the header.
	for {
		// Increment the nonce first to guarantee that a new header is formed
		// - this helps check for pointer errors.
		header.Nonce[0]++
		id := crypto.HashObject(header)
		if bytes.Compare(target[:], id[:]) >= 0 {
			break
		}
	}
	return header
}
Exemple #14
0
// SubmitHeader accepts a block header.
func (m *Miner) SubmitHeader(bh types.BlockHeader) error {
	if err := m.tg.Add(); err != nil {
		return err
	}
	defer m.tg.Done()

	// Because a call to managedSubmitBlock is required at the end of this
	// function, the first part needs to be wrapped in an anonymous function
	// for lock safety.
	var b types.Block
	err := func() error {
		m.mu.Lock()
		defer m.mu.Unlock()

		// Lookup the block that corresponds to the provided header.
		nonce := bh.Nonce
		bh.Nonce = [8]byte{}
		bPointer, bExists := m.blockMem[bh]
		arbData, arbExists := m.arbDataMem[bh]
		if !bExists || !arbExists {
			return errLateHeader
		}

		// Block is going to be passed to external memory, but the memory pointed
		// to by the transactions slice is still being modified - needs to be
		// copied. Same with the memory being pointed to by the arb data slice.
		b = *bPointer
		txns := make([]types.Transaction, len(b.Transactions))
		copy(txns, b.Transactions)
		b.Transactions = txns
		b.Transactions[0].ArbitraryData = [][]byte{arbData[:]}
		b.Nonce = nonce

		// Sanity check - block should have same id as header.
		bh.Nonce = nonce
		if types.BlockID(crypto.HashObject(bh)) != b.ID() {
			m.log.Critical("block reconstruction failed")
		}
		return nil
	}()
	if err != nil {
		m.log.Println("ERROR during call to SubmitHeader, pre SubmitBlock:", err)
		return err
	}
	err = m.managedSubmitBlock(b)
	if err != nil {
		m.log.Println("ERROR returned by managedSubmitBlock:", err)
		return err
	}
	return nil
}
Exemple #15
0
// StringToSeed converts a string to a wallet seed.
func StringToSeed(str string, did mnemonics.DictionaryID) (Seed, error) {
	// Decode the string into the checksummed byte slice.
	checksumSeedBytes, err := mnemonics.FromString(str, did)
	if err != nil {
		return Seed{}, err
	}

	// Copy the seed from the checksummed slice.
	var seed Seed
	copy(seed[:], checksumSeedBytes)
	fullChecksum := crypto.HashObject(seed)
	if len(checksumSeedBytes) != crypto.EntropySize+SeedChecksumSize || !bytes.Equal(fullChecksum[:SeedChecksumSize], checksumSeedBytes[crypto.EntropySize:]) {
		return Seed{}, errors.New("seed failed checksum verification")
	}
	return seed, nil
}
Exemple #16
0
// acceptTransactionSet verifies that a transaction set is allowed to be in the
// transaction pool, and then adds it to the transaction pool.
func (tp *TransactionPool) acceptTransactionSet(ts []types.Transaction) error {
	if len(ts) == 0 {
		return errEmptySet
	}

	// Check the composition of the transaction set, including fees and
	// IsStandard rules.
	err := tp.checkTransactionSetComposition(ts)
	if err != nil {
		return err
	}

	// Check for conflicts with other transactions, which would indicate a
	// double-spend. Legal children of a transaction set will also trigger the
	// conflict-detector.
	oids := relatedObjectIDs(ts)
	var conflicts []TransactionSetID
	for _, oid := range oids {
		conflict, exists := tp.knownObjects[oid]
		if exists {
			conflicts = append(conflicts, conflict)
		}
	}
	if len(conflicts) > 0 {
		return tp.handleConflicts(ts, conflicts)
	}
	cc, err := tp.consensusSet.TryTransactionSet(ts)
	if err != nil {
		return modules.NewConsensusConflict(err.Error())
	}

	// Add the transaction set to the pool.
	setID := TransactionSetID(crypto.HashObject(ts))
	tp.transactionSets[setID] = ts
	for _, oid := range oids {
		tp.knownObjects[oid] = setID
	}
	tp.transactionSetDiffs[setID] = cc
	tp.transactionListSize += len(encoding.Marshal(ts))
	return nil
}
Exemple #17
0
// initEncryption checks that the provided encryption key is the valid
// encryption key for the wallet. If encryption has not yet been established
// for the wallet, an encryption key is created.
func (w *Wallet) initEncryption(masterKey crypto.TwofishKey) (modules.Seed, error) {
	// Check if the wallet encryption key has already been set.
	if len(w.persist.EncryptionVerification) != 0 {
		return modules.Seed{}, errReencrypt
	}

	// Create a random seed and use it to generate the seed file for the
	// wallet.
	var seed modules.Seed
	_, err := rand.Read(seed[:])
	if err != nil {
		return modules.Seed{}, err
	}

	// If the input key is blank, use the seed to create the master key.
	// Otherwise, use the input key.
	if masterKey == (crypto.TwofishKey{}) {
		masterKey = crypto.TwofishKey(crypto.HashObject(seed))
	}
	err = w.createSeed(masterKey, seed)
	if err != nil {
		return modules.Seed{}, err
	}

	// Establish the encryption verification using the masterKey. After this
	// point, the wallet is encrypted.
	uk := uidEncryptionKey(masterKey, w.persist.UID)
	encryptionBase := make([]byte, encryptionVerificationLen)
	w.persist.EncryptionVerification, err = uk.EncryptBytes(encryptionBase)
	if err != nil {
		return modules.Seed{}, err
	}
	err = w.saveSettings()
	if err != nil {
		return modules.Seed{}, err
	}
	return seed, nil
}
// TestIntegrationHeaderForWorkUpdates checks that HeaderForWork starts
// returning headers on the new block after a block has been submitted to the
// consensus set.
func TestIntegrationHeaderForWorkUpdates(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	mt, err := createMinerTester("TestIntegrationHeaderForWorkUpdates")
	if err != nil {
		t.Fatal(err)
	}

	// Get a header to advance into the header memory.
	_, _, err = mt.miner.HeaderForWork()
	if err != nil {
		t.Fatal(err)
	}

	// Submit a block, which should trigger a header change.
	_, err = mt.miner.AddBlock()
	if err != nil {
		t.Fatal(err)
	}

	// Get a header to grind on.
	header, target, err := mt.miner.HeaderForWork()
	if err != nil {
		t.Fatal(err)
	}
	solvedHeader := solveHeader(header, target)

	// Submit the header.
	err = mt.miner.SubmitHeader(solvedHeader)
	if err != nil {
		t.Fatal(err)
	}
	if !mt.cs.InCurrentPath(types.BlockID(crypto.HashObject(solvedHeader))) {
		t.Error("header from solved block is not in the current path")
	}
}
Exemple #19
0
// walletInitHandler handles API calls to /wallet/init.
func (api *API) walletInitHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	var encryptionKey crypto.TwofishKey
	if req.FormValue("encryptionpassword") != "" {
		encryptionKey = crypto.TwofishKey(crypto.HashObject(req.FormValue("encryptionpassword")))
	}
	seed, err := api.wallet.Encrypt(encryptionKey)
	if err != nil {
		WriteError(w, Error{"error when calling /wallet/init: " + err.Error()}, http.StatusBadRequest)
		return
	}

	dictID := mnemonics.DictionaryID(req.FormValue("dictionary"))
	if dictID == "" {
		dictID = "english"
	}
	seedStr, err := modules.SeedToString(seed, dictID)
	if err != nil {
		WriteError(w, Error{"error when calling /wallet/init: " + err.Error()}, http.StatusBadRequest)
		return
	}
	WriteJSON(w, WalletInitPOST{
		PrimarySeed: seedStr,
	})
}
Exemple #20
0
// SubmitHeader accepts a block header.
func (m *Miner) SubmitHeader(bh types.BlockHeader) error {
	m.mu.Lock()

	// Lookup the block that corresponds to the provided header.
	var b types.Block
	nonce := bh.Nonce
	bh.Nonce = [8]byte{}
	bPointer, bExists := m.blockMem[bh]
	arbData, arbExists := m.arbDataMem[bh]
	if !bExists || !arbExists {
		m.log.Println("ERROR:", errLateHeader)
		m.mu.Unlock()
		return errLateHeader
	}

	// Block is going to be passed to external memory, but the memory pointed
	// to by the transactions slice is still being modified - needs to be
	// copied. Same with the memory being pointed to by the arb data slice.
	b = *bPointer
	txns := make([]types.Transaction, len(b.Transactions))
	copy(txns, b.Transactions)
	b.Transactions = txns
	b.Transactions[0].ArbitraryData = [][]byte{arbData[:]}
	b.Nonce = nonce

	// Sanity check - block should have same id as header.
	if build.DEBUG {
		bh.Nonce = nonce
		if types.BlockID(crypto.HashObject(bh)) != b.ID() {
			panic("block reconstruction failed")
		}
	}

	m.mu.Unlock()
	return m.SubmitBlock(b)
}
Exemple #21
0
// walletEncryptHandlerPOST handles a POST call to /wallet/encrypt.
func (srv *Server) walletEncryptHandlerPOST(w http.ResponseWriter, req *http.Request) {
	var encryptionKey crypto.TwofishKey
	if req.FormValue("encryptionpassword") != "" {
		encryptionKey = crypto.TwofishKey(crypto.HashObject(req.FormValue("encryptionpassword")))
	}
	seed, err := srv.wallet.Encrypt(encryptionKey)
	if err != nil {
		writeError(w, "error when calling /wallet/encrypt: "+err.Error(), http.StatusBadRequest)
		return
	}

	dictID := mnemonics.DictionaryID(req.FormValue("dictionary"))
	if dictID == "" {
		dictID = "english"
	}
	seedStr, err := modules.SeedToString(seed, dictID)
	if err != nil {
		writeError(w, "error when calling /wallet/encrypt: "+err.Error(), http.StatusBadRequest)
		return
	}
	writeJSON(w, WalletEncryptPOST{
		PrimarySeed: seedStr,
	})
}
Exemple #22
0
// handleConflicts detects whether the conflicts in the transaction pool are
// legal children of the new transaction pool set or not.
func (tp *TransactionPool) handleConflicts(ts []types.Transaction, conflicts []TransactionSetID) error {
	// Create a list of all the transaction ids that compose the set of
	// conflicts.
	conflictMap := make(map[types.TransactionID]TransactionSetID)
	for _, conflict := range conflicts {
		conflictSet := tp.transactionSets[conflict]
		for _, conflictTxn := range conflictSet {
			conflictMap[conflictTxn.ID()] = conflict
		}
	}

	// Discard all duplicate transactions from the input transaction set.
	var dedupSet []types.Transaction
	for _, t := range ts {
		_, exists := conflictMap[t.ID()]
		if exists {
			continue
		}
		dedupSet = append(dedupSet, t)
	}
	if len(dedupSet) == 0 {
		return modules.ErrDuplicateTransactionSet
	}
	// If transactions were pruned, it's possible that the set of
	// dependencies/conflicts has also reduced. To minimize computational load
	// on the consensus set, we want to prune out all of the conflicts that are
	// no longer relevant. As an example, consider the transaction set {A}, the
	// set {B}, and the new set {A, C}, where C is dependent on B. {A} and {B}
	// are both conflicts, but after deduplication {A} is no longer a conflict.
	// This is recursive, but it is guaranteed to run only once as the first
	// deduplication is guaranteed to be complete.
	if len(dedupSet) < len(ts) {
		oids := relatedObjectIDs(dedupSet)
		var conflicts []TransactionSetID
		for _, oid := range oids {
			conflict, exists := tp.knownObjects[oid]
			if exists {
				conflicts = append(conflicts, conflict)
			}
		}
		return tp.handleConflicts(dedupSet, conflicts)
	}

	// Merge all of the conflict sets with the input set (input set goes last
	// to preserve dependency ordering), and see if the set as a whole is both
	// small enough to be legal and valid as a set. If no, return an error. If
	// yes, add the new set to the pool, and eliminate the old set. The output
	// diff objects can be repeated, (no need to remove those). Just need to
	// remove the conflicts from tp.transactionSets.
	var superset []types.Transaction
	supersetMap := make(map[TransactionSetID]struct{})
	for _, conflict := range conflictMap {
		supersetMap[conflict] = struct{}{}
	}
	for conflict := range supersetMap {
		superset = append(superset, tp.transactionSets[conflict]...)
	}
	superset = append(superset, dedupSet...)

	// Check the composition of the transaction set, including fees and
	// IsStandard rules (this is a new set, the rules must be rechecked).
	err := tp.checkTransactionSetComposition(superset)
	if err != nil {
		return err
	}

	// Check that the transaction set is valid.
	cc, err := tp.consensusSet.TryTransactionSet(superset)
	if err != nil {
		return modules.NewConsensusConflict(err.Error())
	}

	// Remove the conflicts from the transaction pool. The diffs do not need to
	// be removed, they will be overwritten later in the function.
	for _, conflict := range conflictMap {
		conflictSet := tp.transactionSets[conflict]
		tp.transactionListSize -= len(encoding.Marshal(conflictSet))
		delete(tp.transactionSets, conflict)
		delete(tp.transactionSetDiffs, conflict)
	}

	// Add the transaction set to the pool.
	setID := TransactionSetID(crypto.HashObject(superset))
	tp.transactionSets[setID] = superset
	for _, diff := range cc.SiacoinOutputDiffs {
		tp.knownObjects[ObjectID(diff.ID)] = setID
	}
	for _, diff := range cc.FileContractDiffs {
		tp.knownObjects[ObjectID(diff.ID)] = setID
	}
	for _, diff := range cc.SiafundOutputDiffs {
		tp.knownObjects[ObjectID(diff.ID)] = setID
	}
	tp.transactionSetDiffs[setID] = cc
	tp.transactionListSize += len(encoding.Marshal(superset))
	return nil
}
Exemple #23
0
// TestIntegrationBlocksMined checks that the BlocksMined function correctly
// indicates the number of real blocks and stale blocks that have been mined.
func TestIntegrationBlocksMined(t *testing.T) {
	mt, err := createMinerTester("TestIntegrationBlocksMined")
	if err != nil {
		t.Fatal(err)
	}

	// Get an unsolved header.
	unsolvedHeader, target, err := mt.miner.HeaderForWork()
	if err != nil {
		t.Fatal(err)
	}
	// Unsolve the header - necessary because the target is very low when
	// mining.
	for {
		unsolvedHeader.Nonce[0]++
		id := crypto.HashObject(unsolvedHeader)
		if bytes.Compare(target[:], id[:]) < 0 {
			break
		}
	}

	// Get two solved headers.
	header1, target, err := mt.miner.HeaderForWork()
	if err != nil {
		t.Fatal(err)
	}
	header1 = solveHeader(header1, target)
	header2, target, err := mt.miner.HeaderForWork()
	if err != nil {
		t.Fatal(err)
	}
	header2 = solveHeader(header2, target)

	// Submit the unsolved header followed by the two solved headers, this
	// should result in 1 real block mined and 1 stale block mined.
	err = mt.miner.SubmitHeader(unsolvedHeader)
	if err != modules.ErrBlockUnsolved {
		t.Fatal(err)
	}
	err = mt.miner.SubmitHeader(header1)
	if err != nil {
		t.Fatal(err)
	}
	err = mt.miner.SubmitHeader(header2)
	if err != modules.ErrNonExtendingBlock {
		t.Fatal(err)
	}
	goodBlocks, staleBlocks := mt.miner.BlocksMined()
	if goodBlocks != 1 {
		t.Error("expexting 1 good block")
	}
	if staleBlocks != 1 {
		t.Error("expecting 1 stale block, got", staleBlocks)
	}

	// Reboot the miner and verify that the block record has persisted.
	err = mt.miner.Close()
	if err != nil {
		t.Fatal(err)
	}
	rebootMiner, err := New(mt.cs, mt.tpool, mt.wallet, filepath.Join(mt.persistDir, modules.MinerDir))
	if err != nil {
		t.Fatal(err)
	}
	goodBlocks, staleBlocks = rebootMiner.BlocksMined()
	if goodBlocks != 1 {
		t.Error("expexting 1 good block")
	}
	if staleBlocks != 1 {
		t.Error("expecting 1 stale block, got", staleBlocks)
	}
}
Exemple #24
0
// ID returns the ID of a Block, which is calculated by hashing the
// concatenation of the block's parent's ID, nonce, and the result of the
// b.MerkleRoot().
func (b Block) ID() BlockID {
	return BlockID(crypto.HashObject(b.Header()))
}
Exemple #25
0
// SiaClaimOutputID returns the ID of the SiacoinOutput that is created when
// the siafund output is spent. The ID is the hash the SiafundOutputID.
func (id SiafundOutputID) SiaClaimOutputID() SiacoinOutputID {
	return SiacoinOutputID(crypto.HashObject(id))
}
Exemple #26
0
// String returns the hex representation of the unlock hash as a string - this
// includes a checksum.
func (uh UnlockHash) String() string {
	uhChecksum := crypto.HashObject(uh)
	return fmt.Sprintf("%x%x", uh[:], uhChecksum[:UnlockHashChecksumSize])
}
Exemple #27
0
// TestPrimarySeed checks that the correct seed is returned when calling
// PrimarySeed.
func TestPrimarySeed(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}

	// Create a wallet and fetch the seed at startup.
	dir := build.TempDir(modules.WalletDir, "TestPrimarySeed")
	g, err := gateway.New(":0", filepath.Join(dir, modules.GatewayDir))
	if err != nil {
		t.Fatal(err)
	}
	cs, err := consensus.New(g, filepath.Join(dir, modules.ConsensusDir))
	if err != nil {
		t.Fatal(err)
	}
	tp, err := transactionpool.New(cs, g)
	if err != nil {
		t.Fatal(err)
	}
	w, err := New(cs, tp, filepath.Join(dir, modules.WalletDir))
	if err != nil {
		t.Fatal(err)
	}
	seed, err := w.Encrypt(crypto.TwofishKey{})
	if err != nil {
		t.Fatal(err)
	}
	err = w.Unlock(crypto.TwofishKey(crypto.HashObject(seed)))
	if err != nil {
		t.Fatal(err)
	}

	primarySeed, progress, err := w.PrimarySeed()
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(primarySeed[:], seed[:]) {
		t.Error("PrimarySeed is returning a value inconsitent with the seed returned by Encrypt")
	}
	if progress != 0 {
		t.Error("primary seed is returning the wrong progress")
	}
	_, err = w.NextAddress()
	if err != nil {
		t.Fatal(err)
	}
	_, progress, err = w.PrimarySeed()
	if err != nil {
		t.Fatal(err)
	}
	if progress != 1 {
		t.Error("primary seed is returning the wrong progress")
	}

	// Lock then unlock the wallet and check the responses.
	err = w.Lock()
	if err != nil {
		t.Fatal(err)
	}
	_, _, err = w.PrimarySeed()
	if err != modules.ErrLockedWallet {
		t.Error("unexpected err:", err)
	}
	err = w.Unlock(crypto.TwofishKey(crypto.HashObject(seed)))
	if err != nil {
		t.Fatal(err)
	}
	primarySeed, progress, err = w.PrimarySeed()
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(primarySeed[:], seed[:]) {
		t.Error("PrimarySeed is returning a value inconsitent with the seed returned by Encrypt")
	}
	if progress != 1 {
		t.Error("progress reporting an unexpected value")
	}
}
Exemple #28
0
// TestLoadSeed checks that a seed can be successfully recovered from a wallet,
// and then remain available on subsequent loads of the wallet.
func TestLoadSeed(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	wt, err := createWalletTester("TestLoadSeed")
	if err != nil {
		t.Fatal(err)
	}
	seed, _, err := wt.wallet.PrimarySeed()
	if err != nil {
		t.Fatal(err)
	}
	allSeeds, err := wt.wallet.AllSeeds()
	if err != nil {
		t.Fatal(err)
	}
	if len(allSeeds) != 1 {
		t.Error("AllSeeds should be returning the primary seed.")
	}
	if allSeeds[0] != seed {
		t.Error("AllSeeds returned the wrong seed")
	}

	dir := filepath.Join(build.TempDir(modules.WalletDir, "TestLoadSeed - 0"), modules.WalletDir)
	w, err := New(wt.cs, wt.tpool, dir)
	if err != nil {
		t.Fatal(err)
	}
	newSeed, err := w.Encrypt(crypto.TwofishKey{})
	if err != nil {
		t.Fatal(err)
	}
	err = w.Unlock(crypto.TwofishKey(crypto.HashObject(newSeed)))
	if err != nil {
		t.Fatal(err)
	}
	// Balance of wallet should be 0.
	siacoinBal, _, _ := w.ConfirmedBalance()
	if siacoinBal.Cmp(types.NewCurrency64(0)) != 0 {
		t.Error("fresh wallet should not have a balance")
	}
	err = w.LoadSeed(crypto.TwofishKey(crypto.HashObject(newSeed)), seed)
	if err != nil {
		t.Fatal(err)
	}
	allSeeds, err = w.AllSeeds()
	if err != nil {
		t.Fatal(err)
	}
	if len(allSeeds) != 2 {
		t.Error("AllSeeds should be returning the primary seed with the recovery seed.")
	}
	if !bytes.Equal(allSeeds[0][:], newSeed[:]) {
		t.Error("AllSeeds returned the wrong seed")
	}
	if !bytes.Equal(allSeeds[1][:], seed[:]) {
		t.Error("AllSeeds returned the wrong seed")
	}

	// Rather than worry about a rescan, which isn't implemented and has
	// synchronization difficulties, just load a new wallet from the same
	// settings file - the same effect is achieved without the difficulties.
	w2, err := New(wt.cs, wt.tpool, dir)
	if err != nil {
		t.Fatal(err)
	}
	err = w2.Unlock(crypto.TwofishKey(crypto.HashObject(newSeed)))
	if err != nil {
		t.Fatal(err)
	}
	siacoinBal2, _, _ := w2.ConfirmedBalance()
	if siacoinBal2.Cmp(types.NewCurrency64(0)) <= 0 {
		t.Error("wallet failed to load a seed with money in it")
	}
	allSeeds, err = w2.AllSeeds()
	if err != nil {
		t.Fatal(err)
	}
	if len(allSeeds) != 2 {
		t.Error("AllSeeds should be returning the primary seed with the recovery seed.")
	}
	if !bytes.Equal(allSeeds[0][:], newSeed[:]) {
		t.Error("AllSeeds returned the wrong seed")
	}
	if !bytes.Equal(allSeeds[1][:], seed[:]) {
		t.Error("AllSeeds returned the wrong seed")
	}
}
Exemple #29
0
// TestPrimarySeed checks that the correct seed is returned when calling
// PrimarySeed.
func TestPrimarySeed(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	// Start with a blank wallet tester.
	wt, err := createBlankWalletTester("TestPrimarySeed")
	if err != nil {
		t.Fatal(err)
	}

	// Create a seed and unlock the wallet.
	seed, err := wt.wallet.Encrypt(crypto.TwofishKey{})
	if err != nil {
		t.Fatal(err)
	}
	err = wt.wallet.Unlock(crypto.TwofishKey(crypto.HashObject(seed)))
	if err != nil {
		t.Fatal(err)
	}

	// Try getting an address, see that the seed advances correctly.
	primarySeed, progress, err := wt.wallet.PrimarySeed()
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(primarySeed[:], seed[:]) {
		t.Error("PrimarySeed is returning a value inconsitent with the seed returned by Encrypt")
	}
	if progress != 0 {
		t.Error("primary seed is returning the wrong progress")
	}
	_, err = wt.wallet.NextAddress()
	if err != nil {
		t.Fatal(err)
	}
	_, progress, err = wt.wallet.PrimarySeed()
	if err != nil {
		t.Fatal(err)
	}
	if progress != 1 {
		t.Error("primary seed is returning the wrong progress")
	}

	// Lock then unlock the wallet and check the responses.
	err = wt.wallet.Lock()
	if err != nil {
		t.Fatal(err)
	}
	_, _, err = wt.wallet.PrimarySeed()
	if err != modules.ErrLockedWallet {
		t.Error("unexpected err:", err)
	}
	err = wt.wallet.Unlock(crypto.TwofishKey(crypto.HashObject(seed)))
	if err != nil {
		t.Fatal(err)
	}
	primarySeed, progress, err = wt.wallet.PrimarySeed()
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(primarySeed[:], seed[:]) {
		t.Error("PrimarySeed is returning a value inconsitent with the seed returned by Encrypt")
	}
	if progress != 1 {
		t.Error("progress reporting an unexpected value")
	}
}
Exemple #30
0
// ReceiveConsensusSetUpdate gets called to inform the transaction pool of
// changes to the consensus set.
func (tp *TransactionPool) ReceiveConsensusSetUpdate(cc modules.ConsensusChange) {
	lockID := tp.mu.Lock()
	defer tp.mu.Unlock(lockID)

	// Save all of the reverted transactions. Transactions need to appear in
	// 'unconfirmedTxns' in the same order that they would appear in the
	// blockchain. 'revertedBlocks' is backwards (first element has highest
	// height), so each time a new block processed, the transactions need to be
	// prepended to the list of unconfirmed transactions.
	var unconfirmedTxns []types.Transaction
	for _, block := range cc.RevertedBlocks {
		unconfirmedTxns = append(block.Transactions, unconfirmedTxns...)
	}

	// Delete the hashes of each unconfirmed transaction from the 'already
	// seen' list.
	for _, txn := range unconfirmedTxns {
		// Sanity check - transaction should be in the list of already seen
		// transactions.
		if build.DEBUG {
			_, exists := tp.transactions[crypto.HashObject(txn)]
			if !exists {
				panic("transaction should be in the list of already seen transactions")
			}
		}
		delete(tp.transactions, crypto.HashObject(txn))
	}

	// Add all of the current unconfirmed transactions to the unconfirmed
	// transaction list.
	unconfirmedTxns = append(unconfirmedTxns, tp.transactionList...)

	// Purge the pool of unconfirmed transactions so that there is no
	// interference from unconfirmed transactions during the application of
	// potentially conflicting transactions that have been added to the
	// blockchain.
	tp.purge()

	// Apply consensus set diffs and adjust the height.
	tp.applyDiffs(cc.SiacoinOutputDiffs, cc.FileContractDiffs, cc.SiafundOutputDiffs, modules.DiffApply)
	tp.consensusSetHeight -= types.BlockHeight(len(cc.RevertedBlocks))
	tp.consensusSetHeight += types.BlockHeight(len(cc.AppliedBlocks))

	// Mark all of the newly applied transactions as 'already seen'.
	for _, block := range cc.AppliedBlocks {
		for _, txn := range block.Transactions {
			tp.transactions[crypto.HashObject(txn)] = struct{}{}
		}
	}

	// Add all potential unconfirmed transactions back into the pool after
	// checking that they are still valid.
	for _, txn := range unconfirmedTxns {
		// Skip transactions that are now in the consensus set or are otherwise
		// repeats.
		_, exists := tp.transactions[crypto.HashObject(txn)]
		if exists {
			continue
		}

		// Check that the transaction is still valid given the updated
		// consensus set.
		err := tp.validUnconfirmedTransaction(txn)
		if err != nil {
			continue
		}

		// Add the transaction back to the pool.
		tp.addTransactionToPool(txn)
	}

	// Inform subscribers that an update has executed.
	tp.updateSubscribers(cc, tp.transactionList, tp.unconfirmedSiacoinOutputDiffs())
}