Go implementation

peoplecoin

Standard library only. Primitives, the state machine, and the nine operations — no networking, no video, no wallet yet.

state/block.go

160 lines

1package state
2
3import (
4 "crypto/sha256"
5 "encoding/binary"
6 "errors"
7
8 "peoplecoin/trie"
9)
10
11// Header is a block header. Its hash is the signing message; the block
12// hash is SHA256(header_hash ‖ sig).
13type Header struct {
14 Number uint64
15 Slot uint64
16 Time uint64 // Unix time of the slot's start: prev.time + (slot − prev.slot)·60
17 TxTrie Hash
18 Coin Hash
19 Token Hash
20 Election [2]Hash
21 Rng [2]Hash
22 Reg [2]Hash
23 Prev Hash // previous block hash; zero for the first block
24 Validator Address
25 Nonce Hash // election commit preimage: SHA256(validator ‖ nonce) is the vote
26 Sig [64]byte
27}
28
29// HeaderSize is the fixed encoded size: 3×8 + 12×32 + 64.
30const HeaderSize = 24 + 384 + 64
31
32var ErrHeaderSize = errors.New("state: bad header size")
33
34// signingBytes is the header without the signature.
35func (h *Header) signingBytes() []byte {
36 b := make([]byte, 0, HeaderSize-64)
37 b = binary.BigEndian.AppendUint64(b, h.Number)
38 b = binary.BigEndian.AppendUint64(b, h.Slot)
39 b = binary.BigEndian.AppendUint64(b, h.Time)
40 for _, r := range []Hash{h.TxTrie, h.Coin, h.Token, h.Election[0], h.Election[1], h.Rng[0], h.Rng[1], h.Reg[0], h.Reg[1], h.Prev, h.Validator, h.Nonce} {
41 b = append(b, r[:]...)
42 }
43 return b
44}
45
46// SigningHash is SHA256 over number ‖ slot ‖ time ‖ the nine roots ‖ prev ‖
47// validator ‖ nonce.
48func (h *Header) SigningHash() Hash { return sha256.Sum256(h.signingBytes()) }
49
50// Hash is the block hash, SHA256(header_hash ‖ sig).
51func (h *Header) Hash() Hash {
52 sh := h.SigningHash()
53 return sha256.Sum256(append(sh[:], h.Sig[:]...))
54}
55
56// Bytes encodes the header.
57func (h *Header) Bytes() []byte { return append(h.signingBytes(), h.Sig[:]...) }
58
59// DecodeHeader parses a header.
60func DecodeHeader(b []byte) (*Header, error) {
61 if len(b) != HeaderSize {
62 return nil, ErrHeaderSize
63 }
64 h := &Header{}
65 h.Number = binary.BigEndian.Uint64(b)
66 h.Slot = binary.BigEndian.Uint64(b[8:])
67 h.Time = binary.BigEndian.Uint64(b[16:])
68 p := 24
69 for _, dst := range []*Hash{&h.TxTrie, &h.Coin, &h.Token, &h.Election[0], &h.Election[1], &h.Rng[0], &h.Rng[1], &h.Reg[0], &h.Reg[1], &h.Prev, &h.Validator, &h.Nonce} {
70 copy(dst[:], b[p:p+32])
71 p += 32
72 }
73 copy(h.Sig[:], b[p:])
74 return h, nil
75}
76
77// setRoots writes the state roots into the header.
78func (h *Header) setRoots(tx trie.Hash, r Roots) {
79 h.TxTrie = Hash(tx)
80 h.Coin = Hash(r.Coin)
81 h.Token = Hash(r.Token)
82 h.Election = [2]Hash{Hash(r.Election[0]), Hash(r.Election[1])}
83 h.Rng = [2]Hash{Hash(r.Rng[0]), Hash(r.Rng[1])}
84 h.Reg = [2]Hash{Hash(r.Reg[0]), Hash(r.Reg[1])}
85}
86
87// rootsMatch reports whether the header commits to exactly these roots.
88func (h *Header) rootsMatch(tx trie.Hash, r Roots) bool {
89 return h.TxTrie == Hash(tx) && h.Coin == Hash(r.Coin) && h.Token == Hash(r.Token) &&
90 h.Election[0] == Hash(r.Election[0]) && h.Election[1] == Hash(r.Election[1]) &&
91 h.Rng[0] == Hash(r.Rng[0]) && h.Rng[1] == Hash(r.Rng[1]) &&
92 h.Reg[0] == Hash(r.Reg[0]) && h.Reg[1] == Hash(r.Reg[1])
93}
94
95// Block is a header and its operations. The order of operations does not
96// matter to validation; the tx trie commits to the set.
97type Block struct {
98 Header Header
99 Ops []Op
100}
101
102// Bytes encodes header ‖ uint32 n ‖ (uint32 len ‖ op)…
103func (b *Block) Bytes() []byte {
104 out := b.Header.Bytes()
105 out = binary.BigEndian.AppendUint32(out, uint32(len(b.Ops)))
106 for _, op := range b.Ops {
107 enc := op.Bytes()
108 out = binary.BigEndian.AppendUint32(out, uint32(len(enc)))
109 out = append(out, enc...)
110 }
111 return out
112}
113
114// DecodeBlock parses a block.
115func DecodeBlock(b []byte) (*Block, error) {
116 if len(b) < HeaderSize+4 {
117 return nil, ErrEncoding
118 }
119 h, err := DecodeHeader(b[:HeaderSize])
120 if err != nil {
121 return nil, err
122 }
123 blk := &Block{Header: *h}
124 b = b[HeaderSize:]
125 n := binary.BigEndian.Uint32(b)
126 b = b[4:]
127 for i := uint32(0); i < n; i++ {
128 if len(b) < 4 {
129 return nil, ErrEncoding
130 }
131 l := binary.BigEndian.Uint32(b)
132 b = b[4:]
133 if uint64(len(b)) < uint64(l) {
134 return nil, ErrEncoding
135 }
136 op, err := DecodeOp(b[:l])
137 if err != nil {
138 return nil, err
139 }
140 blk.Ops = append(blk.Ops, op)
141 b = b[l:]
142 }
143 if len(b) != 0 {
144 return nil, ErrEncoding
145 }
146 return blk, nil
147}
148
149// Better reports whether tip a should be preferred over tip b under the
150// fork choice: the higher block number wins; at equal numbers the lower
151// slot (fewer skipped slots) wins; otherwise keep b. The rule that forks
152// cannot reach back past two period boundaries is enforced by whoever
153// keeps the chains, by refusing to consider a fork that diverges earlier.
154func Better(a, b *State) bool {
155 if a.Number != b.Number {
156 return a.Number > b.Number
157 }
158 return a.Slot < b.Slot
159}