package state import ( "encoding/json" "fmt" "io" "io/ioutil" "github.com/tendermint/go-ed25519" . "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/blocks" . "github.com/tendermint/tendermint/common" ) const ( AccountStatusNominal = byte(0x00) AccountStatusBonded = byte(0x01) AccountStatusUnbonding = byte(0x02) AccountStatusDupedOut = byte(0x03) ) type Account struct { Id uint64 // Numeric id of account, incrementing. PubKey []byte } func ReadAccount(r io.Reader, n *int64, err *error) Account { return Account{ Id: ReadUInt64(r, n, err), PubKey: ReadByteSlice(r, n, err), } } func (account Account) WriteTo(w io.Writer) (n int64, err error) { WriteUInt64(w, account.Id, &n, &err) WriteByteSlice(w, account.PubKey, &n, &err) return } func (account Account) VerifyBytes(msg []byte, sig Signature) bool { if sig.SignerId != account.Id { panic("account.id doesn't match sig.signerid") } if len(sig.Bytes) == 0 { panic("signature is empty") } v1 := &ed25519.Verify{ Message: msg, PubKey: account.PubKey, Signature: sig.Bytes, } ok := ed25519.VerifyBatch([]*ed25519.Verify{v1}) return ok } func (account Account) Verify(o Signable) bool { sig := o.GetSignature() o.SetSignature(Signature{}) // clear msg := BinaryBytes(o) o.SetSignature(sig) // restore return account.VerifyBytes(msg, sig) } func (account Account) String() string { return fmt.Sprintf("Account{%v:%X}", account.Id, account.PubKey[:6]) } //----------------------------------------------------------------------------- type AccountDetail struct { Account Sequence uint Balance uint64 Status byte } func ReadAccountDetail(r io.Reader, n *int64, err *error) *AccountDetail { return &AccountDetail{ Account: ReadAccount(r, n, err), Sequence: ReadUVarInt(r, n, err), Balance: ReadUInt64(r, n, err), Status: ReadByte(r, n, err), } } func (accDet *AccountDetail) WriteTo(w io.Writer) (n int64, err error) { WriteBinary(w, accDet.Account, &n, &err) WriteUVarInt(w, accDet.Sequence, &n, &err) WriteUInt64(w, accDet.Balance, &n, &err) WriteByte(w, accDet.Status, &n, &err) return } func (accDet *AccountDetail) String() string { return fmt.Sprintf("AccountDetail{%v:%X Sequence:%v Balance:%v Status:%X}", accDet.Id, accDet.PubKey, accDet.Sequence, accDet.Balance, accDet.Status) } func (accDet *AccountDetail) Copy() *AccountDetail { accDetCopy := *accDet return &accDetCopy } //------------------------------------- var AccountDetailCodec = accountDetailCodec{} type accountDetailCodec struct{} func (abc accountDetailCodec) Encode(accDet interface{}, w io.Writer, n *int64, err *error) { WriteBinary(w, accDet.(*AccountDetail), n, err) } func (abc accountDetailCodec) Decode(r io.Reader, n *int64, err *error) interface{} { return ReadAccountDetail(r, n, err) } func (abc accountDetailCodec) Compare(o1 interface{}, o2 interface{}) int { panic("AccountDetailCodec.Compare not implemented") } //----------------------------------------------------------------------------- type PrivAccount struct { Account PrivKey []byte } // Generates a new account with private key. // The Account.Id is empty since it isn't in the blockchain. func GenPrivAccount() *PrivAccount { privKey := CRandBytes(32) pubKey := ed25519.MakePubKey(privKey) return &PrivAccount{ Account: Account{ Id: uint64(0), PubKey: pubKey, }, PrivKey: privKey, } } // The Account.Id is empty since it isn't in the blockchain. func PrivAccountFromJSON(jsonBlob []byte) (privAccount *PrivAccount) { err := json.Unmarshal(jsonBlob, &privAccount) if err != nil { Panicf("Couldn't read PrivAccount: %v", err) } return } // The Account.Id is empty since it isn't in the blockchain. func PrivAccountFromFile(file string) *PrivAccount { jsonBlob, err := ioutil.ReadFile(file) if err != nil { Panicf("Couldn't read PrivAccount from file: %v", err) } return PrivAccountFromJSON(jsonBlob) } func (pa *PrivAccount) SignBytes(msg []byte) Signature { signature := ed25519.SignMessage(msg, pa.PrivKey, pa.PubKey) sig := Signature{ SignerId: pa.Id, Bytes: signature, } return sig } func (pa *PrivAccount) Sign(o Signable) { if !o.GetSignature().IsZero() { panic("Cannot sign: already signed") } msg := BinaryBytes(o) sig := pa.SignBytes(msg) o.SetSignature(sig) }