/
blockchain.go
154 lines (109 loc) · 2.81 KB
/
blockchain.go
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package blockchain
import (
"encoding/json"
"os"
t "github.com/Flur3x/go-chain/types"
"github.com/google/go-cmp/cmp"
logging "github.com/op/go-logging"
)
var log = logging.MustGetLogger("")
// State contains the general blockchain state, most importantly all mined blocks.
type State struct {
Blocks []t.Block `json:"blocks"`
}
// IsValidChain validates if all blocks contain valid hashes and are chained properly through their "lastHash".
func (s *State) IsValidChain() bool {
isGenesisBlockValid := cmp.Equal(s.Blocks[0], NewGenesisBlock())
hasOnlyValidHashes := func() bool {
for i := 1; i < len(s.Blocks); i++ {
isHashValid, err := VerifyBlockHash(s.Blocks[i])
isLastHashValid := s.Blocks[i].LastHash == s.Blocks[i-1].Hash
if err != nil {
log.Warning("Block hash verification error. Might be a corrupt block:\n", s.Blocks[i])
return false
}
if !isHashValid || !isLastHashValid {
return false
}
}
return true
}
return isGenesisBlockValid && hasOnlyValidHashes()
}
// New returns a "State" struct with the genesis block as the first and only value in it's "Blocks" slice.
func New() State {
blockSlice := make([]t.Block, 1, 100)
blockSlice[0] = NewGenesisBlock()
state := State{Blocks: blockSlice}
setState(state)
return state
}
// AddBlock adds a "Block" to the given blockchain state.
func AddBlock(b t.Block) error {
s, err := GetState()
if err != nil {
return err
}
s.Blocks = append(s.Blocks, b)
if err := setState(s); err != nil {
return err
}
log.Info("::::: Block added to chain :::::\n\n", b)
return nil
}
// LastBlock returns the last Block of the current chain
func LastBlock() (t.Block, error) {
s, err := GetState()
if err != nil {
return t.Block{}, nil
}
return s.Blocks[len(s.Blocks)-1], nil
}
// GetState returns the current state of the blockchain with all their blocks.
func GetState() (State, error) {
return readFromFile()
}
func (s State) String() string {
var blocks string
for _, block := range s.Blocks {
blocks = blocks + block.String()
}
return blocks
}
func setState(state State) error {
return writeToFile(state)
}
func readFromFile() (State, error) {
var state State
file, err := stateFile()
defer file.Close()
if err != nil {
return state, err
}
decoder := json.NewDecoder(file)
if err := decoder.Decode(&state); err != nil {
return state, err
}
return state, nil
}
func writeToFile(state State) error {
file, err := stateFile()
defer file.Close()
if err != nil {
return err
}
if err := file.Truncate(0); err != nil {
return err
}
if _, err := file.Seek(0, 0); err != nil {
return err
}
encoder := json.NewEncoder(file)
if err := encoder.Encode(state); err != nil {
return err
}
return err
}
func stateFile() (*os.File, error) {
return os.OpenFile("state.json", os.O_RDWR|os.O_CREATE, 0666)
}