forked from bitmark-inc/bitmarkd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
setup.go
186 lines (148 loc) · 3.95 KB
/
setup.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// Copyright (c) 2014-2017 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockring
import (
"github.com/bitmark-inc/bitmarkd/blockdigest"
"github.com/bitmark-inc/bitmarkd/fault"
"github.com/bitmark-inc/bitmarkd/genesis"
"github.com/bitmark-inc/bitmarkd/mode"
"github.com/bitmark-inc/logger"
"sync"
)
// internal constants
const (
Size = 20 // size of ring buffer
)
// type to hold a block's digest and its crc64 check code
type ringBuffer struct {
number uint64 // block number
crc uint64 // CRC64_ECMA(block_number, complete_block_bytes)
digest blockdigest.Digest // header digest
}
// globals for background proccess
type ringData struct {
sync.RWMutex // to allow locking
log *logger.L
height uint64
ring [Size]ringBuffer
ringIndex int
// set once during initialise
initialised bool
}
// global data
var globalData ringData
// setup the current block data
func Initialise() error {
globalData.Lock()
defer globalData.Unlock()
// no need to start if already started
if globalData.initialised {
return fault.ErrAlreadyInitialised
}
log := logger.New("ring")
if nil == log {
return fault.ErrInvalidLoggerChannel
}
globalData.log = log
log.Info("starting…")
// zero height and fill ring with default values
if err := clearRingBuffer(log); nil != err {
return err
}
// all data initialised
globalData.initialised = true
return nil
}
// shudown the block system
func Finalise() error {
globalData.Lock()
defer globalData.Unlock()
if !globalData.initialised {
return fault.ErrNotInitialised
}
globalData.log.Info("shutting down…")
globalData.log.Flush()
// finally...
globalData.initialised = false
globalData.log.Info("finished")
globalData.log.Flush()
return nil
}
// fetch latest crc value
func GetLatestCRC() uint64 {
globalData.Lock()
i := globalData.ringIndex - 1
if i < 0 {
i = len(globalData.ring) - 1
}
crc := globalData.ring[i].crc
globalData.Unlock()
return crc
}
// fetch a digest from the ring if present
func DigestForBlock(number uint64) *blockdigest.Digest {
globalData.Lock()
defer globalData.Unlock()
// check if in the cache
i := globalData.height - number
if i < Size {
j := globalData.ringIndex - 1 - int(i)
if j < 0 {
j += Size
}
if number != globalData.ring[j].number {
fault.Panicf("block.DigestForBlock: ring buffer corrupted block number, actual: %d expected: %d", globalData.ring[j].number, number)
}
return &globalData.ring[j].digest
}
return nil
}
// store a block ant its digest
func Put(number uint64, digest blockdigest.Digest, packedBlock []byte) {
// start of critical section
globalData.Lock()
defer globalData.Unlock()
globalData.log.Infof("put block number: %d", number)
if 0 != globalData.height && globalData.height+1 != number {
fault.Panicf("block number: actual: %d expected: %d", number, globalData.height+1)
}
i := globalData.ringIndex
globalData.ring[i].number = number
globalData.ring[i].digest = digest
globalData.ring[i].crc = CRC(number, packedBlock)
i = i + 1
if i >= len(globalData.ring) {
i = 0
}
globalData.ringIndex = i
globalData.height = number
}
func Clear(log *logger.L) error {
globalData.Lock()
defer globalData.Unlock()
return clearRingBuffer(log)
}
// must hold lock to call this
func clearRingBuffer(log *logger.L) error {
// set initial crc depending on mode
number := genesis.BlockNumber
digest := genesis.LiveGenesisDigest
block := genesis.LiveGenesisBlock
if mode.IsTesting() {
digest = genesis.TestGenesisDigest
block = genesis.TestGenesisBlock
}
// default CRC of appropriate genesis block
crc := CRC(number, block)
// fill ring with default values
globalData.ringIndex = 0
for i := 0; i < len(globalData.ring); i += 1 {
globalData.ring[i].number = number
globalData.ring[i].digest = digest
globalData.ring[i].crc = crc
}
// zero the height so next put will succeed
globalData.height = 0
return nil
}