state/state.go
236 lines
1// Package state is the ledger: the seven tries, the seed and the period,2// and the rules by which a block moves them forward.3//4// A State is immutable. Applying a block returns a new State that shares5// unchanged subtrees with the old one, so any State ever produced stays6// valid — that is what makes validating a block against pre-block state,7// and switching between forks, straightforward.8//9// The rotating tries hold the current period in [0] and the next in [1]:10// the registry being paired now and the one being registered for next11// month; the votes producing blocks now and the votes for next month; the12// numbers being revealed now and the commits for next month. At the period13// boundary the reveals are tallied into the seed, [1] rotates into [0], and14// [1] and the token trie are cleared.15package state1617import (18"errors"1920"peoplecoin/feistel"21"peoplecoin/params"22"peoplecoin/period"23"peoplecoin/rng"24"peoplecoin/trie"25"peoplecoin/wire"26)2728// Address is a 32-byte public key; Hash a 32-byte digest.29type (30Address = [32]byte31Hash = [32]byte32)3334// State is the ledger at the tip of a chain.35type State struct {36Number uint64 // last block number; 0 at genesis37Slot uint64 // last block slot; 0 at genesis38Time uint64 // Unix time of that slot's start; genesis time at genesis39Period uint64 // period the rotating tries describe40PrevHash Hash // hash of the last block; zero at genesis41Seed Hash // this period's seed4243Coin *trie.Trie // plain, keyed by UTXO ID44Token *trie.Trie // plain, keyed by UTXO ID45Election [2]*trie.Trie // storage, keyed by voter46Rng [2]*trie.Trie // storage, keyed by address47Reg [2]*trie.Trie // storage, keyed by address4849Tally *rng.Tally // this period's reveals; side state, not committed50}5152// Roots are the trie roots committed in a header.53type Roots struct {54Coin, Token trie.Hash55Election [2]trie.Hash56Rng [2]trie.Hash57Reg [2]trie.Hash58}5960// Empty returns a state with no entries at slot 0 of period 0, starting at61// Unix time genesisTime, which by convention is 07:00 UTC on a Saturday.62func Empty(genesisTime uint64) *State {63return &State{64Time: genesisTime,65Coin: trie.NewPlain(),66Token: trie.NewPlain(),67Election: [2]*trie.Trie{trie.NewStorage(), trie.NewStorage()},68Rng: [2]*trie.Trie{trie.NewStorage(), trie.NewStorage()},69Reg: [2]*trie.Trie{trie.NewStorage(), trie.NewStorage()},70Tally: rng.NewTally(),71}72}7374// GenesisVote is a vote present in election_trie[0] at genesis.75type GenesisVote struct {76Voter Address77Commit Hash // SHA256(validator ‖ nonce)78}7980// GenesisToken is a token present at genesis, keyed by an arbitrary ID.81type GenesisToken struct {82ID Hash83Entry wire.TokenEntry84}8586var ErrGenesis = errors.New("state: invalid genesis")8788// Genesis builds the state the chain starts from at Unix time genesisTime:89// a registry to be paired in period 0, at least one vote for period 0's90// blocks, and the tokens — at least one election token — from which period91// 1's votes and registry are made. The coin trie is empty; the first UBI is92// paid by the first reveals, in period 1.93func Genesis(genesisTime uint64, registry []Address, votes []GenesisVote, tokens []GenesisToken) (*State, error) {94s := Empty(genesisTime)95var err error96for _, a := range registry {97if s.Reg[0], err = s.Reg[0].Insert(a, (&wire.RegEntry{}).Bytes()); err != nil {98return nil, ErrGenesis99}100}101for _, v := range votes {102if s.Election[0], err = s.Election[0].Insert(v.Voter, (&wire.ElectionEntry{Commit: v.Commit}).Bytes()); err != nil {103return nil, ErrGenesis104}105}106for _, t := range tokens {107if s.Token, err = s.Token.Insert(t.ID, t.Entry.Bytes()); err != nil {108return nil, ErrGenesis109}110}111if s.Election[0].Count() == 0 {112return nil, ErrGenesis113}114return s, nil115}116117// Roots returns the trie roots of the state.118func (s *State) Roots() Roots {119return Roots{120Coin: s.Coin.Root(),121Token: s.Token.Root(),122Election: [2]trie.Hash{s.Election[0].Root(), s.Election[1].Root()},123Rng: [2]trie.Hash{s.Rng[0].Root(), s.Rng[1].Root()},124Reg: [2]trie.Hash{s.Reg[0].Root(), s.Reg[1].Root()},125}126}127128// rotate crosses one period boundary.129func (s *State) rotate() *State {130n := *s131n.Period = s.Period + 1132n.Seed = s.Tally.NextSeed()133n.Election = [2]*trie.Trie{s.Election[1], trie.NewStorage()}134n.Rng = [2]*trie.Trie{s.Rng[1], trie.NewStorage()}135n.Reg = [2]*trie.Trie{s.Reg[1], trie.NewStorage()}136n.Token = trie.NewPlain()137n.Tally = rng.NewTally()138return &n139}140141// AtSlot returns the state as it stands at slot: rotated through every142// period boundary between the tip and slot. The tip itself is unchanged.143func (s *State) AtSlot(slot uint64) *State {144cur := s145for cur.Period < period.Of(slot) {146cur = cur.rotate()147}148return cur149}150151// TimeOf returns the Unix time at which slot begins, from the tip's own152// clock: no genesis constant is needed, every header carries the time.153func (s *State) TimeOf(slot uint64) uint64 {154return s.Time + (slot-s.Slot)*params.SlotSeconds155}156157// SlotAt returns the slot containing Unix time t, or the tip's slot for158// earlier times.159func (s *State) SlotAt(t uint64) uint64 {160if t < s.Time {161return s.Slot162}163return s.Slot + (t-s.Time)/params.SlotSeconds164}165166// Partner returns the address paired with addr in this period's registry,167// or false if addr is not registered or is the odd one out.168func (s *State) Partner(addr Address) (Address, bool) {169idx, ok := s.Reg[0].Rank(addr)170if !ok {171return Address{}, false172}173p, ok := feistel.Partner(idx, s.Reg[0].Count(), s.Seed)174if !ok {175return Address{}, false176}177key, _, _ := s.Reg[0].Select(p)178return Address(key), true179}180181// Validator returns the voter whose election entry owns slot, and the182// commit that hides the validator; false if there are no votes, in which183// case no block can be produced.184func (s *State) Validator(slot uint64) (voter Address, commit Hash, ok bool) {185n := s.Election[0].Count()186if n == 0 {187return Address{}, Hash{}, false188}189key, val, _ := s.Election[0].Select(rng.ValidatorIndex(s.Seed, slot, n))190e, err := wire.DecodeElectionEntry(val)191if err != nil {192return Address{}, Hash{}, false193}194return Address(key), e.Commit, true195}196197// CoinEntry returns the decoded coin entry at id.198func (s *State) CoinEntry(id Hash) (*wire.CoinEntry, bool) {199v, ok := s.Coin.Get(id)200if !ok {201return nil, false202}203e, err := wire.DecodeCoinEntry(v)204return e, err == nil205}206207// TokenEntry returns the decoded token entry at id.208func (s *State) TokenEntry(id Hash) (*wire.TokenEntry, bool) {209v, ok := s.Token.Get(id)210if !ok {211return nil, false212}213e, err := wire.DecodeTokenEntry(v)214return e, err == nil215}216217// RegEntry returns the decoded registration at addr in reg_trie[which].218func (s *State) RegEntry(which int, addr Address) (*wire.RegEntry, bool) {219v, ok := s.Reg[which].Get(addr)220if !ok {221return nil, false222}223e, err := wire.DecodeRegEntry(v)224return e, err == nil225}226227// RngEntry returns the decoded RNG entry at addr in rng_trie[which].228func (s *State) RngEntry(which int, addr Address) (*wire.RngEntry, bool) {229v, ok := s.Rng[which].Get(addr)230if !ok {231return nil, false232}233e, err := wire.DecodeRngEntry(v)234return e, err == nil235}