Go implementation

peoplecoin

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

wire/wire_test.go

188 lines

1package wire
2
3import (
4 "bytes"
5 "crypto/sha256"
6 "encoding/binary"
7 "testing"
8
9 "peoplecoin/params"
10)
11
12func addr(b byte) Address {
13 var a Address
14 for i := range a {
15 a[i] = b
16 }
17 return a
18}
19
20func TestCoinEntryRoundTrip(t *testing.T) {
21 e := &CoinEntry{Owners: []Address{addr(1), addr(2), addr(3)}, Threshold: 2, Amount: 123456789, Slot: 40320}
22 b := e.Bytes()
23 if len(b) != 1+96+1+8+4 {
24 t.Fatalf("length %d", len(b))
25 }
26 if b[0] != 3 || b[97] != 2 || binary.BigEndian.Uint64(b[98:]) != 123456789 || binary.BigEndian.Uint32(b[106:]) != 40320 {
27 t.Fatal("layout")
28 }
29 d, err := DecodeCoinEntry(b)
30 if err != nil || d.Threshold != 2 || d.Amount != 123456789 || d.Slot != 40320 || len(d.Owners) != 3 || d.Owners[2] != addr(3) {
31 t.Fatalf("decode: %v %+v", err, d)
32 }
33 if !bytes.Equal(d.Bytes(), b) {
34 t.Fatal("re-encode")
35 }
36 // Strictness.
37 if _, err := DecodeCoinEntry(append(b, 0)); err == nil {
38 t.Fatal("trailing byte accepted")
39 }
40 if _, err := DecodeCoinEntry(b[:len(b)-1]); err == nil {
41 t.Fatal("truncated accepted")
42 }
43 if _, err := DecodeCoinEntry([]byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); err == nil {
44 t.Fatal("zero owners accepted")
45 }
46 nine := &CoinEntry{Owners: make([]Address, 9), Threshold: 1, Amount: 1}
47 if _, err := DecodeCoinEntry(nine.Bytes()); err == nil {
48 t.Fatal("nine owners accepted")
49 }
50 zeroT := &CoinEntry{Owners: []Address{addr(1)}, Threshold: 0, Amount: 1}
51 if _, err := DecodeCoinEntry(zeroT.Bytes()); err == nil {
52 t.Fatal("threshold 0 accepted")
53 }
54 bigT := &CoinEntry{Owners: []Address{addr(1)}, Threshold: 2, Amount: 1}
55 if _, err := DecodeCoinEntry(bigT.Bytes()); err == nil {
56 t.Fatal("threshold > owners accepted")
57 }
58}
59
60func TestTokenEntryRoundTrip(t *testing.T) {
61 e := &TokenEntry{TokenID: params.TokenInvite, Owner: addr(9), MixCount: 31}
62 b := e.Bytes()
63 if len(b) != 37 || b[0] != 2 || binary.BigEndian.Uint32(b[33:]) != 31 {
64 t.Fatal("layout")
65 }
66 d, err := DecodeTokenEntry(b)
67 if err != nil || *d != *e {
68 t.Fatalf("decode: %v", err)
69 }
70 if _, err := DecodeTokenEntry(b[:36]); err == nil {
71 t.Fatal("truncated accepted")
72 }
73}
74
75func TestRegEntryRoundTrip(t *testing.T) {
76 for _, e := range []RegEntry{{}, {Verify: true}, {Verify: true, Verified: true}, {Verified: true}} {
77 b := e.Bytes()
78 d, err := DecodeRegEntry(b)
79 if err != nil || *d != e {
80 t.Fatalf("decode %v: %v", e, err)
81 }
82 }
83 if _, err := DecodeRegEntry([]byte{2, 0}); err == nil {
84 t.Fatal("non-boolean byte accepted")
85 }
86 if _, err := DecodeRegEntry([]byte{1}); err == nil {
87 t.Fatal("short accepted")
88 }
89}
90
91func TestHashEntries(t *testing.T) {
92 c := sha256.Sum256([]byte("c"))
93 r, err := DecodeRngEntry((&RngEntry{Commit: c}).Bytes())
94 if err != nil || r.Commit != c {
95 t.Fatal("rng entry")
96 }
97 e, err := DecodeElectionEntry((&ElectionEntry{Commit: c}).Bytes())
98 if err != nil || e.Commit != c {
99 t.Fatal("election entry")
100 }
101 if _, err := DecodeRngEntry(c[:31]); err == nil {
102 t.Fatal("short rng accepted")
103 }
104}
105
106func TestEncoders(t *testing.T) {
107 ids := []Address{addr(1), addr(2)}
108 b, err := EncodeIDs(ids)
109 if err != nil || len(b) != 65 || b[0] != 2 || !bytes.Equal(b[33:], ids[1][:]) {
110 t.Fatal("ids")
111 }
112 if _, err := EncodeIDs(make([]Address, 256)); err != ErrTooMany {
113 t.Fatal("256 ids accepted")
114 }
115 tb, err := EncodeTransferOutputs([]TransferOutput{{Amount: 5, Owners: []Address{addr(7)}, Threshold: 1}})
116 if err != nil || len(tb) != 1+8+1+32+1 || tb[0] != 1 || binary.BigEndian.Uint64(tb[1:]) != 5 || tb[9] != 1 || tb[42] != 1 {
117 t.Fatal("transfer outputs")
118 }
119 mb, err := EncodeMixOutputs([]MixOutput{{MixCount: 3, Owner: addr(4)}})
120 if err != nil || len(mb) != 1+4+32 || binary.BigEndian.Uint32(mb[1:]) != 3 {
121 t.Fatal("mix outputs")
122 }
123}
124
125func cat(parts ...[]byte) []byte { return bytes.Join(parts, nil) }
126
127func sl(a Address) []byte { return a[:] }
128
129func TestSigningMessages(t *testing.T) {
130 inputs := []Address{addr(1)}
131 outputs := []TransferOutput{{Amount: 10, Owners: []Address{addr(2)}, Threshold: 1}}
132 in, _ := EncodeIDs(inputs)
133 out, _ := EncodeTransferOutputs(outputs)
134 hi, ho := sha256.Sum256(in), sha256.Sum256(out)
135 want := sha256.Sum256(cat([]byte{0x00}, hi[:], ho[:]))
136 if got, _ := TransferMessage(inputs, outputs); got != want {
137 t.Fatal("transfer message")
138 }
139
140 mo := []MixOutput{{MixCount: 1, Owner: addr(3)}}
141 mob, _ := EncodeMixOutputs(mo)
142 hmo := sha256.Sum256(mob)
143 want = sha256.Sum256(cat([]byte{0x01, 0x02}, hi[:], hmo[:]))
144 if got, _ := MixMessage(2, inputs, mo); got != want {
145 t.Fatal("mix message")
146 }
147
148 c := sha256.Sum256([]byte("commit"))
149 if VoteMessage(addr(1), c) != sha256.Sum256(cat([]byte{0x02}, sl(addr(1)), c[:])) {
150 t.Fatal("vote message")
151 }
152 want = sha256.Sum256(cat([]byte{0x03}, hi[:]))
153 if got, _ := RegisterMessage(inputs); got != want {
154 t.Fatal("register message")
155 }
156 per := []byte{0, 0, 0, 7}
157 if VerifyMessage(7, addr(5)) != sha256.Sum256(cat([]byte{0x04}, per, sl(addr(5)))) {
158 t.Fatal("verify message")
159 }
160 if VerifiedMessage(7, addr(5)) != sha256.Sum256(cat([]byte{0x05}, per, sl(addr(5)))) {
161 t.Fatal("verified message")
162 }
163 if CommitMessage(7, addr(5), c) != sha256.Sum256(cat([]byte{0x06}, per, sl(addr(5)), c[:])) {
164 t.Fatal("commit message")
165 }
166 if RevealMessage(7, addr(5), c) != sha256.Sum256(cat([]byte{0x07}, per, sl(addr(5)), c[:])) {
167 t.Fatal("reveal message")
168 }
169 if PruneMessage(addr(6)) != sha256.Sum256(cat([]byte{0xFF}, sl(addr(6)))) {
170 t.Fatal("prune message")
171 }
172
173 // Cross-operation and cross-period separation.
174 if VerifyMessage(7, addr(5)) == VerifiedMessage(7, addr(5)) || VerifyMessage(7, addr(5)) == VerifyMessage(8, addr(5)) {
175 t.Fatal("messages must differ by opcode and period")
176 }
177}
178
179func TestIdentifiers(t *testing.T) {
180 tx := sha256.Sum256([]byte("tx"))
181 if UTXOID(tx, 2) != sha256.Sum256(cat(tx[:], []byte{2})) {
182 t.Fatal("utxo id")
183 }
184 if FeeUTXOID(0x0102, 1) != sha256.Sum256([]byte{0, 0, 0, 0, 0, 0, 1, 2, 1}) {
185 t.Fatal("fee utxo id")
186 }
187}