Implement register and update headers as handler

This commit is contained in:
Ethan Frey 2017-07-14 16:29:29 +02:00
parent 8747bd5a8b
commit 9c1e695d46
5 changed files with 172 additions and 16 deletions

View File

@ -1 +1,40 @@
package ibc
import (
"fmt"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/basecoin/errors"
)
// nolint
var (
errChainNotRegistered = fmt.Errorf("Chain not registered")
errChainAlreadyExists = fmt.Errorf("Chain already exists")
// errNotMember = fmt.Errorf("Not a member")
// errInsufficientSigs = fmt.Errorf("Not enough signatures")
// errNoMembers = fmt.Errorf("No members specified")
// errTooManyMembers = fmt.Errorf("Too many members specified")
// errNotEnoughMembers = fmt.Errorf("Not enough members specified")
IBCCodeChainNotRegistered = abci.CodeType(1001)
IBCCodeChainAlreadyExists = abci.CodeType(1002)
IBCCodePacketAlreadyExists = abci.CodeType(1003)
IBCCodeUnknownHeight = abci.CodeType(1004)
IBCCodeInvalidCommit = abci.CodeType(1005)
IBCCodeInvalidProof = abci.CodeType(1006)
)
func ErrNotRegistered(chainID string) error {
return errors.WithMessage(chainID, errChainNotRegistered, IBCCodeChainNotRegistered)
}
func IsNotRegistetedErr(err error) bool {
return errors.IsSameError(errChainNotRegistered, err)
}
func ErrAlreadyRegistered(chainID string) error {
return errors.WithMessage(chainID, errChainAlreadyExists, IBCCodeChainAlreadyExists)
}
func IsAlreadyRegistetedErr(err error) bool {
return errors.IsSameError(errChainAlreadyExists, err)
}

View File

@ -2,6 +2,8 @@ package ibc
import (
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/errors"
"github.com/tendermint/basecoin/stack"
"github.com/tendermint/basecoin/state"
)
@ -30,11 +32,73 @@ func (Handler) Name() string {
// CheckTx verifies the packet is formated correctly, and has the proper sequence
// for a registered chain
func (h Handler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
return res, nil
err = tx.ValidateBasic()
if err != nil {
return res, err
}
switch t := tx.Unwrap().(type) {
case RegisterChainTx:
return h.initSeed(ctx, store, t)
case UpdateChainTx:
return h.updateSeed(ctx, store, t)
}
return res, errors.ErrUnknownTxType(tx.Unwrap())
}
// DeliverTx verifies all signatures on the tx and updated the chain state
// apropriately
func (h Handler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
return res, nil
err = tx.ValidateBasic()
if err != nil {
return res, err
}
switch t := tx.Unwrap().(type) {
case RegisterChainTx:
return h.initSeed(ctx, store, t)
case UpdateChainTx:
return h.updateSeed(ctx, store, t)
}
return res, errors.ErrUnknownTxType(tx.Unwrap())
}
// initSeed imports the first seed for this chain and accepts it as the root of trust
func (h Handler) initSeed(ctx basecoin.Context, store state.KVStore,
t RegisterChainTx) (res basecoin.Result, err error) {
chainID := t.ChainID()
s := NewChainSet(store)
err = s.Register(chainID, ctx.BlockHeight(), t.Seed.Height())
if err != nil {
return res, err
}
space := stack.PrefixedStore(chainID, store)
provider := newDBProvider(space)
err = provider.StoreSeed(t.Seed)
return res, err
}
// updateSeed checks the seed against the existing chain data and rejects it if it
// doesn't fit (or no chain data)
func (h Handler) updateSeed(ctx basecoin.Context, store state.KVStore,
t UpdateChainTx) (res basecoin.Result, err error) {
chainID := t.ChainID()
if !NewChainSet(store).Exists([]byte(chainID)) {
return res, ErrNotRegistered(chainID)
}
// load the certifier for this chain
seed := t.Seed
space := stack.PrefixedStore(chainID, store)
cert, err := newCertifier(space, chainID, seed.Height())
if err != nil {
return res, err
}
// this will import the seed if it is valid in the current context
err = cert.Update(seed.Checkpoint, seed.Validators)
return res, err
}

View File

@ -14,14 +14,22 @@ const (
prefixPacket = "p"
)
// newCertifier loads up the current state of this chain to make a proper
func newCertifier(chainID string, store state.KVStore) (*certifiers.InquiringCertifier, error) {
// newCertifier loads up the current state of this chain to make a proper certifier
// it will load the most recent height before block h if h is positive
// if h < 0, it will load the latest height
func newCertifier(store state.KVStore, chainID string, h int) (*certifiers.InquiringCertifier, error) {
// each chain has their own prefixed subspace
space := stack.PrefixedStore(chainID, store)
p := newDBProvider(space)
p := newDBProvider(store)
// this gets the most recent verified seed
seed, err := certifiers.LatestSeed(p)
var seed certifiers.Seed
var err error
if h > 0 {
// this gets the most recent verified seed below the specified height
seed, err = p.GetByHeight(h)
} else {
// 0 or negative means start at latest seed
seed, err = certifiers.LatestSeed(p)
}
if err != nil {
return nil, err
}

View File

@ -1 +1,49 @@
package ibc
import (
"github.com/tendermint/basecoin/stack"
"github.com/tendermint/basecoin/state"
wire "github.com/tendermint/go-wire"
)
const (
// this is the prefix for the list of chains
// we otherwise use the chainid as prefix, so this must not be an
// alpha-numeric byte
prefixChains = "**"
)
// ChainInfo is the global info we store for each registered chain,
// besides the headers, proofs, and packets
type ChainInfo struct {
RegisteredAt uint64 `json:"registered_at"`
RemoteBlock int `json:"remote_block"`
}
// ChainSet is the set of all registered chains
type ChainSet struct {
*state.Set
}
// NewChainSet loads or initialized the ChainSet
func NewChainSet(store state.KVStore) ChainSet {
space := stack.PrefixedStore(prefixChains, store)
return ChainSet{
Set: state.NewSet(space),
}
}
// Register adds the named chain with some info
// returns error if already present
func (c ChainSet) Register(chainID string, ourHeight uint64, theirHeight int) error {
if c.Exists([]byte(chainID)) {
return ErrAlreadyRegistered(chainID)
}
info := ChainInfo{
RegisteredAt: ourHeight,
RemoteBlock: theirHeight,
}
data := wire.BinaryBytes(info)
c.Set.Set([]byte(chainID), data)
return nil
}

View File

@ -1,7 +1,6 @@
package ibc
import (
abci "github.com/tendermint/abci/types"
"github.com/tendermint/light-client/certifiers"
merkle "github.com/tendermint/merkleeyes/iavl"
@ -21,13 +20,6 @@ const (
TypeUpdateChain = NameIBC + "/update"
TypePacketCreate = NameIBC + "/create"
TypePacketPost = NameIBC + "/post"
IBCCodeEncodingError = abci.CodeType(1001)
IBCCodeChainAlreadyExists = abci.CodeType(1002)
IBCCodePacketAlreadyExists = abci.CodeType(1003)
IBCCodeUnknownHeight = abci.CodeType(1004)
IBCCodeInvalidCommit = abci.CodeType(1005)
IBCCodeInvalidProof = abci.CodeType(1006)
)
func init() {
@ -73,6 +65,11 @@ func (u UpdateChainTx) ValidateBasic() error {
return u.Seed.ValidateBasic(u.ChainID())
}
// Wrap - used to satisfy TxInner
func (u UpdateChainTx) Wrap() basecoin.Tx {
return basecoin.Tx{u}
}
// PacketCreateTx is meant to be called by IPC, another module...
//
// this is the tx that will be sent to another app and the permissions it