state/apply.go
637 lines
1package state23import (4"errors"5"fmt"6"math"78"peoplecoin/coin"9"peoplecoin/params"10"peoplecoin/period"11"peoplecoin/rng"12"peoplecoin/sig"13"peoplecoin/trie"14"peoplecoin/wire"15)1617// Validation errors. Each identifies the rule that failed.18var (19ErrPhase = errors.New("state: operation not allowed in this phase")20ErrSignature = errors.New("state: bad signature")21ErrMissingInput = errors.New("state: input not found")22ErrDuplicate = errors.New("state: duplicate input")23ErrAmount = errors.New("state: bad amount")24ErrOwners = errors.New("state: bad owners or threshold")25ErrInsufficient = errors.New("state: outputs exceed spendable inputs")26ErrOverflow = errors.New("state: arithmetic overflow")27ErrTokenType = errors.New("state: wrong token type")28ErrTokenOwner = errors.New("state: tokens have different owners")29ErrTokenCount = errors.New("state: wrong number of tokens")30ErrMixCount = errors.New("state: mix counts do not add up")31ErrMixCap = errors.New("state: mix count over cap")32ErrOccupied = errors.New("state: address already has an entry")33ErrNotRegistered = errors.New("state: no registration")34ErrAlreadySet = errors.New("state: flag already set")35ErrNoPartner = errors.New("state: no partner this period")36ErrPartnerNotVerify = errors.New("state: pair has not both verified")37ErrCommitZero = errors.New("state: commit is zero")38ErrCommitSet = errors.New("state: commit already set")39ErrNoCommit = errors.New("state: nothing to reveal")40ErrPreimage = errors.New("state: preimage does not match commit")41ErrNotPrunable = errors.New("state: entry not prunable")42ErrConflict = errors.New("state: two operations write the same key")43ErrDuplicateTx = errors.New("state: duplicate transaction")44ErrSlotTooLarge = errors.New("state: slot does not fit a coin entry")4546ErrNumber = errors.New("state: wrong block number")47ErrSlot = errors.New("state: slot not after the previous block")48ErrTime = errors.New("state: time is not the slot's start")49ErrPrev = errors.New("state: prev does not match")50ErrNoValidator = errors.New("state: no validators this period")51ErrValidator = errors.New("state: validator does not match the slot's vote")52ErrRoots = errors.New("state: header roots do not match state")53ErrHeaderSig = errors.New("state: bad header signature")54)5556type trieID uint85758const (59tCoin trieID = iota60tToken61tElection162tRng063tRng164tReg065tReg166)6768type writeKind uint86970const (71wInsert writeKind = iota72wUpdate73wDelete74)7576// write is one change to one trie key.77type write struct {78t trieID79key trie.Key80kind writeKind81value []byte82}8384// effect is what a valid operation does to the state.85type effect struct {86writes []write87fee uint64 // added to the block's fee accumulator88vote *uint64 // RNG vote cast by a Reveal89}9091func (e *effect) insert(t trieID, key Hash, value []byte) {92e.writes = append(e.writes, write{t, trie.Key(key), wInsert, value})93}9495func (e *effect) update(t trieID, key Hash, value []byte) {96e.writes = append(e.writes, write{t, trie.Key(key), wUpdate, value})97}9899func (e *effect) del(t trieID, key Hash) {100e.writes = append(e.writes, write{t, trie.Key(key), wDelete, nil})101}102103func (s *State) trieFor(t trieID) **trie.Trie {104switch t {105case tCoin:106return &s.Coin107case tToken:108return &s.Token109case tElection1:110return &s.Election[1]111case tRng0:112return &s.Rng[0]113case tRng1:114return &s.Rng[1]115case tReg0:116return &s.Reg[0]117default:118return &s.Reg[1]119}120}121122// ctx is what an operation is validated in.123type ctx struct {124slot uint64125period uint32126txHash Hash127}128129func checkSig(pub Address, msg Hash, s [64]byte) error {130if !sig.Verify(sig.PublicKey(pub), msg[:], sig.Signature(s)) {131return ErrSignature132}133return nil134}135136func noDuplicates(ids []Address) error {137seen := make(map[Address]bool, len(ids))138for _, id := range ids {139if seen[id] {140return ErrDuplicate141}142seen[id] = true143}144return nil145}146147func add(a, b uint64) (uint64, error) {148if a > math.MaxUint64-b {149return 0, ErrOverflow150}151return a + b, nil152}153154// validate checks op against s, the pre-block state, and returns its effect.155func (s *State) validate(op Op, c ctx) (*effect, error) {156switch o := op.(type) {157case *Transfer:158return s.validateTransfer(o, c)159case *Mix:160return s.validateMix(o, c)161case *Vote:162return s.validateVote(o, c)163case *Register:164return s.validateRegister(o, c)165case *Verify:166return s.validateVerify(o, c)167case *Verified:168return s.validateVerified(o, c)169case *Commit:170return s.validateCommit(o, c)171case *Reveal:172return s.validateReveal(o, c)173case *Prune:174return s.validatePrune(o, c)175}176return nil, ErrOpcode177}178179func (s *State) validateTransfer(o *Transfer, c ctx) (*effect, error) {180if len(o.Inputs) == 0 || len(o.Inputs) > 255 || len(o.Outputs) == 0 || len(o.Outputs) > 255 {181return nil, ErrEncoding182}183if c.slot > math.MaxUint32 {184return nil, ErrSlotTooLarge185}186if err := noDuplicates(o.Inputs); err != nil {187return nil, err188}189msg, err := o.Message(c.period)190if err != nil {191return nil, err192}193var sumEff, sumSpend, sumOut uint64194sigIdx := 0195e := &effect{}196for _, id := range o.Inputs {197entry, ok := s.CoinEntry(id)198if !ok {199return nil, ErrMissingInput200}201t := coin.Elapsed(entry.Slot, c.slot)202if sumEff, err = add(sumEff, coin.Effective(entry.Amount, t)); err != nil {203return nil, err204}205if sumSpend, err = add(sumSpend, coin.Spendable(entry.Amount, t)); err != nil {206return nil, err207}208// Exactly threshold signatures, matched against the owners in209// ascending owner-index order; a signature that matches no owner210// after the previous match is a failure.211ownerIdx := 0212for k := 0; k < int(entry.Threshold); k++ {213if sigIdx >= len(o.Sigs) {214return nil, ErrSignature215}216matched := -1217for j := ownerIdx; j < len(entry.Owners); j++ {218if checkSig(entry.Owners[j], msg, o.Sigs[sigIdx]) == nil {219matched = j220break221}222}223if matched < 0 {224return nil, ErrSignature225}226ownerIdx = matched + 1227sigIdx++228}229e.del(tCoin, id)230}231if sigIdx != len(o.Sigs) {232return nil, ErrSignature // extra signatures233}234for i, out := range o.Outputs {235if out.Amount == 0 {236return nil, ErrAmount237}238if len(out.Owners) < 1 || len(out.Owners) > params.MaxOwners || out.Threshold < 1 || int(out.Threshold) > len(out.Owners) {239return nil, ErrOwners240}241if sumOut, err = add(sumOut, out.Amount); err != nil {242return nil, err243}244entry := wire.CoinEntry{Owners: out.Owners, Threshold: out.Threshold, Amount: out.Amount, Slot: uint32(c.slot)}245e.insert(tCoin, wire.UTXOID(c.txHash, uint8(i)), entry.Bytes())246}247if sumOut > sumSpend {248return nil, ErrInsufficient249}250e.fee = sumEff - sumOut251return e, nil252}253254func (s *State) validateMix(o *Mix, c ctx) (*effect, error) {255if len(o.Inputs) == 0 || len(o.Inputs) > 255 || len(o.Outputs) != len(o.Inputs) || len(o.Sigs) != len(o.Inputs) {256return nil, ErrTokenCount257}258if err := noDuplicates(o.Inputs); err != nil {259return nil, err260}261msg, err := o.Message(c.period)262if err != nil {263return nil, err264}265var sumIn, sumOut uint64266e := &effect{}267for i, id := range o.Inputs {268entry, ok := s.TokenEntry(id)269if !ok {270return nil, ErrMissingInput271}272if entry.TokenID != o.TokenID {273return nil, ErrTokenType274}275if err := checkSig(entry.Owner, msg, o.Sigs[i]); err != nil {276return nil, err277}278sumIn += uint64(entry.MixCount) + 1279e.del(tToken, id)280}281for i, out := range o.Outputs {282if out.MixCount > params.MaxMixes {283return nil, ErrMixCap284}285sumOut += uint64(out.MixCount)286entry := wire.TokenEntry{TokenID: o.TokenID, Owner: out.Owner, MixCount: out.MixCount}287e.insert(tToken, wire.UTXOID(c.txHash, uint8(i)), entry.Bytes())288}289if sumIn != sumOut {290return nil, ErrMixCount291}292return e, nil293}294295func (s *State) validateVote(o *Vote, c ctx) (*effect, error) {296entry, ok := s.TokenEntry(o.TokenUTXO)297if !ok {298return nil, ErrMissingInput299}300if entry.TokenID != params.TokenElection {301return nil, ErrTokenType302}303msg, _ := o.Message(c.period)304if err := checkSig(entry.Owner, msg, o.Sig); err != nil {305return nil, err306}307if s.Election[1].Has(entry.Owner) {308return nil, ErrOccupied309}310e := &effect{}311e.del(tToken, o.TokenUTXO)312e.insert(tElection1, entry.Owner, (&wire.ElectionEntry{Commit: o.Commit}).Bytes())313return e, nil314}315316func (s *State) validateRegister(o *Register, c ctx) (*effect, error) {317n := len(o.TokenUTXOs)318if n != 1 && n != params.InviteCost {319return nil, ErrTokenCount320}321if err := noDuplicates(o.TokenUTXOs); err != nil {322return nil, err323}324want := params.TokenInvite325if n == 1 {326want = params.TokenRegistration327}328var owner Address329e := &effect{}330for i, id := range o.TokenUTXOs {331entry, ok := s.TokenEntry(id)332if !ok {333return nil, ErrMissingInput334}335if entry.TokenID != want {336return nil, ErrTokenType337}338if i == 0 {339owner = entry.Owner340} else if entry.Owner != owner {341return nil, ErrTokenOwner342}343e.del(tToken, id)344}345msg, err := o.Message(c.period)346if err != nil {347return nil, err348}349if err := checkSig(owner, msg, o.Sig); err != nil {350return nil, err351}352if s.Reg[1].Has(owner) {353return nil, ErrOccupied354}355e.insert(tReg1, owner, (&wire.RegEntry{}).Bytes())356return e, nil357}358359func (s *State) validateVerify(o *Verify, c ctx) (*effect, error) {360entry, ok := s.RegEntry(0, o.RegAddr)361if !ok {362return nil, ErrNotRegistered363}364if entry.Verify {365return nil, ErrAlreadySet366}367msg, _ := o.Message(c.period)368if err := checkSig(o.RegAddr, msg, o.Sig); err != nil {369return nil, err370}371e := &effect{}372entry.Verify = true373e.update(tReg0, o.RegAddr, entry.Bytes())374return e, nil375}376377func (s *State) validateVerified(o *Verified, c ctx) (*effect, error) {378entry, ok := s.RegEntry(0, o.RegAddr)379if !ok {380return nil, ErrNotRegistered381}382if entry.Verified {383return nil, ErrAlreadySet384}385partner, ok := s.Partner(o.RegAddr)386if !ok {387return nil, ErrNoPartner388}389pentry, _ := s.RegEntry(0, partner)390if !entry.Verify || pentry == nil || !pentry.Verify {391return nil, ErrPartnerNotVerify392}393msg, _ := o.Message(c.period)394if err := checkSig(o.RegAddr, msg, o.Sig); err != nil {395return nil, err396}397if s.Rng[1].Has(o.RegAddr) {398return nil, ErrOccupied399}400e := &effect{}401entry.Verified = true402e.update(tReg0, o.RegAddr, entry.Bytes())403for i, id := range []uint8{params.TokenElection, params.TokenRegistration, params.TokenInvite} {404tok := wire.TokenEntry{TokenID: id, Owner: o.RegAddr}405e.insert(tToken, wire.UTXOID(c.txHash, uint8(i)), tok.Bytes())406}407e.insert(tRng1, o.RegAddr, (&wire.RngEntry{}).Bytes())408return e, nil409}410411func (s *State) validateCommit(o *Commit, c ctx) (*effect, error) {412entry, ok := s.RngEntry(1, o.RngAddr)413if !ok {414return nil, ErrNotRegistered415}416if entry.Commit != (Hash{}) {417return nil, ErrCommitSet418}419if o.Commit == (Hash{}) {420return nil, ErrCommitZero421}422msg, _ := o.Message(c.period)423if err := checkSig(o.RngAddr, msg, o.Sig); err != nil {424return nil, err425}426e := &effect{}427e.update(tRng1, o.RngAddr, (&wire.RngEntry{Commit: o.Commit}).Bytes())428return e, nil429}430431func (s *State) validateReveal(o *Reveal, c ctx) (*effect, error) {432if c.slot > math.MaxUint32 {433return nil, ErrSlotTooLarge434}435entry, ok := s.RngEntry(0, o.RngAddr)436if !ok {437return nil, ErrNotRegistered438}439if entry.Commit == (Hash{}) {440return nil, ErrNoCommit441}442if rng.Commit(o.Preimage) != entry.Commit {443return nil, ErrPreimage444}445msg, _ := o.Message(c.period)446if err := checkSig(o.RngAddr, msg, o.Sig); err != nil {447return nil, err448}449e := &effect{}450e.update(tRng0, o.RngAddr, (&wire.RngEntry{}).Bytes())451ubi := wire.CoinEntry{Owners: []Address{o.RngAddr}, Threshold: 1, Amount: params.UBIPerPeriod, Slot: uint32(c.slot)}452e.insert(tCoin, wire.UTXOID(c.txHash, 0), ubi.Bytes())453v := rng.Vote(o.RngAddr, o.Preimage, s.Seed, s.Rng[0].Count())454e.vote = &v455return e, nil456}457458func (s *State) validatePrune(o *Prune, c ctx) (*effect, error) {459entry, ok := s.CoinEntry(o.UTXO)460if !ok {461return nil, ErrMissingInput462}463t := coin.Elapsed(entry.Slot, c.slot)464if !coin.Prunable(entry.Amount, t) {465return nil, ErrNotPrunable466}467e := &effect{fee: coin.Effective(entry.Amount, t)}468e.del(tCoin, o.UTXO)469return e, nil470}471472// ---------------------------------------------------------------------473// Block execution474475type keyRef struct {476t trieID477key trie.Key478}479480// execute validates ops against pre (the state at the block's slot, before481// the block) and returns the post state, with fees paid to validator and482// voter, and the tx trie root.483func execute(pre *State, number, slot uint64, validator, voter Address, ops []Op) (*State, trie.Hash, error) {484c := ctx{slot: slot, period: period.Number(slot)}485phase := period.Phase(slot)486txTrie := trie.NewPlain()487written := map[keyRef]bool{}488effects := make([]*effect, 0, len(ops))489var fee uint64490491for _, op := range ops {492if !allowedPhase(op.Opcode(), phase) {493return nil, trie.Hash{}, fmt.Errorf("%w: opcode 0x%02x in phase %d", ErrPhase, op.Opcode(), phase)494}495txh, err := TxHash(op, c.period)496if err != nil {497return nil, trie.Hash{}, err498}499if txTrie, err = txTrie.Insert(trie.Key(txh), op.Bytes()); err != nil {500return nil, trie.Hash{}, ErrDuplicateTx501}502c.txHash = txh503eff, err := pre.validate(op, c)504if err != nil {505return nil, trie.Hash{}, fmt.Errorf("tx %x: %w", txh[:4], err)506}507for _, w := range eff.writes {508ref := keyRef{w.t, w.key}509if written[ref] {510return nil, trie.Hash{}, ErrConflict511}512written[ref] = true513}514if fee, err = add(fee, eff.fee); err != nil {515return nil, trie.Hash{}, err516}517effects = append(effects, eff)518}519520post := *pre521post.Tally = pre.Tally.Clone()522for _, eff := range effects {523for _, w := range eff.writes {524tr := post.trieFor(w.t)525var err error526switch w.kind {527case wInsert:528*tr, err = (*tr).Insert(w.key, w.value)529case wUpdate:530*tr, err = (*tr).Update(w.key, w.value)531case wDelete:532*tr, err = (*tr).Delete(w.key)533}534if err != nil {535// Cannot happen: every write was validated against pre and536// no two writes share a key.537return nil, trie.Hash{}, fmt.Errorf("state: apply: %w", err)538}539}540if eff.vote != nil {541post.Tally.Add(*eff.vote)542}543}544545// Fees: split 50-50, odd unit to the validator; only non-zero outputs.546if fee > 0 {547if slot > math.MaxUint32 {548return nil, trie.Hash{}, ErrSlotTooLarge549}550voterShare := fee / 2551validatorShare := fee - voterShare552for i, p := range []struct {553owner Address554amount uint64555}{{validator, validatorShare}, {voter, voterShare}} {556if p.amount == 0 {557continue558}559entry := wire.CoinEntry{Owners: []Address{p.owner}, Threshold: 1, Amount: p.amount, Slot: uint32(slot)}560var err error561if post.Coin, err = post.Coin.Insert(trie.Key(wire.FeeUTXOID(number, uint8(i))), entry.Bytes()); err != nil {562return nil, trie.Hash{}, ErrConflict563}564}565}566return &post, txTrie.Root(), nil567}568569// checkHeader validates the parts of the header that do not depend on the570// operations, and returns the pre-block state and the slot's voter.571func (s *State) checkHeader(h *Header) (pre *State, voter Address, err error) {572if h.Number != s.Number+1 {573return nil, Address{}, ErrNumber574}575if h.Slot <= s.Slot {576return nil, Address{}, ErrSlot577}578// time is the slot's start, carried forward from the previous header579// so that no node needs to remember the genesis time.580if h.Time != s.TimeOf(h.Slot) {581return nil, Address{}, ErrTime582}583if h.Prev != s.PrevHash {584return nil, Address{}, ErrPrev585}586pre = s.AtSlot(h.Slot)587voter, commit, ok := pre.Validator(h.Slot)588if !ok {589return nil, Address{}, ErrNoValidator590}591if rng.ElectionCommit(h.Validator, h.Nonce) != commit {592return nil, Address{}, ErrValidator593}594return pre, voter, nil595}596597// Apply validates b on top of s and returns the resulting state.598func (s *State) Apply(b *Block) (*State, error) {599h := &b.Header600pre, voter, err := s.checkHeader(h)601if err != nil {602return nil, err603}604post, txRoot, err := execute(pre, h.Number, h.Slot, h.Validator, voter, b.Ops)605if err != nil {606return nil, err607}608if !h.rootsMatch(txRoot, post.Roots()) {609return nil, ErrRoots610}611if err := checkSig(h.Validator, h.SigningHash(), h.Sig); err != nil {612return nil, ErrHeaderSig613}614post.Number, post.Slot, post.Time, post.PrevHash = h.Number, h.Slot, h.Time, h.Hash()615return post, nil616}617618// Assemble produces and signs the block for slot on top of s, containing619// ops, which must all be valid; the caller filters its mempool first. It620// returns the block and the state after it.621func (s *State) Assemble(slot uint64, ops []Op, validator *sig.Keypair, nonce Hash) (*Block, *State, error) {622h := &Header{Number: s.Number + 1, Slot: slot, Time: s.TimeOf(slot), Prev: s.PrevHash, Validator: Address(validator.Public), Nonce: nonce}623pre, voter, err := s.checkHeader(h)624if err != nil {625return nil, nil, err626}627post, txRoot, err := execute(pre, h.Number, h.Slot, h.Validator, voter, ops)628if err != nil {629return nil, nil, err630}631h.setRoots(txRoot, post.Roots())632sh := h.SigningHash()633h.Sig = validator.Sign(sh[:])634post.Number, post.Slot, post.Time, post.PrevHash = h.Number, h.Slot, h.Time, h.Hash()635return &Block{Header: *h, Ops: ops}, post, nil636}