// 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) }) }
// 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) }) }
// 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 }
// 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) }) }
// 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) }) }
// 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) } }
// 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)) }) }
// 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)) }) }
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) }
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) }
// 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 }
// 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 }
// 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) } }
// 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)) }) }
// 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) }
// 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) }