implementing ExecTx...

This commit is contained in:
Jae Kwon 2014-10-07 23:11:04 -07:00
parent 0c206aa748
commit 18e2d4bf48
14 changed files with 265 additions and 247 deletions

View File

@ -6,7 +6,7 @@ import (
) )
type Signable interface { type Signable interface {
GenDocument() []byte Binary
GetSignature() Signature GetSignature() Signature
SetSignature(Signature) SetSignature(Signature)
} }

View File

@ -19,59 +19,56 @@ Validation Txs:
*/ */
type Tx interface { type Tx interface {
Type() byte Signable
//IsValidation() bool GetSequence() uint64
Binary
} }
const ( const (
// Account transactions // Account transactions
txTypeSend = byte(0x01) TxTypeSend = byte(0x01)
txTypeName = byte(0x02) TxTypeName = byte(0x02)
// Validation transactions // Validation transactions
txTypeBond = byte(0x11) TxTypeBond = byte(0x11)
txTypeUnbond = byte(0x12) TxTypeUnbond = byte(0x12)
txTypeTimeout = byte(0x13) TxTypeTimeout = byte(0x13)
txTypeDupeout = byte(0x14) TxTypeDupeout = byte(0x14)
) )
func ReadTx(r io.Reader, n *int64, err *error) Tx { func ReadTx(r io.Reader, n *int64, err *error) Tx {
switch t := ReadByte(r, n, err); t { switch t := ReadByte(r, n, err); t {
case txTypeSend: case TxTypeSend:
return &SendTx{ return &SendTx{
BaseTx: ReadBaseTx(r, n, err), BaseTx: ReadBaseTx(r, n, err),
Fee: ReadUInt64(r, n, err), Fee: ReadUInt64(r, n, err),
To: ReadUInt64(r, n, err), To: ReadUInt64(r, n, err),
Amount: ReadUInt64(r, n, err), Amount: ReadUInt64(r, n, err),
} }
case txTypeName: case TxTypeName:
return &NameTx{ return &NameTx{
BaseTx: ReadBaseTx(r, n, err), BaseTx: ReadBaseTx(r, n, err),
Fee: ReadUInt64(r, n, err), Fee: ReadUInt64(r, n, err),
Name: ReadString(r, n, err), Name: ReadString(r, n, err),
PubKey: ReadByteSlice(r, n, err), PubKey: ReadByteSlice(r, n, err),
} }
case txTypeBond: case TxTypeBond:
return &BondTx{ return &BondTx{
BaseTx: ReadBaseTx(r, n, err), BaseTx: ReadBaseTx(r, n, err),
Fee: ReadUInt64(r, n, err), Fee: ReadUInt64(r, n, err),
UnbondTo: ReadUInt64(r, n, err), UnbondTo: ReadUInt64(r, n, err),
Amount: ReadUInt64(r, n, err),
} }
case txTypeUnbond: case TxTypeUnbond:
return &UnbondTx{ return &UnbondTx{
BaseTx: ReadBaseTx(r, n, err), BaseTx: ReadBaseTx(r, n, err),
Fee: ReadUInt64(r, n, err), Fee: ReadUInt64(r, n, err),
Amount: ReadUInt64(r, n, err),
} }
case txTypeTimeout: case TxTypeTimeout:
return &TimeoutTx{ return &TimeoutTx{
BaseTx: ReadBaseTx(r, n, err), BaseTx: ReadBaseTx(r, n, err),
AccountId: ReadUInt64(r, n, err), AccountId: ReadUInt64(r, n, err),
Penalty: ReadUInt64(r, n, err), Penalty: ReadUInt64(r, n, err),
} }
case txTypeDupeout: case TxTypeDupeout:
return &DupeoutTx{ return &DupeoutTx{
BaseTx: ReadBaseTx(r, n, err), BaseTx: ReadBaseTx(r, n, err),
VoteA: *ReadVote(r, n, err), VoteA: *ReadVote(r, n, err),
@ -103,6 +100,10 @@ func (tx BaseTx) WriteTo(w io.Writer) (n int64, err error) {
return return
} }
func (tx *BaseTx) GetSequence() uint64 {
return tx.Sequence
}
func (tx *BaseTx) GetSignature() Signature { func (tx *BaseTx) GetSignature() Signature {
return tx.Signature return tx.Signature
} }
@ -120,12 +121,8 @@ type SendTx struct {
Amount uint64 Amount uint64
} }
func (tx *SendTx) Type() byte {
return txTypeSend
}
func (tx *SendTx) WriteTo(w io.Writer) (n int64, err error) { func (tx *SendTx) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, tx.Type(), &n, &err) WriteByte(w, TxTypeSend, &n, &err)
WriteBinary(w, tx.BaseTx, &n, &err) WriteBinary(w, tx.BaseTx, &n, &err)
WriteUInt64(w, tx.Fee, &n, &err) WriteUInt64(w, tx.Fee, &n, &err)
WriteUInt64(w, tx.To, &n, &err) WriteUInt64(w, tx.To, &n, &err)
@ -142,12 +139,8 @@ type NameTx struct {
PubKey []byte PubKey []byte
} }
func (tx *NameTx) Type() byte {
return txTypeName
}
func (tx *NameTx) WriteTo(w io.Writer) (n int64, err error) { func (tx *NameTx) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, tx.Type(), &n, &err) WriteByte(w, TxTypeName, &n, &err)
WriteBinary(w, tx.BaseTx, &n, &err) WriteBinary(w, tx.BaseTx, &n, &err)
WriteUInt64(w, tx.Fee, &n, &err) WriteUInt64(w, tx.Fee, &n, &err)
WriteString(w, tx.Name, &n, &err) WriteString(w, tx.Name, &n, &err)
@ -161,19 +154,13 @@ type BondTx struct {
BaseTx BaseTx
Fee uint64 Fee uint64
UnbondTo uint64 UnbondTo uint64
Amount uint64
}
func (tx *BondTx) Type() byte {
return txTypeBond
} }
func (tx *BondTx) WriteTo(w io.Writer) (n int64, err error) { func (tx *BondTx) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, tx.Type(), &n, &err) WriteByte(w, TxTypeBond, &n, &err)
WriteBinary(w, tx.BaseTx, &n, &err) WriteBinary(w, tx.BaseTx, &n, &err)
WriteUInt64(w, tx.Fee, &n, &err) WriteUInt64(w, tx.Fee, &n, &err)
WriteUInt64(w, tx.UnbondTo, &n, &err) WriteUInt64(w, tx.UnbondTo, &n, &err)
WriteUInt64(w, tx.Amount, &n, &err)
return return
} }
@ -181,19 +168,13 @@ func (tx *BondTx) WriteTo(w io.Writer) (n int64, err error) {
type UnbondTx struct { type UnbondTx struct {
BaseTx BaseTx
Fee uint64 Fee uint64
Amount uint64
}
func (tx *UnbondTx) Type() byte {
return txTypeUnbond
} }
func (tx *UnbondTx) WriteTo(w io.Writer) (n int64, err error) { func (tx *UnbondTx) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, tx.Type(), &n, &err) WriteByte(w, TxTypeUnbond, &n, &err)
WriteBinary(w, tx.BaseTx, &n, &err) WriteBinary(w, tx.BaseTx, &n, &err)
WriteUInt64(w, tx.Fee, &n, &err) WriteUInt64(w, tx.Fee, &n, &err)
WriteUInt64(w, tx.Amount, &n, &err)
return return
} }
@ -205,12 +186,8 @@ type TimeoutTx struct {
Penalty uint64 Penalty uint64
} }
func (tx *TimeoutTx) Type() byte {
return txTypeTimeout
}
func (tx *TimeoutTx) WriteTo(w io.Writer) (n int64, err error) { func (tx *TimeoutTx) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, tx.Type(), &n, &err) WriteByte(w, TxTypeTimeout, &n, &err)
WriteBinary(w, tx.BaseTx, &n, &err) WriteBinary(w, tx.BaseTx, &n, &err)
WriteUInt64(w, tx.AccountId, &n, &err) WriteUInt64(w, tx.AccountId, &n, &err)
WriteUInt64(w, tx.Penalty, &n, &err) WriteUInt64(w, tx.Penalty, &n, &err)
@ -225,22 +202,10 @@ type DupeoutTx struct {
VoteB Vote VoteB Vote
} }
func (tx *DupeoutTx) Type() byte {
return txTypeDupeout
}
func (tx *DupeoutTx) WriteTo(w io.Writer) (n int64, err error) { func (tx *DupeoutTx) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, tx.Type(), &n, &err) WriteByte(w, TxTypeDupeout, &n, &err)
WriteBinary(w, tx.BaseTx, &n, &err) WriteBinary(w, tx.BaseTx, &n, &err)
WriteBinary(w, &tx.VoteA, &n, &err) WriteBinary(w, &tx.VoteA, &n, &err)
WriteBinary(w, &tx.VoteB, &n, &err) WriteBinary(w, &tx.VoteB, &n, &err)
return return
} }
func (tx *DupeoutTx) GenDocument() []byte {
oldSig := tx.Signature
tx.Signature = Signature{}
doc := BinaryBytes(tx)
tx.Signature = oldSig
return doc
}

View File

@ -49,14 +49,6 @@ func (v *Vote) WriteTo(w io.Writer) (n int64, err error) {
return return
} }
func (v *Vote) GenDocument() []byte {
oldSig := v.Signature
v.Signature = Signature{}
doc := BinaryBytes(v)
v.Signature = oldSig
return doc
}
func (v *Vote) GetSignature() Signature { func (v *Vote) GetSignature() Signature {
return v.Signature return v.Signature
} }

View File

@ -402,7 +402,7 @@ OUTER_LOOP:
// Signs a vote document and broadcasts it. // Signs a vote document and broadcasts it.
func (conR *ConsensusReactor) signAndBroadcastVote(rs *RoundState, vote *Vote) { func (conR *ConsensusReactor) signAndBroadcastVote(rs *RoundState, vote *Vote) {
if rs.PrivValidator != nil { if rs.PrivValidator != nil {
rs.PrivValidator.SignVote(vote) rs.PrivValidator.Sign(vote)
conR.conS.AddVote(vote) conR.conS.AddVote(vote)
msg := p2p.TypedMessage{msgTypeVote, vote} msg := p2p.TypedMessage{msgTypeVote, vote}
conR.sw.Broadcast(VoteCh, msg) conR.sw.Broadcast(VoteCh, msg)

View File

@ -45,8 +45,8 @@ func (pol *POL) WriteTo(w io.Writer) (n int64, err error) {
func (pol *POL) Verify(vset *ValidatorSet) error { func (pol *POL) Verify(vset *ValidatorSet) error {
talliedVotingPower := uint64(0) talliedVotingPower := uint64(0)
voteDoc := (&Vote{Height: pol.Height, Round: pol.Round, voteDoc := BinaryBytes(&Vote{Height: pol.Height, Round: pol.Round,
Type: VoteTypeBare, BlockHash: pol.BlockHash}).GenDocument() Type: VoteTypeBare, BlockHash: pol.BlockHash})
seenValidators := map[uint64]struct{}{} seenValidators := map[uint64]struct{}{}
for _, sig := range pol.Votes { for _, sig := range pol.Votes {
@ -59,7 +59,7 @@ func (pol *POL) Verify(vset *ValidatorSet) error {
if validator == nil { if validator == nil {
return Errorf("Invalid validator for vote %v for POL %v", sig, pol) return Errorf("Invalid validator for vote %v for POL %v", sig, pol)
} }
if !validator.Verify(voteDoc, sig) { if !validator.VerifyBytes(voteDoc, sig) {
return Errorf("Invalid signature for vote %v for POL %v", sig, pol) return Errorf("Invalid signature for vote %v for POL %v", sig, pol)
} }
@ -80,9 +80,9 @@ func (pol *POL) Verify(vset *ValidatorSet) error {
return Errorf("Invalid validator for commit %v for POL %v", sig, pol) return Errorf("Invalid validator for commit %v for POL %v", sig, pol)
} }
commitDoc := (&Vote{Height: pol.Height, Round: round, commitDoc := BinaryBytes(&Vote{Height: pol.Height, Round: round,
Type: VoteTypeCommit, BlockHash: pol.BlockHash}).GenDocument() // TODO cache Type: VoteTypeCommit, BlockHash: pol.BlockHash}) // TODO cache
if !validator.Verify(commitDoc, sig) { if !validator.VerifyBytes(commitDoc, sig) {
return Errorf("Invalid signature for commit %v for POL %v", sig, pol) return Errorf("Invalid signature for commit %v for POL %v", sig, pol)
} }

View File

@ -13,14 +13,14 @@ type PrivValidator struct {
db *db_.LevelDB db *db_.LevelDB
} }
// Double signing results in an error. // Double signing results in a panic.
func (pv *PrivValidator) SignProposal(proposal *Proposal) { func (pv *PrivValidator) Sign(o Signable) {
//TODO: prevent double signing. switch o.(type) {
pv.SignSignable(proposal) case *Proposal:
} //TODO: prevent double signing.
pv.PrivAccount.Sign(o.(*Proposal))
// Double signing results in an error. case *Vote:
func (pv *PrivValidator) SignVote(vote *Vote) { //TODO: prevent double signing.
//TODO: prevent double signing. pv.PrivAccount.Sign(o.(*Vote))
pv.SignSignable(vote) }
} }

View File

@ -58,14 +58,6 @@ func (p *Proposal) WriteTo(w io.Writer) (n int64, err error) {
return return
} }
func (p *Proposal) GenDocument() []byte {
oldSig := p.Signature
p.Signature = Signature{}
doc := BinaryBytes(p)
p.Signature = oldSig
return doc
}
func (p *Proposal) GetSignature() Signature { func (p *Proposal) GetSignature() Signature {
return p.Signature return p.Signature
} }

View File

@ -178,7 +178,7 @@ func (cs *ConsensusState) SetProposal(proposal *Proposal) error {
} }
// Verify signature // Verify signature
if !cs.Proposer.Verify(proposal.GenDocument(), proposal.Signature) { if !cs.Proposer.Verify(proposal) {
return ErrInvalidProposalSignature return ErrInvalidProposalSignature
} }
@ -220,7 +220,7 @@ func (cs *ConsensusState) MakeProposal() {
// Make proposal // Make proposal
proposal := NewProposal(cs.Height, cs.Round, blockPartSet.Total(), blockPartSet.RootHash(), proposal := NewProposal(cs.Height, cs.Round, blockPartSet.Total(), blockPartSet.RootHash(),
polPartSet.Total(), polPartSet.RootHash()) polPartSet.Total(), polPartSet.RootHash())
cs.PrivValidator.SignProposal(proposal) cs.PrivValidator.Sign(proposal)
// Set fields // Set fields
cs.Proposal = proposal cs.Proposal = proposal

View File

@ -69,7 +69,7 @@ func (vs *VoteSet) AddVote(vote *Vote) (bool, error) {
} }
// Check signature. // Check signature.
if !val.Verify(vote.GenDocument(), vote.Signature) { if !val.Verify(vote) {
// Bad signature. // Bad signature.
return false, ErrVoteInvalidSignature return false, ErrVoteInvalidSignature
} }

View File

@ -3,8 +3,6 @@ package merkle
import ( import (
"bytes" "bytes"
"container/list" "container/list"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
) )
const defaultCacheCapacity = 1000 // TODO make configurable. const defaultCacheCapacity = 1000 // TODO make configurable.
@ -117,83 +115,6 @@ func (t *IAVLTree) Copy() Tree {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// TODO: make TypedTree work with the underlying tree to cache the decoded value.
type TypedTree struct {
Tree Tree
keyCodec Codec
valueCodec Codec
}
func NewTypedTree(tree Tree, keyCodec, valueCodec Codec) *TypedTree {
return &TypedTree{
Tree: tree,
keyCodec: keyCodec,
valueCodec: valueCodec,
}
}
func (t *TypedTree) Has(key interface{}) bool {
bytes, err := t.keyCodec.Write(key)
if err != nil {
Panicf("Error from keyCodec: %v", err)
}
return t.Tree.Has(bytes)
}
func (t *TypedTree) Get(key interface{}) interface{} {
keyBytes, err := t.keyCodec.Write(key)
if err != nil {
Panicf("Error from keyCodec: %v", err)
}
valueBytes := t.Tree.Get(keyBytes)
if valueBytes == nil {
return nil
}
value, err := t.valueCodec.Read(valueBytes)
if err != nil {
Panicf("Error from valueCodec: %v", err)
}
return value
}
func (t *TypedTree) Set(key interface{}, value interface{}) bool {
keyBytes, err := t.keyCodec.Write(key)
if err != nil {
Panicf("Error from keyCodec: %v", err)
}
valueBytes, err := t.valueCodec.Write(value)
if err != nil {
Panicf("Error from valueCodec: %v", err)
}
return t.Tree.Set(keyBytes, valueBytes)
}
func (t *TypedTree) Remove(key interface{}) (interface{}, error) {
keyBytes, err := t.keyCodec.Write(key)
if err != nil {
Panicf("Error from keyCodec: %v", err)
}
valueBytes, err := t.Tree.Remove(keyBytes)
if valueBytes == nil {
return nil, err
}
value, err_ := t.valueCodec.Read(valueBytes)
if err_ != nil {
Panicf("Error from valueCodec: %v", err)
}
return value, err
}
func (t *TypedTree) Copy() *TypedTree {
return &TypedTree{
Tree: t.Tree.Copy(),
keyCodec: t.keyCodec,
valueCodec: t.valueCodec,
}
}
//-----------------------------------------------------------------------------
type nodeElement struct { type nodeElement struct {
node *IAVLNode node *IAVLNode
elem *list.Element elem *list.Element

81
merkle/typed_tree.go Normal file
View File

@ -0,0 +1,81 @@
package merkle
import (
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
)
// TODO: make TypedTree work with the underlying tree to cache the decoded value.
type TypedTree struct {
Tree Tree
keyCodec Codec
valueCodec Codec
}
func NewTypedTree(tree Tree, keyCodec, valueCodec Codec) *TypedTree {
return &TypedTree{
Tree: tree,
keyCodec: keyCodec,
valueCodec: valueCodec,
}
}
func (t *TypedTree) Has(key interface{}) bool {
bytes, err := t.keyCodec.Write(key)
if err != nil {
Panicf("Error from keyCodec: %v", err)
}
return t.Tree.Has(bytes)
}
func (t *TypedTree) Get(key interface{}) interface{} {
keyBytes, err := t.keyCodec.Write(key)
if err != nil {
Panicf("Error from keyCodec: %v", err)
}
valueBytes := t.Tree.Get(keyBytes)
if valueBytes == nil {
return nil
}
value, err := t.valueCodec.Read(valueBytes)
if err != nil {
Panicf("Error from valueCodec: %v", err)
}
return value
}
func (t *TypedTree) Set(key interface{}, value interface{}) bool {
keyBytes, err := t.keyCodec.Write(key)
if err != nil {
Panicf("Error from keyCodec: %v", err)
}
valueBytes, err := t.valueCodec.Write(value)
if err != nil {
Panicf("Error from valueCodec: %v", err)
}
return t.Tree.Set(keyBytes, valueBytes)
}
func (t *TypedTree) Remove(key interface{}) (interface{}, error) {
keyBytes, err := t.keyCodec.Write(key)
if err != nil {
Panicf("Error from keyCodec: %v", err)
}
valueBytes, err := t.Tree.Remove(keyBytes)
if valueBytes == nil {
return nil, err
}
value, err_ := t.valueCodec.Read(valueBytes)
if err_ != nil {
Panicf("Error from valueCodec: %v", err)
}
return value, err
}
func (t *TypedTree) Copy() *TypedTree {
return &TypedTree{
Tree: t.Tree.Copy(),
keyCodec: t.keyCodec,
valueCodec: t.valueCodec,
}
}

View File

@ -9,8 +9,9 @@ import (
) )
const ( const (
AccountBalanceStatusNominal = byte(0x00) AccountDetailStatusNominal = byte(0x00)
AccountBalanceStatusBonded = byte(0x01) AccountDetailStatusBonded = byte(0x01)
AccountDetailStatusUnbonding = byte(0x02)
) )
type Account struct { type Account struct {
@ -31,7 +32,7 @@ func (account Account) WriteTo(w io.Writer) (n int64, err error) {
return return
} }
func (account Account) Verify(msg []byte, sig Signature) bool { func (account Account) VerifyBytes(msg []byte, sig Signature) bool {
if sig.SignerId != account.Id { if sig.SignerId != account.Id {
panic("account.id doesn't match sig.signerid") panic("account.id doesn't match sig.signerid")
} }
@ -44,32 +45,37 @@ func (account Account) Verify(msg []byte, sig Signature) bool {
return ok return ok
} }
func (account Account) VerifySignable(o Signable) bool { func (account Account) Verify(o Signable) bool {
msg := o.GenDocument()
sig := o.GetSignature() sig := o.GetSignature()
return account.Verify(msg, sig) o.SetSignature(Signature{}) // clear
msg := BinaryBytes(o)
o.SetSignature(sig) // restore
return account.VerifyBytes(msg, sig)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
type AccountBalance struct { type AccountDetail struct {
Account Account
Balance uint64 Sequence uint64
Status byte Balance uint64
Status byte
} }
func ReadAccountBalance(r io.Reader, n *int64, err *error) *AccountBalance { func ReadAccountDetail(r io.Reader, n *int64, err *error) *AccountDetail {
return &AccountBalance{ return &AccountDetail{
Account: ReadAccount(r, n, err), Account: ReadAccount(r, n, err),
Balance: ReadUInt64(r, n, err), Sequence: ReadUInt64(r, n, err),
Status: ReadByte(r, n, err), Balance: ReadUInt64(r, n, err),
Status: ReadByte(r, n, err),
} }
} }
func (accBal AccountBalance) WriteTo(w io.Writer) (n int64, err error) { func (accDet AccountDetail) WriteTo(w io.Writer) (n int64, err error) {
WriteBinary(w, accBal.Account, &n, &err) WriteBinary(w, accDet.Account, &n, &err)
WriteUInt64(w, accBal.Balance, &n, &err) WriteUInt64(w, accDet.Sequence, &n, &err)
WriteByte(w, accBal.Status, &n, &err) WriteUInt64(w, accDet.Balance, &n, &err)
WriteByte(w, accDet.Status, &n, &err)
return return
} }
@ -94,7 +100,7 @@ func GenPrivAccount() *PrivAccount {
} }
} }
func (pa *PrivAccount) Sign(msg []byte) Signature { func (pa *PrivAccount) SignBytes(msg []byte) Signature {
signature := crypto.SignMessage(msg, pa.PrivKey, pa.PubKey) signature := crypto.SignMessage(msg, pa.PrivKey, pa.PubKey)
sig := Signature{ sig := Signature{
SignerId: pa.Id, SignerId: pa.Id,
@ -103,8 +109,9 @@ func (pa *PrivAccount) Sign(msg []byte) Signature {
return sig return sig
} }
func (pa *PrivAccount) SignSignable(o Signable) { func (pa *PrivAccount) Sign(o Signable) {
msg := o.GenDocument() o.SetSignature(Signature{}) // clear
sig := pa.Sign(msg) msg := BinaryBytes(o)
sig := pa.SignBytes(msg)
o.SetSignature(sig) o.SetSignature(sig)
} }

View File

@ -11,7 +11,7 @@ func TestSignAndValidate(t *testing.T) {
account := &privAccount.Account account := &privAccount.Account
msg := CRandBytes(128) msg := CRandBytes(128)
sig := privAccount.Sign(msg) sig := privAccount.SignBytes(msg)
t.Logf("msg: %X, sig: %X", msg, sig) t.Logf("msg: %X, sig: %X", msg, sig)
// Test the signature // Test the signature

View File

@ -12,62 +12,67 @@ import (
) )
var ( var (
ErrStateInvalidAccountId = errors.New("Error State invalid account id")
ErrStateInvalidSignature = errors.New("Error State invalid signature")
ErrStateInvalidSequenceNumber = errors.New("Error State invalid sequence number") ErrStateInvalidSequenceNumber = errors.New("Error State invalid sequence number")
ErrStateInvalidAccountState = errors.New("Error State invalid account state")
ErrStateInvalidValidationStateHash = errors.New("Error State invalid ValidationStateHash") ErrStateInvalidValidationStateHash = errors.New("Error State invalid ValidationStateHash")
ErrStateInvalidAccountStateHash = errors.New("Error State invalid AccountStateHash") ErrStateInvalidAccountStateHash = errors.New("Error State invalid AccountStateHash")
ErrStateInsufficientFunds = errors.New("Error State insufficient funds")
stateKey = []byte("stateKey") stateKey = []byte("stateKey")
minBondAmount = uint64(1) // TODO adjust
) )
type accountBalanceCodec struct{} type accountDetailCodec struct{}
func (abc accountBalanceCodec) Write(accBal interface{}) (accBalBytes []byte, err error) { func (abc accountDetailCodec) Write(accDet interface{}) (accDetBytes []byte, err error) {
w := new(bytes.Buffer) w := new(bytes.Buffer)
_, err = accBal.(*AccountBalance).WriteTo(w) _, err = accDet.(*AccountDetail).WriteTo(w)
return w.Bytes(), err return w.Bytes(), err
} }
func (abc accountBalanceCodec) Read(accBalBytes []byte) (interface{}, error) { func (abc accountDetailCodec) Read(accDetBytes []byte) (interface{}, error) {
n, err, r := new(int64), new(error), bytes.NewBuffer(accBalBytes) n, err, r := new(int64), new(error), bytes.NewBuffer(accDetBytes)
return ReadAccountBalance(r, n, err), *err return ReadAccountDetail(r, n, err), *err
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// NOTE: not goroutine-safe. // NOTE: not goroutine-safe.
type State struct { type State struct {
DB DB DB DB
Height uint32 // Last known block height Height uint32 // Last known block height
BlockHash []byte // Last known block hash BlockHash []byte // Last known block hash
CommitTime time.Time CommitTime time.Time
AccountBalances *merkle.TypedTree AccountDetails *merkle.TypedTree
Validators *ValidatorSet Validators *ValidatorSet
} }
func GenesisState(db DB, genesisTime time.Time, accBals []*AccountBalance) *State { func GenesisState(db DB, genesisTime time.Time, accDets []*AccountDetail) *State {
// TODO: Use "uint64Codec" instead of BasicCodec // TODO: Use "uint64Codec" instead of BasicCodec
accountBalances := merkle.NewTypedTree(merkle.NewIAVLTree(db), BasicCodec, accountBalanceCodec{}) accountDetails := merkle.NewTypedTree(merkle.NewIAVLTree(db), BasicCodec, accountDetailCodec{})
validators := map[uint64]*Validator{} validators := map[uint64]*Validator{}
for _, accBal := range accBals { for _, accDet := range accDets {
accountBalances.Set(accBal.Id, accBal) accountDetails.Set(accDet.Id, accDet)
validators[accBal.Id] = &Validator{ validators[accDet.Id] = &Validator{
Account: accBal.Account, Account: accDet.Account,
BondHeight: 0, BondHeight: 0,
VotingPower: accBal.Balance, VotingPower: accDet.Balance,
Accum: 0, Accum: 0,
} }
} }
validatorSet := NewValidatorSet(validators) validatorSet := NewValidatorSet(validators)
return &State{ return &State{
DB: db, DB: db,
Height: 0, Height: 0,
BlockHash: nil, BlockHash: nil,
CommitTime: genesisTime, CommitTime: genesisTime,
AccountBalances: accountBalances, AccountDetails: accountDetails,
Validators: validatorSet, Validators: validatorSet,
} }
} }
@ -83,8 +88,8 @@ func LoadState(db DB) *State {
s.Height = ReadUInt32(reader, &n, &err) s.Height = ReadUInt32(reader, &n, &err)
s.CommitTime = ReadTime(reader, &n, &err) s.CommitTime = ReadTime(reader, &n, &err)
s.BlockHash = ReadByteSlice(reader, &n, &err) s.BlockHash = ReadByteSlice(reader, &n, &err)
accountBalancesHash := ReadByteSlice(reader, &n, &err) accountDetailsHash := ReadByteSlice(reader, &n, &err)
s.AccountBalances = merkle.NewTypedTree(merkle.LoadIAVLTreeFromHash(db, accountBalancesHash), BasicCodec, accountBalanceCodec{}) s.AccountDetails = merkle.NewTypedTree(merkle.LoadIAVLTreeFromHash(db, accountDetailsHash), BasicCodec, accountDetailCodec{})
var validators = map[uint64]*Validator{} var validators = map[uint64]*Validator{}
for reader.Len() > 0 { for reader.Len() > 0 {
validator := ReadValidator(reader, &n, &err) validator := ReadValidator(reader, &n, &err)
@ -103,14 +108,14 @@ func LoadState(db DB) *State {
// is saved here. // is saved here.
func (s *State) Save(commitTime time.Time) { func (s *State) Save(commitTime time.Time) {
s.CommitTime = commitTime s.CommitTime = commitTime
s.AccountBalances.Tree.Save() s.AccountDetails.Tree.Save()
var buf bytes.Buffer var buf bytes.Buffer
var n int64 var n int64
var err error var err error
WriteUInt32(&buf, s.Height, &n, &err) WriteUInt32(&buf, s.Height, &n, &err)
WriteTime(&buf, commitTime, &n, &err) WriteTime(&buf, commitTime, &n, &err)
WriteByteSlice(&buf, s.BlockHash, &n, &err) WriteByteSlice(&buf, s.BlockHash, &n, &err)
WriteByteSlice(&buf, s.AccountBalances.Tree.Hash(), &n, &err) WriteByteSlice(&buf, s.AccountDetails.Tree.Hash(), &n, &err)
for _, validator := range s.Validators.Map() { for _, validator := range s.Validators.Map() {
WriteBinary(&buf, validator, &n, &err) WriteBinary(&buf, validator, &n, &err)
} }
@ -122,26 +127,76 @@ func (s *State) Save(commitTime time.Time) {
func (s *State) Copy() *State { func (s *State) Copy() *State {
return &State{ return &State{
DB: s.DB, DB: s.DB,
Height: s.Height, Height: s.Height,
CommitTime: s.CommitTime, CommitTime: s.CommitTime,
BlockHash: s.BlockHash, BlockHash: s.BlockHash,
AccountBalances: s.AccountBalances.Copy(), AccountDetails: s.AccountDetails.Copy(),
Validators: s.Validators.Copy(), Validators: s.Validators.Copy(),
} }
} }
// If the tx is invalid, an error will be returned. // If the tx is invalid, an error will be returned.
// Unlike AppendBlock(), state will not be altered. // Unlike AppendBlock(), state will not be altered.
func (s *State) ExecTx(tx Tx) error { func (s *State) ExecTx(tx Tx) error {
/* accDet := s.GetAccountDetail(tx.GetSignature().SignerId)
// Get the signer's incr if accDet == nil {
signerId := tx.Signature().SignerId return ErrStateInvalidAccountId
if mem.state.AccountSequence(signerId) != tx.Sequence() { }
return ErrStateInvalidSequenceNumber // Check signature
if !accDet.Verify(tx) {
return ErrStateInvalidSignature
}
// Check sequence
if tx.GetSequence() <= accDet.Sequence {
return ErrStateInvalidSequenceNumber
}
// Exec tx
switch tx.(type) {
case *SendTx:
stx := tx.(*SendTx)
toAccDet := s.GetAccountDetail(stx.To)
// Accounts must be nominal
if accDet.Status != AccountDetailStatusNominal {
return ErrStateInvalidAccountState
} }
*/ if toAccDet.Status != AccountDetailStatusNominal {
// XXX commit the tx return ErrStateInvalidAccountState
}
// Check account balance
if accDet.Balance < stx.Fee+stx.Amount {
return ErrStateInsufficientFunds
}
// Check existence of destination account
if toAccDet == nil {
return ErrStateInvalidAccountId
}
// Good!
accDet.Balance -= (stx.Fee + stx.Amount)
toAccDet.Balance += (stx.Amount)
s.SetAccountDetail(accDet)
s.SetAccountDetail(toAccDet)
//case *NameTx
case *BondTx:
btx := tx.(*BondTx)
// Account must be nominal
if accDet.Status != AccountDetailStatusNominal {
return ErrStateInvalidAccountState
}
// Check account balance
if accDet.Balance < minBondAmount {
return ErrStateInsufficientFunds
}
// TODO: max number of validators?
// Good!
accDet.Balance -= btx.Fee // remaining balance are bonded coins.
accDet.Status = AccountDetailStatusBonded
s.SetAccountDetail(accDet)
// XXX add validator
case *UnbondTx:
case *TimeoutTx:
case *DupeoutTx:
}
panic("Implement ExecTx()") panic("Implement ExecTx()")
return nil return nil
} }
@ -170,7 +225,7 @@ func (s *State) AppendBlock(b *Block) error {
if !bytes.Equal(s.Validators.Hash(), b.ValidationStateHash) { if !bytes.Equal(s.Validators.Hash(), b.ValidationStateHash) {
return ErrStateInvalidValidationStateHash return ErrStateInvalidValidationStateHash
} }
if !bytes.Equal(s.AccountBalances.Tree.Hash(), b.AccountStateHash) { if !bytes.Equal(s.AccountDetails.Tree.Hash(), b.AccountStateHash) {
return ErrStateInvalidAccountStateHash return ErrStateInvalidAccountStateHash
} }
@ -179,10 +234,15 @@ func (s *State) AppendBlock(b *Block) error {
return nil return nil
} }
func (s *State) AccountBalance(accountId uint64) *AccountBalance { func (s *State) GetAccountDetail(accountId uint64) *AccountDetail {
accBal := s.AccountBalances.Get(accountId) accDet := s.AccountDetails.Get(accountId)
if accBal == nil { if accDet == nil {
return nil return nil
} }
return accBal.(*AccountBalance) return accDet.(*AccountDetail)
}
// Returns false if new, true if updated.
func (s *State) SetAccountDetail(accDet *AccountDetail) (updated bool) {
return s.AccountDetails.Set(accDet.Id, accDet)
} }