Merge pull request #314 from cosmos/sdk2-app

New App structure
This commit is contained in:
Ethan Buchman 2018-01-05 11:30:51 -05:00 committed by GitHub
commit 8f14e4be66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 383 additions and 277 deletions

View File

@ -11,10 +11,10 @@ import (
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/types"
)
const mainKeyHeader = "header"
var mainHeaderKey = []byte("header")
// App - The ABCI application
type App struct {
@ -23,17 +23,20 @@ type App struct {
// App name from abci.Info
name string
// DeliverTx (main) state
store MultiStore
// Main (uncached) state
ms types.CommitMultiStore
// CheckTx state
storeCheck CacheMultiStore
// CheckTx state, a cache-wrap of `.ms`.
msCheck types.CacheMultiStore
// DeliverTx state, a cache-wrap of `.ms`.
msDeliver types.CacheMultiStore
// Current block header
header *abci.Header
header abci.Header
// Handler for CheckTx and DeliverTx.
handler sdk.Handler
handler types.Handler
// Cached validator changes from DeliverTx
valUpdates []abci.Validator
@ -48,59 +51,71 @@ func NewApp(name string) *App {
}
}
func (app *App) SetStore(store MultiStore) {
app.store = store
func (app *App) Name() string {
return app.name
}
func (app *App) SetHandler(handler Handler) {
func (app *App) SetCommitMultiStore(ms types.CommitMultiStore) {
app.ms = ms
}
func (app *App) SetHandler(handler types.Handler) {
app.handler = handler
}
func (app *App) LoadLatestVersion() error {
store := app.store
store.LoadLastVersion()
app.ms.LoadLatestVersion()
return app.initFromStore()
}
func (app *App) LoadVersion(version int64) error {
store := app.store
store.LoadVersion(version)
app.ms.LoadVersion(version)
return app.initFromStore()
}
// Initializes the remaining logic from app.store.
// The last CommitID of the multistore.
func (app *App) LastCommitID() types.CommitID {
return app.ms.LastCommitID()
}
// The last commited block height.
func (app *App) LastBlockHeight() int64 {
return app.ms.LastCommitID().Version
}
// Initializes the remaining logic from app.ms.
func (app *App) initFromStore() error {
store := app.store
lastCommitID := store.LastCommitID()
main := store.GetKVStore("main")
header := (*abci.Header)(nil)
storeCheck := store.CacheMultiStore()
lastCommitID := app.ms.LastCommitID()
main := app.ms.GetKVStore("main")
header := abci.Header{}
// Main store should exist.
if store.GetKVStore("main") == nil {
if app.ms.GetKVStore("main") == nil {
return errors.New("App expects MultiStore with 'main' KVStore")
}
// If we've committed before, we expect main://<mainKeyHeader>.
// If we've committed before, we expect main://<mainHeaderKey>.
if !lastCommitID.IsZero() {
headerBytes, ok := main.Get(mainKeyHeader)
if !ok {
errStr := fmt.Sprintf("Version > 0 but missing key %s", mainKeyHeader)
headerBytes := main.Get(mainHeaderKey)
if len(headerBytes) == 0 {
errStr := fmt.Sprintf("Version > 0 but missing key %s", mainHeaderKey)
return errors.New(errStr)
}
err = proto.Unmarshal(headerBytes, header)
err := proto.Unmarshal(headerBytes, &header)
if err != nil {
return errors.Wrap(err, "Failed to parse Header")
}
if header.Height != lastCommitID.Version {
errStr := fmt.Sprintf("Expected main://%s.Height %v but got %v", mainKeyHeader, version, headerHeight)
lastVersion := lastCommitID.Version
if header.Height != lastVersion {
errStr := fmt.Sprintf("Expected main://%s.Height %v but got %v", mainHeaderKey, lastVersion, header.Height)
return errors.New(errStr)
}
}
// Set App state.
app.header = header
app.storeCheck = app.store.CacheMultiStore()
app.msCheck = nil
app.msDeliver = nil
app.valUpdates = nil
return nil
@ -108,157 +123,121 @@ func (app *App) initFromStore() error {
//----------------------------------------
// DeliverTx - ABCI - dispatches to the handler
func (app *App) DeliverTx(txBytes []byte) abci.ResponseDeliverTx {
// Implements ABCI
func (app *App) Info(req abci.RequestInfo) abci.ResponseInfo {
lastCommitID := app.ms.LastCommitID()
return abci.ResponseInfo{
Data: app.name,
LastBlockHeight: lastCommitID.Version,
LastBlockAppHash: lastCommitID.Hash,
}
}
// Implements ABCI
func (app *App) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOption) {
// TODO: Implement
return
}
// Implements ABCI
func (app *App) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) {
// TODO: Use req.Validators
return
}
// Implements ABCI
func (app *App) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
// TODO: See app/query.go
return
}
// Implements ABCI
func (app *App) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) {
app.header = req.Header
app.msDeliver = app.ms.CacheMultiStore()
app.msCheck = app.ms.CacheMultiStore()
return
}
// Implements ABCI
func (app *App) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) {
// Initialize arguments to Handler.
var isCheckTx = true
var ctx = types.NewContext(app.header, isCheckTx, txBytes)
var tx types.Tx = nil // nil until a decorator parses one.
// Run the handler.
var result = app.handler(ctx, app.ms, tx)
// Tell the blockchain engine (i.e. Tendermint).
return abci.ResponseCheckTx{
Code: result.Code,
Data: result.Data,
Log: result.Log,
GasWanted: result.GasWanted,
Fee: cmn.KI64Pair{
[]byte(result.FeeDenom),
result.FeeAmount,
},
Tags: result.Tags,
}
}
// Implements ABCI
func (app *App) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
// Initialize arguments to Handler.
var isCheckTx = false
var ctx = sdk.NewContext(app.header, isCheckTx, txBytes)
var store = app.store
var tx Tx = nil // nil until a decorator parses one.
var ctx = types.NewContext(app.header, isCheckTx, txBytes)
var tx types.Tx = nil // nil until a decorator parses one.
// Run the handler.
var result = app.handler(ctx, app.store, tx)
var result = app.handler(ctx, app.ms, tx)
// After-handler hooks.
if result.Code == abci.CodeType_OK {
app.valUpdates = append(app.valUpdates, result.ValUpdate)
if result.Code == abci.CodeTypeOK {
app.valUpdates = append(app.valUpdates, result.ValidatorUpdates...)
} else {
// Even though the Code is not OK, there will be some side effects,
// like those caused by fee deductions or sequence incrementations.
}
// Tell the blockchain engine (i.e. Tendermint).
return abci.ResponseDeliverTx{
Code: result.Code,
Data: result.Data,
Log: result.Log,
Tags: result.Tags,
}
}
// CheckTx - ABCI - dispatches to the handler
func (app *App) CheckTx(txBytes []byte) abci.ResponseCheckTx {
// Initialize arguments to Handler.
var isCheckTx = true
var ctx = sdk.NewContext(app.header, isCheckTx, txBytes)
var store = app.store
var tx Tx = nil // nil until a decorator parses one.
// Run the handler.
var result = app.handler(ctx, app.store, tx)
// Tell the blockchain engine (i.e. Tendermint).
return abci.ResponseDeliverTx{
Code: result.Code,
Data: result.Data,
Log: result.Log,
Gas: result.Gas,
FeeDenom: result.FeeDenom,
FeeAmount: result.FeeAmount,
GasWanted: result.GasWanted,
GasUsed: result.GasUsed,
Tags: result.Tags,
}
}
// Info - ABCI
func (app *App) Info(req abci.RequestInfo) abci.ResponseInfo {
lastCommitID := app.store.LastCommitID()
return abci.ResponseInfo{
Data: app.Name,
LastBlockHeight: lastCommitID.Version,
LastBlockAppHash: lastCommitID.Hash,
}
}
// SetOption - ABCI
func (app *App) SetOption(key string, value string) string {
return "Not Implemented"
}
// Query - ABCI
func (app *App) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
/*
XXX Make this work with MultiStore.
XXX It will require some interfaces updates in store/types.go.
if len(reqQuery.Data) == 0 {
resQuery.Log = "Query cannot be zero length"
resQuery.Code = abci.CodeType_EncodingError
return
}
// set the query response height to current
tree := app.state.Committed()
height := reqQuery.Height
if height == 0 {
// TODO: once the rpc actually passes in non-zero
// heights we can use to query right after a tx
// we must retrun most recent, even if apphash
// is not yet in the blockchain
withProof := app.CommittedHeight() - 1
if tree.Tree.VersionExists(withProof) {
height = withProof
} else {
height = app.CommittedHeight()
}
}
resQuery.Height = height
switch reqQuery.Path {
case "/store", "/key": // Get by key
key := reqQuery.Data // Data holds the key bytes
resQuery.Key = key
if reqQuery.Prove {
value, proof, err := tree.GetVersionedWithProof(key, height)
if err != nil {
resQuery.Log = err.Error()
break
}
resQuery.Value = value
resQuery.Proof = proof.Bytes()
} else {
value := tree.Get(key)
resQuery.Value = value
}
default:
resQuery.Code = abci.CodeType_UnknownRequest
resQuery.Log = cmn.Fmt("Unexpected Query path: %v", reqQuery.Path)
}
return
*/
}
// Commit implements abci.Application
func (app *App) Commit() (res abci.Result) {
commitID := app.store.Commit()
app.logger.Debug("Commit synced",
"commit", commitID,
)
return abci.NewResultOK(hash, "")
}
// InitChain - ABCI
func (app *App) InitChain(req abci.RequestInitChain) {}
// BeginBlock - ABCI
func (app *App) BeginBlock(req abci.RequestBeginBlock) {
app.header = req.Header
}
// EndBlock - ABCI
// Returns a list of all validator changes made in this block
func (app *App) EndBlock(height uint64) (res abci.ResponseEndBlock) {
// XXX Update to res.Updates.
res.Diffs = app.valUpdates
// Implements ABCI
func (app *App) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
res.ValidatorUpdates = app.valUpdates
app.valUpdates = nil
return
}
// Implements ABCI
func (app *App) Commit() (res abci.ResponseCommit) {
app.msDeliver.Write()
commitID := app.ms.Commit()
app.logger.Debug("Commit synced",
"commit", commitID,
)
return abci.ResponseCommit{
Data: commitID.Hash,
}
}
//----------------------------------------
// Misc.
// Return index of list with validator of same PubKey, or -1 if no match
func pubKeyIndex(val *abci.Validator, list []*abci.Validator) int {
for i, v := range list {
@ -275,38 +254,3 @@ func pubKeyIndex(val *abci.Validator, list []*abci.Validator) int {
func makeDefaultLogger() log.Logger {
return log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
}
// InitState - used to setup state (was SetOption)
// to be call from setting up the genesis file
func (app *InitApp) InitState(module, key, value string) error {
state := app.Append()
logger := app.Logger().With("module", module, "key", key)
if module == sdk.ModuleNameBase {
if key == sdk.ChainKey {
app.info.SetChainID(state, value)
return nil
}
logger.Error("Invalid genesis option")
return fmt.Errorf("Unknown base option: %s", key)
}
log, err := app.initState.InitState(logger, state, module, key, value)
if err != nil {
logger.Error("Invalid genesis option", "err", err)
} else {
logger.Info(log)
}
return err
}
// InitChain - ABCI - sets the initial validators
func (app *InitApp) InitChain(req abci.RequestInitChain) {
// return early if no InitValidator registered
if app.initVals == nil {
return
}
logger, store := app.Logger(), app.Append()
app.initVals.InitValidators(logger, store, req.Validators)
}

View File

@ -7,12 +7,13 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/store"
"github.com/cosmos/cosmos-sdk/types"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-crypto"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
)
func TestBasic(t *testing.T) {
@ -24,31 +25,33 @@ func TestBasic(t *testing.T) {
}
// Create app.
app := sdk.NewApp(t.Name())
app.SetStore(mockMultiStore())
app.SetHandler(func(ctx Context, store MultiStore, tx Tx) Result {
app := NewApp(t.Name())
app.SetCommitMultiStore(newCommitMultiStore())
app.SetHandler(func(ctx types.Context, store types.MultiStore, tx types.Tx) types.Result {
// This could be a decorator.
fromJSON(ctx.TxBytes(), &tx)
var ttx testTx
fromJSON(ctx.TxBytes(), &ttx)
fmt.Println(">>", tx)
// XXX
return types.Result{}
})
// Load latest state, which should be empty.
err := app.LoadLatestVersion()
assert.Nil(t, err)
assert.Equal(t, app.NextVersion(), 1)
assert.Equal(t, app.LastBlockHeight(), int64(0))
// Create the validators
var numVals = 3
var valSet = make([]*abci.Validator, numVals)
var valSet = make([]abci.Validator, numVals)
for i := 0; i < numVals; i++ {
valSet[i] = makeVal(secret(i))
}
// Initialize the chain
app.InitChain(abci.RequestInitChain{
Validators: valset,
Validators: valSet,
})
// Simulate the start of a block.
@ -62,24 +65,26 @@ func TestBasic(t *testing.T) {
}
txBytes := toJSON(tx)
res := app.DeliverTx(txBytes)
require.True(res.IsOK(), "%#v", res)
assert.True(t, res.IsOK(), "%#v", res)
}
// Simulate the end of a block.
// Get the summary of validator updates.
res := app.EndBlock(app.height)
res := app.EndBlock(abci.RequestEndBlock{})
valUpdates := res.ValidatorUpdates
// Assert that validator updates are correct.
for _, val := range valSet {
// Sanity
assert.NotEqual(t, len(val.PubKey), 0)
// Find matching update and splice it out.
for j := 0; j < len(valUpdates); {
assert.NotEqual(len(valUpdates.PubKey), 0)
valUpdate := valUpdates[j]
// Matched.
if bytes.Equal(valUpdate.PubKey, val.PubKey) {
assert.Equal(valUpdate.NewPower, val.Power+1)
assert.Equal(t, valUpdate.Power, val.Power+1)
if j < len(valUpdates)-1 {
// Splice it out.
valUpdates = append(valUpdates[:j], valUpdates[j+1:]...)
@ -100,9 +105,9 @@ func randPower() int64 {
return cmn.RandInt64()
}
func makeVal(secret string) *abci.Validator {
return &abci.Validator{
PubKey: makePubKey(string).Bytes(),
func makeVal(secret string) abci.Validator {
return abci.Validator{
PubKey: makePubKey(secret).Bytes(),
Power: randPower(),
}
}
@ -112,29 +117,43 @@ func makePubKey(secret string) crypto.PubKey {
}
func makePrivKey(secret string) crypto.PrivKey {
return crypto.GenPrivKeyEd25519FromSecret([]byte(id))
privKey := crypto.GenPrivKeyEd25519FromSecret([]byte(secret))
return privKey.Wrap()
}
func secret(index int) []byte {
return []byte(fmt.Sprintf("secret%d", index))
func secret(index int) string {
return fmt.Sprintf("secret%d", index)
}
func copyVal(val *abci.Validator) *abci.Validator {
val2 := *val
return &val2
func copyVal(val abci.Validator) abci.Validator {
// val2 := *val
// return &val2
return val
}
func toJSON(o interface{}) []byte {
bytes, err := json.Marshal(o)
bz, err := json.Marshal(o)
if err != nil {
panic(err)
}
return bytes
// fmt.Println(">> toJSON:", string(bz))
return bz
}
func fromJSON(bytes []byte, ptr interface{}) {
err := json.Unmarshal(bytes, ptr)
func fromJSON(bz []byte, ptr interface{}) {
// fmt.Println(">> fromJSON:", string(bz))
err := json.Unmarshal(bz, ptr)
if err != nil {
panic(err)
}
}
// Creates a sample CommitMultiStore
func newCommitMultiStore() types.CommitMultiStore {
dbMain := dbm.NewMemDB()
dbXtra := dbm.NewMemDB()
ms := store.NewMultiStore(dbMain) // Also store rootMultiStore metadata here (it shouldn't clash)
ms.SetSubstoreLoader("main", store.NewIAVLStoreLoader(dbMain, 0, 0))
ms.SetSubstoreLoader("xtra", store.NewIAVLStoreLoader(dbXtra, 0, 0))
return ms
}

54
app/query.go Normal file
View File

@ -0,0 +1,54 @@
package app
/*
XXX Make this work with MultiStore.
XXX It will require some interfaces updates in store/types.go.
if len(reqQuery.Data) == 0 {
resQuery.Log = "Query cannot be zero length"
resQuery.Code = abci.CodeType_EncodingError
return
}
// set the query response height to current
tree := app.state.Committed()
height := reqQuery.Height
if height == 0 {
// TODO: once the rpc actually passes in non-zero
// heights we can use to query right after a tx
// we must retrun most recent, even if apphash
// is not yet in the blockchain
withProof := app.CommittedHeight() - 1
if tree.Tree.VersionExists(withProof) {
height = withProof
} else {
height = app.CommittedHeight()
}
}
resQuery.Height = height
switch reqQuery.Path {
case "/store", "/key": // Get by key
key := reqQuery.Data // Data holds the key bytes
resQuery.Key = key
if reqQuery.Prove {
value, proof, err := tree.GetVersionedWithProof(key, height)
if err != nil {
resQuery.Log = err.Error()
break
}
resQuery.Value = value
resQuery.Proof = proof.Bytes()
} else {
value := tree.Get(key)
resQuery.Value = value
}
default:
resQuery.Code = abci.CodeType_UnknownRequest
resQuery.Log = cmn.Fmt("Unexpected Query path: %v", reqQuery.Path)
}
return
*/

4
glide.lock generated
View File

@ -112,7 +112,7 @@ imports:
- leveldb/table
- leveldb/util
- name: github.com/tendermint/abci
version: e4b9f1abe794a2117a59738a1294e09b46d0fa00
version: 8f87efd7f86c2bae9df69aff69d715593d5d79f7
subpackages:
- client
- example/dummy
@ -174,7 +174,7 @@ imports:
- types
- version
- name: github.com/tendermint/tmlibs
version: b70ae4919befb6ae3e5cb40ae8174e122e771d08
version: b25df389db3c98f4b964bd39511c199f02d07715
subpackages:
- autofile
- cli

View File

@ -48,6 +48,7 @@ type iavlStore struct {
tree *iavl.VersionedTree
// How many old versions we hold onto.
// A value of 0 means keep all history.
numHistory int64
}
@ -71,7 +72,7 @@ func (st *iavlStore) Commit() CommitID {
}
// Release an old version of history
if st.numHistory < st.tree.Version64() {
if st.numHistory > 0 && (st.numHistory < st.tree.Version64()) {
toRelease := version - st.numHistory
st.tree.DeleteVersion(toRelease)
}

View File

@ -26,6 +26,8 @@ type rootMultiStore struct {
substores map[string]CommitStore
}
var _ CommitMultiStore = (*rootMultiStore)(nil)
func NewMultiStore(db dbm.DB) *rootMultiStore {
return &rootMultiStore{
db: db,
@ -35,6 +37,7 @@ func NewMultiStore(db dbm.DB) *rootMultiStore {
}
}
// Implements CommitMultiStore.
func (rs *rootMultiStore) SetSubstoreLoader(name string, loader CommitStoreLoader) {
if _, ok := rs.storeLoaders[name]; ok {
panic(fmt.Sprintf("rootMultiStore duplicate substore name " + name))
@ -42,17 +45,18 @@ func (rs *rootMultiStore) SetSubstoreLoader(name string, loader CommitStoreLoade
rs.storeLoaders[name] = loader
}
// Call once after all calls to SetSubstoreLoader are complete.
// Implements CommitMultiStore.
func (rs *rootMultiStore) GetSubstore(name string) CommitStore {
return rs.substores[name]
}
// Implements CommitMultiStore.
func (rs *rootMultiStore) LoadLatestVersion() error {
ver := getLatestVersion(rs.db)
return rs.LoadVersion(ver)
}
// NOTE: Returns 0 unless LoadVersion() or LoadLatestVersion() is called.
func (rs *rootMultiStore) NextVersion() int64 {
return rs.nextVersion
}
// Implements CommitMultiStore.
func (rs *rootMultiStore) LoadVersion(ver int64) error {
// Special logic for version 0
@ -106,10 +110,13 @@ func (rs *rootMultiStore) LoadVersion(ver int64) error {
return nil
}
// Implements CommitStore
//----------------------------------------
// +CommitStore
// Implements CommitStore.
func (rs *rootMultiStore) Commit() CommitID {
// Commit substores
// Commit substores.
version := rs.nextVersion
state := commitSubstores(version, rs.substores)
@ -129,27 +136,36 @@ func (rs *rootMultiStore) Commit() CommitID {
return commitID
}
// Implements CommitStore
// Implements CommitStore.
func (rs *rootMultiStore) CacheWrap() CacheWrap {
return rs.CacheMultiStore().(CacheWrap)
}
// Get the last committed CommitID
//----------------------------------------
// +MultiStore
// Implements MultiStore.
func (rs *rootMultiStore) LastCommitID() CommitID {
return rs.lastCommitID
}
// Implements MultiStore
// Implements MultiStore.
// NOTE: Returns 0 unless LoadVersion() or LoadLatestVersion() is called.
func (rs *rootMultiStore) NextVersion() int64 {
return rs.nextVersion
}
// Implements MultiStore.
func (rs *rootMultiStore) CacheMultiStore() CacheMultiStore {
return newCacheMultiStoreFromRMS(rs)
}
// Implements MultiStore
func (rs *rootMultiStore) GetCommitStore(name string) CommitStore {
// Implements MultiStore.
func (rs *rootMultiStore) GetStore(name string) interface{} {
return rs.substores[name]
}
// Implements MultiStore
// Implements MultiStore.
func (rs *rootMultiStore) GetKVStore(name string) KVStore {
return rs.substores[name].(KVStore)
}
@ -208,6 +224,7 @@ func (sc substoreCore) Hash() []byte {
}
//----------------------------------------
// Misc.
func getLatestVersion(db dbm.DB) int64 {
var latest int64

View File

@ -1,12 +1,9 @@
package coin
package types
import (
"fmt"
"sort"
"strconv"
"strings"
"github.com/pkg/errors"
)
// Coin hold some amount of one currency
@ -200,3 +197,11 @@ var _ sort.Interface = Coins{}
// Sort is a helper function to sort the set of coins inplace
func (coins Coins) Sort() { sort.Sort(coins) }
//----------------------------------------
// Misc
type Coinser interface {
GetCoins() Coins
SetCoins(Coins)
}

View File

@ -1,4 +1,4 @@
package coin
package types
import (
"testing"

View File

@ -2,10 +2,46 @@ package types
import (
"context"
"github.com/golang/protobuf/proto"
abci "github.com/tendermint/abci/types"
)
/*
A note on Context security:
The intent of Context is for it to be an immutable object that can be cloned
and updated cheaply with WithValue() and passed forward to the next decorator
or handler. For example,
```golang
func Decorator(ctx Context, ms MultiStore, tx Tx, next Handler) Result {
// Clone and update context with new kv pair.
ctx2 := ctx.WithValueSDK(key, value)
// Call the next decorator/handler.
res := next(ctx2, ms, tx)
// At this point, while `ctx` and `ctx2`'s shallow values haven't changed,
// it's possible that slices or addressable struct fields have been
// modified by the call to `next(...)`.
//
// This is generally undesirable because it prevents a decorator from
// rolling back all side effects--which is the intent of immutable
// `Context`s and store cache-wraps.
}
```
While well-written decorators wouldn't mutate any mutable context values, a malicious or buggy plugin can create unwanted side-effects, so it is highly advised for users of Context to only set immutable values. To help enforce this contract, we require values to be certain primitive types, or a Cloner.
*/
type Cloner interface {
Clone() interface{} // deep copy
}
type Context struct {
context.Context
// Don't add any other fields here,
@ -13,9 +49,7 @@ type Context struct {
}
func NewContext(header abci.Header, isCheckTx bool, txBytes []byte) Context {
c := Context{
Context: context.Background(),
}
c := Context{context.Background()}
c = c.setBlockHeader(header)
c = c.setBlockHeight(header.Height)
c = c.setChainID(header.ChainID)
@ -24,16 +58,47 @@ func NewContext(header abci.Header, isCheckTx bool, txBytes []byte) Context {
return c
}
// The original context.Context API.
func (c Context) WithValue(key interface{}, value interface{}) context.Context {
return context.WithValue(c.Context, key, value)
func (c Context) Value(key interface{}) interface{} {
value := c.Context.Value(key)
if cloner, ok := value.(Cloner); ok {
return cloner.Clone()
}
if message, ok := value.(proto.Message); ok {
return proto.Clone(message)
}
return value
}
// Like WithValue() but retains this API.
func (c Context) WithValueSDK(key interface{}, value interface{}) Context {
return Context{
Context: context.WithValue(c.Context, key, value),
}
func (c Context) WithValue(key interface{}, value Cloner) Context {
return c.withValue(key, value)
}
func (c Context) WithValueProto(key interface{}, value proto.Message) Context {
return c.withValue(key, value)
}
func (c Context) WithValueString(key interface{}, value string) Context {
return c.withValue(key, value)
}
func (c Context) WithValueInt32(key interface{}, value int32) Context {
return c.withValue(key, value)
}
func (c Context) WithValueUint32(key interface{}, value uint32) Context {
return c.withValue(key, value)
}
func (c Context) WithValueUint64(key interface{}, value uint64) Context {
return c.withValue(key, value)
}
func (c Context) WithValueUnsafe(key interface{}, value interface{}) Context {
return c.withValue(key, value)
}
func (c Context) withValue(key interface{}, value interface{}) Context {
return Context{context.WithValue(c.Context, key, value)}
}
//----------------------------------------
@ -71,25 +136,26 @@ func (c Context) TxBytes() []byte {
// Unexposed to prevent overriding.
func (c Context) setBlockHeader(header abci.Header) Context {
return c.WithValueSDK(contextKeyBlockHeader, header)
var _ proto.Message = &header // for cloning.
return c.withValue(contextKeyBlockHeader, header)
}
// Unexposed to prevent overriding.
func (c Context) setBlockHeight(height int64) Context {
return c.WithValueSDK(contextKeyBlockHeight, height)
return c.withValue(contextKeyBlockHeight, height)
}
// Unexposed to prevent overriding.
func (c Context) setChainID(chainID string) Context {
return c.WithValueSDK(contextKeyChainID, chainID)
return c.withValue(contextKeyChainID, chainID)
}
// Unexposed to prevent overriding.
func (c Context) setIsCheckTx(isCheckTx bool) Context {
return c.WithValueSDK(contextKeyIsCheckTx, isCheckTx)
return c.withValue(contextKeyIsCheckTx, isCheckTx)
}
// Unexposed to prevent overriding.
func (c Context) setTxBytes(txBytes []byte) Context {
return c.WithValueSDK(contextKeyTxBytes, txBytes)
return c.withValue(contextKeyTxBytes, txBytes)
}

View File

@ -17,8 +17,8 @@ type Result struct {
// Log is just debug information. NOTE: nondeterministic.
Log string
// GasAllocated is the maximum units of work we allow this tx to perform.
GasAllocated int64
// GasWanted is the maximum units of work we allow this tx to perform.
GasWanted int64
// GasUsed is the amount of gas actually consumed. NOTE: not used.
GasUsed int64
@ -28,7 +28,7 @@ type Result struct {
FeeDenom string
// Changes to the validator set.
ValSetDiff []abci.Validator
ValidatorUpdates []abci.Validator
// Tags are used for transaction indexing and pubsub.
Tags []cmn.KVPair

View File

@ -26,7 +26,7 @@ type MultiStore interface {
// call CacheMultiStore.Write().
CacheMultiStore() CacheMultiStore
// Convenience
// Convenience for fetching substores.
GetStore(name string) interface{}
GetKVStore(name string) KVStore
}
@ -56,7 +56,11 @@ type CommitMultiStore interface {
// Add a substore loader.
SetSubstoreLoader(name string, loader CommitStoreLoader)
// Gets the substore, which is a CommitSubstore.
GetSubstore(name string) CommitStore
// Load the latest persisted version.
// Called once after all calls to SetSubstoreLoader are complete.
LoadLatestVersion() error
// Load a specific persisted version. When you load an old version, or

View File

@ -1,6 +0,0 @@
package coin
type Coinser interface {
GetCoins() Coins
SetCoins(Coins)
}

View File

@ -5,6 +5,7 @@ import (
"github.com/pkg/errors"
"github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire/data"
)
@ -16,7 +17,7 @@ type GenesisAccount struct {
Address data.Bytes `json:"address"`
// this from types.Account (don't know how to embed this properly)
PubKey crypto.PubKey `json:"pub_key"` // May be nil, if not known.
Balance Coins `json:"coins"`
Balance types.Coins `json:"coins"`
}
// ToAccount - GenesisAccount struct to a basecoin Account

View File

@ -4,6 +4,7 @@ package coin
import (
"fmt"
"github.com/cosmos/cosmos-sdk/types"
cmn "github.com/tendermint/tmlibs/common"
)
@ -17,7 +18,7 @@ type CoinMsg interface {
// Input is a source of coins in a transaction.
type Input struct {
Address cmn.Bytes
Coins Coins
Coins types.Coins
}
func (in Input) ValidateBasic() error {
@ -38,7 +39,7 @@ func (txIn TxInput) String() string {
}
// NewTxInput - create a transaction input, used with SendTx
func NewTxInput(addr Actor, coins Coins) TxInput {
func NewTxInput(addr Actor, coins types.Coins) TxInput {
input := TxInput{
Address: addr,
Coins: coins,
@ -50,8 +51,8 @@ func NewTxInput(addr Actor, coins Coins) TxInput {
// TxOutput - expected coin movement output, used with SendTx
type TxOutput struct {
Address Actor `json:"address"`
Coins Coins `json:"coins"`
Address Actor `json:"address"`
Coins types.Coins `json:"coins"`
}
// ValidateBasic - validate transaction output
@ -77,7 +78,7 @@ func (txOut TxOutput) String() string {
}
// NewTxOutput - create a transaction output, used with SendTx
func NewTxOutput(addr Actor, coins Coins) TxOutput {
func NewTxOutput(addr Actor, coins types.Coins) TxOutput {
output := TxOutput{
Address: addr,
Coins: coins,
@ -103,7 +104,7 @@ func NewSendTx(in []TxInput, out []TxOutput) SendTx { // types.Tx {
// NewSendOneTx is a helper for the standard (?) case where there is exactly
// one sender and one recipient
func NewSendOneTx(sender, recipient Actor, amount Coins) SendTx {
func NewSendOneTx(sender, recipient Actor, amount types.Coins) SendTx {
in := []TxInput{{Address: sender, Coins: amount}}
out := []TxOutput{{Address: recipient, Coins: amount}}
return SendTx{Inputs: in, Outputs: out}
@ -120,7 +121,7 @@ func (tx SendTx) ValidateBasic() error {
return ErrNoOutputs()
}
// make sure all inputs and outputs are individually valid
var totalIn, totalOut Coins
var totalIn, totalOut types.Coins
for _, in := range tx.Inputs {
if err := in.ValidateBasic(); err != nil {
return err
@ -153,11 +154,11 @@ type CreditTx struct {
// Credit is the amount to change the credit...
// This may be negative to remove some over-issued credit,
// but can never bring the credit or the balance to negative
Credit Coins `json:"credit"`
Credit types.Coins `json:"credit"`
}
// NewCreditTx - modify the credit granted to a given account
func NewCreditTx(debitor Actor, credit Coins) CreditTx {
func NewCreditTx(debitor Actor, credit types.Coins) CreditTx {
return CreditTx{Debitor: debitor, Credit: credit}
}