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 operation3// — which is also its transaction hash — and UTXO identifiers.4//5// All fields are big-endian. Variable-length fields carry a uint8 length6// prefix. Addresses are 32 bytes, the period is a uint32, coin entry slots7// are uint32, amounts uint64, output indices uint8. Decoding is strict:8// trailing bytes, bad lengths and out-of-range fields are errors, so that9// every encoding has exactly one valid form.10package wire1112import (13"crypto/sha256"14"encoding/binary"15"errors"1617"peoplecoin/params"18)1920var (21ErrLength = errors.New("wire: bad length")22ErrTooMany = errors.New("wire: list longer than 255")23ErrOwners = errors.New("wire: bad owner count")24)2526// Address is a 32-byte public key or trie key.27type Address = [32]byte2829// Hash is a 32-byte SHA-256 digest.30type Hash = [32]byte3132// ---------------------------------------------------------------------33// Trie entries3435// CoinEntry is an unspent output in the coin trie.36type CoinEntry struct {37Owners []Address // public keys, up to MaxOwners38Threshold uint8 // signatures required39Amount uint64 // nominal, never changes40Slot uint32 // slot at creation41}4243// Bytes encodes the entry: uint8 n ‖ owners ‖ threshold ‖ amount ‖ slot.44func (e *CoinEntry) Bytes() []byte {45b := make([]byte, 0, 1+32*len(e.Owners)+1+8+4)46b = append(b, uint8(len(e.Owners)))47for i := range e.Owners {48b = append(b, e.Owners[i][:]...)49}50b = append(b, e.Threshold)51b = binary.BigEndian.AppendUint64(b, e.Amount)52b = binary.BigEndian.AppendUint32(b, e.Slot)53return b54}5556// DecodeCoinEntry parses a CoinEntry, requiring 1 ≤ owners ≤ MaxOwners and57// 1 ≤ threshold ≤ owners.58func DecodeCoinEntry(b []byte) (*CoinEntry, error) {59if len(b) < 1 {60return nil, ErrLength61}62n := int(b[0])63if n < 1 || n > params.MaxOwners {64return nil, ErrOwners65}66if len(b) != 1+32*n+1+8+4 {67return nil, ErrLength68}69e := &CoinEntry{Owners: make([]Address, n)}70p := 171for i := 0; i < n; i++ {72copy(e.Owners[i][:], b[p:p+32])73p += 3274}75e.Threshold = b[p]76p++77if e.Threshold < 1 || int(e.Threshold) > n {78return nil, ErrOwners79}80e.Amount = binary.BigEndian.Uint64(b[p:])81e.Slot = binary.BigEndian.Uint32(b[p+8:])82return e, nil83}8485// TokenEntry is an election, registration or invite token.86type TokenEntry struct {87TokenID uint888Owner Address89MixCount uint3290}9192// Bytes encodes token_id ‖ owner ‖ mix_count.93func (e *TokenEntry) Bytes() []byte {94b := make([]byte, 0, 1+32+4)95b = append(b, e.TokenID)96b = append(b, e.Owner[:]...)97return binary.BigEndian.AppendUint32(b, e.MixCount)98}99100// DecodeTokenEntry parses a TokenEntry.101func DecodeTokenEntry(b []byte) (*TokenEntry, error) {102if len(b) != 1+32+4 {103return nil, ErrLength104}105e := &TokenEntry{TokenID: b[0], MixCount: binary.BigEndian.Uint32(b[33:])}106copy(e.Owner[:], b[1:33])107return e, nil108}109110// RegEntry is a registration: two flags set during the event.111type RegEntry struct {112Verify bool // this person attests the partner was there113Verified bool // both flags were set; tokens paid114}115116// Bytes encodes verify ‖ verified as 0x00/0x01 bytes.117func (e *RegEntry) Bytes() []byte {118return []byte{boolByte(e.Verify), boolByte(e.Verified)}119}120121// DecodeRegEntry parses a RegEntry; only 0 and 1 are valid flag bytes.122func DecodeRegEntry(b []byte) (*RegEntry, error) {123if len(b) != 2 || b[0] > 1 || b[1] > 1 {124return nil, ErrLength125}126return &RegEntry{Verify: b[0] == 1, Verified: b[1] == 1}, nil127}128129func boolByte(v bool) byte {130if v {131return 1132}133return 0134}135136// RngEntry holds a commit to a number; zero after the reveal or before the137// commit.138type RngEntry struct {139Commit Hash140}141142// Bytes encodes the commit.143func (e *RngEntry) Bytes() []byte { return append([]byte{}, e.Commit[:]...) }144145// DecodeRngEntry parses an RngEntry.146func DecodeRngEntry(b []byte) (*RngEntry, error) {147if len(b) != 32 {148return nil, ErrLength149}150var e RngEntry151copy(e.Commit[:], b)152return &e, nil153}154155// ElectionEntry is a vote: SHA256(validator_pubkey ‖ nonce).156type ElectionEntry struct {157Commit Hash158}159160// Bytes encodes the commit.161func (e *ElectionEntry) Bytes() []byte { return append([]byte{}, e.Commit[:]...) }162163// DecodeElectionEntry parses an ElectionEntry.164func DecodeElectionEntry(b []byte) (*ElectionEntry, error) {165if len(b) != 32 {166return nil, ErrLength167}168var e ElectionEntry169copy(e.Commit[:], b)170return &e, nil171}172173// ---------------------------------------------------------------------174// Operation payloads175176// TransferOutput is one output of a Transfer.177type TransferOutput struct {178Amount uint64179Owners []Address180Threshold uint8181}182183// MixOutput is one output of a Mix.184type MixOutput struct {185MixCount uint32186Owner Address187}188189// EncodeIDs encodes a list of 32-byte IDs with a uint8 length prefix.190func EncodeIDs(ids []Address) ([]byte, error) {191if len(ids) > 255 {192return nil, ErrTooMany193}194b := make([]byte, 0, 1+32*len(ids))195b = append(b, uint8(len(ids)))196for i := range ids {197b = append(b, ids[i][:]...)198}199return b, nil200}201202// EncodeTransferOutputs encodes uint8 n ‖ (amount ‖ uint8 owners ‖ owners ‖203// threshold)…204func EncodeTransferOutputs(outs []TransferOutput) ([]byte, error) {205if len(outs) > 255 {206return nil, ErrTooMany207}208b := []byte{uint8(len(outs))}209for i := range outs {210if len(outs[i].Owners) > 255 {211return nil, ErrTooMany212}213b = binary.BigEndian.AppendUint64(b, outs[i].Amount)214b = append(b, uint8(len(outs[i].Owners)))215for j := range outs[i].Owners {216b = append(b, outs[i].Owners[j][:]...)217}218b = append(b, outs[i].Threshold)219}220return b, nil221}222223// EncodeMixOutputs encodes uint8 n ‖ (mix_count ‖ owner)…224func EncodeMixOutputs(outs []MixOutput) ([]byte, error) {225if len(outs) > 255 {226return nil, ErrTooMany227}228b := []byte{uint8(len(outs))}229for i := range outs {230b = binary.BigEndian.AppendUint32(b, outs[i].MixCount)231b = append(b, outs[i].Owner[:]...)232}233return b, nil234}235236// ---------------------------------------------------------------------237// Signing messages. The opcode prefix prevents cross-operation replay; the238// period prevents replay across periods for address-keyed operations.239240func hashParts(parts ...[]byte) Hash {241h := sha256.New()242for _, p := range parts {243h.Write(p)244}245var out Hash246copy(out[:], h.Sum(nil))247return out248}249250func u32(v uint32) []byte { return binary.BigEndian.AppendUint32(nil, v) }251252// TransferMessage is SHA256(0x00 ‖ SHA256(inputs) ‖ SHA256(outputs)).253func TransferMessage(inputs []Address, outputs []TransferOutput) (Hash, error) {254in, err := EncodeIDs(inputs)255if err != nil {256return Hash{}, err257}258out, err := EncodeTransferOutputs(outputs)259if err != nil {260return Hash{}, err261}262hi, ho := sha256.Sum256(in), sha256.Sum256(out)263return hashParts([]byte{params.OpTransfer}, hi[:], ho[:]), nil264}265266// MixMessage is SHA256(0x01 ‖ token_id ‖ SHA256(inputs) ‖ SHA256(outputs)).267func MixMessage(tokenID uint8, inputs []Address, outputs []MixOutput) (Hash, error) {268in, err := EncodeIDs(inputs)269if err != nil {270return Hash{}, err271}272out, err := EncodeMixOutputs(outputs)273if err != nil {274return Hash{}, err275}276hi, ho := sha256.Sum256(in), sha256.Sum256(out)277return hashParts([]byte{params.OpMix, tokenID}, hi[:], ho[:]), nil278}279280// VoteMessage is SHA256(0x02 ‖ token_utxo ‖ commit).281func VoteMessage(tokenUTXO, commit Hash) Hash {282return hashParts([]byte{params.OpVote}, tokenUTXO[:], commit[:])283}284285// RegisterMessage is SHA256(0x03 ‖ SHA256(token_utxos)).286func RegisterMessage(tokenUTXOs []Address) (Hash, error) {287in, err := EncodeIDs(tokenUTXOs)288if err != nil {289return Hash{}, err290}291hi := sha256.Sum256(in)292return hashParts([]byte{params.OpRegister}, hi[:]), nil293}294295// VerifyMessage is SHA256(0x04 ‖ period ‖ reg_addr).296func VerifyMessage(period uint32, regAddr Address) Hash {297return hashParts([]byte{params.OpVerify}, u32(period), regAddr[:])298}299300// VerifiedMessage is SHA256(0x05 ‖ period ‖ reg_addr).301func VerifiedMessage(period uint32, regAddr Address) Hash {302return hashParts([]byte{params.OpVerified}, u32(period), regAddr[:])303}304305// CommitMessage is SHA256(0x06 ‖ period ‖ rng_addr ‖ commit).306func CommitMessage(period uint32, rngAddr Address, commit Hash) Hash {307return hashParts([]byte{params.OpCommit}, u32(period), rngAddr[:], commit[:])308}309310// RevealMessage is SHA256(0x07 ‖ period ‖ rng_addr ‖ preimage).311func RevealMessage(period uint32, rngAddr Address, preimage Hash) Hash {312return hashParts([]byte{params.OpReveal}, u32(period), rngAddr[:], preimage[:])313}314315// PruneMessage is SHA256(0xFF ‖ utxo_id); Prune is unsigned.316func PruneMessage(utxoID Hash) Hash {317return hashParts([]byte{params.OpPrune}, utxoID[:])318}319320// ---------------------------------------------------------------------321// Identifiers322323// The transaction hash — the operation's key in the tx trie — is its324// signing message: it commits to every field, and it is unique by325// construction, since every operation either consumes an input named in326// the message or makes a once-per-period change keyed by the period and327// address in it. Signatures are outside the hash, so it is fixed before328// signing and cannot be changed by re-signing; the block commits to them329// through the trie leaf, whose value is the full operation.330331// UTXOID identifies output index of the operation with hash txHash:332// SHA256(tx_hash ‖ output_index). Verified mints election 0, registration333// 1 and invite 2; Reveal pays the UBI at 0; Transfer outputs in order.334func UTXOID(txHash Hash, index uint8) Hash {335return hashParts(txHash[:], []byte{index})336}337338// 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 {341return hashParts(binary.BigEndian.AppendUint64(nil, number), []byte{index})342}