-
Notifications
You must be signed in to change notification settings - Fork 77
/
header.go
216 lines (190 loc) · 6.16 KB
/
header.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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package block
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// Header holds the base info of a block.
type Header struct {
// Version of the block.
Version uint32
// hash of the previous block.
PrevHash util.Uint256
// Root hash of a transaction list.
MerkleRoot util.Uint256
// Timestamp is a millisecond-precision timestamp.
// The time stamp of each block must be later than the previous block's time stamp.
// Generally, the difference between two block's time stamps is about 15 seconds and imprecision is allowed.
// The height of the block must be exactly equal to the height of the previous block plus 1.
Timestamp uint64
// Nonce is block random number.
Nonce uint64
// index/height of the block
Index uint32
// Contract address of the next miner
NextConsensus util.Uint160
// Script used to validate the block
Script transaction.Witness
// StateRootEnabled specifies if the header contains state root.
StateRootEnabled bool
// PrevStateRoot is the state root of the previous block.
PrevStateRoot util.Uint256
// PrimaryIndex is the index of the primary consensus node for this block.
PrimaryIndex byte
// Hash of this block, created when binary encoded (double SHA256).
hash util.Uint256
}
// baseAux is used to marshal/unmarshal to/from JSON, it's almost the same
// as original Base, but with Nonce and NextConsensus fields differing and
// Hash added.
type baseAux struct {
Hash util.Uint256 `json:"hash"`
Version uint32 `json:"version"`
PrevHash util.Uint256 `json:"previousblockhash"`
MerkleRoot util.Uint256 `json:"merkleroot"`
Timestamp uint64 `json:"time"`
Nonce string `json:"nonce"`
Index uint32 `json:"index"`
NextConsensus string `json:"nextconsensus"`
PrimaryIndex byte `json:"primary"`
PrevStateRoot *util.Uint256 `json:"previousstateroot,omitempty"`
Witnesses []transaction.Witness `json:"witnesses"`
}
// Hash returns the hash of the block.
func (b *Header) Hash() util.Uint256 {
if b.hash.Equals(util.Uint256{}) {
b.createHash()
}
return b.hash
}
// DecodeBinary implements the Serializable interface.
func (b *Header) DecodeBinary(br *io.BinReader) {
b.decodeHashableFields(br)
witnessCount := br.ReadVarUint()
if br.Err == nil && witnessCount != 1 {
br.Err = errors.New("wrong witness count")
return
}
b.Script.DecodeBinary(br)
}
// EncodeBinary implements the Serializable interface.
func (b *Header) EncodeBinary(bw *io.BinWriter) {
b.encodeHashableFields(bw)
bw.WriteVarUint(1)
b.Script.EncodeBinary(bw)
}
// createHash creates the hash of the block.
// When calculating the hash value of the block, instead of processing the entire block,
// only the header (without the signatures) is added as an input for the hash. It differs
// from the complete block only in that it doesn't contain transactions, but their hashes
// are used for MerkleRoot hash calculation. Therefore, adding/removing/changing any
// transaction affects the header hash and there is no need to use the complete block for
// hash calculation.
func (b *Header) createHash() {
buf := io.NewBufBinWriter()
// No error can occur while encoding hashable fields.
b.encodeHashableFields(buf.BinWriter)
b.hash = hash.Sha256(buf.Bytes())
}
// encodeHashableFields will only encode the fields used for hashing.
// see Hash() for more information about the fields.
func (b *Header) encodeHashableFields(bw *io.BinWriter) {
bw.WriteU32LE(b.Version)
bw.WriteBytes(b.PrevHash[:])
bw.WriteBytes(b.MerkleRoot[:])
bw.WriteU64LE(b.Timestamp)
bw.WriteU64LE(b.Nonce)
bw.WriteU32LE(b.Index)
bw.WriteB(b.PrimaryIndex)
bw.WriteBytes(b.NextConsensus[:])
if b.StateRootEnabled {
bw.WriteBytes(b.PrevStateRoot[:])
}
}
// decodeHashableFields decodes the fields used for hashing.
// see Hash() for more information about the fields.
func (b *Header) decodeHashableFields(br *io.BinReader) {
b.Version = br.ReadU32LE()
br.ReadBytes(b.PrevHash[:])
br.ReadBytes(b.MerkleRoot[:])
b.Timestamp = br.ReadU64LE()
b.Nonce = br.ReadU64LE()
b.Index = br.ReadU32LE()
b.PrimaryIndex = br.ReadB()
br.ReadBytes(b.NextConsensus[:])
if b.StateRootEnabled {
br.ReadBytes(b.PrevStateRoot[:])
}
// Make the hash of the block here so we dont need to do this
// again.
if br.Err == nil {
b.createHash()
}
}
// MarshalJSON implements the json.Marshaler interface.
func (b Header) MarshalJSON() ([]byte, error) {
aux := baseAux{
Hash: b.Hash(),
Version: b.Version,
PrevHash: b.PrevHash,
MerkleRoot: b.MerkleRoot,
Timestamp: b.Timestamp,
Nonce: fmt.Sprintf("%016X", b.Nonce),
Index: b.Index,
PrimaryIndex: b.PrimaryIndex,
NextConsensus: address.Uint160ToString(b.NextConsensus),
Witnesses: []transaction.Witness{b.Script},
}
if b.StateRootEnabled {
aux.PrevStateRoot = &b.PrevStateRoot
}
return json.Marshal(aux)
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (b *Header) UnmarshalJSON(data []byte) error {
var aux = new(baseAux)
var nextC util.Uint160
err := json.Unmarshal(data, aux)
if err != nil {
return err
}
var nonce uint64
if len(aux.Nonce) != 0 {
nonce, err = strconv.ParseUint(aux.Nonce, 16, 64)
if err != nil {
return err
}
}
nextC, err = address.StringToUint160(aux.NextConsensus)
if err != nil {
return err
}
if len(aux.Witnesses) != 1 {
return errors.New("wrong number of witnesses")
}
b.Version = aux.Version
b.PrevHash = aux.PrevHash
b.MerkleRoot = aux.MerkleRoot
b.Timestamp = aux.Timestamp
b.Nonce = nonce
b.Index = aux.Index
b.PrimaryIndex = aux.PrimaryIndex
b.NextConsensus = nextC
b.Script = aux.Witnesses[0]
if b.StateRootEnabled {
if aux.PrevStateRoot == nil {
return errors.New("'previousstateroot' is empty")
}
b.PrevStateRoot = *aux.PrevStateRoot
}
if !aux.Hash.Equals(b.Hash()) {
return errors.New("json 'hash' doesn't match block hash")
}
return nil
}