1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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)
}
|