-
Notifications
You must be signed in to change notification settings - Fork 4
/
chain.go
189 lines (169 loc) · 4.53 KB
/
chain.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
187
188
189
package gpbft
import (
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"strings"
)
// Opaque type representing a tipset.
// This is expected to be:
// - a canonical sequence of CIDs of block headers identifying a tipset,
// - a commitment to the resulting power table,
// - a commitment to additional derived values.
// However, GossipPBFT doesn't need to know anything about that structure.
type TipSet = []byte
// A chain of tipsets comprising a base (the last finalised tipset from which the chain extends).
// and (possibly empty) suffix.
// Tipsets are assumed to be built contiguously on each other,
// though epochs may be missing due to null rounds.
// The zero value is not a valid chain, and represents a "bottom" value
// when used in a Granite message.
type ECChain []TipSet
// A map key for a chain. The zero value means "bottom".
type ChainKey string
// Creates a new chain.
func NewChain(base TipSet, suffix ...TipSet) (ECChain, error) {
var chain ECChain = []TipSet{base}
chain = append(chain, suffix...)
if err := chain.Validate(); err != nil {
return nil, err
}
return chain, nil
}
func (c ECChain) IsZero() bool {
return len(c) == 0
}
// Returns the base tipset.
func (c ECChain) Base() TipSet {
return c[0]
}
// Returns the suffix of the chain after the base.
// An empty slice for a zero value.
func (c ECChain) Suffix() []TipSet {
if c.IsZero() {
return nil
}
return c[1:]
}
// Returns the last tipset in the chain.
// This could be the base tipset if there is no suffix.
// This will panic on a zero value.
func (c ECChain) Head() TipSet {
return c[len(c)-1]
}
// Returns a new chain with the same base and no suffix.
// Invalid for a zero value.
func (c ECChain) BaseChain() ECChain {
return ECChain{c[0]}
}
func (c ECChain) Extend(tip TipSet) ECChain {
return append(c, tip)
}
// Returns a chain with suffix (after the base) truncated to a maximum length.
// Prefix(0) returns the base chain.
// Invalid for a zero value.
func (c ECChain) Prefix(to int) ECChain {
return c[:to+1]
}
// Compares two ECChains for equality.
func (c ECChain) Eq(other ECChain) bool {
if len(c) != len(other) {
return false
}
for i := range c {
if !bytes.Equal(c[i], other[i]) {
return false
}
}
return true
}
// Checks whether two chains have the same base.
// Always false for a zero value.
func (c ECChain) SameBase(other ECChain) bool {
if c.IsZero() || other.IsZero() {
return false
}
return bytes.Equal(c.Base(), other.Base())
}
// Check whether a chain has a specific base tipset.
// Always false for a zero value.
func (c ECChain) HasBase(t TipSet) bool {
if c.IsZero() || len(t) == 0 {
return false
}
return bytes.Equal(c[0], t)
}
// Checks whether a chain has some prefix (including the base).
// Always false for a zero value.
func (c ECChain) HasPrefix(other ECChain) bool {
if c.IsZero() || other.IsZero() {
return false
}
if len(other) > len(c) {
return false
}
for i := range other {
if !bytes.Equal(c[i], other[i]) {
return false
}
}
return true
}
// Checks whether a chain has some tipset (including as its base).
func (c ECChain) HasTipset(t TipSet) bool {
if len(t) == 0 {
// Chain can never contain zero-valued TipSet.
return false
}
for _, t2 := range c {
if bytes.Equal(t, t2) {
return true
}
}
return false
}
// Validate verifies the integrity of the chain, returning an error if it finds any issues.
// A chain is valid if it meets the following criteria:
// 1) All contained elements have non-zero values. Use TipSet.IsZero to check for zero-valued elements.
// 2) TipSets are arranged in strictly increasing order by their epochs, without any repetitions.
// An entirely zero-valued chain itself is deemed valid. See ECChain.IsZero.
func (c ECChain) Validate() error {
if c.IsZero() {
return nil
}
for _, tipSet := range c {
if len(tipSet) == 0 {
return errors.New("chain cannot contain zero-valued tip sets")
}
}
return nil
}
// Returns an identifier for the chain suitable for use as a map key.
// This must completely determine the sequence of tipsets in the chain.
func (c ECChain) Key() ChainKey {
var ln int
for _, t := range c {
ln += 4 // for length
ln += len(t) // for data
}
var buf bytes.Buffer
buf.Grow(ln)
for _, t := range c {
_ = binary.Write(&buf, binary.BigEndian, uint32(len(t)))
buf.Write(t)
}
return ChainKey(buf.String())
}
func (c ECChain) String() string {
var b strings.Builder
b.WriteString("[")
for i, t := range c {
b.WriteString(hex.EncodeToString(t))
if i < len(c)-1 {
b.WriteString(", ")
}
}
b.WriteString("]")
return b.String()
}