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)
|
|
}
|