-
Notifications
You must be signed in to change notification settings - Fork 77
/
block_base.go
201 lines (172 loc) · 5.76 KB
/
block_base.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
package block
import (
"encoding/json"
"errors"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"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"
)
// Base holds the base info of a block
type Base 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 previous block's time stamp.
// Generally the difference of two block's time stamp 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
// 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
// Network magic number this block belongs to. This one actually is not
// a part of the wire-representation of Block, but it's absolutely
// necessary for correct signing/verification.
Network netmode.Magic
// Hash of this block, created when binary encoded (double SHA256).
hash util.Uint256
// Hash of the block used to verify it (single SHA256).
verificationHash 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"`
Index uint32 `json:"index"`
NextConsensus string `json:"nextconsensus"`
Witnesses []transaction.Witness `json:"witnesses"`
}
// Verify verifies the integrity of the Base.
func (b *Base) Verify() bool {
// TODO: Need a persisted blockchain for this.
return true
}
// Hash returns the hash of the block.
func (b *Base) Hash() util.Uint256 {
if b.hash.Equals(util.Uint256{}) {
b.createHash()
}
return b.hash
}
// VerificationHash returns the hash of the block used to verify it.
func (b *Base) VerificationHash() util.Uint256 {
if b.verificationHash.Equals(util.Uint256{}) {
b.createHash()
}
return b.verificationHash
}
// DecodeBinary implements Serializable interface.
func (b *Base) 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 Serializable interface
func (b *Base) EncodeBinary(bw *io.BinWriter) {
b.encodeHashableFields(bw)
bw.WriteVarUint(1)
b.Script.EncodeBinary(bw)
}
// GetSignedPart returns serialized hashable data of the block.
func (b *Base) GetSignedPart() []byte {
buf := io.NewBufBinWriter()
buf.WriteU32LE(uint32(b.Network))
// No error can occure while encoding hashable fields.
b.encodeHashableFields(buf.BinWriter)
return buf.Bytes()
}
// createHash creates the hash of the block.
// When calculating the hash value of the block, instead of calculating the entire block,
// only first seven fields in the block head will be calculated, which are
// version, PrevBlock, MerkleRoot, timestamp, and height, the nonce, NextMiner.
// Since MerkleRoot already contains the hash value of all transactions,
// the modification of transaction will influence the hash value of the block.
func (b *Base) createHash() {
bb := b.GetSignedPart()
b.verificationHash = hash.Sha256(bb)
b.hash = hash.Sha256(b.verificationHash.BytesBE())
}
// encodeHashableFields will only encode the fields used for hashing.
// see Hash() for more information about the fields.
func (b *Base) encodeHashableFields(bw *io.BinWriter) {
bw.WriteU32LE(b.Version)
bw.WriteBytes(b.PrevHash[:])
bw.WriteBytes(b.MerkleRoot[:])
bw.WriteU64LE(b.Timestamp)
bw.WriteU32LE(b.Index)
bw.WriteBytes(b.NextConsensus[:])
}
// decodeHashableFields decodes the fields used for hashing.
// see Hash() for more information about the fields.
func (b *Base) decodeHashableFields(br *io.BinReader) {
b.Version = br.ReadU32LE()
br.ReadBytes(b.PrevHash[:])
br.ReadBytes(b.MerkleRoot[:])
b.Timestamp = br.ReadU64LE()
b.Index = br.ReadU32LE()
br.ReadBytes(b.NextConsensus[:])
// Make the hash of the block here so we dont need to do this
// again.
if br.Err == nil {
b.createHash()
}
}
// MarshalJSON implements json.Marshaler interface.
func (b Base) MarshalJSON() ([]byte, error) {
aux := baseAux{
Hash: b.Hash(),
Version: b.Version,
PrevHash: b.PrevHash,
MerkleRoot: b.MerkleRoot,
Timestamp: b.Timestamp,
Index: b.Index,
NextConsensus: address.Uint160ToString(b.NextConsensus),
Witnesses: []transaction.Witness{b.Script},
}
return json.Marshal(aux)
}
// UnmarshalJSON implements json.Unmarshaler interface.
func (b *Base) UnmarshalJSON(data []byte) error {
var aux = new(baseAux)
var nextC util.Uint160
err := json.Unmarshal(data, aux)
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.Index = aux.Index
b.NextConsensus = nextC
b.Script = aux.Witnesses[0]
if !aux.Hash.Equals(b.Hash()) {
return errors.New("json 'hash' doesn't match block hash")
}
return nil
}