Example #1
0
File: blockdb.go Project: mm3/Sia
// RemoveBlock removes a block from the "end" of the chain, i.e. the block
// with the largest height.
func (db *boltDB) RemoveBlock() error {
	return db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("chain"))
		key := encoding.EncUint64(uint64(b.Stats().KeyN - 1))
		return b.Delete(key)
	})
}
Example #2
0
// popPath removes a block from the "end" of the chain, i.e. the block
// with the largest height.
func (db *setDB) popPath() error {
	return db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket(BlockPath)
		key := encoding.EncUint64(uint64(b.Stats().KeyN - 1))
		return b.Delete(key)
	})
}
Example #3
0
// openDB loads the set database and populates it with the necessary buckets
func openDB(filename string) (*setDB, error) {
	db, err := persist.OpenDatabase(meta, filename)
	if err != nil {
		return nil, err
	}

	// Enumerate the database buckets.
	buckets := [][]byte{
		BlockPath,
		BlockMap,
		SiacoinOutputs,
		FileContracts,
		FileContractExpirations,
		SiafundOutputs,
		SiafundPool,
		DSCOBuckets,
	}

	// Initialize the database.
	err = db.Update(func(tx *bolt.Tx) error {
		// Create the database buckets.
		for _, bucket := range buckets {
			_, err := tx.CreateBucketIfNotExists(bucket)
			if err != nil {
				return err
			}
		}

		// Initilize the consistency guards.
		cg, err := tx.CreateBucketIfNotExists(ConsistencyGuard)
		if err != nil {
			return err
		}
		gs := cg.Get(GuardStart)
		ge := cg.Get(GuardEnd)
		// Database is consistent if both are nil, or if both are equal.
		// Database is inconsistent otherwise.
		if (gs != nil && ge != nil && bytes.Equal(gs, ge)) || gs == nil && ge == nil {
			cg.Put(GuardStart, encoding.EncUint64(1))
			cg.Put(GuardEnd, encoding.EncUint64(1))
			return nil
		}
		return errDBInconsistent
	})
	return &setDB{db, true}, err
}
Example #4
0
// pushPath inserts a block into the database at the "end" of the chain, i.e.
// the current height + 1.
//
// DEPRECATED
func (db *setDB) pushPath(bid types.BlockID) error {
	value := encoding.Marshal(bid)
	return db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket(BlockPath)
		key := encoding.EncUint64(uint64(b.Stats().KeyN))
		return b.Put(key, value)
	})
}
Example #5
0
File: blockdb.go Project: mm3/Sia
// AddBlock inserts a block into the database at the "end" of the chain, i.e.
// the current height + 1.
func (db *boltDB) AddBlock(block types.Block) error {
	value := encoding.Marshal(block)
	return db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("chain"))
		key := encoding.EncUint64(uint64(b.Stats().KeyN))
		return b.Put(key, value)
	})
}
Example #6
0
// stopConsistencyGuard is the complement function to startConsistencyGuard.
// startConsistencyGuard should be called any time that consensus changes are
// starting, and stopConsistencyGuard should be called when the consensus
// changes are finished. The guards are necessary because one set of changes
// may occur over multiple boltdb transactions.
func (db *setDB) stopConsistencyGuard() {
	err := db.Update(func(tx *bolt.Tx) error {
		cg := tx.Bucket(ConsistencyGuard)
		i := encoding.DecUint64(cg.Get(GuardEnd))
		return cg.Put(GuardEnd, encoding.EncUint64(i+1))
	})
	if err != nil && build.DEBUG {
		panic(err)
	}
}
Example #7
0
// startConsistencyGuard activates a consistency guard on the database. This is
// necessary because the consensus set makes one atomic database change, but
// does so using several boltdb transactions. The 'guard' is actually two
// values, a 'GuardStart' and a 'GuardEnd'. 'GuardStart' is incremented when
// consensus changes begin, and 'GuardEnd' is incremented when consensus
// changes finish. If 'GuardStart' is not equal to 'GuardEnd' when
// startConsistencyGuard is called, the database is likely corrupt.
func (db *setDB) startConsistencyGuard() error {
	return db.Update(func(tx *bolt.Tx) error {
		cg := tx.Bucket(ConsistencyGuard)
		gs := cg.Get(GuardStart)
		if !bytes.Equal(gs, cg.Get(GuardEnd)) {
			return errDBInconsistent
		}
		i := encoding.DecUint64(gs)
		return cg.Put(GuardStart, encoding.EncUint64(i+1))
	})
}
Example #8
0
// startConsistencyGuard activates a consistency guard on the database. This is
// necessary because the consensus set makes one atomic database change, but
// does so using several boltdb transactions. The 'guard' is actually two
// values, a 'GuardStart' and a 'GuardEnd'. 'GuardStart' is incremented when
// consensus changes begin, and 'GuardEnd' is incremented when consensus
// changes finish. If 'GuardStart' is not equal to 'GuardEnd' when
// startConsistencyGuard is called, the database is likely corrupt.
func (db *setDB) startConsistencyGuard() error {
	return db.Update(func(tx *bolt.Tx) error {
		cg := tx.Bucket(ConsistencyGuard)
		gs := cg.Get(GuardStart)
		if !bytes.Equal(gs, cg.Get(GuardEnd)) {
			println("Database is inconsistent - please reset your database by redownloading it or loading a consistent backup. This can happen if you close Sia unexpectedly.")
			return errDBInconsistent
		}
		i := encoding.DecUint64(gs)
		return cg.Put(GuardStart, encoding.EncUint64(i+1))
	})
}
Example #9
0
func removeDSCOBucket(tx *bolt.Tx, bh types.BlockHeight) error {
	bhBytes := encoding.EncUint64(uint64(bh))
	bucketID := append(prefix_dsco, bhBytes...)
	err := tx.DeleteBucket(bucketID)
	if err != nil {
		return err
	}
	b := tx.Bucket(DSCOBuckets)
	if build.DEBUG && b.Get(bhBytes) == nil {
		panic(errNilItem)
	}
	return b.Delete(bhBytes)
}
Example #10
0
func addDSCO(tx *bolt.Tx, bh types.BlockHeight, id types.SiacoinOutputID, sco types.SiacoinOutput) error {
	// Sanity check - output should not already be in the siacoin outputs set.
	if build.DEBUG && isSiacoinOutput(tx, id) {
		panic(errOutputAlreadyMature)
	}
	dscoBucketID := append(prefix_dsco, encoding.EncUint64(uint64(bh))...)
	dscoBucket := tx.Bucket(dscoBucketID)
	if build.DEBUG && dscoBucket.Get(id[:]) != nil {
		panic(errRepeatInsert)
	}
	emsco := encoding.Marshal(sco)
	return dscoBucket.Put(id[:], emsco)
}
Example #11
0
File: blockdb.go Project: mm3/Sia
// Block returns the block at the given height.
func (db *boltDB) Block(height types.BlockHeight) (types.Block, error) {
	key := encoding.EncUint64(uint64(height))
	var block types.Block
	err := db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("chain"))
		value := b.Get(key)
		if value == nil {
			return ErrUnknownBlock
		}
		// TODO: move outside the tx?
		// NOTE: value is not valid outside the tx
		return encoding.Unmarshal(value, &block)
	})
	return block, err
}
Example #12
0
// 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
}
Example #13
0
// addDSCO adds a delayed siacoin output to the consnesus set.
func addDSCO(tx *bolt.Tx, bh types.BlockHeight, id types.SiacoinOutputID, sco types.SiacoinOutput) {
	// Sanity check - dsco should never have a value of zero.
	// An error in the consensus code means sometimes there are 0-value dscos
	// in the blockchain. A hardfork will fix this.
	/*
		if build.DEBUG && sco.Value.IsZero() {
			panic("zero-value dsco being added")
		}
	*/
	// Sanity check - output should not already be in the full set of outputs.
	if build.DEBUG && tx.Bucket(SiacoinOutputs).Get(id[:]) != nil {
		panic("dsco already in output set")
	}
	dscoBucketID := append(prefixDSCO, encoding.EncUint64(uint64(bh))...)
	dscoBucket := tx.Bucket(dscoBucketID)
	// Sanity check - should not be adding an item already in the db.
	if build.DEBUG && dscoBucket.Get(id[:]) != nil {
		panic(errRepeatInsert)
	}
	err := dscoBucket.Put(id[:], encoding.Marshal(sco))
	if build.DEBUG && err != nil {
		panic(err)
	}
}
Example #14
0
// generateAndApplyDiff will verify the block and then integrate it into the
// consensus state. These two actions must happen at the same time because
// transactions are allowed to depend on each other. We can't be sure that a
// transaction is valid unless we have applied all of the previous transactions
// in the block, which means we need to apply while we verify.
func (cs *ConsensusSet) generateAndApplyDiff(pb *processedBlock) error {
	// Sanity check - the block being applied should have the current block as
	// a parent.
	if build.DEBUG && pb.Parent != cs.currentBlockID() {
		panic(errInvalidSuccessor)
	}

	// Update the state to point to the new block.
	err := cs.db.Update(func(tx *bolt.Tx) error {
		bid := pb.Block.ID()
		err := tx.Bucket(BlockPath).Put(encoding.EncUint64(uint64(pb.Height)), bid[:])
		if err != nil {
			return err
		}
		createDSCOBucket(tx, pb.Height+types.MaturityDelay)
		return nil
	})
	if err != nil {
		panic(err)
	}

	// diffsGenerated is set to true as soon as we start changing the set of
	// diffs in the block node. If at any point the block is found to be
	// invalid, the diffs can be safely reversed.
	pb.DiffsGenerated = true

	// Validate and apply each transaction in the block. They cannot be
	// validated all at once because some transactions may not be valid until
	// previous transactions have been applied.
	for _, txn := range pb.Block.Transactions {
		err = cs.db.Update(func(tx *bolt.Tx) error {
			err := cs.validTxTransaction(tx, txn)
			if err != nil {
				return err
			}
			return nil
		})
		if err != nil {
			// Awkward: need to apply the matured outputs otherwise the diff
			// structure malforms due to the way the delayedOutput maps are
			// created and destroyed.
			updateErr := cs.db.Update(func(tx *bolt.Tx) error {
				return cs.applyMaturedSiacoinOutputs(tx, pb)
			})
			if updateErr != nil {
				return errors.New(updateErr.Error() + " and " + err.Error())
			}
			cs.commitDiffSet(pb, modules.DiffRevert)
			cs.dosBlocks[pb.Block.ID()] = struct{}{}
			return err
		}

		updateErr := cs.db.Update(func(tx *bolt.Tx) error {
			err = cs.applyTransaction(tx, pb, txn)
			if err != nil {
				return err
			}
			return nil
		})
		if updateErr != nil {
			return err
		}
	}

	// After all of the transactions have been applied, 'maintenance' is
	// applied on the block. This includes adding any outputs that have reached
	// maturity, applying any contracts with missed storage proofs, and adding
	// the miner payouts to the list of delayed outputs.
	err = cs.applyMaintenance(pb)
	if err != nil {
		return err
	}

	if build.DEBUG {
		pb.ConsensusSetHash = cs.consensusSetHash()
	}

	// Replace the unprocessed block in the block map with a processed one
	return cs.db.Update(func(tx *bolt.Tx) error {
		id := pb.Block.ID()
		blockMap := tx.Bucket(BlockMap)
		return blockMap.Put(id[:], encoding.Marshal(*pb))
	})
}
Example #15
0
// MarshalSia implements the encoding.SiaMarshaler interface.
func (b Block) MarshalSia(w io.Writer) error {
	w.Write(b.ParentID[:])
	w.Write(b.Nonce[:])
	w.Write(encoding.EncUint64(uint64(b.Timestamp)))
	return encoding.NewEncoder(w).EncodeAll(b.MinerPayouts, b.Transactions)
}
Example #16
0
// pushPath adds a block to the BlockPath at current height + 1.
func pushPath(tx *bolt.Tx, bid types.BlockID) error {
	b := tx.Bucket(BlockPath)
	key := encoding.EncUint64(uint64(b.Stats().KeyN))
	value := encoding.Marshal(bid)
	return b.Put(key, value)
}