aboutsummaryrefslogtreecommitdiff
path: root/pkg/blockchain/blockchain.go
diff options
context:
space:
mode:
authorgithub-classroom[bot] <66690702+github-classroom[bot]@users.noreply.github.com>2022-02-28 19:36:23 +0000
committergithub-classroom[bot] <66690702+github-classroom[bot]@users.noreply.github.com>2022-02-28 19:36:23 +0000
commit1dd0508d5d3c737f1ee9c723f580baf73b1cfd70 (patch)
tree6adcc5ef85f9cf0bbb205c577da0bac9148114dd /pkg/blockchain/blockchain.go
Initial commit
Diffstat (limited to 'pkg/blockchain/blockchain.go')
-rw-r--r--pkg/blockchain/blockchain.go261
1 files changed, 261 insertions, 0 deletions
diff --git a/pkg/blockchain/blockchain.go b/pkg/blockchain/blockchain.go
new file mode 100644
index 0000000..c456be1
--- /dev/null
+++ b/pkg/blockchain/blockchain.go
@@ -0,0 +1,261 @@
+package blockchain
+
+import (
+ "Chain/pkg/block"
+ "Chain/pkg/blockchain/blockinfodatabase"
+ "Chain/pkg/blockchain/chainwriter"
+ "Chain/pkg/blockchain/coindatabase"
+ "Chain/pkg/utils"
+)
+
+// BlockChain is the main type of this project.
+// Length is the length of the active chain.
+// LastBlock is the last block of the active chain.
+// LastHash is the hash of the last block of the active chain.
+// UnsafeHashes are the hashes of the "unsafe" blocks on the
+// active chain. These "unsafe" blocks may be reverted during a
+// fork.
+// maxHashes is the number of unsafe hashes that the chain keeps track of.
+// BlockInfoDB is a pointer to a block info database
+// ChainWriter is a pointer to a chain writer.
+// CoinDB is a pointer to a coin database.
+type BlockChain struct {
+ Length uint32
+ LastBlock *block.Block
+ LastHash string
+ UnsafeHashes []string
+ maxHashes int
+
+ BlockInfoDB *blockinfodatabase.BlockInfoDatabase
+ ChainWriter *chainwriter.ChainWriter
+ CoinDB *coindatabase.CoinDatabase
+}
+
+// New returns a blockchain given a Config.
+func New(config *Config) *BlockChain {
+ genBlock := GenesisBlock(config)
+ hash := genBlock.Hash()
+ bc := &BlockChain{
+ Length: 1,
+ LastBlock: genBlock,
+ LastHash: hash,
+ UnsafeHashes: []string{},
+ maxHashes: 6,
+ BlockInfoDB: blockinfodatabase.New(blockinfodatabase.DefaultConfig()),
+ ChainWriter: chainwriter.New(chainwriter.DefaultConfig()),
+ CoinDB: coindatabase.New(coindatabase.DefaultConfig()),
+ }
+ // have to store the genesis block
+ bc.CoinDB.StoreBlock(genBlock.Transactions, true)
+ ub := &chainwriter.UndoBlock{}
+ br := bc.ChainWriter.StoreBlock(genBlock, ub, 1)
+ bc.BlockInfoDB.StoreBlockRecord(hash, br)
+ return bc
+}
+
+// GenesisBlock creates the genesis Block, using the Config's
+// InitialSubsidy and GenesisPublicKey.
+func GenesisBlock(config *Config) *block.Block {
+ txo := &block.TransactionOutput{
+ Amount: config.InitialSubsidy,
+ LockingScript: config.GenesisPublicKey,
+ }
+ genTx := &block.Transaction{
+ Version: 0,
+ Inputs: nil,
+ Outputs: []*block.TransactionOutput{txo},
+ LockTime: 0,
+ }
+ return &block.Block{
+ Header: &block.Header{
+ Version: 0,
+ PreviousHash: "",
+ MerkleRoot: "",
+ DifficultyTarget: "",
+ Nonce: 0,
+ Timestamp: 0,
+ },
+ Transactions: []*block.Transaction{genTx},
+ }
+}
+
+// HandleBlock handles a new Block. At a high level, it:
+// (1) Validates and stores the Block.
+// (2) Stores the Block and resulting Undoblock to Disk.
+// (3) Stores the BlockRecord in the BlockInfoDatabase.
+// (4) Handles a fork, if necessary.
+// (5) Updates the BlockChain's fields.
+func (bc *BlockChain) HandleBlock(b *block.Block) {
+ //TODO
+}
+
+// makeUndoBlock returns an UndoBlock given a slice of Transaction.
+func (bc *BlockChain) makeUndoBlock(txs []*block.Transaction) *chainwriter.UndoBlock {
+ var transactionHashes []string
+ var outputIndexes []uint32
+ var amounts []uint32
+ var lockingScripts []string
+ for _, tx := range txs {
+ for _, txi := range tx.Inputs {
+ cl := coindatabase.CoinLocator{
+ ReferenceTransactionHash: txi.ReferenceTransactionHash,
+ OutputIndex: txi.OutputIndex,
+ }
+ coin := bc.CoinDB.GetCoin(cl)
+ // if the coin is nil it means this isn't even a possible fork
+ if coin == nil {
+ return &chainwriter.UndoBlock{
+ TransactionInputHashes: nil,
+ OutputIndexes: nil,
+ Amounts: nil,
+ LockingScripts: nil,
+ }
+ }
+ transactionHashes = append(transactionHashes, txi.ReferenceTransactionHash)
+ outputIndexes = append(outputIndexes, txi.OutputIndex)
+ amounts = append(amounts, coin.TransactionOutput.Amount)
+ lockingScripts = append(lockingScripts, coin.TransactionOutput.LockingScript)
+ }
+ }
+ return &chainwriter.UndoBlock{
+ TransactionInputHashes: transactionHashes,
+ OutputIndexes: outputIndexes,
+ Amounts: amounts,
+ LockingScripts: lockingScripts,
+ }
+}
+
+// getBlock uses the ChainWriter to retrieve a Block from Disk
+// given that Block's hash
+func (bc *BlockChain) getBlock(blockHash string) *block.Block {
+ br := bc.BlockInfoDB.GetBlockRecord(blockHash)
+ fi := &chainwriter.FileInfo{
+ FileName: br.BlockFile,
+ StartOffset: br.BlockStartOffset,
+ EndOffset: br.BlockEndOffset,
+ }
+ return bc.ChainWriter.ReadBlock(fi)
+}
+
+// getUndoBlock uses the ChainWriter to retrieve an UndoBlock
+// from Disk given the corresponding Block's hash
+func (bc *BlockChain) getUndoBlock(blockHash string) *chainwriter.UndoBlock {
+ br := bc.BlockInfoDB.GetBlockRecord(blockHash)
+ fi := &chainwriter.FileInfo{
+ FileName: br.UndoFile,
+ StartOffset: br.UndoStartOffset,
+ EndOffset: br.UndoEndOffset,
+ }
+ return bc.ChainWriter.ReadUndoBlock(fi)
+}
+
+// GetBlocks retrieves a slice of blocks from the main chain given a
+// starting and ending height, inclusive. Given a chain of length 50,
+// GetBlocks(10, 20) returns blocks 10 through 20.
+func (bc *BlockChain) GetBlocks(start, end uint32) []*block.Block {
+ if start >= end || end <= 0 || start <= 0 || end > bc.Length {
+ utils.Debug.Printf("cannot get chain blocks with values start: %v end: %v", start, end)
+ }
+
+ var blocks []*block.Block
+ currentHeight := bc.Length
+ nextHash := bc.LastBlock.Hash()
+
+ for currentHeight >= start {
+ br := bc.BlockInfoDB.GetBlockRecord(nextHash)
+ fi := &chainwriter.FileInfo{
+ FileName: br.BlockFile,
+ StartOffset: br.BlockStartOffset,
+ EndOffset: br.BlockEndOffset,
+ }
+ if currentHeight <= end {
+ nextBlock := bc.ChainWriter.ReadBlock(fi)
+ blocks = append(blocks, nextBlock)
+ }
+ nextHash = br.Header.PreviousHash
+ currentHeight--
+ }
+ return reverseBlocks(blocks)
+}
+
+// GetHashes retrieves a slice of hashes from the main chain given a
+// starting and ending height, inclusive. Given a BlockChain of length
+// 50, GetHashes(10, 20) returns the hashes of Blocks 10 through 20.
+func (bc *BlockChain) GetHashes(start, end uint32) []string {
+ if start >= end || end <= 0 || start <= 0 || end > bc.Length {
+ utils.Debug.Printf("cannot get chain blocks with values start: %v end: %v", start, end)
+ }
+
+ var hashes []string
+ currentHeight := bc.Length
+ nextHash := bc.LastBlock.Hash()
+
+ for currentHeight >= start {
+ br := bc.BlockInfoDB.GetBlockRecord(nextHash)
+ if currentHeight <= end {
+ hashes = append(hashes, nextHash)
+ }
+ nextHash = br.Header.PreviousHash
+ currentHeight--
+ }
+ return reverseHashes(hashes)
+}
+
+// appendsToActiveChain returns whether a Block appends to the
+// BlockChain's active chain or not.
+func (bc *BlockChain) appendsToActiveChain(b *block.Block) bool {
+ return bc.LastBlock.Hash() == b.Header.PreviousHash
+}
+
+// getForkedBlocks returns a slice of Blocks given a starting hash.
+// It returns a maximum of maxHashes Blocks, where maxHashes is the
+// BlockChain's maximum number of unsafe hashes.
+func (bc *BlockChain) getForkedBlocks(startHash string) []*block.Block {
+ unsafeHashes := make(map[string]bool)
+ for _, h := range bc.UnsafeHashes {
+ unsafeHashes[h] = true
+ }
+ var forkedBlocks []*block.Block
+ nextHash := startHash
+ for i := 0; i < len(bc.UnsafeHashes); i++ {
+ forkedBlock := bc.getBlock(nextHash)
+ forkedBlocks = append(forkedBlocks, forkedBlock)
+ if _, ok := unsafeHashes[nextHash]; ok {
+ return forkedBlocks
+ }
+ nextHash = forkedBlock.Header.PreviousHash
+ }
+ return forkedBlocks
+}
+
+// getBlocksAndUndoBlocks returns a slice of n Blocks with a
+// corresponding slice of n UndoBlocks.
+func (bc *BlockChain) getBlocksAndUndoBlocks(n int) ([]*block.Block, []*chainwriter.UndoBlock) {
+ var blocks []*block.Block
+ var undoBlocks []*chainwriter.UndoBlock
+ nextHash := bc.LastHash
+ for i := 0; i < n; i++ {
+ b := bc.getBlock(nextHash)
+ ub := bc.getUndoBlock(nextHash)
+ blocks = append(blocks, b)
+ undoBlocks = append(undoBlocks, ub)
+ nextHash = b.Header.PreviousHash
+ }
+ return blocks, undoBlocks
+}
+
+// reverseBlocks returns a reversed slice of Blocks.
+func reverseBlocks(s []*block.Block) []*block.Block {
+ for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
+ s[i], s[j] = s[j], s[i]
+ }
+ return s
+}
+
+// reverseHashes returns a reversed slice of hashes.
+func reverseHashes(s []string) []string {
+ for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
+ s[i], s[j] = s[j], s[i]
+ }
+ return s
+}