Go implementation

peoplecoin

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

wire/wire.go

343 lines

1// Package wire defines the byte encodings the protocol hashes and signs:
2// the entries stored in the tries, the signing message of every operation
3// — which is also its transaction hash — and UTXO identifiers.
4//
5// All fields are big-endian. Variable-length fields carry a uint8 length
6// prefix. Addresses are 32 bytes, the period is a uint32, coin entry slots
7// are uint32, amounts uint64, output indices uint8. Decoding is strict:
8// trailing bytes, bad lengths and out-of-range fields are errors, so that
9// every encoding has exactly one valid form.
10package wire
11
12import (
13 "crypto/sha256"
14 "encoding/binary"
15 "errors"
16
17 "peoplecoin/params"
18)
19
20var (
21 ErrLength = errors.New("wire: bad length")
22 ErrTooMany = errors.New("wire: list longer than 255")
23 ErrOwners = errors.New("wire: bad owner count")
24)
25
26// Address is a 32-byte public key or trie key.
27type Address = [32]byte
28
29// Hash is a 32-byte SHA-256 digest.
30type Hash = [32]byte
31
32// ---------------------------------------------------------------------
33// Trie entries
34
35// CoinEntry is an unspent output in the coin trie.
36type CoinEntry struct {
37 Owners []Address // public keys, up to MaxOwners
38 Threshold uint8 // signatures required
39 Amount uint64 // nominal, never changes
40 Slot uint32 // slot at creation
41}
42
43// Bytes encodes the entry: uint8 n ‖ owners ‖ threshold ‖ amount ‖ slot.
44func (e *CoinEntry) Bytes() []byte {
45 b := make([]byte, 0, 1+32*len(e.Owners)+1+8+4)
46 b = append(b, uint8(len(e.Owners)))
47 for i := range e.Owners {
48 b = append(b, e.Owners[i][:]...)
49 }
50 b = append(b, e.Threshold)
51 b = binary.BigEndian.AppendUint64(b, e.Amount)
52 b = binary.BigEndian.AppendUint32(b, e.Slot)
53 return b
54}
55
56// DecodeCoinEntry parses a CoinEntry, requiring 1 ≤ owners ≤ MaxOwners and
57// 1 ≤ threshold ≤ owners.
58func DecodeCoinEntry(b []byte) (*CoinEntry, error) {
59 if len(b) < 1 {
60 return nil, ErrLength
61 }
62 n := int(b[0])
63 if n < 1 || n > params.MaxOwners {
64 return nil, ErrOwners
65 }
66 if len(b) != 1+32*n+1+8+4 {
67 return nil, ErrLength
68 }
69 e := &CoinEntry{Owners: make([]Address, n)}
70 p := 1
71 for i := 0; i < n; i++ {
72 copy(e.Owners[i][:], b[p:p+32])
73 p += 32
74 }
75 e.Threshold = b[p]
76 p++
77 if e.Threshold < 1 || int(e.Threshold) > n {
78 return nil, ErrOwners
79 }
80 e.Amount = binary.BigEndian.Uint64(b[p:])
81 e.Slot = binary.BigEndian.Uint32(b[p+8:])
82 return e, nil
83}
84
85// TokenEntry is an election, registration or invite token.
86type TokenEntry struct {
87 TokenID uint8
88 Owner Address
89 MixCount uint32
90}
91
92// Bytes encodes token_id ‖ owner ‖ mix_count.
93func (e *TokenEntry) Bytes() []byte {
94 b := make([]byte, 0, 1+32+4)
95 b = append(b, e.TokenID)
96 b = append(b, e.Owner[:]...)
97 return binary.BigEndian.AppendUint32(b, e.MixCount)
98}
99
100// DecodeTokenEntry parses a TokenEntry.
101func DecodeTokenEntry(b []byte) (*TokenEntry, error) {
102 if len(b) != 1+32+4 {
103 return nil, ErrLength
104 }
105 e := &TokenEntry{TokenID: b[0], MixCount: binary.BigEndian.Uint32(b[33:])}
106 copy(e.Owner[:], b[1:33])
107 return e, nil
108}
109
110// RegEntry is a registration: two flags set during the event.
111type RegEntry struct {
112 Verify bool // this person attests the partner was there
113 Verified bool // both flags were set; tokens paid
114}
115
116// Bytes encodes verify ‖ verified as 0x00/0x01 bytes.
117func (e *RegEntry) Bytes() []byte {
118 return []byte{boolByte(e.Verify), boolByte(e.Verified)}
119}
120
121// DecodeRegEntry parses a RegEntry; only 0 and 1 are valid flag bytes.
122func DecodeRegEntry(b []byte) (*RegEntry, error) {
123 if len(b) != 2 || b[0] > 1 || b[1] > 1 {
124 return nil, ErrLength
125 }
126 return &RegEntry{Verify: b[0] == 1, Verified: b[1] == 1}, nil
127}
128
129func boolByte(v bool) byte {
130 if v {
131 return 1
132 }
133 return 0
134}
135
136// RngEntry holds a commit to a number; zero after the reveal or before the
137// commit.
138type RngEntry struct {
139 Commit Hash
140}
141
142// Bytes encodes the commit.
143func (e *RngEntry) Bytes() []byte { return append([]byte{}, e.Commit[:]...) }
144
145// DecodeRngEntry parses an RngEntry.
146func DecodeRngEntry(b []byte) (*RngEntry, error) {
147 if len(b) != 32 {
148 return nil, ErrLength
149 }
150 var e RngEntry
151 copy(e.Commit[:], b)
152 return &e, nil
153}
154
155// ElectionEntry is a vote: SHA256(validator_pubkey ‖ nonce).
156type ElectionEntry struct {
157 Commit Hash
158}
159
160// Bytes encodes the commit.
161func (e *ElectionEntry) Bytes() []byte { return append([]byte{}, e.Commit[:]...) }
162
163// DecodeElectionEntry parses an ElectionEntry.
164func DecodeElectionEntry(b []byte) (*ElectionEntry, error) {
165 if len(b) != 32 {
166 return nil, ErrLength
167 }
168 var e ElectionEntry
169 copy(e.Commit[:], b)
170 return &e, nil
171}
172
173// ---------------------------------------------------------------------
174// Operation payloads
175
176// TransferOutput is one output of a Transfer.
177type TransferOutput struct {
178 Amount uint64
179 Owners []Address
180 Threshold uint8
181}
182
183// MixOutput is one output of a Mix.
184type MixOutput struct {
185 MixCount uint32
186 Owner Address
187}
188
189// EncodeIDs encodes a list of 32-byte IDs with a uint8 length prefix.
190func EncodeIDs(ids []Address) ([]byte, error) {
191 if len(ids) > 255 {
192 return nil, ErrTooMany
193 }
194 b := make([]byte, 0, 1+32*len(ids))
195 b = append(b, uint8(len(ids)))
196 for i := range ids {
197 b = append(b, ids[i][:]...)
198 }
199 return b, nil
200}
201
202// EncodeTransferOutputs encodes uint8 n ‖ (amount ‖ uint8 owners ‖ owners ‖
203// threshold)…
204func EncodeTransferOutputs(outs []TransferOutput) ([]byte, error) {
205 if len(outs) > 255 {
206 return nil, ErrTooMany
207 }
208 b := []byte{uint8(len(outs))}
209 for i := range outs {
210 if len(outs[i].Owners) > 255 {
211 return nil, ErrTooMany
212 }
213 b = binary.BigEndian.AppendUint64(b, outs[i].Amount)
214 b = append(b, uint8(len(outs[i].Owners)))
215 for j := range outs[i].Owners {
216 b = append(b, outs[i].Owners[j][:]...)
217 }
218 b = append(b, outs[i].Threshold)
219 }
220 return b, nil
221}
222
223// EncodeMixOutputs encodes uint8 n ‖ (mix_count ‖ owner)…
224func EncodeMixOutputs(outs []MixOutput) ([]byte, error) {
225 if len(outs) > 255 {
226 return nil, ErrTooMany
227 }
228 b := []byte{uint8(len(outs))}
229 for i := range outs {
230 b = binary.BigEndian.AppendUint32(b, outs[i].MixCount)
231 b = append(b, outs[i].Owner[:]...)
232 }
233 return b, nil
234}
235
236// ---------------------------------------------------------------------
237// Signing messages. The opcode prefix prevents cross-operation replay; the
238// period prevents replay across periods for address-keyed operations.
239
240func hashParts(parts ...[]byte) Hash {
241 h := sha256.New()
242 for _, p := range parts {
243 h.Write(p)
244 }
245 var out Hash
246 copy(out[:], h.Sum(nil))
247 return out
248}
249
250func u32(v uint32) []byte { return binary.BigEndian.AppendUint32(nil, v) }
251
252// TransferMessage is SHA256(0x00 ‖ SHA256(inputs) ‖ SHA256(outputs)).
253func TransferMessage(inputs []Address, outputs []TransferOutput) (Hash, error) {
254 in, err := EncodeIDs(inputs)
255 if err != nil {
256 return Hash{}, err
257 }
258 out, err := EncodeTransferOutputs(outputs)
259 if err != nil {
260 return Hash{}, err
261 }
262 hi, ho := sha256.Sum256(in), sha256.Sum256(out)
263 return hashParts([]byte{params.OpTransfer}, hi[:], ho[:]), nil
264}
265
266// MixMessage is SHA256(0x01 ‖ token_id ‖ SHA256(inputs) ‖ SHA256(outputs)).
267func MixMessage(tokenID uint8, inputs []Address, outputs []MixOutput) (Hash, error) {
268 in, err := EncodeIDs(inputs)
269 if err != nil {
270 return Hash{}, err
271 }
272 out, err := EncodeMixOutputs(outputs)
273 if err != nil {
274 return Hash{}, err
275 }
276 hi, ho := sha256.Sum256(in), sha256.Sum256(out)
277 return hashParts([]byte{params.OpMix, tokenID}, hi[:], ho[:]), nil
278}
279
280// VoteMessage is SHA256(0x02 ‖ token_utxo ‖ commit).
281func VoteMessage(tokenUTXO, commit Hash) Hash {
282 return hashParts([]byte{params.OpVote}, tokenUTXO[:], commit[:])
283}
284
285// RegisterMessage is SHA256(0x03 ‖ SHA256(token_utxos)).
286func RegisterMessage(tokenUTXOs []Address) (Hash, error) {
287 in, err := EncodeIDs(tokenUTXOs)
288 if err != nil {
289 return Hash{}, err
290 }
291 hi := sha256.Sum256(in)
292 return hashParts([]byte{params.OpRegister}, hi[:]), nil
293}
294
295// VerifyMessage is SHA256(0x04 ‖ period ‖ reg_addr).
296func VerifyMessage(period uint32, regAddr Address) Hash {
297 return hashParts([]byte{params.OpVerify}, u32(period), regAddr[:])
298}
299
300// VerifiedMessage is SHA256(0x05 ‖ period ‖ reg_addr).
301func VerifiedMessage(period uint32, regAddr Address) Hash {
302 return hashParts([]byte{params.OpVerified}, u32(period), regAddr[:])
303}
304
305// CommitMessage is SHA256(0x06 ‖ period ‖ rng_addr ‖ commit).
306func CommitMessage(period uint32, rngAddr Address, commit Hash) Hash {
307 return hashParts([]byte{params.OpCommit}, u32(period), rngAddr[:], commit[:])
308}
309
310// RevealMessage is SHA256(0x07 ‖ period ‖ rng_addr ‖ preimage).
311func RevealMessage(period uint32, rngAddr Address, preimage Hash) Hash {
312 return hashParts([]byte{params.OpReveal}, u32(period), rngAddr[:], preimage[:])
313}
314
315// PruneMessage is SHA256(0xFF ‖ utxo_id); Prune is unsigned.
316func PruneMessage(utxoID Hash) Hash {
317 return hashParts([]byte{params.OpPrune}, utxoID[:])
318}
319
320// ---------------------------------------------------------------------
321// Identifiers
322
323// The transaction hash — the operation's key in the tx trie — is its
324// signing message: it commits to every field, and it is unique by
325// construction, since every operation either consumes an input named in
326// the message or makes a once-per-period change keyed by the period and
327// address in it. Signatures are outside the hash, so it is fixed before
328// signing and cannot be changed by re-signing; the block commits to them
329// through the trie leaf, whose value is the full operation.
330
331// UTXOID identifies output index of the operation with hash txHash:
332// SHA256(tx_hash ‖ output_index). Verified mints election 0, registration
333// 1 and invite 2; Reveal pays the UBI at 0; Transfer outputs in order.
334func UTXOID(txHash Hash, index uint8) Hash {
335 return hashParts(txHash[:], []byte{index})
336}
337
338// FeeUTXOID identifies a block's fee outputs: SHA256(number ‖ index),
339// 0 the validator and 1 the voter.
340func FeeUTXOID(number uint64, index uint8) Hash {
341 return hashParts(binary.BigEndian.AppendUint64(nil, number), []byte{index})
342}