aboutsummaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/block/block.go90
-rw-r--r--pkg/block/transaction.go128
-rw-r--r--pkg/blockchain/blockchain.go261
-rw-r--r--pkg/blockchain/blockinfodatabase/blockinfodatabase.go32
-rw-r--r--pkg/blockchain/blockinfodatabase/blockrecord.go65
-rw-r--r--pkg/blockchain/blockinfodatabase/config.go12
-rw-r--r--pkg/blockchain/chainwriter/chainwriter.go127
-rw-r--r--pkg/blockchain/chainwriter/config.go23
-rw-r--r--pkg/blockchain/chainwriter/fileinfo.go8
-rw-r--r--pkg/blockchain/chainwriter/readwrite.go41
-rw-r--r--pkg/blockchain/chainwriter/undoblock.go60
-rw-r--r--pkg/blockchain/coindatabase/coin.go29
-rw-r--r--pkg/blockchain/coindatabase/coindatabase.go265
-rw-r--r--pkg/blockchain/coindatabase/coinrecord.go48
-rw-r--r--pkg/blockchain/coindatabase/config.go15
-rw-r--r--pkg/blockchain/config.go39
-rw-r--r--pkg/pro/chain.pb.go858
-rw-r--r--pkg/pro/chain.proto63
-rw-r--r--pkg/utils/logging.go55
-rw-r--r--pkg/utils/utils.go13
20 files changed, 2232 insertions, 0 deletions
diff --git a/pkg/block/block.go b/pkg/block/block.go
new file mode 100644
index 0000000..843f43f
--- /dev/null
+++ b/pkg/block/block.go
@@ -0,0 +1,90 @@
+package block
+
+import (
+ "Chain/pkg/pro"
+ "crypto/sha256"
+ "fmt"
+ "google.golang.org/protobuf/proto"
+)
+
+// Header provides information about the Block.
+// Version is the Block's version.
+// PreviousHash is the hash of the previous Block.
+// MerkleRoot is the hash of all the Block's Transactions.
+// DifficultyTarget is the difficulty of achieving a winning Nonce.
+// Nonce is a "number only used once" that satisfies the DifficultyTarget.
+// Timestamp is when the Block was successfully mined.
+type Header struct {
+ Version uint32
+ PreviousHash string
+ MerkleRoot string
+ DifficultyTarget string
+ Nonce uint32
+ Timestamp uint32
+}
+
+// Block includes a Header and a slice of Transactions.
+type Block struct {
+ Header *Header
+ Transactions []*Transaction
+}
+
+// EncodeHeader returns a pro.Header given a Header.
+func EncodeHeader(header *Header) *pro.Header {
+ return &pro.Header{
+ Version: header.Version,
+ PreviousHash: header.PreviousHash,
+ MerkleRoot: header.MerkleRoot,
+ DifficultyTarget: header.DifficultyTarget,
+ Nonce: header.Nonce,
+ Timestamp: header.Timestamp,
+ }
+}
+
+// DecodeHeader returns a Header given a pro.Header.
+func DecodeHeader(pheader *pro.Header) *Header {
+ return &Header{
+ Version: pheader.GetVersion(),
+ PreviousHash: pheader.GetPreviousHash(),
+ MerkleRoot: pheader.GetMerkleRoot(),
+ DifficultyTarget: pheader.GetDifficultyTarget(),
+ Nonce: pheader.GetNonce(),
+ Timestamp: pheader.GetTimestamp(),
+ }
+}
+
+// EncodeBlock returns a pro.Block given a Block.
+func EncodeBlock(b *Block) *pro.Block {
+ var ptxs []*pro.Transaction
+ for _, tx := range b.Transactions {
+ ptxs = append(ptxs, EncodeTransaction(tx))
+ }
+ return &pro.Block{
+ Header: EncodeHeader(b.Header),
+ Transactions: ptxs,
+ }
+}
+
+// DecodeBlock returns a Block given a pro.Block.
+func DecodeBlock(pb *pro.Block) *Block {
+ var txs []*Transaction
+ for _, ptx := range pb.GetTransactions() {
+ txs = append(txs, DecodeTransaction(ptx))
+ }
+ return &Block{
+ Header: DecodeHeader(pb.GetHeader()),
+ Transactions: txs,
+ }
+}
+
+// Hash returns the hash of the block (which is done via the header)
+func (block *Block) Hash() string {
+ h := sha256.New()
+ pb := EncodeHeader(block.Header)
+ bytes, err := proto.Marshal(pb)
+ if err != nil {
+ fmt.Errorf("[block.Hash()] Unable to marshal block")
+ }
+ h.Write(bytes)
+ return fmt.Sprintf("%x", h.Sum(nil))
+}
diff --git a/pkg/block/transaction.go b/pkg/block/transaction.go
new file mode 100644
index 0000000..0102120
--- /dev/null
+++ b/pkg/block/transaction.go
@@ -0,0 +1,128 @@
+package block
+
+import (
+ "Chain/pkg/pro"
+ "crypto/sha256"
+ "fmt"
+ "google.golang.org/protobuf/proto"
+)
+
+// TransactionInput is used as the input to create a TransactionOutput.
+// Recall that TransactionInputs generate TransactionOutputs which in turn
+// generate new TransactionInputs and so forth.
+// ReferenceTransactionHash is the hash of the parent TransactionOutput's Transaction.
+// OutputIndex is the index of the parent TransactionOutput's Transaction.
+// Signature verifies that the payer can spend the referenced TransactionOutput.
+type TransactionInput struct {
+ ReferenceTransactionHash string
+ OutputIndex uint32
+ UnlockingScript string
+}
+
+// TransactionOutput is an output created from a TransactionInput.
+// Recall that TransactionOutputs generate TransactionInputs which in turn
+// generate new TransactionOutputs and so forth.
+// Amount is how much this TransactionOutput is worth.
+// PublicKey is used to verify the payee's signature.
+type TransactionOutput struct {
+ Amount uint32
+ LockingScript string
+}
+
+// Transaction contains information about a transaction.
+// Version is the version of this transaction.
+// Inputs is a slice of TransactionInputs.
+// Outputs is a slice of TransactionOutputs.
+// LockTime is the future time after which the Transaction is valid.
+type Transaction struct {
+ Version uint32
+ Inputs []*TransactionInput
+ Outputs []*TransactionOutput
+ LockTime uint32
+}
+
+// EncodeTransactionInput returns a pro.TransactionInput input
+// given a TransactionInput.
+func EncodeTransactionInput(txi *TransactionInput) *pro.TransactionInput {
+ return &pro.TransactionInput{
+ ReferenceTransactionHash: txi.ReferenceTransactionHash,
+ OutputIndex: txi.OutputIndex,
+ UnlockingScript: txi.UnlockingScript,
+ }
+}
+
+// DecodeTransactionInput returns a TransactionInput given
+// a pro.TransactionInput.
+func DecodeTransactionInput(ptxi *pro.TransactionInput) *TransactionInput {
+ return &TransactionInput{
+ ReferenceTransactionHash: ptxi.GetReferenceTransactionHash(),
+ OutputIndex: ptxi.GetOutputIndex(),
+ UnlockingScript: ptxi.GetUnlockingScript(),
+ }
+}
+
+// EncodeTransactionOutput returns a pro.TransactionOutput given
+// a TransactionOutput.
+func EncodeTransactionOutput(txo *TransactionOutput) *pro.TransactionOutput {
+ return &pro.TransactionOutput{
+ Amount: txo.Amount,
+ LockingScript: txo.LockingScript,
+ }
+}
+
+// DecodeTransactionOutput returns a TransactionOutput given
+// a pro.TransactionOutput.
+func DecodeTransactionOutput(ptxo *pro.TransactionOutput) *TransactionOutput {
+ return &TransactionOutput{
+ Amount: ptxo.GetAmount(),
+ LockingScript: ptxo.GetLockingScript(),
+ }
+}
+
+// EncodeTransaction returns a pro.Transaction given a Transaction.
+func EncodeTransaction(tx *Transaction) *pro.Transaction {
+ var ptxis []*pro.TransactionInput
+ for _, txi := range tx.Inputs {
+ ptxis = append(ptxis, EncodeTransactionInput(txi))
+ }
+ var ptxos []*pro.TransactionOutput
+ for _, txo := range tx.Outputs {
+ ptxos = append(ptxos, EncodeTransactionOutput(txo))
+ }
+ return &pro.Transaction{
+ Version: tx.Version,
+ Inputs: ptxis,
+ Outputs: ptxos,
+ LockTime: tx.LockTime,
+ }
+}
+
+// DecodeTransaction returns a Transaction given a pro.Transaction.
+func DecodeTransaction(ptx *pro.Transaction) *Transaction {
+ var txis []*TransactionInput
+ for _, ptxi := range ptx.GetInputs() {
+ txis = append(txis, DecodeTransactionInput(ptxi))
+ }
+ var txos []*TransactionOutput
+ for _, ptxo := range ptx.GetOutputs() {
+ txos = append(txos, DecodeTransactionOutput(ptxo))
+ }
+ return &Transaction{
+ Version: ptx.GetVersion(),
+ Inputs: txis,
+ Outputs: txos,
+ LockTime: ptx.GetLockTime(),
+ }
+}
+
+// Hash returns the hash of the transaction
+func (tx *Transaction) Hash() string {
+ h := sha256.New()
+ pt := EncodeTransaction(tx)
+ bytes, err := proto.Marshal(pt)
+ if err != nil {
+ fmt.Errorf("[tx.Hash()] Unable to marshal transaction")
+ }
+ h.Write(bytes)
+ return fmt.Sprintf("%x", h.Sum(nil))
+}
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
+}
diff --git a/pkg/blockchain/blockinfodatabase/blockinfodatabase.go b/pkg/blockchain/blockinfodatabase/blockinfodatabase.go
new file mode 100644
index 0000000..c49a625
--- /dev/null
+++ b/pkg/blockchain/blockinfodatabase/blockinfodatabase.go
@@ -0,0 +1,32 @@
+package blockinfodatabase
+
+import (
+ "Chain/pkg/utils"
+ "github.com/syndtr/goleveldb/leveldb"
+)
+
+// BlockInfoDatabase is a wrapper for a levelDB
+type BlockInfoDatabase struct {
+ db *leveldb.DB
+}
+
+// New returns a BlockInfoDatabase given a Config
+func New(config *Config) *BlockInfoDatabase {
+ db, err := leveldb.OpenFile(config.DatabasePath, nil)
+ if err != nil {
+ utils.Debug.Printf("Unable to initialize BlockInfoDatabase with path {%v}", config.DatabasePath)
+ }
+ return &BlockInfoDatabase{db: db}
+}
+
+// StoreBlockRecord stores a BlockRecord in the BlockInfoDatabase.
+func (blockInfoDB *BlockInfoDatabase) StoreBlockRecord(hash string, blockRecord *BlockRecord) {
+ //TODO
+}
+
+// GetBlockRecord returns a BlockRecord from the BlockInfoDatabase given
+// the relevant block's hash.
+func (blockInfoDB *BlockInfoDatabase) GetBlockRecord(hash string) *BlockRecord {
+ //TODO
+ return nil
+}
diff --git a/pkg/blockchain/blockinfodatabase/blockrecord.go b/pkg/blockchain/blockinfodatabase/blockrecord.go
new file mode 100644
index 0000000..8f8846a
--- /dev/null
+++ b/pkg/blockchain/blockinfodatabase/blockrecord.go
@@ -0,0 +1,65 @@
+package blockinfodatabase
+
+import (
+ "Chain/pkg/block"
+ "Chain/pkg/pro"
+)
+
+// BlockRecord contains information about where a Block
+// and its UndoBlock are stored on Disk.
+// Header is the Block's Header.
+// Height is the height of the Block.
+// NumberOfTransactions is the number of Transactions in the Block.
+// BlockFile is the name of the file where the Block is stored.
+// BlockStartOffset is the starting offset of the Block within the
+// BlockFile.
+// BlockEndOffset is the ending offset of the Block within
+// the BlockFile.
+// UndoFile is the name of the file where the UndoBlock is stored.
+// UndoStartOffset is the starting offset of the UndoBlock within
+// the UndoFile.
+// UndoEndOffset is the ending offset of the UndoBlock within the
+// UndoFile.
+type BlockRecord struct {
+ Header *block.Header
+ Height uint32
+ NumberOfTransactions uint32
+
+ BlockFile string
+ BlockStartOffset uint32
+ BlockEndOffset uint32
+
+ UndoFile string
+ UndoStartOffset uint32
+ UndoEndOffset uint32
+}
+
+// EncodeBlockRecord returns a pro.BlockRecord given a BlockRecord.
+func EncodeBlockRecord(br *BlockRecord) *pro.BlockRecord {
+ return &pro.BlockRecord{
+ Header: block.EncodeHeader(br.Header),
+ Height: br.Height,
+ NumberOfTransactions: br.NumberOfTransactions,
+ BlockFile: br.BlockFile,
+ BlockStartOffset: br.BlockStartOffset,
+ BlockEndOffset: br.BlockEndOffset,
+ UndoFile: br.UndoFile,
+ UndoStartOffset: br.UndoStartOffset,
+ UndoEndOffset: br.UndoEndOffset,
+ }
+}
+
+// DecodeBlockRecord returns a BlockRecord given a pro.BlockRecord.
+func DecodeBlockRecord(pbr *pro.BlockRecord) *BlockRecord {
+ return &BlockRecord{
+ Header: block.DecodeHeader(pbr.GetHeader()),
+ Height: pbr.GetHeight(),
+ NumberOfTransactions: pbr.GetNumberOfTransactions(),
+ BlockFile: pbr.GetBlockFile(),
+ BlockStartOffset: pbr.GetBlockStartOffset(),
+ BlockEndOffset: pbr.GetBlockEndOffset(),
+ UndoFile: pbr.GetUndoFile(),
+ UndoStartOffset: pbr.GetUndoStartOffset(),
+ UndoEndOffset: pbr.GetUndoEndOffset(),
+ }
+}
diff --git a/pkg/blockchain/blockinfodatabase/config.go b/pkg/blockchain/blockinfodatabase/config.go
new file mode 100644
index 0000000..ec1990b
--- /dev/null
+++ b/pkg/blockchain/blockinfodatabase/config.go
@@ -0,0 +1,12 @@
+package blockinfodatabase
+
+// Config is the BlockInfoDatabase's configuration options.
+type Config struct {
+ DatabasePath string
+}
+
+// DefaultConfig returns the default configuration for the
+// BlockInfoDatabase.
+func DefaultConfig() *Config {
+ return &Config{DatabasePath: "./blockinfodata"}
+}
diff --git a/pkg/blockchain/chainwriter/chainwriter.go b/pkg/blockchain/chainwriter/chainwriter.go
new file mode 100644
index 0000000..67a7d49
--- /dev/null
+++ b/pkg/blockchain/chainwriter/chainwriter.go
@@ -0,0 +1,127 @@
+package chainwriter
+
+import (
+ "Chain/pkg/block"
+ "Chain/pkg/blockchain/blockinfodatabase"
+ "Chain/pkg/pro"
+ "Chain/pkg/utils"
+ "google.golang.org/protobuf/proto"
+ "log"
+ "os"
+)
+
+// ChainWriter handles all I/O for the BlockChain. It stores and retrieves
+// Blocks and UndoBlocks.
+// See config.go for more information on its fields.
+// Block files are of the format:
+// "DataDirectory/BlockFileName_CurrentBlockFileNumber.FileExtension"
+// Ex: "data/block_0.txt"
+// UndoBlock files are of the format:
+// "DataDirectory/UndoFileName_CurrentUndoFileNumber.FileExtension"
+// Ex: "data/undo_0.txt"
+type ChainWriter struct {
+ // data storage information
+ FileExtension string
+ DataDirectory string
+
+ // block information
+ BlockFileName string
+ CurrentBlockFileNumber uint32
+ CurrentBlockOffset uint32
+ MaxBlockFileSize uint32
+
+ // undo block information
+ UndoFileName string
+ CurrentUndoFileNumber uint32
+ CurrentUndoOffset uint32
+ MaxUndoFileSize uint32
+}
+
+// New returns a ChainWriter given a Config.
+func New(config *Config) *ChainWriter {
+ if err := os.MkdirAll(config.DataDirectory, 0700); err != nil {
+ log.Fatalf("Could not create ChainWriter's data directory")
+ }
+ return &ChainWriter{
+ FileExtension: config.FileExtension,
+ DataDirectory: config.DataDirectory,
+ BlockFileName: config.BlockFileName,
+ CurrentBlockFileNumber: 0,
+ CurrentBlockOffset: 0,
+ MaxBlockFileSize: config.MaxBlockFileSize,
+ UndoFileName: config.UndoFileName,
+ CurrentUndoFileNumber: 0,
+ CurrentUndoOffset: 0,
+ MaxUndoFileSize: config.MaxUndoFileSize,
+ }
+}
+
+// StoreBlock stores a Block and its corresponding UndoBlock to Disk,
+// returning a BlockRecord that contains information for later retrieval.
+func (cw *ChainWriter) StoreBlock(bl *block.Block, undoBlock *UndoBlock, height uint32) *blockinfodatabase.BlockRecord {
+ // serialize block
+ b := block.EncodeBlock(bl)
+ serializedBlock, err := proto.Marshal(b)
+ if err != nil {
+ utils.Debug.Printf("Failed to marshal block")
+ }
+ // serialize undo block
+ ub := EncodeUndoBlock(undoBlock)
+ serializedUndoBlock, err := proto.Marshal(ub)
+ if err != nil {
+ utils.Debug.Printf("Failed to marshal undo block")
+ }
+ // write block to disk
+ bfi := cw.WriteBlock(serializedBlock)
+ // create an empty file info, which we will update if the function is passed an undo block.
+ ufi := &FileInfo{}
+ if undoBlock.Amounts != nil {
+ ufi = cw.WriteUndoBlock(serializedUndoBlock)
+ }
+
+ return &blockinfodatabase.BlockRecord{
+ Header: bl.Header,
+ Height: height,
+ NumberOfTransactions: uint32(len(bl.Transactions)),
+ BlockFile: bfi.FileName,
+ BlockStartOffset: bfi.StartOffset,
+ BlockEndOffset: bfi.EndOffset,
+ UndoFile: ufi.FileName,
+ UndoStartOffset: ufi.StartOffset,
+ UndoEndOffset: ufi.EndOffset,
+ }
+}
+
+// WriteBlock writes a serialized Block to Disk and returns
+// a FileInfo for storage information.
+func (cw *ChainWriter) WriteBlock(serializedBlock []byte) *FileInfo {
+ //TODO
+ return nil
+}
+
+// WriteUndoBlock writes a serialized UndoBlock to Disk and returns
+// a FileInfo for storage information.
+func (cw *ChainWriter) WriteUndoBlock(serializedUndoBlock []byte) *FileInfo {
+ //TODO
+ return nil
+}
+
+// ReadBlock returns a Block given a FileInfo.
+func (cw *ChainWriter) ReadBlock(fi *FileInfo) *block.Block {
+ bytes := readFromDisk(fi)
+ pb := &pro.Block{}
+ if err := proto.Unmarshal(bytes, pb); err != nil {
+ utils.Debug.Printf("failed to unmarshal block from file info {%v}", fi)
+ }
+ return block.DecodeBlock(pb)
+}
+
+// ReadUndoBlock returns an UndoBlock given a FileInfo.
+func (cw *ChainWriter) ReadUndoBlock(fi *FileInfo) *UndoBlock {
+ bytes := readFromDisk(fi)
+ pub := &pro.UndoBlock{}
+ if err := proto.Unmarshal(bytes, pub); err != nil {
+ utils.Debug.Printf("failed to unmarshal undo block from file info {%v}", fi)
+ }
+ return DecodeUndoBlock(pub)
+}
diff --git a/pkg/blockchain/chainwriter/config.go b/pkg/blockchain/chainwriter/config.go
new file mode 100644
index 0000000..e217f7a
--- /dev/null
+++ b/pkg/blockchain/chainwriter/config.go
@@ -0,0 +1,23 @@
+package chainwriter
+
+// Config is the ChainWriter's configuration options.
+type Config struct {
+ FileExtension string
+ DataDirectory string
+ BlockFileName string
+ UndoFileName string
+ MaxBlockFileSize uint32
+ MaxUndoFileSize uint32
+}
+
+// DefaultConfig returns the default Config for the ChainWriter.
+func DefaultConfig() *Config {
+ return &Config{
+ FileExtension: ".txt",
+ DataDirectory: "data",
+ BlockFileName: "block",
+ UndoFileName: "undo",
+ MaxBlockFileSize: 1024,
+ MaxUndoFileSize: 1024,
+ }
+}
diff --git a/pkg/blockchain/chainwriter/fileinfo.go b/pkg/blockchain/chainwriter/fileinfo.go
new file mode 100644
index 0000000..95987b0
--- /dev/null
+++ b/pkg/blockchain/chainwriter/fileinfo.go
@@ -0,0 +1,8 @@
+package chainwriter
+
+// FileInfo determines where a Block or UndoBlock is stored.
+type FileInfo struct {
+ FileName string
+ StartOffset uint32
+ EndOffset uint32
+}
diff --git a/pkg/blockchain/chainwriter/readwrite.go b/pkg/blockchain/chainwriter/readwrite.go
new file mode 100644
index 0000000..32944a4
--- /dev/null
+++ b/pkg/blockchain/chainwriter/readwrite.go
@@ -0,0 +1,41 @@
+package chainwriter
+
+import (
+ "log"
+ "os"
+)
+
+// writeToDisk appends a slice of bytes to a file.
+func writeToDisk(fileName string, data []byte) {
+ file, err := os.OpenFile(fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
+ if err != nil {
+ log.Fatalf("Unable to open file {%v}", fileName)
+ }
+ if _, err := file.Write(data); err != nil {
+ file.Close() // ignore error; Write error takes precedence
+ log.Fatalf("Failed to write to file {%v}", fileName)
+ }
+ if err := file.Close(); err != nil {
+ log.Fatalf("Failed to close file {%v}", fileName)
+ }
+}
+
+// readFromDisk return a slice of bytes from a file, given a FileInfo.
+func readFromDisk(info *FileInfo) []byte {
+ file, err := os.Open(info.FileName)
+ if err != nil {
+ log.Fatalf("Unable to open file {%v}", info.FileName)
+ }
+ if _, err2 := file.Seek(int64(info.StartOffset), 0); err2 != nil {
+ log.Fatalf("Failed to seek to {%v} in file {%v}", info.StartOffset, info.FileName)
+ }
+ numBytes := info.EndOffset - info.StartOffset
+ buf := make([]byte, numBytes)
+ if n, err3 := file.Read(buf); uint32(n) != info.EndOffset-info.StartOffset || err3 != nil {
+ log.Fatalf("Failed to read {%v} bytes from file {%v}", numBytes, info.FileName)
+ }
+ if err4 := file.Close(); err4 != nil {
+ log.Fatalf("Failed to close file {%v}", info.FileName)
+ }
+ return buf
+}
diff --git a/pkg/blockchain/chainwriter/undoblock.go b/pkg/blockchain/chainwriter/undoblock.go
new file mode 100644
index 0000000..5827f57
--- /dev/null
+++ b/pkg/blockchain/chainwriter/undoblock.go
@@ -0,0 +1,60 @@
+package chainwriter
+
+import "Chain/pkg/pro"
+
+// UndoBlock is used to reverse the side effects causes by a Block.
+// When the chain reverts a block's Transactions, it must both (1)
+// remove newly created TransactionOutputs and (2) convert
+// TransactionInputs back into available TransactionOutputs.
+// This struct helps with (2).
+// TransactionInputHashes are the hashes of the TransactionInputs that
+// the UndoBlock must revert.
+// OutputIndexes are the OutputIndexes of the TransactionInputs.
+// Amounts are the amounts of the parent TransactionOutputs.
+// LockingScripts are the locking scripts of the parent TransactionOutputs.
+type UndoBlock struct {
+ TransactionInputHashes []string
+ OutputIndexes []uint32
+ Amounts []uint32
+ LockingScripts []string
+}
+
+// EncodeUndoBlock returns a pro.UndoBlock given an UndoBlock.
+func EncodeUndoBlock(ub *UndoBlock) *pro.UndoBlock {
+ var transactionInputHashes []string
+ var outputIndexes []uint32
+ var amounts []uint32
+ var lockingScripts []string
+ for i := 0; i < len(ub.TransactionInputHashes); i++ {
+ transactionInputHashes = append(transactionInputHashes, ub.TransactionInputHashes[i])
+ outputIndexes = append(outputIndexes, ub.OutputIndexes[i])
+ amounts = append(amounts, ub.Amounts[i])
+ lockingScripts = append(lockingScripts, ub.LockingScripts[i])
+ }
+ return &pro.UndoBlock{
+ TransactionInputHashes: transactionInputHashes,
+ OutputIndexes: outputIndexes,
+ Amounts: amounts,
+ LockingScripts: lockingScripts,
+ }
+}
+
+// DecodeUndoBlock returns an UndoBlock given a pro.UndoBlock
+func DecodeUndoBlock(pub *pro.UndoBlock) *UndoBlock {
+ var transactionInputHashes []string
+ var outputIndexes []uint32
+ var amounts []uint32
+ var lockingScripts []string
+ for i := 0; i < len(pub.GetTransactionInputHashes()); i++ {
+ transactionInputHashes = append(transactionInputHashes, pub.GetTransactionInputHashes()[i])
+ outputIndexes = append(outputIndexes, pub.GetOutputIndexes()[i])
+ amounts = append(amounts, pub.GetAmounts()[i])
+ lockingScripts = append(lockingScripts, pub.GetLockingScripts()[i])
+ }
+ return &UndoBlock{
+ TransactionInputHashes: transactionInputHashes,
+ OutputIndexes: outputIndexes,
+ Amounts: amounts,
+ LockingScripts: lockingScripts,
+ }
+}
diff --git a/pkg/blockchain/coindatabase/coin.go b/pkg/blockchain/coindatabase/coin.go
new file mode 100644
index 0000000..4281ac1
--- /dev/null
+++ b/pkg/blockchain/coindatabase/coin.go
@@ -0,0 +1,29 @@
+package coindatabase
+
+import "Chain/pkg/block"
+
+// Coin is used by the CoinDatabase to keep track of unspent
+// TransactionOutputs.
+// TransactionOutput is the underlying TransactionOutput.
+// IsSpent is whether that TransactionOutput has been spent.
+// Active is whether that TransactionOutput is one created by
+// Blocks on the active Chain.
+type Coin struct {
+ TransactionOutput *block.TransactionOutput
+ IsSpent bool
+}
+
+// CoinLocator is a dumbed down TransactionInput, used
+// as a key to Coins in the CoinDatabase's mainCache.
+type CoinLocator struct {
+ ReferenceTransactionHash string
+ OutputIndex uint32
+}
+
+// makeCoinLocator returns a CoinLocator given a TransactionInput.
+func makeCoinLocator(txi *block.TransactionInput) CoinLocator {
+ return CoinLocator{
+ ReferenceTransactionHash: txi.ReferenceTransactionHash,
+ OutputIndex: txi.OutputIndex,
+ }
+}
diff --git a/pkg/blockchain/coindatabase/coindatabase.go b/pkg/blockchain/coindatabase/coindatabase.go
new file mode 100644
index 0000000..83b3026
--- /dev/null
+++ b/pkg/blockchain/coindatabase/coindatabase.go
@@ -0,0 +1,265 @@
+package coindatabase
+
+import (
+ "Chain/pkg/block"
+ "Chain/pkg/blockchain/chainwriter"
+ "Chain/pkg/pro"
+ "Chain/pkg/utils"
+ "fmt"
+ "github.com/syndtr/goleveldb/leveldb"
+ "google.golang.org/protobuf/proto"
+)
+
+// CoinDatabase keeps track of Coins.
+// db is a levelDB for persistent storage.
+// mainCache stores as many Coins as possible for rapid validation.
+// mainCacheSize is how many Coins are currently in the mainCache.
+// mainCacheCapacity is the maximum number of Coins that the mainCache
+// can store before it must flush.
+type CoinDatabase struct {
+ db *leveldb.DB
+ mainCache map[CoinLocator]*Coin
+ mainCacheSize uint32
+ mainCacheCapacity uint32
+}
+
+// New returns a CoinDatabase given a Config.
+func New(config *Config) *CoinDatabase {
+ db, err := leveldb.OpenFile(config.DatabasePath, nil)
+ if err != nil {
+ utils.Debug.Printf("Unable to initialize BlockInfoDatabase with path {%v}", config.DatabasePath)
+ }
+ return &CoinDatabase{
+ db: db,
+ mainCache: make(map[CoinLocator]*Coin),
+ mainCacheSize: 0,
+ mainCacheCapacity: config.MainCacheCapacity,
+ }
+}
+
+// ValidateBlock returns whether a Block's Transactions are valid.
+func (coinDB *CoinDatabase) ValidateBlock(transactions []*block.Transaction) bool {
+ for _, tx := range transactions {
+ if err := coinDB.validateTransaction(tx); err != nil {
+ utils.Debug.Printf("%v", err)
+ return false
+ }
+ }
+ return true
+}
+
+// validateTransaction checks whether a Transaction's inputs are valid Coins.
+// If the Coins have already been spent or do not exist, validateTransaction
+// returns an error.
+func (coinDB *CoinDatabase) validateTransaction(transaction *block.Transaction) error {
+ for _, txi := range transaction.Inputs {
+ key := makeCoinLocator(txi)
+ if coin, ok := coinDB.mainCache[key]; ok {
+ if coin.IsSpent {
+ return fmt.Errorf("[validateTransaction] coin already spent")
+ }
+ continue
+ }
+ if data, err := coinDB.db.Get([]byte(txi.ReferenceTransactionHash), nil); err != nil {
+ return fmt.Errorf("[validateTransaction] coin not in leveldb")
+ } else {
+ pcr := &pro.CoinRecord{}
+ if err2 := proto.Unmarshal(data, pcr); err2 != nil {
+ utils.Debug.Printf("Failed to unmarshal record from hash {%v}:", txi.ReferenceTransactionHash, err)
+ }
+ cr := DecodeCoinRecord(pcr)
+ if !contains(cr.OutputIndexes, txi.OutputIndex) {
+ return fmt.Errorf("[validateTransaction] coin record did not still contain output required for transaction input ")
+ }
+ }
+ }
+ return nil
+}
+
+// UndoCoins handles reverting a Block. It:
+// (1) erases the Coins created by a Block and
+// (2) marks the Coins used to create those Transactions as unspent.
+func (coinDB *CoinDatabase) UndoCoins(blocks []*block.Block, undoBlocks []*chainwriter.UndoBlock) {
+ //TODO
+}
+
+// addCoinsToRecord adds coins to a CoinRecord given an UndoBlock and
+// returns the updated CoinRecord.
+func (coinDB *CoinDatabase) addCoinsToRecord(cr *CoinRecord, ub *chainwriter.UndoBlock) *CoinRecord {
+ cr.OutputIndexes = append(cr.OutputIndexes, ub.OutputIndexes...)
+ cr.Amounts = append(cr.Amounts, ub.Amounts...)
+ cr.LockingScripts = append(cr.LockingScripts, ub.LockingScripts...)
+ return cr
+}
+
+// FlushMainCache flushes the mainCache to the db.
+func (coinDB *CoinDatabase) FlushMainCache() {
+ // update coin records
+ updatedCoinRecords := make(map[string]*CoinRecord)
+ for cl := range coinDB.mainCache {
+ // check whether we already updated this record
+ var cr *CoinRecord
+
+ // (1) get our coin record
+ // first check our map, in case we already updated the coin record given
+ // a previous coin
+ if cr2, ok := updatedCoinRecords[cl.ReferenceTransactionHash]; ok {
+ cr = cr2
+ } else {
+ // if we haven't already update this coin record, retrieve from db
+ data, err := coinDB.db.Get([]byte(cl.ReferenceTransactionHash), nil)
+ if err != nil {
+ utils.Debug.Printf("[FlushMainCache] coin record not in leveldb")
+ }
+ pcr := &pro.CoinRecord{}
+ if err = proto.Unmarshal(data, pcr); err != nil {
+ utils.Debug.Printf("Failed to unmarshal record from hash {%v}:%v", cl.ReferenceTransactionHash, err)
+ }
+ cr = DecodeCoinRecord(pcr)
+ }
+ // (2) remove the coin from the record if it's been spent
+ if coinDB.mainCache[cl].IsSpent {
+ cr = coinDB.removeCoinFromRecord(cr, cl.OutputIndex)
+ }
+ updatedCoinRecords[cl.ReferenceTransactionHash] = cr
+ delete(coinDB.mainCache, cl)
+ }
+ coinDB.mainCacheSize = 0
+ // write the new records
+ for key, cr := range updatedCoinRecords {
+ if len(cr.OutputIndexes) <= 1 {
+ err := coinDB.db.Delete([]byte(key), nil)
+ if err != nil {
+ utils.Debug.Printf("[FlushMainCache] failed to delete key {%v}", key)
+ }
+ } else {
+ coinDB.putRecordInDB(key, cr)
+ }
+ }
+}
+
+// StoreBlock handles storing a newly minted Block. It:
+// (1) removes spent TransactionOutputs (if active)
+// (2) stores new TransactionOutputs as Coins in the mainCache (if active)
+// (3) stores CoinRecords for the Transactions in the db.
+func (coinDB *CoinDatabase) StoreBlock(transactions []*block.Transaction, active bool) {
+ //TODO
+}
+
+// removeCoinFromDB removes a Coin from a CoinRecord, deleting the CoinRecord
+// from the db entirely if it is the last remaining Coin in the CoinRecord.
+func (coinDB *CoinDatabase) removeCoinFromDB(txHash string, cl CoinLocator) {
+ // 3. If the coin is not in the main cache, retrieve from the database
+ // 4. Delete coin records if they only have one coin
+ // 5. Remove coins from the record
+ cr := coinDB.getCoinRecordFromDB(txHash)
+ switch {
+ case cr == nil:
+ return
+ case len(cr.Amounts) <= 1:
+ if err := coinDB.db.Delete([]byte(txHash), nil); err != nil {
+ utils.Debug.Printf("[removeCoinFromDB] failed to remove {%v} from db", txHash)
+ }
+ default:
+ cr = coinDB.removeCoinFromRecord(cr, cl.OutputIndex)
+ coinDB.putRecordInDB(txHash, cr)
+ }
+}
+
+// putRecordInDB puts a CoinRecord into the db.
+func (coinDB *CoinDatabase) putRecordInDB(txHash string, cr *CoinRecord) {
+ record := EncodeCoinRecord(cr)
+ if err2 := coinDB.db.Put([]byte(txHash), []byte(record.String()), nil); err2 != nil {
+ utils.Debug.Printf("Unable to store block record for key {%v}", txHash)
+ }
+}
+
+// removeCoinFromRecord returns an updated CoinRecord. It removes the Coin
+// with the given outputIndex, if the Coin exists in the CoinRecord.
+func (coinDB *CoinDatabase) removeCoinFromRecord(cr *CoinRecord, outputIndex uint32) *CoinRecord {
+ index := indexOf(cr.OutputIndexes, outputIndex)
+ if index < 0 {
+ return cr
+ }
+ cr.OutputIndexes = append(cr.OutputIndexes[:index], cr.OutputIndexes[index+1:]...)
+ cr.Amounts = append(cr.Amounts[:index], cr.Amounts[index+1:]...)
+ cr.LockingScripts = append(cr.LockingScripts[:index], cr.LockingScripts[index+1:]...)
+ return cr
+}
+
+// createCoinRecord returns a CoinRecord for the provided Transaction.
+func (coinDB *CoinDatabase) createCoinRecord(tx *block.Transaction) *CoinRecord {
+ var outputIndexes []uint32
+ var amounts []uint32
+ var LockingScripts []string
+ for i, txo := range tx.Outputs {
+ outputIndexes = append(outputIndexes, uint32(i))
+ amounts = append(amounts, txo.Amount)
+ LockingScripts = append(LockingScripts, txo.LockingScript)
+ }
+ cr := &CoinRecord{
+ Version: 0,
+ OutputIndexes: outputIndexes,
+ Amounts: amounts,
+ LockingScripts: LockingScripts,
+ }
+ return cr
+}
+
+// getCoinRecordFromDB returns a CoinRecord from the db given a hash.
+func (coinDB *CoinDatabase) getCoinRecordFromDB(txHash string) *CoinRecord {
+ if data, err := coinDB.db.Get([]byte(txHash), nil); err != nil {
+ utils.Debug.Printf("[validateTransaction] coin not in leveldb")
+ return nil
+ } else {
+ pcr := &pro.CoinRecord{}
+ if err := proto.Unmarshal(data, pcr); err != nil {
+ utils.Debug.Printf("Failed to unmarshal record from hash {%v}:", txHash, err)
+ }
+ cr := DecodeCoinRecord(pcr)
+ return cr
+ }
+}
+
+// GetCoin returns a Coin given a CoinLocator. It first checks the
+// mainCache, then checks the db. If the Coin doesn't exist, it returns nil.
+func (coinDB *CoinDatabase) GetCoin(cl CoinLocator) *Coin {
+ if coin, ok := coinDB.mainCache[cl]; ok {
+ return coin
+ }
+ cr := coinDB.getCoinRecordFromDB(cl.ReferenceTransactionHash)
+ if cr == nil {
+ return nil
+ }
+ index := indexOf(cr.OutputIndexes, cl.OutputIndex)
+ if index < 0 {
+ return nil
+ }
+ return &Coin{
+ TransactionOutput: &block.TransactionOutput{
+ Amount: cr.Amounts[index],
+ LockingScript: cr.LockingScripts[index],
+ },
+ IsSpent: false,
+ }
+}
+
+// contains returns true if an int slice s contains element e, false if it does not.
+func contains(s []uint32, e uint32) bool {
+ for _, a := range s {
+ if a == e {
+ return true
+ }
+ }
+ return false
+}
+
+//indexOf returns the index of element e in int slice s, -1 if the element does not exist.
+func indexOf(s []uint32, e uint32) int {
+ for i, a := range s {
+ if a == e {
+ return i
+ }
+ }
+ return -1
+}
diff --git a/pkg/blockchain/coindatabase/coinrecord.go b/pkg/blockchain/coindatabase/coinrecord.go
new file mode 100644
index 0000000..6de45c3
--- /dev/null
+++ b/pkg/blockchain/coindatabase/coinrecord.go
@@ -0,0 +1,48 @@
+package coindatabase
+
+import "Chain/pkg/pro"
+
+// CoinRecord is a record of which coins created by a Transaction
+// have been spent. It is stored in the CoinDatabase's db.
+type CoinRecord struct {
+ Version uint32
+ OutputIndexes []uint32
+ Amounts []uint32
+ LockingScripts []string
+}
+
+// EncodeCoinRecord returns a pro.CoinRecord given a CoinRecord.
+func EncodeCoinRecord(cr *CoinRecord) *pro.CoinRecord {
+ var outputIndexes []uint32
+ var amounts []uint32
+ var lockingScripts []string
+ for i := 0; i < len(cr.OutputIndexes); i++ {
+ outputIndexes = append(outputIndexes, cr.OutputIndexes[i])
+ amounts = append(amounts, cr.Amounts[i])
+ lockingScripts = append(lockingScripts, cr.LockingScripts[i])
+ }
+ return &pro.CoinRecord{
+ Version: cr.Version,
+ OutputIndexes: outputIndexes,
+ Amounts: amounts,
+ LockingScripts: lockingScripts,
+ }
+}
+
+// DecodeCoinRecord returns a CoinRecord given a pro.CoinRecord.
+func DecodeCoinRecord(pcr *pro.CoinRecord) *CoinRecord {
+ var outputIndexes []uint32
+ var amounts []uint32
+ var lockingScripts []string
+ for i := 0; i < len(pcr.GetOutputIndexes()); i++ {
+ outputIndexes = append(outputIndexes, pcr.GetOutputIndexes()[i])
+ amounts = append(amounts, pcr.GetAmounts()[i])
+ lockingScripts = append(lockingScripts, pcr.GetLockingScripts()[i])
+ }
+ return &CoinRecord{
+ Version: pcr.GetVersion(),
+ OutputIndexes: outputIndexes,
+ Amounts: amounts,
+ LockingScripts: lockingScripts,
+ }
+}
diff --git a/pkg/blockchain/coindatabase/config.go b/pkg/blockchain/coindatabase/config.go
new file mode 100644
index 0000000..bfb372b
--- /dev/null
+++ b/pkg/blockchain/coindatabase/config.go
@@ -0,0 +1,15 @@
+package coindatabase
+
+// Config is the CoinDatabase's configuration options.
+type Config struct {
+ DatabasePath string
+ MainCacheCapacity uint32
+}
+
+// DefaultConfig returns the CoinDatabase's default Config.
+func DefaultConfig() *Config {
+ return &Config{
+ DatabasePath: "./coindata",
+ MainCacheCapacity: 30,
+ }
+}
diff --git a/pkg/blockchain/config.go b/pkg/blockchain/config.go
new file mode 100644
index 0000000..0ea3e7b
--- /dev/null
+++ b/pkg/blockchain/config.go
@@ -0,0 +1,39 @@
+package blockchain
+
+import (
+ "Chain/pkg/blockchain/blockinfodatabase"
+ "Chain/pkg/blockchain/chainwriter"
+ "Chain/pkg/blockchain/coindatabase"
+)
+
+// Config is the BlockChain's configuration options.
+type Config struct {
+ GenesisPublicKey string
+ InitialSubsidy uint32
+ HasChn bool
+ BlockInfoDBPath string
+ ChainWriterDBPath string
+ CoinDBPath string
+}
+
+// GENPK is the public key that was used
+// for the genesis transaction on the
+// genesis block.
+var GENPK = "3059301306072a8648ce3d020106082a8648ce3d030107034200042418a20458559ae13a0d4bb6ac284c66a5cebb5689563d4cf573473d8c6d5abfa9a21a65dbb3ba2f2d930be7f763f940f9864abaf199a0f0d8d14bedda2dcad9"
+
+// GENPVK is the public key that was used
+// for the genesis transaction on the
+// genesis block.
+var GENPVK = "307702010104202456b0e8bed5c27dcadb044df1af8eaf714084b61a23d17359fb09f3c3f5fff5a00a06082a8648ce3d030107a144034200042418a20458559ae13a0d4bb6ac284c66a5cebb5689563d4cf573473d8c6d5abfa9a21a65dbb3ba2f2d930be7f763f940f9864abaf199a0f0d8d14bedda2dcad9"
+
+// DefaultConfig returns the default configuration for the blockchain.
+func DefaultConfig() *Config {
+ return &Config{
+ GenesisPublicKey: GENPK,
+ InitialSubsidy: 0,
+ HasChn: true,
+ BlockInfoDBPath: blockinfodatabase.DefaultConfig().DatabasePath,
+ ChainWriterDBPath: chainwriter.DefaultConfig().DataDirectory,
+ CoinDBPath: coindatabase.DefaultConfig().DatabasePath,
+ }
+}
diff --git a/pkg/pro/chain.pb.go b/pkg/pro/chain.pb.go
new file mode 100644
index 0000000..cefcb1e
--- /dev/null
+++ b/pkg/pro/chain.pb.go
@@ -0,0 +1,858 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.19.1
+// source: chain.proto
+
+package pro
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Header struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
+ PreviousHash string `protobuf:"bytes,2,opt,name=previous_hash,json=previousHash,proto3" json:"previous_hash,omitempty"`
+ MerkleRoot string `protobuf:"bytes,3,opt,name=merkle_root,json=merkleRoot,proto3" json:"merkle_root,omitempty"`
+ DifficultyTarget string `protobuf:"bytes,4,opt,name=difficulty_target,json=difficultyTarget,proto3" json:"difficulty_target,omitempty"`
+ Nonce uint32 `protobuf:"varint,5,opt,name=nonce,proto3" json:"nonce,omitempty"`
+ Timestamp uint32 `protobuf:"varint,6,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+}
+
+func (x *Header) Reset() {
+ *x = Header{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_chain_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Header) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Header) ProtoMessage() {}
+
+func (x *Header) ProtoReflect() protoreflect.Message {
+ mi := &file_chain_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Header.ProtoReflect.Descriptor instead.
+func (*Header) Descriptor() ([]byte, []int) {
+ return file_chain_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Header) GetVersion() uint32 {
+ if x != nil {
+ return x.Version
+ }
+ return 0
+}
+
+func (x *Header) GetPreviousHash() string {
+ if x != nil {
+ return x.PreviousHash
+ }
+ return ""
+}
+
+func (x *Header) GetMerkleRoot() string {
+ if x != nil {
+ return x.MerkleRoot
+ }
+ return ""
+}
+
+func (x *Header) GetDifficultyTarget() string {
+ if x != nil {
+ return x.DifficultyTarget
+ }
+ return ""
+}
+
+func (x *Header) GetNonce() uint32 {
+ if x != nil {
+ return x.Nonce
+ }
+ return 0
+}
+
+func (x *Header) GetTimestamp() uint32 {
+ if x != nil {
+ return x.Timestamp
+ }
+ return 0
+}
+
+type TransactionInput struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ ReferenceTransactionHash string `protobuf:"bytes,1,opt,name=reference_transaction_hash,json=referenceTransactionHash,proto3" json:"reference_transaction_hash,omitempty"`
+ OutputIndex uint32 `protobuf:"varint,2,opt,name=output_index,json=outputIndex,proto3" json:"output_index,omitempty"`
+ UnlockingScript string `protobuf:"bytes,3,opt,name=unlocking_script,json=unlockingScript,proto3" json:"unlocking_script,omitempty"`
+}
+
+func (x *TransactionInput) Reset() {
+ *x = TransactionInput{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_chain_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *TransactionInput) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TransactionInput) ProtoMessage() {}
+
+func (x *TransactionInput) ProtoReflect() protoreflect.Message {
+ mi := &file_chain_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use TransactionInput.ProtoReflect.Descriptor instead.
+func (*TransactionInput) Descriptor() ([]byte, []int) {
+ return file_chain_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *TransactionInput) GetReferenceTransactionHash() string {
+ if x != nil {
+ return x.ReferenceTransactionHash
+ }
+ return ""
+}
+
+func (x *TransactionInput) GetOutputIndex() uint32 {
+ if x != nil {
+ return x.OutputIndex
+ }
+ return 0
+}
+
+func (x *TransactionInput) GetUnlockingScript() string {
+ if x != nil {
+ return x.UnlockingScript
+ }
+ return ""
+}
+
+type TransactionOutput struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Amount uint32 `protobuf:"varint,1,opt,name=amount,proto3" json:"amount,omitempty"`
+ LockingScript string `protobuf:"bytes,2,opt,name=locking_script,json=lockingScript,proto3" json:"locking_script,omitempty"`
+}
+
+func (x *TransactionOutput) Reset() {
+ *x = TransactionOutput{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_chain_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *TransactionOutput) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TransactionOutput) ProtoMessage() {}
+
+func (x *TransactionOutput) ProtoReflect() protoreflect.Message {
+ mi := &file_chain_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use TransactionOutput.ProtoReflect.Descriptor instead.
+func (*TransactionOutput) Descriptor() ([]byte, []int) {
+ return file_chain_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *TransactionOutput) GetAmount() uint32 {
+ if x != nil {
+ return x.Amount
+ }
+ return 0
+}
+
+func (x *TransactionOutput) GetLockingScript() string {
+ if x != nil {
+ return x.LockingScript
+ }
+ return ""
+}
+
+type Transaction struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
+ Inputs []*TransactionInput `protobuf:"bytes,2,rep,name=inputs,proto3" json:"inputs,omitempty"`
+ Outputs []*TransactionOutput `protobuf:"bytes,3,rep,name=outputs,proto3" json:"outputs,omitempty"`
+ LockTime uint32 `protobuf:"varint,4,opt,name=lock_time,json=lockTime,proto3" json:"lock_time,omitempty"`
+}
+
+func (x *Transaction) Reset() {
+ *x = Transaction{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_chain_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Transaction) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Transaction) ProtoMessage() {}
+
+func (x *Transaction) ProtoReflect() protoreflect.Message {
+ mi := &file_chain_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Transaction.ProtoReflect.Descriptor instead.
+func (*Transaction) Descriptor() ([]byte, []int) {
+ return file_chain_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *Transaction) GetVersion() uint32 {
+ if x != nil {
+ return x.Version
+ }
+ return 0
+}
+
+func (x *Transaction) GetInputs() []*TransactionInput {
+ if x != nil {
+ return x.Inputs
+ }
+ return nil
+}
+
+func (x *Transaction) GetOutputs() []*TransactionOutput {
+ if x != nil {
+ return x.Outputs
+ }
+ return nil
+}
+
+func (x *Transaction) GetLockTime() uint32 {
+ if x != nil {
+ return x.LockTime
+ }
+ return 0
+}
+
+type Block struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Header *Header `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ Transactions []*Transaction `protobuf:"bytes,2,rep,name=transactions,proto3" json:"transactions,omitempty"`
+}
+
+func (x *Block) Reset() {
+ *x = Block{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_chain_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Block) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Block) ProtoMessage() {}
+
+func (x *Block) ProtoReflect() protoreflect.Message {
+ mi := &file_chain_proto_msgTypes[4]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Block.ProtoReflect.Descriptor instead.
+func (*Block) Descriptor() ([]byte, []int) {
+ return file_chain_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *Block) GetHeader() *Header {
+ if x != nil {
+ return x.Header
+ }
+ return nil
+}
+
+func (x *Block) GetTransactions() []*Transaction {
+ if x != nil {
+ return x.Transactions
+ }
+ return nil
+}
+
+type BlockRecord struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Header *Header `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ Height uint32 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"`
+ NumberOfTransactions uint32 `protobuf:"varint,3,opt,name=number_of_transactions,json=numberOfTransactions,proto3" json:"number_of_transactions,omitempty"`
+ BlockFile string `protobuf:"bytes,4,opt,name=block_file,json=blockFile,proto3" json:"block_file,omitempty"`
+ BlockStartOffset uint32 `protobuf:"varint,5,opt,name=block_start_offset,json=blockStartOffset,proto3" json:"block_start_offset,omitempty"`
+ BlockEndOffset uint32 `protobuf:"varint,6,opt,name=block_end_offset,json=blockEndOffset,proto3" json:"block_end_offset,omitempty"`
+ UndoFile string `protobuf:"bytes,7,opt,name=undo_file,json=undoFile,proto3" json:"undo_file,omitempty"`
+ UndoStartOffset uint32 `protobuf:"varint,8,opt,name=undo_start_offset,json=undoStartOffset,proto3" json:"undo_start_offset,omitempty"`
+ UndoEndOffset uint32 `protobuf:"varint,9,opt,name=undo_end_offset,json=undoEndOffset,proto3" json:"undo_end_offset,omitempty"`
+}
+
+func (x *BlockRecord) Reset() {
+ *x = BlockRecord{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_chain_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *BlockRecord) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BlockRecord) ProtoMessage() {}
+
+func (x *BlockRecord) ProtoReflect() protoreflect.Message {
+ mi := &file_chain_proto_msgTypes[5]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use BlockRecord.ProtoReflect.Descriptor instead.
+func (*BlockRecord) Descriptor() ([]byte, []int) {
+ return file_chain_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *BlockRecord) GetHeader() *Header {
+ if x != nil {
+ return x.Header
+ }
+ return nil
+}
+
+func (x *BlockRecord) GetHeight() uint32 {
+ if x != nil {
+ return x.Height
+ }
+ return 0
+}
+
+func (x *BlockRecord) GetNumberOfTransactions() uint32 {
+ if x != nil {
+ return x.NumberOfTransactions
+ }
+ return 0
+}
+
+func (x *BlockRecord) GetBlockFile() string {
+ if x != nil {
+ return x.BlockFile
+ }
+ return ""
+}
+
+func (x *BlockRecord) GetBlockStartOffset() uint32 {
+ if x != nil {
+ return x.BlockStartOffset
+ }
+ return 0
+}
+
+func (x *BlockRecord) GetBlockEndOffset() uint32 {
+ if x != nil {
+ return x.BlockEndOffset
+ }
+ return 0
+}
+
+func (x *BlockRecord) GetUndoFile() string {
+ if x != nil {
+ return x.UndoFile
+ }
+ return ""
+}
+
+func (x *BlockRecord) GetUndoStartOffset() uint32 {
+ if x != nil {
+ return x.UndoStartOffset
+ }
+ return 0
+}
+
+func (x *BlockRecord) GetUndoEndOffset() uint32 {
+ if x != nil {
+ return x.UndoEndOffset
+ }
+ return 0
+}
+
+type CoinRecord struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
+ OutputIndexes []uint32 `protobuf:"varint,3,rep,packed,name=output_indexes,json=outputIndexes,proto3" json:"output_indexes,omitempty"`
+ Amounts []uint32 `protobuf:"varint,4,rep,packed,name=amounts,proto3" json:"amounts,omitempty"`
+ LockingScripts []string `protobuf:"bytes,5,rep,name=locking_scripts,json=lockingScripts,proto3" json:"locking_scripts,omitempty"`
+}
+
+func (x *CoinRecord) Reset() {
+ *x = CoinRecord{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_chain_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CoinRecord) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CoinRecord) ProtoMessage() {}
+
+func (x *CoinRecord) ProtoReflect() protoreflect.Message {
+ mi := &file_chain_proto_msgTypes[6]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CoinRecord.ProtoReflect.Descriptor instead.
+func (*CoinRecord) Descriptor() ([]byte, []int) {
+ return file_chain_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *CoinRecord) GetVersion() uint32 {
+ if x != nil {
+ return x.Version
+ }
+ return 0
+}
+
+func (x *CoinRecord) GetOutputIndexes() []uint32 {
+ if x != nil {
+ return x.OutputIndexes
+ }
+ return nil
+}
+
+func (x *CoinRecord) GetAmounts() []uint32 {
+ if x != nil {
+ return x.Amounts
+ }
+ return nil
+}
+
+func (x *CoinRecord) GetLockingScripts() []string {
+ if x != nil {
+ return x.LockingScripts
+ }
+ return nil
+}
+
+type UndoBlock struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ TransactionInputHashes []string `protobuf:"bytes,1,rep,name=transaction_input_hashes,json=transactionInputHashes,proto3" json:"transaction_input_hashes,omitempty"`
+ OutputIndexes []uint32 `protobuf:"varint,2,rep,packed,name=output_indexes,json=outputIndexes,proto3" json:"output_indexes,omitempty"`
+ Amounts []uint32 `protobuf:"varint,3,rep,packed,name=amounts,proto3" json:"amounts,omitempty"`
+ LockingScripts []string `protobuf:"bytes,4,rep,name=locking_scripts,json=lockingScripts,proto3" json:"locking_scripts,omitempty"`
+}
+
+func (x *UndoBlock) Reset() {
+ *x = UndoBlock{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_chain_proto_msgTypes[7]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *UndoBlock) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UndoBlock) ProtoMessage() {}
+
+func (x *UndoBlock) ProtoReflect() protoreflect.Message {
+ mi := &file_chain_proto_msgTypes[7]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use UndoBlock.ProtoReflect.Descriptor instead.
+func (*UndoBlock) Descriptor() ([]byte, []int) {
+ return file_chain_proto_rawDescGZIP(), []int{7}
+}
+
+func (x *UndoBlock) GetTransactionInputHashes() []string {
+ if x != nil {
+ return x.TransactionInputHashes
+ }
+ return nil
+}
+
+func (x *UndoBlock) GetOutputIndexes() []uint32 {
+ if x != nil {
+ return x.OutputIndexes
+ }
+ return nil
+}
+
+func (x *UndoBlock) GetAmounts() []uint32 {
+ if x != nil {
+ return x.Amounts
+ }
+ return nil
+}
+
+func (x *UndoBlock) GetLockingScripts() []string {
+ if x != nil {
+ return x.LockingScripts
+ }
+ return nil
+}
+
+var File_chain_proto protoreflect.FileDescriptor
+
+var file_chain_proto_rawDesc = []byte{
+ 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc9, 0x01,
+ 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73,
+ 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
+ 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x68,
+ 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x76, 0x69,
+ 0x6f, 0x75, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x65, 0x72, 0x6b, 0x6c,
+ 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65,
+ 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x69, 0x66, 0x66,
+ 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x04, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x10, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x54,
+ 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x05,
+ 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74,
+ 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09,
+ 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x9e, 0x01, 0x0a, 0x10, 0x54, 0x72,
+ 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x3c,
+ 0x0a, 0x1a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e,
+ 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x18, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x54, 0x72, 0x61,
+ 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c,
+ 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x0d, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12,
+ 0x29, 0x0a, 0x10, 0x75, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x75, 0x6e, 0x6c, 0x6f, 0x63,
+ 0x6b, 0x69, 0x6e, 0x67, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x52, 0x0a, 0x11, 0x54, 0x72,
+ 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12,
+ 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
+ 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x6f, 0x63, 0x6b, 0x69,
+ 0x6e, 0x67, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x0d, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x9d,
+ 0x01, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18,
+ 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
+ 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75,
+ 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73,
+ 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x06, 0x69, 0x6e, 0x70,
+ 0x75, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x03,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69,
+ 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74,
+ 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04,
+ 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x5a,
+ 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1f, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65,
+ 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,
+ 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x6e,
+ 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c,
+ 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x74, 0x72,
+ 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xe4, 0x02, 0x0a, 0x0b, 0x42,
+ 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1f, 0x0a, 0x06, 0x68, 0x65,
+ 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x48, 0x65, 0x61,
+ 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x68,
+ 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, 0x65, 0x69,
+ 0x67, 0x68, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x6f, 0x66,
+ 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20,
+ 0x01, 0x28, 0x0d, 0x52, 0x14, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x54, 0x72, 0x61,
+ 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f,
+ 0x63, 0x6b, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62,
+ 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x62, 0x6c, 0x6f, 0x63,
+ 0x6b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x05,
+ 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x74, 0x61, 0x72, 0x74,
+ 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f,
+ 0x65, 0x6e, 0x64, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d,
+ 0x52, 0x0e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x64, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74,
+ 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x6e, 0x64, 0x6f, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x07, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x6e, 0x64, 0x6f, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x2a, 0x0a,
+ 0x11, 0x75, 0x6e, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6f, 0x66, 0x66, 0x73,
+ 0x65, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x75, 0x6e, 0x64, 0x6f, 0x53, 0x74,
+ 0x61, 0x72, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x75, 0x6e, 0x64,
+ 0x6f, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x09, 0x20, 0x01,
+ 0x28, 0x0d, 0x52, 0x0d, 0x75, 0x6e, 0x64, 0x6f, 0x45, 0x6e, 0x64, 0x4f, 0x66, 0x66, 0x73, 0x65,
+ 0x74, 0x22, 0x90, 0x01, 0x0a, 0x0a, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64,
+ 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x75,
+ 0x74, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03,
+ 0x28, 0x0d, 0x52, 0x0d, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65,
+ 0x73, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03,
+ 0x28, 0x0d, 0x52, 0x07, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6c,
+ 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x18, 0x05,
+ 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x53, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x73, 0x22, 0xaf, 0x01, 0x0a, 0x09, 0x55, 0x6e, 0x64, 0x6f, 0x42, 0x6c, 0x6f,
+ 0x63, 0x6b, 0x12, 0x38, 0x0a, 0x18, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f,
+ 0x6e, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01,
+ 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f,
+ 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e,
+ 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x02,
+ 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65,
+ 0x78, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x03,
+ 0x20, 0x03, 0x28, 0x0d, 0x52, 0x07, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x27, 0x0a,
+ 0x0f, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73,
+ 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x53,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2e, 0x2f, 0x70, 0x72, 0x6f,
+ 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_chain_proto_rawDescOnce sync.Once
+ file_chain_proto_rawDescData = file_chain_proto_rawDesc
+)
+
+func file_chain_proto_rawDescGZIP() []byte {
+ file_chain_proto_rawDescOnce.Do(func() {
+ file_chain_proto_rawDescData = protoimpl.X.CompressGZIP(file_chain_proto_rawDescData)
+ })
+ return file_chain_proto_rawDescData
+}
+
+var file_chain_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
+var file_chain_proto_goTypes = []interface{}{
+ (*Header)(nil), // 0: Header
+ (*TransactionInput)(nil), // 1: TransactionInput
+ (*TransactionOutput)(nil), // 2: TransactionOutput
+ (*Transaction)(nil), // 3: Transaction
+ (*Block)(nil), // 4: Block
+ (*BlockRecord)(nil), // 5: BlockRecord
+ (*CoinRecord)(nil), // 6: CoinRecord
+ (*UndoBlock)(nil), // 7: UndoBlock
+}
+var file_chain_proto_depIdxs = []int32{
+ 1, // 0: Transaction.inputs:type_name -> TransactionInput
+ 2, // 1: Transaction.outputs:type_name -> TransactionOutput
+ 0, // 2: Block.header:type_name -> Header
+ 3, // 3: Block.transactions:type_name -> Transaction
+ 0, // 4: BlockRecord.header:type_name -> Header
+ 5, // [5:5] is the sub-list for method output_type
+ 5, // [5:5] is the sub-list for method input_type
+ 5, // [5:5] is the sub-list for extension type_name
+ 5, // [5:5] is the sub-list for extension extendee
+ 0, // [0:5] is the sub-list for field type_name
+}
+
+func init() { file_chain_proto_init() }
+func file_chain_proto_init() {
+ if File_chain_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_chain_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Header); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_chain_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*TransactionInput); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_chain_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*TransactionOutput); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_chain_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Transaction); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_chain_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Block); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_chain_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*BlockRecord); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_chain_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CoinRecord); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_chain_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*UndoBlock); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_chain_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 8,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_chain_proto_goTypes,
+ DependencyIndexes: file_chain_proto_depIdxs,
+ MessageInfos: file_chain_proto_msgTypes,
+ }.Build()
+ File_chain_proto = out.File
+ file_chain_proto_rawDesc = nil
+ file_chain_proto_goTypes = nil
+ file_chain_proto_depIdxs = nil
+}
diff --git a/pkg/pro/chain.proto b/pkg/pro/chain.proto
new file mode 100644
index 0000000..0559684
--- /dev/null
+++ b/pkg/pro/chain.proto
@@ -0,0 +1,63 @@
+syntax = "proto3";
+
+option go_package = "../pro";
+
+message Header {
+ uint32 version = 1;
+ string previous_hash = 2;
+ string merkle_root = 3;
+ string difficulty_target = 4;
+ uint32 nonce = 5;
+ uint32 timestamp = 6;
+}
+
+message TransactionInput {
+ string reference_transaction_hash = 1;
+ uint32 output_index = 2;
+ string unlocking_script = 3;
+}
+
+message TransactionOutput {
+ uint32 amount = 1;
+ string locking_script = 2;
+}
+
+message Transaction {
+ uint32 version = 1;
+ repeated TransactionInput inputs = 2;
+ repeated TransactionOutput outputs = 3;
+ uint32 lock_time = 4;
+}
+
+message Block {
+ Header header = 1;
+ repeated Transaction transactions = 2;
+}
+
+message BlockRecord {
+ Header header = 1;
+ uint32 height = 2;
+ uint32 number_of_transactions = 3;
+
+ string block_file = 4;
+ uint32 block_start_offset = 5;
+ uint32 block_end_offset = 6;
+
+ string undo_file = 7;
+ uint32 undo_start_offset = 8;
+ uint32 undo_end_offset = 9;
+}
+
+message CoinRecord {
+ uint32 version = 1;
+ repeated uint32 output_indexes = 3;
+ repeated uint32 amounts = 4;
+ repeated string locking_scripts = 5;
+}
+
+message UndoBlock {
+ repeated string transaction_input_hashes = 1;
+ repeated uint32 output_indexes = 2;
+ repeated uint32 amounts = 3;
+ repeated string locking_scripts = 4;
+} \ No newline at end of file
diff --git a/pkg/utils/logging.go b/pkg/utils/logging.go
new file mode 100644
index 0000000..d98e78a
--- /dev/null
+++ b/pkg/utils/logging.go
@@ -0,0 +1,55 @@
+package utils
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "strconv"
+ "strings"
+)
+
+/*
+ * Brown University, CS1951L, Summer 2021
+ * Designed by: Colby Anderson, John Roy
+ */
+
+// Debug is optional logger for debugging
+var Debug *log.Logger
+
+// Out is logger to Stdout
+var Out *log.Logger
+
+// Err is logger to Stderr
+var Err *log.Logger
+
+// init initializes the loggers.
+func init() {
+ Debug = log.New(ioutil.Discard, "DEBUG: ", 0)
+ Out = log.New(os.Stdout, "INFO: ", log.Ltime|log.Lshortfile)
+ Err = log.New(os.Stderr, "ERROR: ", log.Ltime|log.Lshortfile)
+}
+
+// SetDebug turns debug print statements on or off.
+func SetDebug(enabled bool) {
+ if enabled {
+ Debug.SetOutput(os.Stdout)
+ } else {
+ Debug.SetOutput(ioutil.Discard)
+ }
+}
+
+func FmtAddr(addr string) string {
+ if addr == "" {
+ return ""
+ }
+ colors := []string{"\033[41m", "\033[42m", "\033[43m", "\033[44m", "\033[45m", "\033[46m", "\033[47m"}
+ port, _ := strconv.ParseInt(strings.Split(addr, ":")[1], 10, 64)
+ randomColor := colors[int(port)%len(colors)]
+ return fmt.Sprintf("%v\033[97m[%v]\033[0m", randomColor, addr)
+}
+
+func Colorize(s string, seed int) string {
+ lowestColor, highestColor := 104, 226
+ return fmt.Sprintf("\033[38;5;%vm%v\033[0m", seed%(highestColor-lowestColor)+lowestColor, s)
+}
diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go
new file mode 100644
index 0000000..5055723
--- /dev/null
+++ b/pkg/utils/utils.go
@@ -0,0 +1,13 @@
+package utils
+
+import (
+ "crypto/sha256"
+ "fmt"
+)
+
+// Hash Adapted from: https://blog.8bitzen.com/posts/22-08-2019-how-to-hash-a-struct-in-go
+func Hash(o interface{}) string {
+ h := sha256.New()
+ h.Write([]byte(fmt.Sprintf("%v", o)))
+ return fmt.Sprintf("%x", h.Sum(nil))
+}