Merge pull request #418 from cosmos/feature/remove_tmc_dep

Remove Tendermint Core as dependency to enable build
This commit is contained in:
Ethan Buchman 2018-02-06 13:15:00 -05:00 committed by GitHub
commit 9ca0501a4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
176 changed files with 42 additions and 18574 deletions

View File

@ -25,6 +25,9 @@ dist:
check_tools:
cd tools && $(MAKE) check
update_tools:
cd tools && $(MAKE) glide_update
get_tools:
cd tools && $(MAKE)

View File

@ -1,66 +0,0 @@
# Roadmap for future basecoin development
Warning: there are current plans, they may change based on other developments, needs. The further in the future, the less clear, like all plans.
## 0.6.x - Testnet and Light Client (late June 2017)
The current release cycle is making sure the server is usable for deploying testnets (easy config, safe restarts, moving nodes). Also that we have a useable light client that does full cryptographic prooofs without syncing the entire block headers. See the [changelog](CHANGELOG.md).
Patch release here involve improving the usability of the cli tools, adding subcommands, more flags, helper functions, shell integrations, etc. Please add issues if you find the client tool difficult to use, or deployment troublesome.
## 0.7.x - Towards a modular framework (late July 2017)
**Breaking changes**
* Renaming likely: this release may well lead to a [renaming of the repository](https://github.com/tendermint/basecoin/issues/119) to emphasize that it is a generalized framework. `basecoin` and `basecli` executables will remain with generally unchanged usage.
* This will also provide a tx structure that is very different than the current one, and a non-trivial upgrade of running chains.
The next release cycle involves a big upgrade to the core, especially how one can write modules (aka plugins) as well as configure a basecoin-based executable. The main goal is to leave us with basecoin as a single executable with a similar API, but create a new module/middleware system with a number of standard modules provided (and easy addition of third party modules), so developers can quickly mix-and-match pieces and add custom business logic for there chain.
The main goal here is to migrate from a basecoin with plugins for extra enhancements, to a proper app development framework, of which basecoin is one example app that can quickly be built.
Some ideas:
* Flexible fee/gas system (good for both public and private blockchains)
* Flexible authentication systems (with multi-sig support)
* Basic role permission system
* Abstract IBC to support other transactions from various modules (not just sendtx)
This will be done in conjunction with some sample apps also building on this framework, where other logic is interesting and money transfers is not the central goal, like [trackomatron](https://github.com/tendermint/trackomatron)
## Next steps
The following are three planned steps, the order of which may change. At least one or two of these will most likely occur before any other developments. Clearly, any other feature that are urgent for cosmos can jump the list in priority, but all these pieces are part of the cosmos roadmap, especially the first two.
### 0.8.x??? - Local client API for UI
Beyond the CLI, we want to add more interfaces for easily building a UI on top of the basecoin client. One clear example is a local REST API, so you can easily integrate with an electron app, or a chrome app, just as if you wrote a normal Single-Page Application, but connecting to a local proxy to do full crypto-graphic proofs.
Another **possible** development is providing an SDK, which we can compile with [go-mobile](https://github.com/golang/go/wiki/Mobile) for both Android and iOS to support secure mobile applications. Progress on this front is contingent on participation of an experienced mobile developer.
Further, when the planned enhancements to real-time events happen in tendermint core, we should expose that via a simple subscriber/listener model in these local APIs.
### 0.9.x??? - Proof of Stake and Voting Modules
We should integrate developments on a [proof-of-stake module](https://github.com/tendermint/basecoin-stake) (currently a work-in-progress) and basic voting modules (currently planned) into properly supported for modules. These would provide the basis for dynamic validator set changes with bondign periods, and the basis for making governance decisions (eg. voting to change the block reward).
At this point we would have to give full support to these plugins, and third-party devs can build on them to add more complex delegation or governance logic.
### 0.10.x??? - Database enhancements
Depending on developments with merkleeyes, we would like to increase the expressiveness of the storage layer while maintaining provability of all queries. We would also add a number of new primatives to the key-value store, to allow some general data-structures.
Also, full support for historical queries and performance optimizations of the storage later. But this all depends on supporting developments of another repo, so timing here is unclear. Here are some example ideas:
Merkle proofs:
* **Proof of key-value**: only current proof
* **Proof of missing key**: prove there is no data for that key
* **Proof of range**: one proof for all key-values in a range of keys
* **Proof of highest/lowest in range**: just get one key, for example, prove validator hash with highest height <= H
Data structures:
* **Queues**: provable push-pop operations, split over multiple keys, so it can scale to 1000s of entries without deserializing them all every time.
* **Priority Queues**: as above, but ordered by priority instead of FIFO.
* **Secondary Indexes**: add support for secondary indexes with proofs. So, I can not only prove my balance, but for example, the list of all accouns with a balance of > 1000000 atoms. These indexes would have to be created by the application and stored extra in the database, but if you have a common query that you want proofs/trust, it can be very useful.

View File

@ -1,194 +0,0 @@
package app
import (
"fmt"
"io/ioutil"
"testing"
wire "github.com/tendermint/go-wire"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk"
sdkapp "github.com/cosmos/cosmos-sdk/app"
"github.com/cosmos/cosmos-sdk/modules/auth"
"github.com/cosmos/cosmos-sdk/modules/base"
"github.com/cosmos/cosmos-sdk/modules/coin"
"github.com/cosmos/cosmos-sdk/modules/fee"
"github.com/cosmos/cosmos-sdk/modules/nonce"
"github.com/cosmos/cosmos-sdk/modules/roles"
"github.com/cosmos/cosmos-sdk/stack"
)
type BenchApp struct {
App *sdkapp.BaseApp
Accounts []*coin.AccountWithKey
ChainID string
}
// DefaultHandler - placeholder to just handle sendtx
func DefaultHandler(feeDenom string) sdk.Handler {
// use the default stack
c := coin.NewHandler()
r := roles.NewHandler()
d := stack.NewDispatcher(
c,
stack.WrapHandler(r),
)
return stack.New(
base.Logger{},
stack.Recovery{},
auth.Signatures{},
base.Chain{},
nonce.ReplayCheck{},
roles.NewMiddleware(),
fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank),
).Use(d)
}
func NewBenchApp(h sdk.Handler, chainID string, n int,
persist bool) BenchApp {
logger := log.NewNopLogger()
// logger := log.NewFilter(log.NewTMLogger(os.Stdout), log.AllowError())
// logger = log.NewTracingLogger(logger)
dbDir, cache := "", 0
if persist {
dbDir, _ = ioutil.TempDir("", "bc-app-benchmark")
cache = 500
}
store, err := sdkapp.NewStoreApp("bench", dbDir, cache, logger)
if err != nil {
panic(err)
}
app := sdkapp.NewBaseApp(store, h, nil)
err = app.InitState("base", "chain_id", chainID)
if err != nil {
panic("cannot set chain")
}
// make keys
money := coin.Coins{{"mycoin", 1234567890}}
accts := make([]*coin.AccountWithKey, n)
for i := 0; i < n; i++ {
accts[i] = coin.NewAccountWithKey(money)
err = app.InitState("coin", "account", accts[i].MakeOption())
if err != nil {
panic("can't set account")
}
}
return BenchApp{
App: app,
Accounts: accts,
ChainID: chainID,
}
}
// make a random tx...
func (b BenchApp) makeTx(useFee bool) []byte {
n := len(b.Accounts)
sender := b.Accounts[cmn.RandInt()%n]
recipient := b.Accounts[cmn.RandInt()%n]
amount := coin.Coins{{"mycoin", 123}}
tx := coin.NewSendOneTx(sender.Actor(), recipient.Actor(), amount)
if useFee {
toll := coin.Coin{"mycoin", 2}
tx = fee.NewFee(tx, toll, sender.Actor())
}
sequence := sender.NextSequence()
tx = nonce.NewTx(sequence, []sdk.Actor{sender.Actor()}, tx)
tx = base.NewChainTx(b.ChainID, 0, tx)
stx := auth.NewMulti(tx)
auth.Sign(stx, sender.Key)
res := wire.BinaryBytes(stx.Wrap())
return res
}
func BenchmarkMakeTx(b *testing.B) {
h := DefaultHandler("mycoin")
app := NewBenchApp(h, "bench-chain", 10, false)
b.ResetTimer()
for i := 1; i <= b.N; i++ {
txBytes := app.makeTx(true)
if len(txBytes) < 2 {
panic("cannot commit")
}
}
}
func benchmarkTransfers(b *testing.B, app BenchApp, blockSize int, useFee bool) {
// prepare txs
txs := make([][]byte, b.N)
for i := 1; i <= b.N; i++ {
txBytes := app.makeTx(useFee)
if len(txBytes) < 2 {
panic("cannot make bytes")
}
txs[i-1] = txBytes
}
b.ResetTimer()
for i := 1; i <= b.N; i++ {
res := app.App.DeliverTx(txs[i-1])
if res.IsErr() {
panic(res.Error())
}
if i%blockSize == 0 {
res := app.App.Commit()
if res.IsErr() {
panic("cannot commit")
}
}
}
}
func BenchmarkSimpleTransfer(b *testing.B) {
benchmarks := []struct {
accounts int
blockSize int
useFee bool
toDisk bool
}{
{100, 10, false, false},
{100, 10, true, false},
{100, 200, false, false},
{100, 200, true, false},
{10000, 10, false, false},
{10000, 10, true, false},
{10000, 200, false, false},
{10000, 200, true, false},
{100, 10, false, true},
{100, 10, true, true},
{100, 200, false, true},
{100, 200, true, true},
{10000, 10, false, true},
{10000, 10, true, true},
{10000, 200, false, true},
{10000, 200, true, true},
}
for _, bb := range benchmarks {
prefix := fmt.Sprintf("%d-%d", bb.accounts, bb.blockSize)
if bb.useFee {
prefix += "-fee"
} else {
prefix += "-nofee"
}
if bb.toDisk {
prefix += "-persist"
} else {
prefix += "-memdb"
}
h := DefaultHandler("mycoin")
app := NewBenchApp(h, "bench-chain", bb.accounts, bb.toDisk)
b.Run(prefix, func(sub *testing.B) {
benchmarkTransfers(sub, app, bb.blockSize, bb.useFee)
})
}
}

View File

@ -1,19 +0,0 @@
BenchmarkMakeTx-4 2000 603153 ns/op
BenchmarkSimpleTransfer/100-10-nofee-memdb-4 5000 313154 ns/op
BenchmarkSimpleTransfer/100-10-fee-memdb-4 5000 366534 ns/op
BenchmarkSimpleTransfer/100-200-nofee-memdb-4 5000 296381 ns/op
BenchmarkSimpleTransfer/100-200-fee-memdb-4 5000 350973 ns/op
BenchmarkSimpleTransfer/10000-10-nofee-memdb-4 5000 351425 ns/op
BenchmarkSimpleTransfer/10000-10-fee-memdb-4 3000 410855 ns/op
BenchmarkSimpleTransfer/10000-200-nofee-memdb-4 5000 344839 ns/op
BenchmarkSimpleTransfer/10000-200-fee-memdb-4 5000 394080 ns/op
BenchmarkSimpleTransfer/100-10-nofee-persist-4 3000 433890 ns/op
BenchmarkSimpleTransfer/100-10-fee-persist-4 3000 496133 ns/op
BenchmarkSimpleTransfer/100-200-nofee-persist-4 5000 310174 ns/op
BenchmarkSimpleTransfer/100-200-fee-persist-4 5000 366868 ns/op
BenchmarkSimpleTransfer/10000-10-nofee-persist-4 2000 815755 ns/op
BenchmarkSimpleTransfer/10000-10-fee-persist-4 2000 874532 ns/op
BenchmarkSimpleTransfer/10000-200-nofee-persist-4 5000 567349 ns/op
BenchmarkSimpleTransfer/10000-200-fee-persist-4 5000 621833 ns/op
PASS
ok github.com/tendermint/basecoin/benchmarks 93.047s

View File

@ -1,19 +0,0 @@
BenchmarkMakeTx-4 2000 648379 ns/op
BenchmarkSimpleTransfer/100-10-nofee-memdb-4 5000 356487 ns/op
BenchmarkSimpleTransfer/100-10-fee-memdb-4 5000 413435 ns/op
BenchmarkSimpleTransfer/100-200-nofee-memdb-4 5000 321859 ns/op
BenchmarkSimpleTransfer/100-200-fee-memdb-4 5000 393578 ns/op
BenchmarkSimpleTransfer/10000-10-nofee-memdb-4 5000 379129 ns/op
BenchmarkSimpleTransfer/10000-10-fee-memdb-4 3000 480334 ns/op
BenchmarkSimpleTransfer/10000-200-nofee-memdb-4 5000 384398 ns/op
BenchmarkSimpleTransfer/10000-200-fee-memdb-4 3000 443481 ns/op
BenchmarkSimpleTransfer/100-10-nofee-persist-4 3000 498460 ns/op
BenchmarkSimpleTransfer/100-10-fee-persist-4 3000 559034 ns/op
BenchmarkSimpleTransfer/100-200-nofee-persist-4 5000 314090 ns/op
BenchmarkSimpleTransfer/100-200-fee-persist-4 5000 397457 ns/op
BenchmarkSimpleTransfer/10000-10-nofee-persist-4 2000 845872 ns/op
BenchmarkSimpleTransfer/10000-10-fee-persist-4 2000 929205 ns/op
BenchmarkSimpleTransfer/10000-200-nofee-persist-4 5000 596601 ns/op
BenchmarkSimpleTransfer/10000-200-fee-persist-4 5000 667093 ns/op
PASS
ok github.com/tendermint/basecoin/benchmarks 97.097s

View File

@ -1,19 +0,0 @@
BenchmarkMakeTx-4 2000 660064 ns/op
BenchmarkSimpleTransfer/100-10-nofee-memdb-4 5000 338378 ns/op
BenchmarkSimpleTransfer/100-10-fee-memdb-4 5000 380171 ns/op
BenchmarkSimpleTransfer/100-200-nofee-memdb-4 5000 306365 ns/op
BenchmarkSimpleTransfer/100-200-fee-memdb-4 5000 359344 ns/op
BenchmarkSimpleTransfer/10000-10-nofee-memdb-4 5000 366057 ns/op
BenchmarkSimpleTransfer/10000-10-fee-memdb-4 3000 433549 ns/op
BenchmarkSimpleTransfer/10000-200-nofee-memdb-4 5000 351662 ns/op
BenchmarkSimpleTransfer/10000-200-fee-memdb-4 3000 421573 ns/op
BenchmarkSimpleTransfer/100-10-nofee-persist-4 3000 479848 ns/op
BenchmarkSimpleTransfer/100-10-fee-persist-4 3000 544164 ns/op
BenchmarkSimpleTransfer/100-200-nofee-persist-4 5000 327999 ns/op
BenchmarkSimpleTransfer/100-200-fee-persist-4 5000 385751 ns/op
BenchmarkSimpleTransfer/10000-10-nofee-persist-4 2000 852128 ns/op
BenchmarkSimpleTransfer/10000-10-fee-persist-4 2000 1055130 ns/op
BenchmarkSimpleTransfer/10000-200-nofee-persist-4 5000 642872 ns/op
BenchmarkSimpleTransfer/10000-200-fee-persist-4 3000 686337 ns/op
PASS
ok github.com/tendermint/basecoin/benchmarks 91.717s

View File

@ -1,33 +0,0 @@
package auto
import (
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// AutoCompleteCmd - command to generate bash autocompletions
var AutoCompleteCmd = &cobra.Command{
Use: "complete",
Short: "generate bash autocompletions",
RunE: doAutoComplete,
}
// nolint - flags
const (
FlagOutput = "file"
)
func init() {
AutoCompleteCmd.Flags().String(FlagOutput, "", "file to output bash autocompletion")
AutoCompleteCmd.MarkFlagFilename(FlagOutput)
}
func doAutoComplete(cmd *cobra.Command, args []string) error {
output := viper.GetString(FlagOutput)
if output == "" {
return cmd.Root().GenBashCompletion(os.Stdout)
}
return cmd.Root().GenBashCompletionFile(output)
}

View File

@ -1,46 +0,0 @@
package commits
import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/light-client/certifiers/files"
"github.com/cosmos/cosmos-sdk/client/commands"
)
var exportCmd = &cobra.Command{
Use: "export <file>",
Short: "Export selected commits to given file",
Long: `Exports the most recent commit to a binary file.
If desired, you can select by an older height or validator hash.
`,
RunE: commands.RequireInit(exportCommit),
SilenceUsage: true,
}
func init() {
exportCmd.Flags().Int(heightFlag, 0, "Show the commit with closest height to this")
exportCmd.Flags().String(hashFlag, "", "Show the commit matching the validator hash")
RootCmd.AddCommand(exportCmd)
}
func exportCommit(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a filepath to output")
}
path := args[0]
// load the seed as specified
trust, _ := commands.GetProviders()
h := viper.GetInt(heightFlag)
hash := viper.GetString(hashFlag)
fc, err := loadCommit(trust, h, hash, "")
if err != nil {
return err
}
// now get the output file and write it
return files.SaveFullCommitJSON(fc, path)
}

View File

@ -1,59 +0,0 @@
package commits
import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/light-client/certifiers/files"
"github.com/cosmos/cosmos-sdk/client/commands"
)
const (
dryFlag = "dry-run"
)
var importCmd = &cobra.Command{
Use: "import <file>",
Short: "Imports a new commit from the given file",
Long: `Validate this file and update to the given commit if secure.`,
RunE: commands.RequireInit(importCommit),
SilenceUsage: true,
}
func init() {
importCmd.Flags().Bool(dryFlag, false, "Test the import fully, but do not import")
RootCmd.AddCommand(importCmd)
}
func importCommit(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide an input file")
}
// prepare the certifier
cert, err := commands.GetCertifier()
if err != nil {
return err
}
// parse the input file
path := args[0]
fc, err := files.LoadFullCommitJSON(path)
if err != nil {
return err
}
// just do simple checks in --dry-run
if viper.GetBool(dryFlag) {
fmt.Printf("Testing commit %d/%X\n", fc.Height(), fc.ValidatorsHash())
err = fc.ValidateBasic(cert.ChainID())
} else {
fmt.Printf("Importing commit %d/%X\n", fc.Height(), fc.ValidatorsHash())
err = cert.Update(fc)
}
return err
}

View File

@ -1,15 +0,0 @@
package commits
import "github.com/spf13/cobra"
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
Use: "commits",
Short: "Verify commits from your local store",
Long: `Commits allows you to inspect and update the validator set for the chain.
Since all security in a PoS system is based on having the correct validator
set, it is important to inspect the commits to maintain the security, which
is used to verify all header and merkle proofs.
`,
}

View File

@ -1,74 +0,0 @@
package commits
import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/light-client/certifiers"
"github.com/tendermint/light-client/certifiers/files"
"github.com/cosmos/cosmos-sdk/client/commands"
)
const (
heightFlag = "height"
hashFlag = "hash"
fileFlag = "file"
)
var showCmd = &cobra.Command{
Use: "show",
Short: "Show the details of one selected commit",
Long: `Shows the most recent downloaded key by default.
If desired, you can select by height, validator hash, or a file.
`,
RunE: commands.RequireInit(showCommit),
SilenceUsage: true,
}
func init() {
showCmd.Flags().Int(heightFlag, 0, "Show the commit with closest height to this")
showCmd.Flags().String(hashFlag, "", "Show the commit matching the validator hash")
showCmd.Flags().String(fileFlag, "", "Show the commit stored in the given file")
RootCmd.AddCommand(showCmd)
}
func loadCommit(p certifiers.Provider, h int, hash, file string) (fc certifiers.FullCommit, err error) {
// load the commit from the proper place
if h != 0 {
fc, err = p.GetByHeight(h)
} else if hash != "" {
var vhash []byte
vhash, err = hex.DecodeString(hash)
if err == nil {
fc, err = p.GetByHash(vhash)
}
} else if file != "" {
fc, err = files.LoadFullCommitJSON(file)
} else {
// default is latest commit
fc, err = p.LatestCommit()
}
return
}
func showCommit(cmd *cobra.Command, args []string) error {
trust, _ := commands.GetProviders()
h := viper.GetInt(heightFlag)
hash := viper.GetString(hashFlag)
file := viper.GetString(fileFlag)
fc, err := loadCommit(trust, h, hash, file)
if err != nil {
return err
}
// now render it!
data, err := json.MarshalIndent(fc, "", " ")
fmt.Println(string(data))
return err
}

View File

@ -1,52 +0,0 @@
package commits
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/light-client/certifiers"
"github.com/cosmos/cosmos-sdk/client/commands"
)
var updateCmd = &cobra.Command{
Use: "update",
Short: "Update commit to current height if possible",
RunE: commands.RequireInit(updateCommit),
SilenceUsage: true,
}
func init() {
updateCmd.Flags().Int(heightFlag, 0, "Update to this height, not latest")
RootCmd.AddCommand(updateCmd)
}
func updateCommit(cmd *cobra.Command, args []string) error {
cert, err := commands.GetCertifier()
if err != nil {
return err
}
h := viper.GetInt(heightFlag)
var fc certifiers.FullCommit
if h <= 0 {
// get the lastest from our source
fc, err = cert.Source.LatestCommit()
} else {
fc, err = cert.Source.GetByHeight(h)
}
if err != nil {
return err
}
// let the certifier do it's magic to update....
fmt.Printf("Trying to update to height: %d...\n", fc.Height())
err = cert.Update(fc)
if err != nil {
return err
}
fmt.Println("Success!")
return nil
}

View File

@ -1,154 +0,0 @@
/*
Package commands contains any general setup/helpers valid for all subcommands
*/
package commands
import (
"encoding/hex"
"fmt"
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/light-client/certifiers"
"github.com/tendermint/tmlibs/cli"
cmn "github.com/tendermint/tmlibs/common"
rpcclient "github.com/tendermint/tendermint/rpc/client"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/modules/auth"
)
var (
trustedProv certifiers.Provider
sourceProv certifiers.Provider
)
const (
ChainFlag = "chain-id"
NodeFlag = "node"
)
// AddBasicFlags adds --node and --chain-id, which we need for everything
func AddBasicFlags(cmd *cobra.Command) {
cmd.PersistentFlags().String(ChainFlag, "", "Chain ID of tendermint node")
cmd.PersistentFlags().String(NodeFlag, "", "<host>:<port> to tendermint rpc interface for this chain")
}
// GetChainID reads ChainID from the flags
func GetChainID() string {
return viper.GetString(ChainFlag)
}
// GetNode prepares a simple rpc.Client from the flags
func GetNode() rpcclient.Client {
return client.GetNode(viper.GetString(NodeFlag))
}
// GetSourceProvider returns a provider pointing to an rpc handler
func GetSourceProvider() certifiers.Provider {
if sourceProv == nil {
node := viper.GetString(NodeFlag)
sourceProv = client.GetRPCProvider(node)
}
return sourceProv
}
// GetTrustedProvider returns a reference to a local store with cache
func GetTrustedProvider() certifiers.Provider {
if trustedProv == nil {
rootDir := viper.GetString(cli.HomeFlag)
trustedProv = client.GetLocalProvider(rootDir)
}
return trustedProv
}
// GetProviders creates a trusted (local) seed provider and a remote
// provider based on configuration.
func GetProviders() (trusted certifiers.Provider, source certifiers.Provider) {
return GetTrustedProvider(), GetSourceProvider()
}
// GetCertifier constructs a dynamic certifier from the config info
func GetCertifier() (*certifiers.Inquiring, error) {
// load up the latest store....
trust := GetTrustedProvider()
source := GetSourceProvider()
chainID := GetChainID()
return client.GetCertifier(chainID, trust, source)
}
// ParseActor parses an address of form:
// [<chain>:][<app>:]<hex address>
// into a sdk.Actor.
// If app is not specified or "", then assume auth.NameSigs
func ParseActor(input string) (res sdk.Actor, err error) {
chain, app := "", auth.NameSigs
input = strings.TrimSpace(input)
spl := strings.SplitN(input, ":", 3)
if len(spl) == 3 {
chain = spl[0]
spl = spl[1:]
}
if len(spl) == 2 {
if spl[0] != "" {
app = spl[0]
}
spl = spl[1:]
}
addr, err := hex.DecodeString(cmn.StripHex(spl[0]))
if err != nil {
return res, errors.Errorf("Address is invalid hex: %v\n", err)
}
res = sdk.Actor{
ChainID: chain,
App: app,
Address: addr,
}
return
}
// ParseActors takes a comma-separated list of actors and parses them into
// a slice
func ParseActors(key string) (signers []sdk.Actor, err error) {
var act sdk.Actor
for _, k := range strings.Split(key, ",") {
act, err = ParseActor(k)
if err != nil {
return
}
signers = append(signers, act)
}
return
}
// GetOneArg makes sure there is exactly one positional argument
func GetOneArg(args []string, argname string) (string, error) {
if len(args) == 0 {
return "", errors.Errorf("Missing required argument [%s]", argname)
}
if len(args) > 1 {
return "", errors.Errorf("Only accepts one argument [%s]", argname)
}
return args[0], nil
}
// ParseHexFlag takes a flag name and parses the viper contents as hex
func ParseHexFlag(flag string) ([]byte, error) {
arg := viper.GetString(flag)
if arg == "" {
return nil, errors.Errorf("No such flag: %s", flag)
}
value, err := hex.DecodeString(cmn.StripHex(arg))
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("Cannot parse %s", flag))
}
return value, nil
}

View File

@ -1,352 +0,0 @@
package commands
import (
"bytes"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/BurntSushi/toml"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"github.com/tendermint/light-client/certifiers"
"github.com/tendermint/light-client/certifiers/files"
"github.com/tendermint/tmlibs/cli"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tendermint/types"
)
var (
dirPerm = os.FileMode(0700)
)
//nolint
const (
CommitFlag = "commit"
HashFlag = "valhash"
GenesisFlag = "genesis"
FlagTrustNode = "trust-node"
ConfigFile = "config.toml"
)
// InitCmd will initialize the basecli store
var InitCmd = &cobra.Command{
Use: "init",
Short: "Initialize the light client for a new chain",
RunE: runInit,
}
var ResetCmd = &cobra.Command{
Use: "reset_all",
Short: "DANGEROUS: Wipe out all client data, including keys",
RunE: runResetAll,
}
func init() {
InitCmd.Flags().Bool("force-reset", false, "Wipe clean an existing client store, except for keys")
InitCmd.Flags().String(CommitFlag, "", "Commit file to import (optional)")
InitCmd.Flags().String(HashFlag, "", "Trusted validator hash (must match to accept)")
InitCmd.Flags().String(GenesisFlag, "", "Genesis file with chainid and validators (optional)")
}
func runInit(cmd *cobra.Command, args []string) error {
root := viper.GetString(cli.HomeFlag)
if viper.GetBool("force-reset") {
resetRoot(root, true)
}
// make sure we don't have an existing client initialized
inited, err := WasInited(root)
if err != nil {
return err
}
if inited {
return errors.Errorf("%s already is initialized, --force-reset if you really want to wipe it out", root)
}
// clean up dir if init fails
err = doInit(cmd, root)
if err != nil {
resetRoot(root, true)
}
return err
}
// doInit actually creates all the files, on error, we should revert it all
func doInit(cmd *cobra.Command, root string) error {
// read the genesis file if present, and populate --chain-id and --valhash
err := checkGenesis(cmd)
if err != nil {
return err
}
err = initConfigFile(cmd)
if err != nil {
return err
}
err = initTrust()
return err
}
func runResetAll(cmd *cobra.Command, args []string) error {
root := viper.GetString(cli.HomeFlag)
resetRoot(root, false)
return nil
}
func resetRoot(root string, saveKeys bool) {
tmp := filepath.Join(os.TempDir(), cmn.RandStr(16))
keys := filepath.Join(root, "keys")
if saveKeys {
os.Rename(keys, tmp)
}
os.RemoveAll(root)
if saveKeys {
os.Mkdir(root, 0700)
os.Rename(tmp, keys)
}
}
type Runable func(cmd *cobra.Command, args []string) error
// Any commands that require and init'ed basecoin directory
// should wrap their RunE command with RequireInit
// to make sure that the client is initialized.
//
// This cannot be called during PersistentPreRun,
// as they are called from the most specific command first, and root last,
// and the root command sets up viper, which is needed to find the home dir.
func RequireInit(run Runable) Runable {
return func(cmd *cobra.Command, args []string) error {
// otherwise, run the wrappped command
if viper.GetBool(FlagTrustNode) {
return run(cmd, args)
}
// first check if we were Init'ed and if not, return an error
root := viper.GetString(cli.HomeFlag)
init, err := WasInited(root)
if err != nil {
return err
}
if !init {
return errors.Errorf("You must run '%s init' first", cmd.Root().Name())
}
// otherwise, run the wrappped command
return run(cmd, args)
}
}
// WasInited returns true if a basecoin was previously initialized
// in this directory. Important to ensure proper behavior.
//
// Returns error if we have filesystem errors
func WasInited(root string) (bool, error) {
// make sure there is a directory here in any case
os.MkdirAll(root, dirPerm)
// check if there is a config.toml file
cfgFile := filepath.Join(root, "config.toml")
_, err := os.Stat(cfgFile)
if os.IsNotExist(err) {
return false, nil
}
if err != nil {
return false, errors.WithStack(err)
}
// check if there are non-empty checkpoints and validators dirs
dirs := []string{
filepath.Join(root, files.CheckDir),
filepath.Join(root, files.ValDir),
}
// if any of these dirs is empty, then we have no data
for _, d := range dirs {
empty, err := isEmpty(d)
if err != nil {
return false, err
}
if empty {
return false, nil
}
}
// looks like we have everything
return true, nil
}
func checkGenesis(cmd *cobra.Command) error {
genesis := viper.GetString(GenesisFlag)
if genesis == "" {
return nil
}
doc, err := types.GenesisDocFromFile(genesis)
if err != nil {
return err
}
flags := cmd.Flags()
flags.Set(ChainFlag, doc.ChainID)
hash := doc.ValidatorHash()
hexHash := hex.EncodeToString(hash)
flags.Set(HashFlag, hexHash)
return nil
}
// isEmpty returns false if we can read files in this dir.
// if it doesn't exist, read issues, etc... return true
//
// TODO: should we handle errors otherwise?
func isEmpty(dir string) (bool, error) {
// check if we can read the directory, missing is fine, other error is not
d, err := os.Open(dir)
if os.IsNotExist(err) {
return true, nil
}
if err != nil {
return false, errors.WithStack(err)
}
defer d.Close()
// read to see if any (at least one) files here...
files, err := d.Readdirnames(1)
if err == io.EOF {
return true, nil
}
if err != nil {
return false, errors.WithStack(err)
}
empty := len(files) == 0
return empty, nil
}
type Config struct {
Chain string `toml:"chain-id,omitempty"`
Node string `toml:"node,omitempty"`
Output string `toml:"output,omitempty"`
Encoding string `toml:"encoding,omitempty"`
}
func setConfig(flags *pflag.FlagSet, f string, v *string) {
if flags.Changed(f) {
*v = viper.GetString(f)
}
}
func initConfigFile(cmd *cobra.Command) error {
flags := cmd.Flags()
var cfg Config
required := []string{ChainFlag, NodeFlag}
for _, f := range required {
if !flags.Changed(f) {
return errors.Errorf(`"--%s" required`, f)
}
}
setConfig(flags, ChainFlag, &cfg.Chain)
setConfig(flags, NodeFlag, &cfg.Node)
setConfig(flags, cli.OutputFlag, &cfg.Output)
setConfig(flags, cli.EncodingFlag, &cfg.Encoding)
out, err := os.Create(filepath.Join(viper.GetString(cli.HomeFlag), ConfigFile))
if err != nil {
return errors.WithStack(err)
}
defer out.Close()
// save the config file
err = toml.NewEncoder(out).Encode(cfg)
if err != nil {
return errors.WithStack(err)
}
return nil
}
func initTrust() (err error) {
// create a provider....
trust, source := GetProviders()
// load a commit file, or get data from the provider
var fc certifiers.FullCommit
commitFile := viper.GetString(CommitFlag)
if commitFile == "" {
fmt.Println("Loading validator set from tendermint rpc...")
fc, err = source.LatestCommit()
} else {
fmt.Printf("Loading validators from file %s\n", commitFile)
fc, err = files.LoadFullCommit(commitFile)
}
// can't load the commit? abort!
if err != nil {
return err
}
// make sure it is a proper commit
err = fc.ValidateBasic(viper.GetString(ChainFlag))
if err != nil {
return err
}
// validate hash interactively or not
hash := viper.GetString(HashFlag)
if hash != "" {
var hashb []byte
hashb, err = hex.DecodeString(hash)
if err == nil && !bytes.Equal(hashb, fc.ValidatorsHash()) {
err = errors.Errorf("Validator hash doesn't match expectation: %X", fc.ValidatorsHash())
}
} else {
err = validateHash(fc)
}
if err != nil {
return err
}
// if accepted, store commit as current state
trust.StoreCommit(fc)
return nil
}
func validateHash(fc certifiers.FullCommit) error {
// ask the user to verify the validator hash
fmt.Println("\nImportant: if this is incorrect, all interaction with the chain will be insecure!")
fmt.Printf(" Given validator hash valid: %X\n", fc.ValidatorsHash())
fmt.Println("Is this valid (y/n)?")
valid := askForConfirmation()
if !valid {
return errors.New("Invalid validator hash, try init with proper commit later")
}
return nil
}
func askForConfirmation() bool {
var resp string
_, err := fmt.Scanln(&resp)
if err != nil {
fmt.Println("Please type yes or no and then press enter:")
return askForConfirmation()
}
resp = strings.ToLower(resp)
if resp == "y" || resp == "yes" {
return true
} else if resp == "n" || resp == "no" {
return false
} else {
fmt.Println("Please type yes or no and then press enter:")
return askForConfirmation()
}
}

View File

@ -1,117 +0,0 @@
# Keys CLI
This is as much an example how to expose cobra/viper, as for a cli itself
(I think this code is overkill for what go-keys needs). But please look at
the commands, and give feedback and changes.
`RootCmd` calls some initialization functions (`cobra.OnInitialize` and `RootCmd.PersistentPreRunE`) which serve to connect environmental variables and cobra flags, as well as load the config file. It also validates the flags registered on root and creates the cryptomanager, which will be used by all subcommands.
## Help info
```
# keys help
Keys allows you to manage your local keystore for tendermint.
These keys may be in any format supported by go-crypto and can be
used by light-clients, full nodes, or any other application that
needs to sign with a private key.
Usage:
keys [command]
Available Commands:
get Get details of one key
list List all keys
new Create a new public/private key pair
serve Run the key manager as an http server
update Change the password for a private key
Flags:
--keydir string Directory to store private keys (subdir of root) (default "keys")
-o, --output string Output format (text|json) (default "text")
-r, --root string root directory for config and data (default "/Users/ethan/.tlc")
Use "keys [command] --help" for more information about a command.
```
## Getting the config file
The first step is to load in root, by checking the following in order:
* -r, --root command line flag
* TM_ROOT environmental variable
* default ($HOME/.tlc evaluated at runtime)
Once the `rootDir` is established, the script looks for a config file named `keys.{json,toml,yaml,hcl}` in that directory and parses it. These values will provide defaults for flags of the same name.
There is an example config file for testing out locally, which writes keys to `./.mykeys`. You can
## Getting/Setting variables
When we want to get the value of a user-defined variable (eg. `output`), we can call `viper.GetString("output")`, which will do the following checks, until it finds a match:
* Is `--output` command line flag present?
* Is `TM_OUTPUT` environmental variable set?
* Was a config file found and does it have an `output` variable?
* Is there a default set on the command line flag?
If no variable is set and there was no default, we get back "".
This setup allows us to have powerful command line flags, but use env variables or config files (local or 12-factor style) to avoid passing these arguments every time.
## Nesting structures
Sometimes we don't just need key-value pairs, but actually a multi-level config file, like
```
[mail]
from = "no-reply@example.com"
server = "mail.example.com"
port = 567
password = "XXXXXX"
```
This CLI is too simple to warant such a structure, but I think eg. tendermint could benefit from such an approach. Here are some pointers:
* [Accessing nested keys from config files](https://github.com/spf13/viper#accessing-nested-keys)
* [Overriding nested values with envvars](https://www.netlify.com/blog/2016/09/06/creating-a-microservice-boilerplate-in-go/#nested-config-values) - the mentioned outstanding PR is already merged into master!
* Overriding nested values with cli flags? (use `--log_config.level=info` ??)
I'd love to see an example of this fully worked out in a more complex CLI.
## Have your cake and eat it too
It's easy to render data different ways. Some better for viewing, some better for importing to other programs. You can just add some global (persistent) flags to control the output formatting, and everyone gets what they want.
```
# keys list -e hex
All keys:
betty d0789984492b1674e276b590d56b7ae077f81adc
john b77f4720b220d1411a649b6c7f1151eb6b1c226a
# keys list -e btc
All keys:
betty 3uTF4r29CbtnzsNHZoPSYsE4BDwH
john 3ZGp2Md35iw4XVtRvZDUaAEkCUZP
# keys list -e b64 -o json
[
{
"name": "betty",
"address": "0HiZhEkrFnTidrWQ1Wt64Hf4Gtw=",
"pubkey": {
"type": "secp256k1",
"data": "F83WvhT0KwttSoqQqd_0_r2ztUUaQix5EXdO8AZyREoV31Og780NW59HsqTAb2O4hZ-w-j0Z-4b2IjfdqqfhVQ=="
}
},
{
"name": "john",
"address": "t39HILIg0UEaZJtsfxFR62scImo=",
"pubkey": {
"type": "ed25519",
"data": "t1LFmbg_8UTwj-n1wkqmnTp6NfaOivokEhlYySlGYCY="
}
}
]
```

View File

@ -1,49 +0,0 @@
// Copyright © 2017 Ethan Frey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keys
import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
// deleteCmd represents the delete command
var deleteCmd = &cobra.Command{
Use: "delete [name]",
Short: "DANGER: Delete a private key from your system",
RunE: runDeleteCmd,
}
func runDeleteCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a name for the key")
}
name := args[0]
oldpass, err := getPassword("DANGER - enter password to permanently delete key:")
if err != nil {
return err
}
err = GetKeyManager().Delete(name, oldpass)
if err != nil {
return err
}
fmt.Println("Password deleted forever (uh oh!)")
return nil
}

View File

@ -1,42 +0,0 @@
// Copyright © 2017 Ethan Frey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keys
import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
// getCmd represents the get command
var getCmd = &cobra.Command{
Use: "get [name]",
Short: "Get details of one key",
Long: `Return public details of one local key.`,
RunE: runGetCmd,
}
func runGetCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a name for the key")
}
name := args[0]
info, err := GetKeyManager().Get(name)
if err == nil {
printInfo(info)
}
return err
}

View File

@ -1,34 +0,0 @@
// Copyright © 2017 Ethan Frey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keys
import "github.com/spf13/cobra"
// listCmd represents the list command
var listCmd = &cobra.Command{
Use: "list",
Short: "List all keys",
Long: `Return a list of all public keys stored by this key manager
along with their associated name and address.`,
RunE: runListCmd,
}
func runListCmd(cmd *cobra.Command, args []string) error {
infos, err := GetKeyManager().List()
if err == nil {
printInfos(infos)
}
return err
}

View File

@ -1,94 +0,0 @@
// Copyright © 2017 Ethan Frey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keys
import (
"fmt"
"github.com/pkg/errors"
"github.com/tendermint/go-crypto/keys"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/tmlibs/cli"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
flagType = "type"
flagNoBackup = "no-backup"
)
// newCmd represents the new command
var newCmd = &cobra.Command{
Use: "new [name]",
Short: "Create a new public/private key pair",
Long: `Add a public/private key pair to the key store.
The password muts be entered in the terminal and not
passed as a command line argument for security.`,
RunE: runNewCmd,
}
func init() {
newCmd.Flags().StringP(flagType, "t", "ed25519", "Type of key (ed25519|secp256k1|ledger")
newCmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)")
}
func runNewCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a name for the key")
}
name := args[0]
algo := viper.GetString(flagType)
pass, err := getCheckPassword("Enter a passphrase:", "Repeat the passphrase:")
if err != nil {
return err
}
info, seed, err := GetKeyManager().Create(name, pass, algo)
if err == nil {
printCreate(info, seed)
}
return err
}
type NewOutput struct {
Key keys.Info `json:"key"`
Seed string `json:"seed"`
}
func printCreate(info keys.Info, seed string) {
switch viper.Get(cli.OutputFlag) {
case "text":
printInfo(info)
// print seed unless requested not to.
if !viper.GetBool(flagNoBackup) {
fmt.Println("**Important** write this seed phrase in a safe place.")
fmt.Println("It is the only way to recover your account if you ever forget your password.\n")
fmt.Println(seed)
}
case "json":
out := NewOutput{Key: info}
if !viper.GetBool(flagNoBackup) {
out.Seed = seed
}
json, err := data.ToJSON(out)
if err != nil {
panic(err) // really shouldn't happen...
}
fmt.Println(string(json))
}
}

View File

@ -1,61 +0,0 @@
// Copyright © 2017 Ethan Frey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keys
import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
// recoverCmd represents the recover command
var recoverCmd = &cobra.Command{
Use: "recover [name]",
Short: "Recover a private key from a seed phrase",
Long: `Recover a private key from a seed phrase.
I really hope you wrote this down when you created the new key.
The seed is only displayed on creation, never again.
You can also use this to copy a key between multiple testnets,
simply by "recovering" the key in the other nets you want to copy
to. Of course, it has no coins on the other nets, just the same address.`,
RunE: runRecoverCmd,
}
func runRecoverCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a name for the key")
}
name := args[0]
pass, err := getPassword("Enter the new passphrase:")
if err != nil {
return err
}
// not really a password... huh?
seed, err := getSeed("Enter your recovery seed phrase:")
if err != nil {
return err
}
info, err := GetKeyManager().Recover(name, pass, seed)
if err != nil {
return err
}
printInfo(info)
return nil
}

View File

@ -1,44 +0,0 @@
// Copyright © 2017 Ethan Frey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keys
import (
"github.com/spf13/cobra"
keys "github.com/tendermint/go-crypto/keys"
)
var (
manager keys.Manager
)
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
Use: "keys",
Short: "Key manager for tendermint clients",
Long: `Keys allows you to manage your local keystore for tendermint.
These keys may be in any format supported by go-crypto and can be
used by light-clients, full nodes, or any other application that
needs to sign with a private key.`,
}
func init() {
RootCmd.AddCommand(getCmd)
RootCmd.AddCommand(listCmd)
RootCmd.AddCommand(newCmd)
RootCmd.AddCommand(updateCmd)
RootCmd.AddCommand(deleteCmd)
RootCmd.AddCommand(recoverCmd)
}

View File

@ -1,53 +0,0 @@
// Copyright © 2017 Ethan Frey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keys
import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
// updateCmd represents the update command
var updateCmd = &cobra.Command{
Use: "update [name]",
Short: "Change the password for a private key",
RunE: runUpdateCmd,
}
func runUpdateCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a name for the key")
}
name := args[0]
oldpass, err := getPassword("Enter the current passphrase:")
if err != nil {
return err
}
newpass, err := getCheckPassword("Enter the new passphrase:", "Repeat the new passphrase:")
if err != nil {
return err
}
err = GetKeyManager().Update(name, oldpass, newpass)
if err != nil {
return err
}
fmt.Println("Password successfully updated!")
return nil
}

View File

@ -1,131 +0,0 @@
package keys
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/bgentry/speakeasy"
isatty "github.com/mattn/go-isatty"
"github.com/pkg/errors"
"github.com/spf13/viper"
keys "github.com/tendermint/go-crypto/keys"
data "github.com/tendermint/go-wire/data"
"github.com/tendermint/tmlibs/cli"
"github.com/cosmos/cosmos-sdk/client"
)
const MinPassLength = 10
// GetKeyManager initializes a key manager based on the configuration
func GetKeyManager() keys.Manager {
if manager == nil {
rootDir := viper.GetString(cli.HomeFlag)
manager = client.GetKeyManager(rootDir)
}
return manager
}
// if we read from non-tty, we just need to init the buffer reader once,
// in case we try to read multiple passwords (eg. update)
var buf *bufio.Reader
func inputIsTty() bool {
return isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd())
}
func stdinPassword() (string, error) {
if buf == nil {
buf = bufio.NewReader(os.Stdin)
}
pass, err := buf.ReadString('\n')
if err != nil {
return "", err
}
return strings.TrimSpace(pass), nil
}
func getPassword(prompt string) (pass string, err error) {
if inputIsTty() {
pass, err = speakeasy.Ask(prompt)
} else {
pass, err = stdinPassword()
}
if err != nil {
return "", err
}
if len(pass) < MinPassLength {
return "", errors.Errorf("Password must be at least %d characters", MinPassLength)
}
return pass, nil
}
func getSeed(prompt string) (seed string, err error) {
if inputIsTty() {
fmt.Println(prompt)
}
seed, err = stdinPassword()
seed = strings.TrimSpace(seed)
return
}
func getCheckPassword(prompt, prompt2 string) (string, error) {
// simple read on no-tty
if !inputIsTty() {
return getPassword(prompt)
}
// TODO: own function???
pass, err := getPassword(prompt)
if err != nil {
return "", err
}
pass2, err := getPassword(prompt2)
if err != nil {
return "", err
}
if pass != pass2 {
return "", errors.New("Passphrases don't match")
}
return pass, nil
}
func printInfo(info keys.Info) {
switch viper.Get(cli.OutputFlag) {
case "text":
addr, err := data.ToText(info.Address)
if err != nil {
panic(err) // really shouldn't happen...
}
sep := "\t\t"
if len(info.Name) > 7 {
sep = "\t"
}
fmt.Printf("%s%s%s\n", info.Name, sep, addr)
case "json":
json, err := data.ToJSON(info)
if err != nil {
panic(err) // really shouldn't happen...
}
fmt.Println(string(json))
}
}
func printInfos(infos keys.Infos) {
switch viper.Get(cli.OutputFlag) {
case "text":
fmt.Println("All keys:")
for _, i := range infos {
printInfo(i)
}
case "json":
json, err := data.ToJSON(infos)
if err != nil {
panic(err) // really shouldn't happen...
}
fmt.Println(string(json))
}
}

View File

@ -1,66 +0,0 @@
package proxy
import (
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/commands"
)
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
Use: "proxy",
Short: "Run proxy server, verifying tendermint rpc",
Long: `This node will run a secure proxy to a tendermint rpc server.
All calls that can be tracked back to a block header by a proof
will be verified before passing them back to the caller. Other that
that it will present the same interface as a full tendermint node,
just with added trust and running locally.`,
RunE: commands.RequireInit(runProxy),
SilenceUsage: true,
}
const (
bindFlag = "serve"
)
func init() {
RootCmd.Flags().String(bindFlag, ":8888", "Serve the proxy on the given port")
}
// TODO: pass in a proper logger
var logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout))
func init() {
logger = logger.With("module", "main")
logger = log.NewFilter(logger, log.AllowInfo())
}
func runProxy(cmd *cobra.Command, args []string) error {
// First, connect a client
node := commands.GetNode()
bind := viper.GetString(bindFlag)
cert, err := commands.GetCertifier()
if err != nil {
return err
}
sc := client.SecureClient(node, cert)
err = client.StartProxy(sc, bind, logger)
if err != nil {
return err
}
cmn.TrapSignal(func() {
// TODO: close up shop
})
return nil
}

View File

@ -1,121 +0,0 @@
package query
import (
"fmt"
"io"
"os"
"github.com/pkg/errors"
"github.com/spf13/viper"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/iavl"
"github.com/tendermint/light-client/proofs"
rpcclient "github.com/tendermint/tendermint/rpc/client"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/commands"
)
// GetParsed does most of the work of the query commands, but is quite
// opinionated, so if you want more control about parsing, call Get
// directly.
//
// It will try to get the proof for the given key. If it is successful,
// it will return the height and also unserialize proof.Data into the data
// argument (so pass in a pointer to the appropriate struct)
func GetParsed(key []byte, data interface{}, height int, prove bool) (uint64, error) {
bs, h, err := Get(key, height, prove)
if err != nil {
return 0, err
}
err = wire.ReadBinaryBytes(bs, data)
if err != nil {
return 0, err
}
return h, nil
}
// Get queries the given key and returns the value stored there and the
// height we checked at.
//
// If prove is true (and why shouldn't it be?),
// the data is fully verified before returning. If prove is false,
// we just repeat whatever any (potentially malicious) node gives us.
// Only use that if you are running the full node yourself,
// and it is localhost or you have a secure connection (not HTTP)
func Get(key []byte, height int, prove bool) (data.Bytes, uint64, error) {
if height < 0 {
return nil, 0, fmt.Errorf("Height cannot be negative")
}
if !prove {
node := commands.GetNode()
resp, err := node.ABCIQueryWithOptions("/key", key,
rpcclient.ABCIQueryOptions{Trusted: true, Height: uint64(height)})
return data.Bytes(resp.Value), resp.Height, err
}
val, h, _, err := GetWithProof(key, height)
return val, h, err
}
// GetWithProof returns the values stored under a given key at the named
// height as in Get. Additionally, it will return a validated merkle
// proof for the key-value pair if it exists, and all checks pass.
func GetWithProof(key []byte, height int) (data.Bytes, uint64, iavl.KeyProof, error) {
node := commands.GetNode()
cert, err := commands.GetCertifier()
if err != nil {
return nil, 0, nil, err
}
return client.GetWithProof(key, height, node, cert)
}
// ParseHexKey parses the key flag as hex and converts to bytes or returns error
// argname is used to customize the error message
func ParseHexKey(args []string, argname string) ([]byte, error) {
if len(args) == 0 {
return nil, errors.Errorf("Missing required argument [%s]", argname)
}
if len(args) > 1 {
return nil, errors.Errorf("Only accepts one argument [%s]", argname)
}
rawkey := args[0]
if rawkey == "" {
return nil, errors.Errorf("[%s] argument must be non-empty ", argname)
}
// with tx, we always just parse key as hex and use to lookup
return proofs.ParseHexKey(rawkey)
}
// GetHeight reads the viper config for the query height
func GetHeight() int {
return viper.GetInt(FlagHeight)
}
type proof struct {
Height uint64 `json:"height"`
Data interface{} `json:"data"`
}
// FoutputProof writes the output of wrapping height and info
// in the form {"data": <the_data>, "height": <the_height>}
// to the provider io.Writer
func FoutputProof(w io.Writer, v interface{}, height uint64) error {
wrap := &proof{height, v}
blob, err := data.ToJSON(wrap)
if err != nil {
return err
}
_, err = fmt.Fprintf(w, "%s\n", blob)
return err
}
// OutputProof prints the proof to stdout
// reuse this for printing proofs and we should enhance this for text/json,
// better presentation of height
func OutputProof(data interface{}, height uint64) error {
return FoutputProof(os.Stdout, data, height)
}

View File

@ -1,30 +0,0 @@
package query
import (
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client/commands"
)
// nolint
const (
FlagHeight = "height"
)
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
Use: "query",
Short: "Get and store merkle proofs for blockchain data",
Long: `Proofs allows you to validate data and merkle proofs.
These proofs tie the data to a checkpoint, which is managed by "seeds".
Here we can validate these proofs and import/export them to prove specific
data to other peers as needed.
`,
}
func init() {
RootCmd.PersistentFlags().Int(FlagHeight, 0, "Height to query (skip to use latest block)")
RootCmd.PersistentFlags().Bool(commands.FlagTrustNode, false,
"DANGEROUS: blindly trust all results from the server")
RootCmd.PersistentFlags().MarkHidden(commands.FlagTrustNode)
}

View File

@ -1,36 +0,0 @@
package query
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client/commands"
)
// KeyQueryCmd - CLI command to query a state by key with proof
var KeyQueryCmd = &cobra.Command{
Use: "key [key]",
Short: "Handle proofs for state of abci app",
Long: `This will look up a given key in the abci app, verify the proof,
and output it as hex.
If you want json output, use an app-specific command that knows key and value structure.`,
RunE: commands.RequireInit(keyQueryCmd),
}
// Note: we cannot yse GetAndParseAppProof here, as we don't use go-wire to
// parse the object, but rather return the raw bytes
func keyQueryCmd(cmd *cobra.Command, args []string) error {
// parse cli
key, err := ParseHexKey(args, "key")
if err != nil {
return err
}
prove := !viper.GetBool(commands.FlagTrustNode)
val, h, err := Get(key, GetHeight(), prove)
if err != nil {
return err
}
return OutputProof(val, h)
}

View File

@ -1,76 +0,0 @@
package query
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/commands"
)
// TxQueryCmd - CLI command to query a transaction with proof
var TxQueryCmd = &cobra.Command{
Use: "tx [txhash]",
Short: "Handle proofs of commited txs",
Long: `Proofs allows you to validate abci state with merkle proofs.
These proofs tie the data to a checkpoint, which is managed by "seeds".
Here we can validate these proofs and import/export them to prove specific
data to other peers as needed.
`,
RunE: commands.RequireInit(txQueryCmd),
}
func txQueryCmd(cmd *cobra.Command, args []string) error {
// parse cli
// TODO: when querying historical heights is allowed... pass it
// height := GetHeight()
bkey, err := ParseHexKey(args, "txhash")
if err != nil {
return err
}
// get the proof -> this will be used by all prover commands
node := commands.GetNode()
prove := !viper.GetBool(commands.FlagTrustNode)
res, err := node.Tx(bkey, prove)
if err != nil {
return err
}
// no checks if we don't get a proof
if !prove {
return showTx(res.Height, res.Tx)
}
cert, err := commands.GetCertifier()
if err != nil {
return err
}
check, err := client.GetCertifiedCommit(res.Height, node, cert)
if err != nil {
return err
}
err = res.Proof.Validate(check.Header.DataHash)
if err != nil {
return err
}
// note that we return res.Proof.Data, not res.Tx,
// as res.Proof.Validate only verifies res.Proof.Data
return showTx(res.Height, res.Proof.Data)
}
// showTx parses anything that was previously registered as interface{}
func showTx(h int, tx types.Tx) error {
var info interface{}
err := wire.ReadBinaryBytes(tx, &info)
if err != nil {
return err
}
return OutputProof(info, uint64(h))
}

View File

@ -1,49 +0,0 @@
package rpc
import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/tendermint/tendermint/rpc/client"
)
var waitCmd = &cobra.Command{
Use: "wait",
Short: "Wait until a given height, or number of new blocks",
RunE: commands.RequireInit(runWait),
}
func init() {
waitCmd.Flags().Int(FlagHeight, -1, "wait for block height")
waitCmd.Flags().Int(FlagDelta, -1, "wait for given number of nodes")
}
func runWait(cmd *cobra.Command, args []string) error {
c := commands.GetNode()
h := viper.GetInt(FlagHeight)
if h == -1 {
// read from delta
d := viper.GetInt(FlagDelta)
if d == -1 {
return errors.New("Must set --height or --delta")
}
status, err := c.Status()
if err != nil {
return err
}
h = status.LatestBlockHeight + d
}
// now wait
err := client.WaitForHeight(c, h, nil)
if err != nil {
return err
}
fmt.Printf("Chain now at height %d\n", h)
return nil
}

View File

@ -1,67 +0,0 @@
package rpc
import (
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client/commands"
)
var statusCmd = &cobra.Command{
Use: "status",
Short: "Query the status of the node",
RunE: commands.RequireInit(runStatus),
}
func runStatus(cmd *cobra.Command, args []string) error {
c := commands.GetNode()
status, err := c.Status()
if err != nil {
return err
}
return printResult(status)
}
var infoCmd = &cobra.Command{
Use: "info",
Short: "Query info on the abci app",
RunE: commands.RequireInit(runInfo),
}
func runInfo(cmd *cobra.Command, args []string) error {
c := commands.GetNode()
info, err := c.ABCIInfo()
if err != nil {
return err
}
return printResult(info)
}
var genesisCmd = &cobra.Command{
Use: "genesis",
Short: "Query the genesis of the node",
RunE: commands.RequireInit(runGenesis),
}
func runGenesis(cmd *cobra.Command, args []string) error {
c := commands.GetNode()
genesis, err := c.Genesis()
if err != nil {
return err
}
return printResult(genesis)
}
var validatorsCmd = &cobra.Command{
Use: "validators",
Short: "Query the validators of the node",
RunE: commands.RequireInit(runValidators),
}
func runValidators(cmd *cobra.Command, args []string) error {
c := commands.GetNode()
validators, err := c.Validators(nil)
if err != nil {
return err
}
return printResult(validators)
}

View File

@ -1,64 +0,0 @@
package rpc
import (
"fmt"
"github.com/spf13/cobra"
"github.com/tendermint/go-wire/data"
rpcclient "github.com/tendermint/tendermint/rpc/client"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/commands"
)
const (
FlagDelta = "delta"
FlagHeight = "height"
FlagMax = "max"
FlagMin = "min"
)
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
Use: "rpc",
Short: "Query the tendermint rpc, validating everything with a proof",
}
// TODO: add support for subscribing to events????
func init() {
RootCmd.AddCommand(
statusCmd,
infoCmd,
genesisCmd,
validatorsCmd,
blockCmd,
commitCmd,
headersCmd,
waitCmd,
)
}
func getSecureNode() (rpcclient.Client, error) {
// First, connect a client
c := commands.GetNode()
cert, err := commands.GetCertifier()
if err != nil {
return nil, err
}
return client.SecureClient(c, cert), nil
}
// printResult just writes the struct to the console, returns an error if it can't
func printResult(res interface{}) error {
// TODO: handle text mode
// switch viper.Get(cli.OutputFlag) {
// case "text":
// case "json":
json, err := data.ToJSON(res)
if err != nil {
return err
}
fmt.Println(string(json))
return nil
}

View File

@ -1,76 +0,0 @@
package rpc
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client/commands"
)
func init() {
blockCmd.Flags().Int(FlagHeight, -1, "block height")
commitCmd.Flags().Int(FlagHeight, -1, "block height")
headersCmd.Flags().Int(FlagMin, -1, "minimum block height")
headersCmd.Flags().Int(FlagMax, -1, "maximum block height")
}
var blockCmd = &cobra.Command{
Use: "block",
Short: "Get a validated block at a given height",
RunE: commands.RequireInit(runBlock),
}
func runBlock(cmd *cobra.Command, args []string) error {
c, err := getSecureNode()
if err != nil {
return err
}
h := viper.GetInt(FlagHeight)
block, err := c.Block(&h)
if err != nil {
return err
}
return printResult(block)
}
var commitCmd = &cobra.Command{
Use: "commit",
Short: "Get the header and commit signature at a given height",
RunE: commands.RequireInit(runCommit),
}
func runCommit(cmd *cobra.Command, args []string) error {
c, err := getSecureNode()
if err != nil {
return err
}
h := viper.GetInt(FlagHeight)
commit, err := c.Commit(&h)
if err != nil {
return err
}
return printResult(commit)
}
var headersCmd = &cobra.Command{
Use: "headers",
Short: "Get all headers in the given height range",
RunE: commands.RequireInit(runHeaders),
}
func runHeaders(cmd *cobra.Command, args []string) error {
c, err := getSecureNode()
if err != nil {
return err
}
min := viper.GetInt(FlagMin)
max := viper.GetInt(FlagMax)
headers, err := c.BlockchainInfo(min, max)
if err != nil {
return err
}
return printResult(headers)
}

View File

@ -1,250 +0,0 @@
package txs
import (
"bufio"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"github.com/bgentry/speakeasy"
isatty "github.com/mattn/go-isatty"
"github.com/pkg/errors"
"github.com/spf13/viper"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/go-crypto/keys"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/go-wire/data"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/client/commands"
keycmd "github.com/cosmos/cosmos-sdk/client/commands/keys"
"github.com/cosmos/cosmos-sdk/modules/auth"
)
// Validatable represents anything that can be Validated
type Validatable interface {
ValidateBasic() error
}
// GetSigner returns the pub key that will sign the tx
// returns empty key if no name provided
func GetSigner() crypto.PubKey {
name := viper.GetString(FlagName)
manager := keycmd.GetKeyManager()
info, _ := manager.Get(name) // error -> empty pubkey
return info.PubKey
}
// GetSignerAct returns the address of the signer of the tx
// (as we still only support single sig)
func GetSignerAct() (res sdk.Actor) {
// this could be much cooler with multisig...
signer := GetSigner()
if !signer.Empty() {
res = auth.SigPerm(signer.Address())
}
return res
}
// DoTx is a helper function for the lazy :)
//
// It uses only public functions and goes through the standard sequence of
// wrapping the tx with middleware layers, signing it, either preparing it,
// or posting it and displaying the result.
//
// If you want a non-standard flow, just call the various functions directly.
// eg. if you already set the middleware layers in your code, or want to
// output in another format.
func DoTx(tx interface{}) (err error) {
tx, err = Middleware.Wrap(tx)
if err != nil {
return err
}
err = SignTx(tx)
if err != nil {
return err
}
bres, err := PrepareOrPostTx(tx)
if err != nil {
return err
}
if bres == nil {
return nil // successful prep, nothing left to do
}
return OutputTx(bres) // print response of the post
}
// SignTx will validate the tx, and signs it if it is wrapping a Signable.
// Modifies tx in place, and returns an error if it should sign but couldn't
func SignTx(tx interface{}) (err error) {
// TODO: validate tx client-side
// err := tx.ValidateBasic()
// if err != nil {
// return err
// }
// abort early if we don't want to sign
if viper.GetBool(FlagNoSign) {
return nil
}
name := viper.GetString(FlagName)
manager := keycmd.GetKeyManager()
if sign, ok := tx.(keys.Signable); ok {
// TODO: allow us not to sign? if so then what use?
if name == "" {
return errors.New("--name is required to sign tx")
}
err = signTx(manager, sign, name)
}
return err
}
// PrepareOrPostTx checks the flags to decide to prepare the tx for future
// multisig, or to post it to the node. Returns error on any failure.
// If no error and the result is nil, it means it already wrote to file,
// no post, no need to do more.
func PrepareOrPostTx(tx interface{}) (*ctypes.ResultBroadcastTxCommit, error) {
wrote, err := PrepareTx(tx)
// error in prep
if err != nil {
return nil, err
}
// successfully wrote the tx!
if wrote {
return nil, nil
}
// or try to post it
return PostTx(tx)
}
// PrepareTx checks for FlagPrepare and if set, write the tx as json
// to the specified location for later multi-sig. Returns true if it
// handled the tx (no futher work required), false if it did nothing
// (and we should post the tx)
func PrepareTx(tx interface{}) (bool, error) {
prep := viper.GetString(FlagPrepare)
if prep == "" {
return false, nil
}
js, err := data.ToJSON(tx)
if err != nil {
return false, err
}
err = writeOutput(prep, js)
if err != nil {
return false, err
}
return true, nil
}
// PostTx does all work once we construct a proper struct
// it validates the data, signs if needed, transforms to bytes,
// and posts to the node.
func PostTx(tx interface{}) (*ctypes.ResultBroadcastTxCommit, error) {
packet := wire.BinaryBytes(tx)
// post the bytes
node := commands.GetNode()
return node.BroadcastTxCommit(packet)
}
// OutputTx validates if success and prints the tx result to stdout
func OutputTx(res *ctypes.ResultBroadcastTxCommit) error {
if res.CheckTx.IsErr() {
return errors.Errorf("CheckTx: (%d): %s", res.CheckTx.Code, res.CheckTx.Log)
}
if res.DeliverTx.IsErr() {
return errors.Errorf("DeliverTx: (%d): %s", res.DeliverTx.Code, res.DeliverTx.Log)
}
js, err := json.MarshalIndent(res, "", " ")
if err != nil {
return err
}
fmt.Println(string(js))
return nil
}
func signTx(manager keys.Manager, tx keys.Signable, name string) error {
prompt := fmt.Sprintf("Please enter passphrase for %s: ", name)
pass, err := getPassword(prompt)
if err != nil {
return err
}
return manager.Sign(name, pass, tx)
}
// if we read from non-tty, we just need to init the buffer reader once,
// in case we try to read multiple passwords
var buf *bufio.Reader
func inputIsTty() bool {
return isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd())
}
func stdinPassword() (string, error) {
if buf == nil {
buf = bufio.NewReader(os.Stdin)
}
pass, err := buf.ReadString('\n')
if err != nil {
return "", err
}
return strings.TrimSpace(pass), nil
}
func getPassword(prompt string) (pass string, err error) {
if inputIsTty() {
pass, err = speakeasy.Ask(prompt)
} else {
pass, err = stdinPassword()
}
return
}
func writeOutput(file string, d []byte) error {
var writer io.Writer
if file == "-" {
writer = os.Stdout
} else {
f, err := os.Create(file)
if err != nil {
return errors.WithStack(err)
}
defer f.Close()
writer = f
}
_, err := writer.Write(d)
// this returns nil if err == nil
return errors.WithStack(err)
}
func readInput(file string) ([]byte, error) {
var reader io.Reader
// get the input stream
if file == "" || file == "-" {
reader = os.Stdin
} else {
f, err := os.Open(file)
if err != nil {
return nil, errors.WithStack(err)
}
defer f.Close()
reader = f
}
// and read it all!
data, err := ioutil.ReadAll(reader)
return data, errors.WithStack(err)
}

View File

@ -1,61 +0,0 @@
package txs
import (
"encoding/json"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// nolint
const (
FlagName = "name"
FlagNoSign = "no-sign"
FlagIn = "in"
FlagPrepare = "prepare"
)
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
Use: "tx",
Short: "Post tx from json input",
RunE: doRawTx,
}
func init() {
RootCmd.PersistentFlags().String(FlagName, "", "name to sign the tx")
RootCmd.PersistentFlags().Bool(FlagNoSign, false, "don't add a signature")
RootCmd.PersistentFlags().String(FlagPrepare, "", "file to store prepared tx")
RootCmd.Flags().String(FlagIn, "", "file with tx in json format")
}
func doRawTx(cmd *cobra.Command, args []string) error {
raw, err := readInput(viper.GetString(FlagIn))
if err != nil {
return err
}
// parse the input
var tx interface{}
err = json.Unmarshal(raw, &tx)
if err != nil {
return errors.WithStack(err)
}
// sign it
err = SignTx(tx)
if err != nil {
return err
}
// otherwise, post it and display response
bres, err := PrepareOrPostTx(tx)
if err != nil {
return err
}
if bres == nil {
return nil // successful prep, nothing left to do
}
return OutputTx(bres) // print response of the post
}

View File

@ -1,43 +0,0 @@
package txs
import (
"github.com/spf13/pflag"
)
var (
// Middleware must be set in main.go to defined the wrappers we should apply
Middleware Wrapper
)
// Wrapper defines the information needed for each middleware package that
// wraps the data. They should read all configuration out of bounds via viper.
type Wrapper interface {
Wrap(interface{}) (interface{}, error)
Register(*pflag.FlagSet)
}
// Wrappers combines a list of wrapper middlewares.
// The first one is the inner-most layer, eg. Fee, Nonce, Chain, Auth
type Wrappers []Wrapper
var _ Wrapper = Wrappers{}
// Wrap applies the wrappers to the passed in tx in order,
// aborting on the first error
func (ws Wrappers) Wrap(tx interface{}) (interface{}, error) {
var err error
for _, w := range ws {
tx, err = w.Wrap(tx)
if err != nil {
break
}
}
return tx, err
}
// Register adds any needed flags to the command
func (ws Wrappers) Register(fs *pflag.FlagSet) {
for _, w := range ws {
w.Register(fs)
}
}

View File

@ -1,21 +0,0 @@
package commands
import (
"fmt"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/version"
)
// CommitHash should be filled by linker flags
var CommitHash = ""
// VersionCmd - command to show the application version
var VersionCmd = &cobra.Command{
Use: "version",
Short: "Show version info",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("%s-%s\n", version.Version, CommitHash)
},
}

View File

@ -1,57 +0,0 @@
package client
import (
"errors"
"github.com/tendermint/light-client/certifiers"
certclient "github.com/tendermint/light-client/certifiers/client"
certerr "github.com/tendermint/light-client/certifiers/errors"
"github.com/tendermint/light-client/certifiers/files"
"github.com/tendermint/light-client/proofs"
rpcclient "github.com/tendermint/tendermint/rpc/client"
)
// GetNode prepares a simple rpc.Client for the given endpoint
func GetNode(url string) rpcclient.Client {
return rpcclient.NewHTTP(url, "/websocket")
}
// GetRPCProvider retuns a certifier compatible data source using
// tendermint RPC
func GetRPCProvider(url string) certifiers.Provider {
return certclient.NewHTTPProvider(url)
}
// GetLocalProvider returns a reference to a file store of headers
// wrapped with an in-memory cache
func GetLocalProvider(dir string) certifiers.Provider {
return certifiers.NewCacheProvider(
certifiers.NewMemStoreProvider(),
files.NewProvider(dir),
)
}
// GetCertifier initializes an inquiring certifier given a fixed chainID
// and a local source of trusted data with at least one seed
func GetCertifier(chainID string, trust certifiers.Provider,
source certifiers.Provider) (*certifiers.Inquiring, error) {
// this gets the most recent verified commit
fc, err := trust.LatestCommit()
if certerr.IsCommitNotFoundErr(err) {
return nil, errors.New("Please run init first to establish a root of trust")
}
if err != nil {
return nil, err
}
cert := certifiers.NewInquiring(chainID, fc, trust, source)
return cert, nil
}
// SecureClient uses a given certifier to wrap an connection to an untrusted
// host and return a cryptographically secure rpc client.
func SecureClient(c rpcclient.Client, cert *certifiers.Inquiring) rpcclient.Client {
return proofs.Wrap(c, cert)
}

View File

@ -1,22 +0,0 @@
package client
import (
"fmt"
"github.com/pkg/errors"
)
//--------------------------------------------
var errNoData = fmt.Errorf("No data returned for query")
// IsNoDataErr checks whether an error is due to a query returning empty data
func IsNoDataErr(err error) bool {
return errors.Cause(err) == errNoData
}
func ErrNoData() error {
return errors.WithStack(errNoData)
}
//--------------------------------------------

View File

@ -1,18 +0,0 @@
package client
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
)
func TestErrorNoData(t *testing.T) {
e1 := ErrNoData()
e1.Error()
assert.True(t, IsNoDataErr(e1))
e2 := errors.New("foobar")
assert.False(t, IsNoDataErr(e2))
assert.False(t, IsNoDataErr(nil))
}

View File

@ -1,30 +0,0 @@
package client
/*
import (
"path/filepath"
"github.com/tendermint/go-crypto/keys"
"github.com/tendermint/go-crypto/keys/cryptostore"
"github.com/tendermint/go-crypto/keys/storage/filestorage"
)
// KeySubdir is the directory name under root where we store the keys
const KeySubdir = "keys"
// GetKeyManager initializes a key manager based on the configuration
func GetKeyManager(rootDir string) keys.Manager {
keyDir := filepath.Join(rootDir, KeySubdir)
// TODO: smarter loading??? with language and fallback?
codec := keys.MustLoadCodec("english")
// and construct the key manager
manager := cryptostore.New(
cryptostore.SecretBox,
filestorage.New(keyDir),
codec,
)
return manager
}
*/

View File

@ -1,68 +0,0 @@
package client
import (
"net/http"
"github.com/tendermint/tmlibs/log"
rpcclient "github.com/tendermint/tendermint/rpc/client"
"github.com/tendermint/tendermint/rpc/core"
rpc "github.com/tendermint/tendermint/rpc/lib/server"
)
const (
wsEndpoint = "/websocket"
)
// StartProxy will start the websocket manager on the client,
// set up the rpc routes to proxy via the given client,
// and start up an http/rpc server on the location given by bind (eg. :1234)
func StartProxy(c rpcclient.Client, bind string, logger log.Logger) error {
c.Start()
r := RPCRoutes(c)
// build the handler...
mux := http.NewServeMux()
rpc.RegisterRPCFuncs(mux, r, logger)
wm := rpc.NewWebsocketManager(r, c)
wm.SetLogger(logger)
core.SetLogger(logger)
mux.HandleFunc(wsEndpoint, wm.WebsocketHandler)
_, err := rpc.StartHTTPServer(bind, mux, logger)
return err
}
// RPCRoutes just routes everything to the given client, as if it were
// a tendermint fullnode.
//
// if we want security, the client must implement it as a secure client
func RPCRoutes(c rpcclient.Client) map[string]*rpc.RPCFunc {
return map[string]*rpc.RPCFunc{
// Subscribe/unsubscribe are reserved for websocket events.
// We can just use the core tendermint impl, which uses the
// EventSwitch we registered in NewWebsocketManager above
"subscribe": rpc.NewWSRPCFunc(core.Subscribe, "event"),
"unsubscribe": rpc.NewWSRPCFunc(core.Unsubscribe, "event"),
// info API
"status": rpc.NewRPCFunc(c.Status, ""),
"blockchain": rpc.NewRPCFunc(c.BlockchainInfo, "minHeight,maxHeight"),
"genesis": rpc.NewRPCFunc(c.Genesis, ""),
"block": rpc.NewRPCFunc(c.Block, "height"),
"commit": rpc.NewRPCFunc(c.Commit, "height"),
"tx": rpc.NewRPCFunc(c.Tx, "hash,prove"),
"validators": rpc.NewRPCFunc(c.Validators, ""),
// broadcast API
"broadcast_tx_commit": rpc.NewRPCFunc(c.BroadcastTxCommit, "tx"),
"broadcast_tx_sync": rpc.NewRPCFunc(c.BroadcastTxSync, "tx"),
"broadcast_tx_async": rpc.NewRPCFunc(c.BroadcastTxAsync, "tx"),
// abci API
"abci_query": rpc.NewRPCFunc(c.ABCIQuery, "path,data,prove"),
"abci_info": rpc.NewRPCFunc(c.ABCIInfo, ""),
}
}

View File

@ -1,116 +0,0 @@
package client
import (
"github.com/pkg/errors"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/iavl"
"github.com/tendermint/light-client/certifiers"
certerr "github.com/tendermint/light-client/certifiers/errors"
"github.com/tendermint/tendermint/rpc/client"
)
// GetWithProof will query the key on the given node, and verify it has
// a valid proof, as defined by the certifier.
//
// If there is any error in checking, returns an error.
// If val is non-empty, proof should be KeyExistsProof
// If val is empty, proof should be KeyMissingProof
func GetWithProof(key []byte, reqHeight int, node client.Client,
cert certifiers.Certifier) (
val data.Bytes, height uint64, proof iavl.KeyProof, err error) {
if reqHeight < 0 {
err = errors.Errorf("Height cannot be negative")
return
}
resp, err := node.ABCIQueryWithOptions("/key", key,
client.ABCIQueryOptions{Height: uint64(reqHeight)})
if err != nil {
return
}
// make sure the proof is the proper height
if !resp.Code.IsOK() {
err = errors.Errorf("Query error %d: %s", resp.Code, resp.Code.String())
return
}
if len(resp.Key) == 0 || len(resp.Proof) == 0 {
err = ErrNoData()
return
}
if resp.Height == 0 {
err = errors.New("Height returned is zero")
return
}
// AppHash for height H is in header H+1
var commit *certifiers.Commit
commit, err = GetCertifiedCommit(int(resp.Height+1), node, cert)
if err != nil {
return
}
if len(resp.Value) > 0 {
// The key was found, construct a proof of existence.
var eproof *iavl.KeyExistsProof
eproof, err = iavl.ReadKeyExistsProof(resp.Proof)
if err != nil {
err = errors.Wrap(err, "Error reading proof")
return
}
// Validate the proof against the certified header to ensure data integrity.
err = eproof.Verify(resp.Key, resp.Value, commit.Header.AppHash)
if err != nil {
err = errors.Wrap(err, "Couldn't verify proof")
return
}
val = data.Bytes(resp.Value)
proof = eproof
} else {
// The key wasn't found, construct a proof of non-existence.
var aproof *iavl.KeyAbsentProof
aproof, err = iavl.ReadKeyAbsentProof(resp.Proof)
if err != nil {
err = errors.Wrap(err, "Error reading proof")
return
}
// Validate the proof against the certified header to ensure data integrity.
err = aproof.Verify(resp.Key, nil, commit.Header.AppHash)
if err != nil {
err = errors.Wrap(err, "Couldn't verify proof")
return
}
err = ErrNoData()
proof = aproof
}
height = resp.Height
return
}
// GetCertifiedCommit gets the signed header for a given height
// and certifies it. Returns error if unable to get a proven header.
func GetCertifiedCommit(h int, node client.Client,
cert certifiers.Certifier) (empty *certifiers.Commit, err error) {
// FIXME: cannot use cert.GetByHeight for now, as it also requires
// Validators and will fail on querying tendermint for non-current height.
// When this is supported, we should use it instead...
client.WaitForHeight(node, h, nil)
cresp, err := node.Commit(&h)
if err != nil {
return
}
commit := certifiers.CommitFromResult(cresp)
// validate downloaded checkpoint with our request and trust store.
if commit.Height() != h {
return empty, certerr.ErrHeightMismatch(h, commit.Height())
}
err = cert.Certify(commit)
return commit, nil
}

View File

@ -1,148 +0,0 @@
package client
import (
"fmt"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/go-wire"
"github.com/tendermint/light-client/certifiers"
certclient "github.com/tendermint/light-client/certifiers/client"
"github.com/tendermint/tmlibs/log"
nm "github.com/tendermint/tendermint/node"
"github.com/tendermint/tendermint/rpc/client"
rpctest "github.com/tendermint/tendermint/rpc/test"
"github.com/tendermint/tendermint/types"
sdkapp "github.com/cosmos/cosmos-sdk/app"
"github.com/cosmos/cosmos-sdk/modules/eyes"
)
var node *nm.Node
func TestMain(m *testing.M) {
logger := log.TestingLogger()
store, err := sdkapp.MockStoreApp("query", logger)
if err != nil {
panic(err)
}
app := sdkapp.NewBaseApp(store, eyes.NewHandler(), nil)
node = rpctest.StartTendermint(app)
code := m.Run()
node.Stop()
node.Wait()
os.Exit(code)
}
func TestAppProofs(t *testing.T) {
assert, require := assert.New(t), require.New(t)
cl := client.NewLocal(node)
client.WaitForHeight(cl, 1, nil)
k := []byte("my-key")
v := []byte("my-value")
tx := eyes.SetTx{Key: k, Value: v}.Wrap()
btx := wire.BinaryBytes(tx)
br, err := cl.BroadcastTxCommit(btx)
require.NoError(err, "%+v", err)
require.EqualValues(0, br.CheckTx.Code, "%#v", br.CheckTx)
require.EqualValues(0, br.DeliverTx.Code)
brh := br.Height
// This sets up our trust on the node based on some past point.
source := certclient.NewProvider(cl)
seed, err := source.GetByHeight(br.Height - 2)
require.NoError(err, "%+v", err)
cert := certifiers.NewStatic("my-chain", seed.Validators)
client.WaitForHeight(cl, 3, nil)
latest, err := source.LatestCommit()
require.NoError(err, "%+v", err)
rootHash := latest.Header.AppHash
// Test existing key.
var data eyes.Data
// verify a query before the tx block has no data (and valid non-exist proof)
bs, height, proof, err := GetWithProof(k, brh-1, cl, cert)
require.NotNil(err)
require.True(IsNoDataErr(err))
require.Nil(bs)
// but given that block it is good
bs, height, proof, err = GetWithProof(k, brh, cl, cert)
require.NoError(err, "%+v", err)
require.NotNil(proof)
require.True(height >= uint64(latest.Header.Height))
// Alexis there is a bug here, somehow the above code gives us rootHash = nil
// and proof.Verify doesn't care, while proofNotExists.Verify fails.
// I am hacking this in to make it pass, but please investigate further.
rootHash = proof.Root()
err = wire.ReadBinaryBytes(bs, &data)
require.NoError(err, "%+v", err)
assert.EqualValues(v, data.Value)
err = proof.Verify(k, bs, rootHash)
assert.NoError(err, "%+v", err)
// Test non-existing key.
missing := []byte("my-missing-key")
bs, _, proof, err = GetWithProof(missing, 0, cl, cert)
require.True(IsNoDataErr(err))
require.Nil(bs)
require.NotNil(proof)
err = proof.Verify(missing, nil, rootHash)
assert.NoError(err, "%+v", err)
err = proof.Verify(k, nil, rootHash)
assert.Error(err)
}
func TestTxProofs(t *testing.T) {
assert, require := assert.New(t), require.New(t)
cl := client.NewLocal(node)
client.WaitForHeight(cl, 1, nil)
tx := eyes.NewSetTx([]byte("key-a"), []byte("value-a"))
btx := types.Tx(wire.BinaryBytes(tx))
br, err := cl.BroadcastTxCommit(btx)
require.NoError(err, "%+v", err)
require.EqualValues(0, br.CheckTx.Code, "%#v", br.CheckTx)
require.EqualValues(0, br.DeliverTx.Code)
fmt.Printf("tx height: %d\n", br.Height)
source := certclient.NewProvider(cl)
seed, err := source.GetByHeight(br.Height - 2)
require.NoError(err, "%+v", err)
cert := certifiers.NewStatic("my-chain", seed.Validators)
// First let's make sure a bogus transaction hash returns a valid non-existence proof.
key := types.Tx([]byte("bogus")).Hash()
res, err := cl.Tx(key, true)
require.NotNil(err)
require.Contains(err.Error(), "not found")
// Now let's check with the real tx hash.
key = btx.Hash()
res, err = cl.Tx(key, true)
require.NoError(err, "%+v", err)
require.NotNil(res)
err = res.Proof.Validate(key)
assert.NoError(err, "%+v", err)
commit, err := GetCertifiedCommit(int(br.Height), cl, cert)
require.Nil(err, "%+v", err)
require.Equal(res.Proof.RootHash, commit.Header.DataHash)
}

View File

@ -1,25 +0,0 @@
## basecoin-server
### Proxy server
This package exposes access to key management i.e
- creating
- listing
- updating
- deleting
The HTTP handlers can be embedded in a larger server that
does things like signing transactions and posting them to a
Tendermint chain (which requires domain-knowledge of the transaction
types and is out of scope of this generic app).
### Key Management
We expose a couple of methods for safely managing your keychain.
If you are embedding this in a larger server, you will typically
want to mount all these paths /keys.
HTTP Method | Route | Description
---|---|---
POST|/|Requires a name and passphrase to create a brand new key
GET|/|Retrieves the list of all available key names, along with their public key and address
GET|/{name} | Updates the passphrase for the given key. It requires you to correctly provide the current passphrase, as well as a new one.
DELETE|/{name} | Permanently delete this private key. It requires you to correctly provide the current passphrase.

View File

@ -1,192 +0,0 @@
package rest
import (
"net/http"
"github.com/gorilla/mux"
"github.com/pkg/errors"
keys "github.com/tendermint/go-crypto/keys"
"github.com/tendermint/tmlibs/common"
sdk "github.com/cosmos/cosmos-sdk"
keycmd "github.com/cosmos/cosmos-sdk/client/commands/keys"
)
type Keys struct {
algo string
manager keys.Manager
}
func DefaultKeysManager() keys.Manager {
return keycmd.GetKeyManager()
}
func NewDefaultKeysManager(algo string) *Keys {
return New(DefaultKeysManager(), algo)
}
func New(manager keys.Manager, algo string) *Keys {
return &Keys{
algo: algo,
manager: manager,
}
}
func (k *Keys) GenerateKey(w http.ResponseWriter, r *http.Request) {
ckReq := &CreateKeyRequest{
Algo: k.algo,
}
if err := common.ParseRequestAndValidateJSON(r, ckReq); err != nil {
common.WriteError(w, err)
return
}
key, seed, err := k.manager.Create(ckReq.Name, ckReq.Passphrase, ckReq.Algo)
if err != nil {
common.WriteError(w, err)
return
}
res := &CreateKeyResponse{Key: key, Seed: seed}
common.WriteSuccess(w, res)
}
func (k *Keys) GetKey(w http.ResponseWriter, r *http.Request) {
query := mux.Vars(r)
name := query["name"]
key, err := k.manager.Get(name)
if err != nil {
common.WriteError(w, err)
return
}
common.WriteSuccess(w, &key)
}
func (k *Keys) ListKeys(w http.ResponseWriter, r *http.Request) {
keys, err := k.manager.List()
if err != nil {
common.WriteError(w, err)
return
}
common.WriteSuccess(w, keys)
}
var (
errNonMatchingPathAndJSONKeyNames = errors.New("path and json key names don't match")
)
func (k *Keys) UpdateKey(w http.ResponseWriter, r *http.Request) {
uReq := new(UpdateKeyRequest)
if err := common.ParseRequestAndValidateJSON(r, uReq); err != nil {
common.WriteError(w, err)
return
}
query := mux.Vars(r)
name := query["name"]
if name != uReq.Name {
common.WriteError(w, errNonMatchingPathAndJSONKeyNames)
return
}
if err := k.manager.Update(uReq.Name, uReq.OldPass, uReq.NewPass); err != nil {
common.WriteError(w, err)
return
}
key, err := k.manager.Get(uReq.Name)
if err != nil {
common.WriteError(w, err)
return
}
common.WriteSuccess(w, &key)
}
func (k *Keys) DeleteKey(w http.ResponseWriter, r *http.Request) {
dReq := new(DeleteKeyRequest)
if err := common.ParseRequestAndValidateJSON(r, dReq); err != nil {
common.WriteError(w, err)
return
}
query := mux.Vars(r)
name := query["name"]
if name != dReq.Name {
common.WriteError(w, errNonMatchingPathAndJSONKeyNames)
return
}
if err := k.manager.Delete(dReq.Name, dReq.Passphrase); err != nil {
common.WriteError(w, err)
return
}
resp := &common.ErrorResponse{Success: true}
common.WriteSuccess(w, resp)
}
func doPostTx(w http.ResponseWriter, r *http.Request) {
tx := new(sdk.Tx)
if err := common.ParseRequestAndValidateJSON(r, tx); err != nil {
common.WriteError(w, err)
return
}
commit, err := PostTx(*tx)
if err != nil {
common.WriteError(w, err)
return
}
common.WriteSuccess(w, commit)
}
func doSign(w http.ResponseWriter, r *http.Request) {
sr := new(SignRequest)
if err := common.ParseRequestAndValidateJSON(r, sr); err != nil {
common.WriteError(w, err)
return
}
tx := sr.Tx
if err := SignTx(sr.Name, sr.Password, tx); err != nil {
common.WriteError(w, err)
return
}
common.WriteSuccess(w, tx)
}
// mux.Router registrars
// RegisterPostTx is a mux.Router handler that exposes POST
// method access to post a transaction to the blockchain.
func RegisterPostTx(r *mux.Router) error {
r.HandleFunc("/tx", doPostTx).Methods("POST")
return nil
}
// RegisterAllCRUD is a convenience method to register all
// CRUD for keys to allow access by methods and routes:
// POST: /keys
// GET: /keys
// GET: /keys/{name}
// POST, PUT: /keys/{name}
// DELETE: /keys/{name}
func (k *Keys) RegisterAllCRUD(r *mux.Router) error {
r.HandleFunc("/keys", k.GenerateKey).Methods("POST")
r.HandleFunc("/keys", k.ListKeys).Methods("GET")
r.HandleFunc("/keys/{name}", k.GetKey).Methods("GET")
r.HandleFunc("/keys/{name}", k.UpdateKey).Methods("POST", "PUT")
r.HandleFunc("/keys/{name}", k.DeleteKey).Methods("DELETE")
return nil
}
// RegisterSignTx is a mux.Router handler that
// exposes POST method access to sign a transaction.
func RegisterSignTx(r *mux.Router) error {
r.HandleFunc("/sign", doSign).Methods("POST")
return nil
}
// End of mux.Router registrars

View File

@ -1,29 +0,0 @@
package rest
import (
"github.com/tendermint/go-crypto/keys"
wire "github.com/tendermint/go-wire"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/client/commands"
keycmd "github.com/cosmos/cosmos-sdk/client/commands/keys"
)
// PostTx is same as a tx
func PostTx(tx sdk.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
packet := wire.BinaryBytes(tx)
// post the bytes
node := commands.GetNode()
return node.BroadcastTxCommit(packet)
}
// SignTx will modify the tx in-place, adding a signature if possible
func SignTx(name, pass string, tx sdk.Tx) error {
if sign, ok := tx.Unwrap().(keys.Signable); ok {
manager := keycmd.GetKeyManager()
return manager.Sign(name, pass, sign)
}
return nil
}

View File

@ -1,52 +0,0 @@
package rest
import (
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/modules/coin"
"github.com/tendermint/go-crypto/keys"
)
type CreateKeyRequest struct {
Name string `json:"name,omitempty" validate:"required,min=3,printascii"`
Passphrase string `json:"password,omitempty" validate:"required,min=10"`
// Algo is the requested algorithm to create the key
Algo string `json:"algo,omitempty"`
}
type DeleteKeyRequest struct {
Name string `json:"name,omitempty" validate:"required,min=3,printascii"`
Passphrase string `json:"password,omitempty" validate:"required,min=10"`
}
type UpdateKeyRequest struct {
Name string `json:"name,omitempty" validate:"required,min=3,printascii"`
OldPass string `json:"password,omitempty" validate:"required,min=10"`
NewPass string `json:"new_passphrase,omitempty" validate:"required,min=10"`
}
type SignRequest struct {
Name string `json:"name,omitempty" validate:"required,min=3,printascii"`
Password string `json:"password,omitempty" validate:"required,min=10"`
Tx sdk.Tx `json:"tx" validate:"required"`
}
type CreateKeyResponse struct {
Key keys.Info `json:"key,omitempty"`
Seed string `json:"seed_phrase,omitempty"`
}
// SendInput is the request to send an amount from one actor to another.
// Note: Not using the `validator:""` tags here because SendInput has
// many fields so it would be nice to figure out all the invalid
// inputs and report them back to the caller, in one shot.
type SendInput struct {
Fees *coin.Coin `json:"fees"`
Multi bool `json:"multi,omitempty"`
Sequence uint32 `json:"sequence"`
To *sdk.Actor `json:"to"`
From *sdk.Actor `json:"from"`
Amount coin.Coins `json:"amount"`
}

View File

@ -1,46 +0,0 @@
package coin
import (
"testing"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/stack"
"github.com/cosmos/cosmos-sdk/state"
)
func makeHandler() stack.Dispatchable {
return NewHandler()
}
func makeSimpleTx(from, to sdk.Actor, amount Coins) sdk.Tx {
in := []TxInput{{Address: from, Coins: amount}}
out := []TxOutput{{Address: to, Coins: amount}}
return NewSendTx(in, out)
}
func BenchmarkSimpleTransfer(b *testing.B) {
h := makeHandler()
store := state.NewMemKVStore()
logger := log.NewNopLogger()
// set the initial account
acct := NewAccountWithKey(Coins{{"mycoin", 1234567890}})
h.InitState(logger, store, NameCoin, "account", acct.MakeOption(), nil)
sender := acct.Actor()
receiver := sdk.Actor{App: "foo", Address: cmn.RandBytes(20)}
// now, loop...
for i := 1; i <= b.N; i++ {
ctx := stack.MockContext("foo", 100).WithPermissions(sender)
tx := makeSimpleTx(sender, receiver, Coins{{"mycoin", 2}})
_, err := h.DeliverTx(ctx, store, tx, nil)
// never should error
if err != nil {
panic(err)
}
}
}

View File

@ -1,44 +0,0 @@
package commands
import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/client/commands/query"
"github.com/cosmos/cosmos-sdk/x/coin"
"github.com/cosmos/cosmos-sdk/stack"
)
// AccountQueryCmd - command to query an account
var AccountQueryCmd = &cobra.Command{
Use: "account [address]",
Short: "Get details of an account, with proof",
RunE: commands.RequireInit(accountQueryCmd),
}
func accountQueryCmd(cmd *cobra.Command, args []string) error {
addr, err := commands.GetOneArg(args, "address")
if err != nil {
return err
}
act, err := commands.ParseActor(addr)
if err != nil {
return err
}
act = coin.ChainAddr(act)
key := stack.PrefixedKey(coin.NameCoin, act.Bytes())
acc := coin.Account{}
prove := !viper.GetBool(commands.FlagTrustNode)
height, err := query.GetParsed(key, &acc, query.GetHeight(), prove)
if client.IsNoDataErr(err) {
return errors.Errorf("Account bytes are empty for address %s ", addr)
} else if err != nil {
return err
}
return query.OutputProof(acc, height)
}

View File

@ -1,105 +0,0 @@
package commands
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/client/commands"
txcmd "github.com/cosmos/cosmos-sdk/client/commands/txs"
"github.com/cosmos/cosmos-sdk/x/coin"
)
// SendTxCmd is CLI command to send tokens between basecoin accounts
var SendTxCmd = &cobra.Command{
Use: "send",
Short: "send tokens from one account to another",
RunE: commands.RequireInit(sendTxCmd),
}
// CreditTxCmd is CLI command to issue credit to one account
var CreditTxCmd = &cobra.Command{
Use: "credit",
Short: "issue credit to one account",
RunE: commands.RequireInit(creditTxCmd),
}
//nolint
const (
FlagTo = "to"
FlagAmount = "amount"
FlagFrom = "from"
)
func init() {
flags := SendTxCmd.Flags()
flags.String(FlagTo, "", "Destination address for the bits")
flags.String(FlagAmount, "", "Coins to send in the format <amt><coin>,<amt><coin>...")
flags.String(FlagFrom, "", "Address sending coins, if not first signer")
fs2 := CreditTxCmd.Flags()
fs2.String(FlagTo, "", "Destination address for the bits")
fs2.String(FlagAmount, "", "Coins to send in the format <amt><coin>,<amt><coin>...")
}
func sendTxCmd(cmd *cobra.Command, args []string) error {
tx, err := readSendTxFlags()
if err != nil {
return err
}
return txcmd.DoTx(tx)
}
func readSendTxFlags() (tx sdk.Tx, err error) {
// parse to address
toAddr, err := commands.ParseActor(viper.GetString(FlagTo))
if err != nil {
return tx, err
}
fromAddr, err := readFromAddr()
if err != nil {
return tx, err
}
amountCoins, err := coin.ParseCoins(viper.GetString(FlagAmount))
if err != nil {
return tx, err
}
// craft the inputs and outputs
tx = coin.NewSendOneTx(fromAddr, toAddr, amountCoins)
return
}
func creditTxCmd(cmd *cobra.Command, args []string) error {
tx, err := readCreditTxFlags()
if err != nil {
return err
}
return txcmd.DoTx(tx)
}
func readCreditTxFlags() (tx sdk.Tx, err error) {
// parse to address
toAddr, err := commands.ParseActor(viper.GetString(FlagTo))
if err != nil {
return tx, err
}
amount, err := coin.ParseCoins(viper.GetString(FlagAmount))
if err != nil {
return tx, err
}
tx = coin.CreditTx{Debitor: toAddr, Credit: amount}.Wrap()
return
}
func readFromAddr() (sdk.Actor, error) {
from := viper.GetString(FlagFrom)
if from == "" {
return txcmd.GetSignerAct(), nil
}
return commands.ParseActor(from)
}

View File

@ -1,48 +0,0 @@
package coin
import (
"bytes"
"github.com/pkg/errors"
"github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire/data"
)
/**** code to parse accounts from genesis docs ***/
// GenesisAccount - genesis account parameters
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 types.Coins `json:"coins"`
}
// ToAccount - GenesisAccount struct to a basecoin Account
func (g GenesisAccount) ToAccount() Account {
return Account{
Coins: g.Balance,
}
}
// GetAddr - Get the address of the genesis account
func (g GenesisAccount) GetAddr() ([]byte, error) {
noAddr, noPk := len(g.Address) == 0, g.PubKey.Empty()
if noAddr {
if noPk {
return nil, errors.New("No address given")
}
return g.PubKey.Address(), nil
}
if noPk { // but is addr...
return g.Address, nil
}
// now, we have both, make sure they check out
if bytes.Equal(g.Address, g.PubKey.Address()) {
return g.Address, nil
}
return nil, errors.New("Address and pubkey don't match")
}

View File

@ -1,251 +0,0 @@
package coin
import (
"github.com/tendermint/go-wire/data"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/errors"
"github.com/cosmos/cosmos-sdk/store"
"github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
// "github.com/cosmos/cosmos-sdk/x/ibc"
// "github.com/cosmos/cosmos-sdk/stack"
)
const (
//NameCoin - name space of the coin module
NameCoin = "coin"
// CostSend is GasAllocation per input/output
CostSend = uint64(10)
// CostCredit is GasAllocation of a credit allocation
CostCredit = uint64(20)
)
// Handler includes an accountant
type Handler struct {
// stack.PassInitValidate
}
// var _ stack.Dispatchable = Handler{}
// NewHandler - new accountant handler for the coin module
func NewHandler() Handler {
return Handler{}
}
// Name - return name space
func (Handler) Name() string {
return NameCoin
}
// AssertDispatcher - to fulfill Dispatchable interface
func (Handler) AssertDispatcher() {}
// CheckTx checks if there is enough money in the account
func (h Handler) CheckTx(ctx types.Context, store store.MultiStore,
tx types.Tx, _ sdk.Checker) (res sdk.CheckResult, err error) {
err = tx.ValidateBasic()
if err != nil {
return res, err
}
switch t := tx.Unwrap().(type) {
case SendTx:
// price based on inputs and outputs
used := uint64(len(t.Inputs) + len(t.Outputs))
return sdk.NewCheck(used*CostSend, ""), h.checkSendTx(ctx, store, t)
case CreditTx:
// default price of 20, constant work
return sdk.NewCheck(CostCredit, ""), h.creditTx(ctx, store, t)
}
return res, errors.ErrUnknownTxType(tx.Unwrap())
}
// DeliverTx moves the money
func (h Handler) DeliverTx(ctx types.Context, store store.MultiStore,
tx types.Tx, cb sdk.Deliver) (res sdk.DeliverResult, err error) {
err = tx.ValidateBasic()
if err != nil {
return res, err
}
switch t := tx.Unwrap().(type) {
case SendTx:
return res, h.sendTx(ctx, store, t, cb)
case CreditTx:
return res, h.creditTx(ctx, store, t)
}
return res, errors.ErrUnknownTxType(tx.Unwrap())
}
// InitState - sets the genesis account balance
func (h Handler) InitState(l log.Logger, store store.MultiStore,
module, key, value string, cb sdk.InitStater) (log string, err error) {
if module != NameCoin {
return "", errors.ErrUnknownModule(module)
}
switch key {
case "account":
return setAccount(store, value)
case "issuer":
return setIssuer(store, value)
}
return "", errors.ErrUnknownKey(key)
}
func (h Handler) sendTx(ctx types.Context, store store.MultiStore,
send SendTx, cb sdk.Deliver) error {
err := checkTx(ctx, send)
if err != nil {
return err
}
// deduct from all input accounts
senders := sdk.Actors{}
for _, in := range send.Inputs {
_, err = ChangeCoins(store, in.Address, in.Coins.Negative())
if err != nil {
return err
}
senders = append(senders, in.Address)
}
// add to all output accounts
for _, out := range send.Outputs {
// TODO: cleaner way, this makes sure we don't consider
// incoming ibc packets with our chain to be remote packets
if out.Address.ChainID == ctx.ChainID() {
out.Address.ChainID = ""
}
_, err = ChangeCoins(store, out.Address, out.Coins)
if err != nil {
return err
}
// now send ibc packet if needed...
if out.Address.ChainID != "" {
// FIXME: if there are many outputs, we need to adjust inputs
// so the amounts in and out match. how?
inputs := make([]TxInput, len(send.Inputs))
for i := range send.Inputs {
inputs[i] = send.Inputs[i]
inputs[i].Address = inputs[i].Address.WithChain(ctx.ChainID())
}
outTx := NewSendTx(inputs, []TxOutput{out})
_ = outTx
/* TODO
packet := ibc.CreatePacketTx{
DestChain: out.Address.ChainID,
Permissions: senders,
Tx: outTx,
}
ibcCtx := ctx.WithPermissions(ibc.AllowIBC(NameCoin))
_, err := cb.DeliverTx(ibcCtx, store, packet.Wrap())
if err != nil {
return err
}
*/
}
}
// a-ok!
return nil
}
func (h Handler) creditTx(ctx types.Context, store store.MultiStore,
credit CreditTx) error {
// first check permissions!!
info, err := loadHandlerInfo(store)
if err != nil {
return err
}
if info.Issuer.Empty() || !ctx.HasPermission(info.Issuer) {
return errors.ErrUnauthorized()
}
// load up the account
addr := ChainAddr(credit.Debitor)
acct, err := GetAccount(store, addr)
if err != nil {
return err
}
// make and check changes
acct.Coins = acct.Coins.Plus(credit.Credit)
if !acct.Coins.IsNonnegative() {
return ErrInsufficientFunds()
}
acct.Credit = acct.Credit.Plus(credit.Credit)
if !acct.Credit.IsNonnegative() {
return ErrInsufficientCredit()
}
err = storeAccount(store, addr.Bytes(), acct)
return err
}
func checkTx(ctx types.Context, send SendTx) error {
// check if all inputs have permission
for _, in := range send.Inputs {
if !ctx.HasPermission(in.Address) {
return errors.ErrUnauthorized()
}
}
return nil
}
func (Handler) checkSendTx(ctx types.Context, store store.MultiStore, send SendTx) error {
err := checkTx(ctx, send)
if err != nil {
return err
}
// now make sure there is money
for _, in := range send.Inputs {
_, err := CheckCoins(store, in.Address, in.Coins.Negative())
if err != nil {
return err
}
}
return nil
}
func setAccount(store store.MultiStore, value string) (log string, err error) {
var acc GenesisAccount
err = data.FromJSON([]byte(value), &acc)
if err != nil {
return "", err
}
acc.Balance.Sort()
addr, err := acc.GetAddr()
if err != nil {
return "", ErrInvalidAddress()
}
// this sets the permission for a public key signature, use that app
actor := auth.SigPerm(addr)
err = storeAccount(store, actor.Bytes(), acc.ToAccount())
if err != nil {
return "", err
}
return "Success", nil
}
// setIssuer sets a permission for some super-powerful account to
// mint money
func setIssuer(store store.MultiStore, value string) (log string, err error) {
var issuer sdk.Actor
err = data.FromJSON([]byte(value), &issuer)
if err != nil {
return "", err
}
err = storeIssuer(store, issuer)
if err != nil {
return "", err
}
return "Success", nil
}

View File

@ -1,381 +0,0 @@
package coin
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/errors"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/stack"
"github.com/cosmos/cosmos-sdk/state"
)
// this makes sure that txs are rejected with invalid data or permissions
func TestHandlerValidation(t *testing.T) {
assert := assert.New(t)
// these are all valid, except for minusCoins
addr1 := sdk.Actor{App: "coin", Address: []byte{1, 2}}
addr2 := sdk.Actor{App: "role", Address: []byte{7, 8}}
someCoins := Coins{{"atom", 123}}
doubleCoins := Coins{{"atom", 246}}
minusCoins := Coins{{"eth", -34}}
cases := []struct {
valid bool
tx sdk.Tx
perms []sdk.Actor
}{
// auth works with different apps
{true,
NewSendTx(
[]TxInput{NewTxInput(addr1, someCoins)},
[]TxOutput{NewTxOutput(addr2, someCoins)}),
[]sdk.Actor{addr1}},
{true,
NewSendTx(
[]TxInput{NewTxInput(addr2, someCoins)},
[]TxOutput{NewTxOutput(addr1, someCoins)}),
[]sdk.Actor{addr1, addr2}},
// check multi-input with both sigs
{true,
NewSendTx(
[]TxInput{NewTxInput(addr1, someCoins), NewTxInput(addr2, someCoins)},
[]TxOutput{NewTxOutput(addr1, doubleCoins)}),
[]sdk.Actor{addr1, addr2}},
// wrong permissions fail
{false,
NewSendTx(
[]TxInput{NewTxInput(addr1, someCoins)},
[]TxOutput{NewTxOutput(addr2, someCoins)}),
[]sdk.Actor{}},
{false,
NewSendTx(
[]TxInput{NewTxInput(addr1, someCoins)},
[]TxOutput{NewTxOutput(addr2, someCoins)}),
[]sdk.Actor{addr2}},
{false,
NewSendTx(
[]TxInput{NewTxInput(addr1, someCoins), NewTxInput(addr2, someCoins)},
[]TxOutput{NewTxOutput(addr1, doubleCoins)}),
[]sdk.Actor{addr1}},
// invalid input fails
{false,
NewSendTx(
[]TxInput{NewTxInput(addr1, minusCoins)},
[]TxOutput{NewTxOutput(addr2, minusCoins)}),
[]sdk.Actor{addr2}},
}
for i, tc := range cases {
ctx := stack.MockContext("base-chain", 100).WithPermissions(tc.perms...)
err := checkTx(ctx, tc.tx.Unwrap().(SendTx))
if tc.valid {
assert.Nil(err, "%d: %+v", i, err)
} else {
assert.NotNil(err, "%d", i)
}
}
}
func TestCheckDeliverSendTx(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
// some sample settings
addr1 := sdk.Actor{App: "coin", Address: []byte{1, 2}}
addr2 := sdk.Actor{App: "role", Address: []byte{7, 8}}
addr3 := sdk.Actor{App: "coin", Address: []byte{6, 5, 4, 3}}
someCoins := Coins{{"atom", 123}}
moreCoins := Coins{{"atom", 6487}}
diffCoins := moreCoins.Minus(someCoins)
otherCoins := Coins{{"eth", 11}}
mixedCoins := someCoins.Plus(otherCoins)
type money struct {
addr sdk.Actor
coins Coins
}
cases := []struct {
init []money
tx sdk.Tx
perms []sdk.Actor
final []money // nil for error
cost uint64 // gas allocated (if not error)
}{
{
[]money{{addr1, moreCoins}},
NewSendTx(
[]TxInput{NewTxInput(addr1, someCoins)},
[]TxOutput{NewTxOutput(addr2, someCoins)}),
[]sdk.Actor{addr1},
[]money{{addr1, diffCoins}, {addr2, someCoins}},
20,
},
// simple multi-sig 2 accounts to 1
{
[]money{{addr1, mixedCoins}, {addr2, moreCoins}},
NewSendTx(
[]TxInput{NewTxInput(addr1, otherCoins), NewTxInput(addr2, someCoins)},
[]TxOutput{NewTxOutput(addr3, mixedCoins)}),
[]sdk.Actor{addr1, addr2},
[]money{{addr1, someCoins}, {addr2, diffCoins}, {addr3, mixedCoins}},
30,
},
// multi-sig with one account sending many times
{
[]money{{addr1, moreCoins.Plus(otherCoins)}},
NewSendTx(
[]TxInput{NewTxInput(addr1, otherCoins), NewTxInput(addr1, someCoins)},
[]TxOutput{NewTxOutput(addr2, mixedCoins)}),
[]sdk.Actor{addr1},
[]money{{addr1, diffCoins}, {addr2, mixedCoins}},
30,
},
// invalid send (not enough money )
{
[]money{{addr1, moreCoins}, {addr2, someCoins}},
NewSendTx(
[]TxInput{NewTxInput(addr2, moreCoins)},
[]TxOutput{NewTxOutput(addr1, moreCoins)}),
[]sdk.Actor{addr1, addr2},
nil,
0,
},
}
h := NewHandler()
for i, tc := range cases {
// setup the cases....
store := state.NewMemKVStore()
for _, m := range tc.init {
acct := Account{Coins: m.coins}
err := storeAccount(store, m.addr.Bytes(), acct)
require.Nil(err, "%d: %+v", i, err)
}
ctx := stack.MockContext("base-chain", 100).WithPermissions(tc.perms...)
// throw-away state for checktx
cache := store.Checkpoint()
cres, err := h.CheckTx(ctx, cache, tc.tx, nil)
// real store for delivertx
_, err2 := h.DeliverTx(ctx, store, tc.tx, nil)
if len(tc.final) > 0 { // valid
assert.Nil(err, "%d: %+v", i, err)
assert.Nil(err2, "%d: %+v", i, err2)
// make sure proper gas is set
assert.Equal(uint64(0), cres.GasPayment, "%d", i)
assert.Equal(tc.cost, cres.GasAllocated, "%d", i)
// make sure the final balances are correct
for _, f := range tc.final {
acct, err := loadAccount(store, f.addr.Bytes())
assert.Nil(err, "%d: %+v", i, err)
assert.Equal(f.coins, acct.Coins)
}
} else {
// both check and deliver should fail
assert.NotNil(err, "%d", i)
assert.NotNil(err2, "%d", i)
}
}
}
func TestInitState(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
// some sample settings
pk := crypto.GenPrivKeySecp256k1().Wrap()
addr := pk.PubKey().Address()
actor := auth.SigPerm(addr)
someCoins := Coins{{"atom", 123}}
otherCoins := Coins{{"eth", 11}}
mixedCoins := someCoins.Plus(otherCoins)
type money struct {
addr sdk.Actor
coins Coins
}
cases := []struct {
init []GenesisAccount
expected []money
}{
{
[]GenesisAccount{{Address: addr, Balance: mixedCoins}},
[]money{{actor, mixedCoins}},
},
}
h := NewHandler()
l := log.NewNopLogger()
for i, tc := range cases {
store := state.NewMemKVStore()
key := "account"
// set the options
for j, gen := range tc.init {
value, err := json.Marshal(gen)
require.Nil(err, "%d,%d: %+v", i, j, err)
_, err = h.InitState(l, store, NameCoin, key, string(value), nil)
require.Nil(err)
}
// check state is proper
for _, f := range tc.expected {
acct, err := loadAccount(store, f.addr.Bytes())
assert.Nil(err, "%d: %+v", i, err)
assert.Equal(f.coins, acct.Coins)
}
}
}
func TestSetIssuer(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
cases := []struct {
issuer sdk.Actor
}{
{sdk.Actor{App: "sig", Address: []byte("gwkfgk")}},
// and set back to empty (nil is valid, but assert.Equals doesn't match)
{sdk.Actor{}},
{sdk.Actor{ChainID: "other", App: "role", Address: []byte("vote")}},
}
h := NewHandler()
l := log.NewNopLogger()
for i, tc := range cases {
store := state.NewMemKVStore()
key := "issuer"
value, err := json.Marshal(tc.issuer)
require.Nil(err, "%d,%d: %+v", i, err)
_, err = h.InitState(l, store, NameCoin, key, string(value), nil)
require.Nil(err, "%+v", err)
// check state is proper
info, err := loadHandlerInfo(store)
assert.Nil(err, "%d: %+v", i, err)
assert.Equal(tc.issuer, info.Issuer)
}
}
func TestDeliverCreditTx(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
// sample coins
someCoins := Coins{{"atom", 6570}}
minusCoins := Coins{{"atom", -1234}}
lessCoins := someCoins.Plus(minusCoins)
otherCoins := Coins{{"eth", 11}}
mixedCoins := someCoins.Plus(otherCoins)
// some sample addresses
owner := sdk.Actor{App: "foo", Address: []byte("rocks")}
addr1 := sdk.Actor{App: "coin", Address: []byte{1, 2}}
key := NewAccountWithKey(someCoins)
addr2 := key.Actor()
addr3 := sdk.Actor{ChainID: "other", App: "sigs", Address: []byte{3, 9}}
h := NewHandler()
store := state.NewMemKVStore()
ctx := stack.MockContext("secret", 77)
// set the owner who can issue credit
js, err := json.Marshal(owner)
require.Nil(err, "%+v", err)
_, err = h.InitState(log.NewNopLogger(), store, "coin", "issuer", string(js), nil)
require.Nil(err, "%+v", err)
// give addr2 some coins to start
_, err = h.InitState(log.NewNopLogger(), store, "coin", "account", key.MakeOption(), nil)
require.Nil(err, "%+v", err)
cases := []struct {
tx sdk.Tx
perm sdk.Actor
check errors.CheckErr
addr sdk.Actor
expected Account
}{
// require permission
{
tx: NewCreditTx(addr1, someCoins),
check: errors.IsUnauthorizedErr,
},
// add credit
{
tx: NewCreditTx(addr1, someCoins),
perm: owner,
check: errors.NoErr,
addr: addr1,
expected: Account{Coins: someCoins, Credit: someCoins},
},
// remove some
{
tx: NewCreditTx(addr1, minusCoins),
perm: owner,
check: errors.NoErr,
addr: addr1,
expected: Account{Coins: lessCoins, Credit: lessCoins},
},
// can't remove more cash than there is
{
tx: NewCreditTx(addr1, otherCoins.Negative()),
perm: owner,
check: IsInsufficientFundsErr,
},
// cumulative with initial state
{
tx: NewCreditTx(addr2, otherCoins),
perm: owner,
check: errors.NoErr,
addr: addr2,
expected: Account{Coins: mixedCoins, Credit: otherCoins},
},
// Even if there is cash, credit can't go negative
{
tx: NewCreditTx(addr2, minusCoins),
perm: owner,
check: IsInsufficientCreditErr,
},
// make sure it works for other chains
{
tx: NewCreditTx(addr3, mixedCoins),
perm: owner,
check: errors.NoErr,
addr: ChainAddr(addr3),
expected: Account{Coins: mixedCoins, Credit: mixedCoins},
},
}
for i, tc := range cases {
myStore := store.Checkpoint()
myCtx := ctx.WithPermissions(tc.perm)
_, err = h.DeliverTx(myCtx, myStore, tc.tx, nil)
assert.True(tc.check(err), "%d: %+v", i, err)
if err == nil {
store.Commit(myStore)
acct, err := GetAccount(store, tc.addr)
require.Nil(err, "%+v", err)
assert.Equal(tc.expected, acct, "%d", i)
}
}
}

View File

@ -1,57 +0,0 @@
package coin
import (
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire/data"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/x/auth"
)
// AccountWithKey is a helper for tests, that includes and account
// along with the private key to access it.
type AccountWithKey struct {
Key crypto.PrivKey
Sequence uint32
Account
}
// NewAccountWithKey creates an account with the given balance
// and a random private key
func NewAccountWithKey(coins Coins) *AccountWithKey {
return &AccountWithKey{
Key: crypto.GenPrivKeyEd25519().Wrap(),
Account: Account{Coins: coins},
}
}
// Address returns the public key address for this account
func (a *AccountWithKey) Address() []byte {
return a.Key.PubKey().Address()
}
// Actor returns the basecoin actor associated with this account
func (a *AccountWithKey) Actor() sdk.Actor {
return auth.SigPerm(a.Key.PubKey().Address())
}
// NextSequence returns the next sequence to sign with
func (a *AccountWithKey) NextSequence() uint32 {
a.Sequence++
return a.Sequence
}
// MakeOption returns a string to use with InitState to initialize this account
//
// This is intended for use in test cases
func (a *AccountWithKey) MakeOption() string {
info := GenesisAccount{
Address: a.Address(),
Balance: a.Coins,
}
js, err := data.ToJSON(info)
if err != nil {
panic(err)
}
return string(js)
}

View File

@ -1,140 +0,0 @@
package coin
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/errors"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/stack"
"github.com/cosmos/cosmos-sdk/state"
wire "github.com/tendermint/go-wire"
)
// TODO: other test making sure tx is output on send, balance is updated
// This makes sure we respond properly to posttx
// TODO: set credit limit
func TestIBCPostPacket(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
otherID := "chain-2"
ourID := "dex"
start := 200
// create the app and our chain
app := stack.New().
IBC(ibc.NewMiddleware()).
Dispatch(
NewHandler(),
stack.WrapHandler(ibc.NewHandler()),
)
ourChain := ibc.NewAppChain(app, ourID)
// set up the other chain and register it with us
otherChain := ibc.NewMockChain(otherID, 7)
registerTx := otherChain.GetRegistrationTx(start).Wrap()
_, err := ourChain.DeliverTx(registerTx)
require.Nil(err, "%+v", err)
// set up a rich guy on this chain
wealth := Coins{{"btc", 300}, {"eth", 2000}, {"ltc", 5000}}
rich := NewAccountWithKey(wealth)
_, err = ourChain.InitState("coin", "account", rich.MakeOption())
require.Nil(err, "%+v", err)
// sends money to another guy on a different chain, now other chain has credit
buddy := sdk.Actor{ChainID: otherID, App: auth.NameSigs, Address: []byte("dude")}
outTx := NewSendOneTx(rich.Actor(), buddy, wealth)
_, err = ourChain.DeliverTx(outTx, rich.Actor())
require.Nil(err, "%+v", err)
// make sure the money moved to the other chain...
cstore := ourChain.GetStore(NameCoin)
acct, err := GetAccount(cstore, ChainAddr(buddy))
require.Nil(err, "%+v", err)
require.Equal(wealth, acct.Coins)
// make sure there is a proper packet for this....
istore := ourChain.GetStore(ibc.NameIBC)
assertPacket(t, istore, otherID, wealth)
// these are the people for testing incoming ibc from the other chain
recipient := sdk.Actor{ChainID: ourID, App: auth.NameSigs, Address: []byte("bar")}
sender := sdk.Actor{ChainID: otherID, App: auth.NameSigs, Address: []byte("foo")}
payment := Coins{{"eth", 100}, {"ltc", 300}}
coinTx := NewSendOneTx(sender, recipient, payment)
wrongCoin := NewSendOneTx(sender, recipient, Coins{{"missing", 20}})
p0 := ibc.NewPacket(coinTx, ourID, 0, sender)
packet0, update0 := otherChain.MakePostPacket(p0, start+5)
require.Nil(ourChain.Update(update0))
p1 := ibc.NewPacket(coinTx, ourID, 1, sender)
packet1, update1 := otherChain.MakePostPacket(p1, start+25)
require.Nil(ourChain.Update(update1))
p2 := ibc.NewPacket(wrongCoin, ourID, 2, sender)
packet2, update2 := otherChain.MakePostPacket(p2, start+50)
require.Nil(ourChain.Update(update2))
ibcPerm := sdk.Actors{ibc.AllowIBC(NameCoin)}
cases := []struct {
packet ibc.PostPacketTx
permissions sdk.Actors
checker errors.CheckErr
}{
// out of order -> error
{packet1, ibcPerm, ibc.IsPacketOutOfOrderErr},
// all good -> execute tx
{packet0, ibcPerm, errors.NoErr},
// all good -> execute tx (even if earlier attempt failed)
{packet1, ibcPerm, errors.NoErr},
// packet 2 attempts to spend money this chain doesn't have
{packet2, ibcPerm, IsInsufficientFundsErr},
}
for i, tc := range cases {
_, err := ourChain.DeliverTx(tc.packet.Wrap(), tc.permissions...)
assert.True(tc.checker(err), "%d: %+v", i, err)
}
// now, make sure the recipient got credited for the 2 successful sendtx
cstore = ourChain.GetStore(NameCoin)
// FIXME: we need to strip off this when it is local chain-id...
// think this throw and handle this better
local := recipient.WithChain("")
acct, err = GetAccount(cstore, local)
require.Nil(err, "%+v", err)
assert.Equal(payment.Plus(payment), acct.Coins)
}
func assertPacket(t *testing.T, istore state.SimpleDB, destID string, amount Coins) {
assert := assert.New(t)
require := require.New(t)
iq := ibc.InputQueue(istore, destID)
require.Equal(0, iq.Size())
q := ibc.OutputQueue(istore, destID)
require.Equal(1, q.Size())
d := q.Item(0)
var res ibc.Packet
err := wire.ReadBinaryBytes(d, &res)
require.Nil(err, "%+v", err)
assert.Equal(destID, res.DestChain)
assert.EqualValues(0, res.Sequence)
stx, ok := res.Tx.Unwrap().(SendTx)
if assert.True(ok) {
assert.Equal(1, len(stx.Outputs))
assert.Equal(amount, stx.Outputs[0].Coins)
}
}

View File

@ -1,166 +0,0 @@
package rest
import (
"fmt"
"net/http"
"strconv"
"strings"
"github.com/gorilla/mux"
"github.com/spf13/viper"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/client/commands/query"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/base"
"github.com/cosmos/cosmos-sdk/x/coin"
"github.com/cosmos/cosmos-sdk/x/fee"
"github.com/cosmos/cosmos-sdk/x/nonce"
"github.com/cosmos/cosmos-sdk/stack"
"github.com/tendermint/tmlibs/common"
)
// SendInput is the request to send an amount from one actor to another.
// Note: Not using the `validator:""` tags here because SendInput has
// many fields so it would be nice to figure out all the invalid
// inputs and report them back to the caller, in one shot.
type SendInput struct {
Fees *coin.Coin `json:"fees"`
Multi bool `json:"multi,omitempty"`
Sequence uint32 `json:"sequence"`
To *sdk.Actor `json:"to"`
From *sdk.Actor `json:"from"`
Amount coin.Coins `json:"amount"`
}
// doQueryAccount is the HTTP handlerfunc to query an account
// It expects a query string with
func doQueryAccount(w http.ResponseWriter, r *http.Request) {
args := mux.Vars(r)
signature := args["signature"]
actor, err := commands.ParseActor(signature)
if err != nil {
common.WriteError(w, err)
return
}
var h int
qHeight := r.URL.Query().Get("height")
if qHeight != "" {
h, err = strconv.Atoi(qHeight)
if err != nil {
common.WriteError(w, err)
return
}
}
actor = coin.ChainAddr(actor)
key := stack.PrefixedKey(coin.NameCoin, actor.Bytes())
account := new(coin.Account)
prove := !viper.GetBool(commands.FlagTrustNode)
height, err := query.GetParsed(key, account, h, prove)
if client.IsNoDataErr(err) {
err := fmt.Errorf("account bytes are empty for address: %q", signature)
common.WriteError(w, err)
return
} else if err != nil {
common.WriteError(w, err)
return
}
if err := query.FoutputProof(w, account, height); err != nil {
common.WriteError(w, err)
}
}
func PrepareSendTx(si *SendInput) sdk.Tx {
tx := coin.NewSendOneTx(*si.From, *si.To, si.Amount)
// fees are optional
if si.Fees != nil && !si.Fees.IsZero() {
tx = fee.NewFee(tx, *si.Fees, *si.From)
}
// only add the actual signer to the nonce
signers := []sdk.Actor{*si.From}
tx = nonce.NewTx(si.Sequence, signers, tx)
tx = base.NewChainTx(commands.GetChainID(), 0, tx)
if si.Multi {
tx = auth.NewMulti(tx).Wrap()
} else {
tx = auth.NewSig(tx).Wrap()
}
return tx
}
func doSend(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
si := new(SendInput)
if err := common.ParseRequestAndValidateJSON(r, si); err != nil {
common.WriteError(w, err)
return
}
var errsList []string
if si.From == nil {
errsList = append(errsList, `"from" cannot be nil`)
}
if si.Sequence <= 0 {
errsList = append(errsList, `"sequence" must be > 0`)
}
if si.To == nil {
errsList = append(errsList, `"to" cannot be nil`)
}
if len(si.Amount) == 0 {
errsList = append(errsList, `"amount" cannot be empty`)
}
if len(errsList) > 0 {
code := http.StatusBadRequest
err := &common.ErrorResponse{
Err: strings.Join(errsList, ", "),
Code: code,
}
common.WriteCode(w, err, code)
return
}
tx := PrepareSendTx(si)
common.WriteSuccess(w, tx)
}
// mux.Router registrars
// RegisterCoinSend is a mux.Router handler that exposes
// POST method access on route /build/send to create a
// transaction for sending money from one account to another.
func RegisterCoinSend(r *mux.Router) error {
r.HandleFunc("/build/send", doSend).Methods("POST")
return nil
}
// RegisterQueryAccount is a mux.Router handler that exposes GET
// method access on route /query/account/{signature} to query accounts
func RegisterQueryAccount(r *mux.Router) error {
r.HandleFunc("/query/account/{signature}", doQueryAccount).Methods("GET")
return nil
}
// RegisterAll is a convenience function to
// register all the handlers in this package.
func RegisterAll(r *mux.Router) error {
funcs := []func(*mux.Router) error{
RegisterCoinSend,
RegisterQueryAccount,
}
for _, fn := range funcs {
if err := fn(r); err != nil {
return err
}
}
return nil
}
// End of mux.Router registrars

View File

@ -1,145 +0,0 @@
package coin
import (
"fmt"
wire "github.com/tendermint/go-wire"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/errors"
"github.com/cosmos/cosmos-sdk/store"
)
// GetAccount - Get account from store and address
func GetAccount(store store.MultiStore, addr sdk.Actor) (Account, error) {
// if the actor is another chain, we use one address for the chain....
addr = ChainAddr(addr)
acct, err := loadAccount(store, addr.Bytes())
// for empty accounts, don't return an error, but rather an empty account
if IsNoAccountErr(err) {
err = nil
}
return acct, err
}
// CheckCoins makes sure there are funds, but doesn't change anything
func CheckCoins(store store.MultiStore, addr sdk.Actor, coins Coins) (Coins, error) {
// if the actor is another chain, we use one address for the chain....
addr = ChainAddr(addr)
acct, err := updateCoins(store, addr, coins)
return acct.Coins, err
}
// ChangeCoins changes the money, returns error if it would be negative
func ChangeCoins(store store.MultiStore, addr sdk.Actor, coins Coins) (Coins, error) {
// if the actor is another chain, we use one address for the chain....
addr = ChainAddr(addr)
acct, err := updateCoins(store, addr, coins)
if err != nil {
return acct.Coins, err
}
err = storeAccount(store, addr.Bytes(), acct)
return acct.Coins, err
}
// ChainAddr collapses all addresses from another chain into one, so we can
// keep an over-all balance
//
// TODO: is there a better way to do this?
func ChainAddr(addr sdk.Actor) sdk.Actor {
if addr.ChainID == "" {
return addr
}
addr.App = ""
addr.Address = nil
return addr
}
// updateCoins will load the account, make all checks, and return the updated account.
//
// it doesn't save anything, that is up to you to decide (Check/Change Coins)
func updateCoins(store store.MultiStore, addr sdk.Actor, coins Coins) (acct Account, err error) {
acct, err = loadAccount(store, addr.Bytes())
// we can increase an empty account...
if IsNoAccountErr(err) && coins.IsPositive() {
err = nil
}
if err != nil {
return acct, err
}
// check amount
final := acct.Coins.Plus(coins)
if !final.IsNonnegative() {
return acct, ErrInsufficientFunds()
}
acct.Coins = final
return acct, nil
}
// Account - coin account structure
type Account struct {
// Coins is how much is on the account
Coins Coins `json:"coins"`
// Credit is how much has been "fronted" to the account
// (this is usually 0 except for trusted chains)
Credit Coins `json:"credit"`
}
func loadAccount(store store.MultiStore, key []byte) (acct Account, err error) {
// fmt.Printf("load: %X\n", key)
data := store.Get(key)
if len(data) == 0 {
return acct, ErrNoAccount()
}
err = wire.ReadBinaryBytes(data, &acct)
if err != nil {
msg := fmt.Sprintf("Error reading account %X", key)
return acct, errors.ErrInternal(msg)
}
return acct, nil
}
func storeAccount(store store.MultiStore, key []byte, acct Account) error {
// fmt.Printf("store: %X\n", key)
bin := wire.BinaryBytes(acct)
store.Set(key, bin)
return nil // real stores can return error...
}
// HandlerInfo - this is global info on the coin handler
type HandlerInfo struct {
Issuer sdk.Actor `json:"issuer"`
}
// TODO: where to store these special pieces??
var handlerKey = []byte{12, 34}
func loadHandlerInfo(store store.KVStore) (info HandlerInfo, err error) {
data := store.Get(handlerKey)
if len(data) == 0 {
return info, nil
}
err = wire.ReadBinaryBytes(data, &info)
if err != nil {
msg := "Error reading handler info"
return info, errors.ErrInternal(msg)
}
return info, nil
}
func storeIssuer(store store.KVStore, issuer sdk.Actor) error {
info, err := loadHandlerInfo(store)
if err != nil {
return err
}
info.Issuer = issuer
d := wire.BinaryBytes(info)
store.Set(handlerKey, d)
return nil // real stores can return error...
}

View File

@ -1,22 +0,0 @@
LINKER_FLAGS:="-X github.com/cosmos/cosmos-sdk/client/commands.CommitHash=`git rev-parse --short HEAD`"
install:
@go install -ldflags $(LINKER_FLAGS) ./cmd/...
test: test_unit test_cli
test_unit:
@go test `glide novendor`
test_cli:
./tests/cli/keys.sh
./tests/cli/rpc.sh
./tests/cli/init.sh
./tests/cli/init-server.sh
./tests/cli/basictx.sh
./tests/cli/roles.sh
./tests/cli/restart.sh
./tests/cli/rest.sh
./tests/cli/ibc.sh
.PHONY: install test test_unit test_cli

View File

@ -1,73 +0,0 @@
// XXX Rename AppHandler to DefaultAppHandler.
// XXX Register with a sdk.BaseApp instance to create Basecoin.
// XXX Create TxParser in anotehr file.
package app
import (
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/store"
)
// AppHandler has no state for now, a more complex app could store state here
type AppHandler struct{}
func NewAppHandler() sdk.Handler {
return AppHandler{}
}
// DeliverTx applies the tx
func (h Handler) DeliverTx(ctx sdk.Context, store store.MultiStore,
msg interface{}) (res sdk.DeliverResult, err error) {
db := store.Get("main").(sdk.KVStore)
// Here we switch on which implementation of tx we use,
// and then take the appropriate action.
switch tx := tx.(type) {
case SendTx:
err = tx.ValidateBasic()
if err != nil {
break
}
db.Set(tx.Key, tx.Value)
res.Data = tx.Key
case RemoveTx:
err = tx.ValidateBasic()
if err != nil {
break
}
db.Remove(tx.Key)
res.Data = tx.Key
default:
err = errors.ErrInvalidFormat(TxWrapper{}, msg)
}
return
}
// CheckTx verifies if it is legit and returns info on how
// to prioritize it in the mempool
func (h Handler) CheckTx(ctx sdk.Context, store store.MultiStore,
msg interface{}) (res sdk.CheckResult, err error) {
// If we wanted to use the store,
// it would look the same (as DeliverTx)
// db := store.Get("main").(sdk.KVStore)
// Make sure it is something valid
tx, ok := msg.(Tx)
if !ok {
return res, errors.ErrInvalidFormat(TxWrapper{}, msg)
}
err = tx.ValidateBasic()
if err != nil {
return
}
// Now return the costs (these should have meaning in your app)
return sdk.CheckResult{
GasAllocated: 50,
GasPayment: 10,
}
}

View File

@ -1,58 +0,0 @@
# Run your own (super) lightweight node
In addition to providing command-line tooling that goes cryptographic verification
on all the data your receive from the node, we have implemented a proxy mode, that
allows you to run a super lightweight node. It does not follow the chain on
every block or even every header, but only as needed. But still providing the
same security as running a full non-validator node on your local machine.
Basically, it runs as a proxy that exposes the same rpc interface as the full node
and connects to a (potentially untrusted) full node. Every response is cryptographically
verified before being passed through, returning an error if it doesn't match.
You can expect 2 rpc calls for every query plus <= 1 query for each validator set
change. Going offline for a while allows you to verify multiple validator set changes
with one call. Cuz at 1 block/sec and 1000 tx/block, it just doesn't make sense
to run a full node just to get security
## Setup
Just initialize your client with the proper validator set as in the [README](README.md)
```
$ export BCHOME=~/.lightnode
$ basecli init --node tcp://<host>:<port> --chain-id <chain>
```
## Running
```
$ basecli proxy --serve tcp://localhost:7890
...
curl localhost:7890/status
curl localhost:7890/block\?height=20
```
You can even subscribe to events over websockets and they are all verified
before passing them though. Though if you want every block, you might as
well run a full (nonvalidating) node.
## Seeds
Every time the validator set changes, the light node verifies if it is legal,
and then creates a seed at that point. These "seeds" are verified checkpoints
that we can trace any proof back to, starting with one on `init`.
To make sure you are based on the most recent header, you can run:
```
basecli seeds update
basecli seeds show
```
## Feedback
This is the first release of basecli and the light-weight proxy. It is secure, but
may not be useful for your workflow. Please try it out and open github issues
for any enhancements or bugs you find. I am aiming to make this a very useful
tool by tendermint 0.11, for which I need community feedback.

View File

@ -1,53 +0,0 @@
# Basic run through of using basecli....
To keep things clear, let's have two shells...
`$` is for basecoin (server), `%` is for basecli (client)
## Set up your basecli with a new key
```
% export BCHOME=~/.democli
% basecli keys new demo
% basecli keys get demo -o json
```
And set up a few more keys for fun...
```
% basecli keys new buddy
% basecli keys list
% ME=$(basecli keys get demo | awk '{print $2}')
% YOU=$(basecli keys get buddy | awk '{print $2}')
```
## Set up a clean basecoin, initialized with your account
```
$ export BCHOME=~/.demoserve
$ basecoin init $ME
$ basecoin start
```
## Connect your basecli the first time
```
% basecli init --chain-id test_chain_id --node tcp://localhost:46657
```
## Check your balances...
```
% basecli query account $ME
% basecli query account $YOU
```
## Send the money
```
% basecli tx send --name demo --amount 1000mycoin --sequence 1 --to $YOU
-> copy hash to HASH
% basecli query tx $HASH
% basecli query account $YOU
```

View File

@ -1,91 +0,0 @@
package main
import (
"os"
"github.com/spf13/cobra"
"github.com/tendermint/tmlibs/cli"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/client/commands/auto"
"github.com/cosmos/cosmos-sdk/client/commands/commits"
"github.com/cosmos/cosmos-sdk/client/commands/keys"
"github.com/cosmos/cosmos-sdk/client/commands/proxy"
"github.com/cosmos/cosmos-sdk/client/commands/query"
rpccmd "github.com/cosmos/cosmos-sdk/client/commands/rpc"
txcmd "github.com/cosmos/cosmos-sdk/client/commands/txs"
authcmd "github.com/cosmos/cosmos-sdk/modules/auth/commands"
basecmd "github.com/cosmos/cosmos-sdk/modules/base/commands"
coincmd "github.com/cosmos/cosmos-sdk/modules/coin/commands"
feecmd "github.com/cosmos/cosmos-sdk/modules/fee/commands"
ibccmd "github.com/cosmos/cosmos-sdk/modules/ibc/commands"
noncecmd "github.com/cosmos/cosmos-sdk/modules/nonce/commands"
rolecmd "github.com/cosmos/cosmos-sdk/modules/roles/commands"
)
// BaseCli - main basecoin client command
var BaseCli = &cobra.Command{
Use: "basecli",
Short: "Light client for Tendermint",
Long: `Basecli is a certifying light client for the basecoin abci app.
It leverages the power of the tendermint consensus algorithm get full
cryptographic proof of all queries while only syncing a fraction of the
block headers.`,
}
func main() {
commands.AddBasicFlags(BaseCli)
// Prepare queries
query.RootCmd.AddCommand(
// These are default parsers, but optional in your app (you can remove key)
query.TxQueryCmd,
query.KeyQueryCmd,
coincmd.AccountQueryCmd,
noncecmd.NonceQueryCmd,
rolecmd.RoleQueryCmd,
ibccmd.IBCQueryCmd,
)
// set up the middleware
txcmd.Middleware = txcmd.Wrappers{
feecmd.FeeWrapper{},
rolecmd.RoleWrapper{},
noncecmd.NonceWrapper{},
basecmd.ChainWrapper{},
authcmd.SigWrapper{},
}
txcmd.Middleware.Register(txcmd.RootCmd.PersistentFlags())
// you will always want this for the base send command
txcmd.RootCmd.AddCommand(
// This is the default transaction, optional in your app
coincmd.SendTxCmd,
coincmd.CreditTxCmd,
// this enables creating roles
rolecmd.CreateRoleTxCmd,
// these are for handling ibc
ibccmd.RegisterChainTxCmd,
ibccmd.UpdateChainTxCmd,
ibccmd.PostPacketTxCmd,
)
// Set up the various commands to use
BaseCli.AddCommand(
commands.InitCmd,
commands.ResetCmd,
keys.RootCmd,
commits.RootCmd,
rpccmd.RootCmd,
query.RootCmd,
txcmd.RootCmd,
proxy.RootCmd,
commands.VersionCmd,
auto.AutoCompleteCmd,
)
cmd := cli.PrepareMainCmd(BaseCli, "BC", os.ExpandEnv("$HOME/.basecli"))
cmd.Execute()
}

View File

@ -1,69 +0,0 @@
package main
import (
"os"
"github.com/spf13/cobra"
"github.com/tendermint/tmlibs/cli"
sdk "github.com/cosmos/cosmos-sdk"
client "github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/modules/auth"
"github.com/cosmos/cosmos-sdk/modules/base"
"github.com/cosmos/cosmos-sdk/modules/coin"
"github.com/cosmos/cosmos-sdk/modules/eyes"
"github.com/cosmos/cosmos-sdk/modules/fee"
"github.com/cosmos/cosmos-sdk/modules/ibc"
"github.com/cosmos/cosmos-sdk/modules/nonce"
"github.com/cosmos/cosmos-sdk/modules/roles"
"github.com/cosmos/cosmos-sdk/server/commands"
"github.com/cosmos/cosmos-sdk/stack"
)
// RootCmd is the entry point for this binary
var RootCmd = &cobra.Command{
Use: "basecoin",
Short: "A cryptocurrency framework in Golang based on Tendermint-Core",
}
// BuildApp constructs the stack we want to use for this app
func BuildApp(feeDenom string) sdk.Handler {
return stack.New(
base.Logger{},
stack.Recovery{},
auth.Signatures{},
base.Chain{},
stack.Checkpoint{OnCheck: true},
nonce.ReplayCheck{},
).
IBC(ibc.NewMiddleware()).
Apps(
roles.NewMiddleware(),
fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank),
stack.Checkpoint{OnDeliver: true},
).
Dispatch(
coin.NewHandler(),
stack.WrapHandler(roles.NewHandler()),
stack.WrapHandler(ibc.NewHandler()),
// and just for run, add eyes as well
stack.WrapHandler(eyes.NewHandler()),
)
}
func main() {
// require all fees in mycoin - change this in your app!
commands.Handler = BuildApp("mycoin")
RootCmd.AddCommand(
commands.InitCmd,
commands.StartCmd,
//commands.RelayCmd,
commands.UnsafeResetAllCmd,
client.VersionCmd,
)
commands.SetUpRoot(RootCmd)
cmd := cli.PrepareMainCmd(RootCmd, "BC", os.ExpandEnv("$HOME/.basecoin"))
cmd.Execute()
}

View File

@ -1,160 +0,0 @@
# baseserver
baseserver is the REST counterpart to basecli
## Compiling and running it
```shell
$ go get -u -v github.com/tendermint/basecoin/cmd/baseserver
$ baseserver init
$ baseserver serve --port 8888
```
to run the server at localhost:8888, otherwise if you don't specify --port,
by default the server will be run on port 8998.
## Supported routes
Route | Method | Completed | Description
---|---|---|---
/keys|GET|✔️|Lists all keys
/keys|POST|✔️|Generate a new key. It expects fields: "name", "algo", "passphrase"
/keys/{name}|GET|✔️|Retrieves the specific key
/keys/{name}|POST/PUT|✔️|Updates the named key
/keys/{name}|DELETE|✔️|Deletes the named key
/build/send|POST|✔️|Send a transaction
/sign|POST|✔️|Sign a transaction
/tx|POST|✖️|Post a transaction to the blockchain
/seeds/status|GET|✖️|Returns the information on the last seed
/build/create_role|POST|✔️|Creates a role. Please note that the role MUST be valid hex for example instead of sending "role", send its hex encoded equivalent "726f6c65"
## Preamble:
In the examples below, we assume that URL is set to `http://localhost:8889`
which can be set for example
URL=http://localhost:8889
## Sample usage
- Generate a key
```shell
$ curl -X POST $URL/keys --data '{"algo": "ed25519", "name": "SampleX", "passphrase": "Say no more"}'
```
```json
{
"key": {
"name": "SampleX",
"address": "603EE63C41E322FC7A247864A9CD0181282EB458",
"pubkey": {
"type": "ed25519",
"data": "C050948CFC087F5E1068C7E244DDC30E03702621CC9442A28E6C9EDA7771AA0C"
}
},
"seed_phrase": "border almost future parade speak soccer bulk orange real brisk caution body river chapter"
}
```
- Sign a key
```shell
$ curl -X POST $URL/sign --data '{
"name": "matt",
"password": "Say no more",
"tx": {
"type": "sigs/multi",
"data": {
"tx": {"type":"coin/send","data":{"inputs":[{"address":{"chain":"","app":"role","addr":"62616E6B32"},"coins":[{"denom":"mycoin","amount":900000}]}],"outputs":[{"address":{"chain":"","app":"sigs","addr":"BDADF167E6CF2CDF2D621E590FF1FED2787A40E0"},"coins":[{"denom":"mycoin","amount":900000}]}]}},
"signatures": null
}
}
}'
```
```json
{
"type": "sigs/multi",
"data": {
"tx": {
"type": "coin/send",
"data": {
"inputs": [
{
"address": {
"chain": "",
"app": "role",
"addr": "62616E6B32"
},
"coins": [
{
"denom": "mycoin",
"amount": 900000
}
]
}
],
"outputs": [
{
"address": {
"chain": "",
"app": "sigs",
"addr": "BDADF167E6CF2CDF2D621E590FF1FED2787A40E0"
},
"coins": [
{
"denom": "mycoin",
"amount": 900000
}
]
}
]
}
},
"signatures": [
{
"Sig": {
"type": "ed25519",
"data": "F6FE3053F1E6C236F886A0D525C1AF840F7831B6E50F7E1108C345AA524303920F09945DA110AD5184B3F45717D7114E368B12AFE027FECECC2FC193D4906A0C"
},
"Pubkey": {
"type": "ed25519",
"data": "0D8D19E527BAE9D1256A3D03009E2708171CDCB71CCDEDA2DC52DD9AD23AEE25"
}
}
]
}
}
```
- Create a role
```shell
$ curl -X POST $URL/build/create_role --data \
'{
"role": "deadbeef",
"signers": [{
"addr": "4FF759D47C81754D8F553DCCAC8651D0AF74C7F9",
"app": "role"
}],
"min_sigs": 1,
"seq": 1
}'
```
```json
{
"type": "chain/tx",
"data": {
"chain_id": "test_chain_id",
"expires_at": 0,
"tx": {
"type": "role/create",
"data": {
"role": "DEADBEEF",
"min_sigs": 1,
"signers": [
{
"chain": "",
"app": "role",
"addr": "4FF759D47C81754D8F553DCCAC8651D0AF74C7F9"
}
]
}
}
}
}
```

View File

@ -1,95 +0,0 @@
package main
import (
"fmt"
"log"
"net/http"
"os"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client/commands"
rest "github.com/cosmos/cosmos-sdk/client/rest"
coinrest "github.com/cosmos/cosmos-sdk/modules/coin/rest"
noncerest "github.com/cosmos/cosmos-sdk/modules/nonce/rest"
rolerest "github.com/cosmos/cosmos-sdk/modules/roles/rest"
"github.com/tendermint/tmlibs/cli"
)
var srvCli = &cobra.Command{
Use: "baseserver",
Short: "Light REST client for tendermint",
Long: `Baseserver presents a nice (not raw hex) interface to the basecoin blockchain structure.`,
}
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Serve the light REST client for tendermint",
Long: "Access basecoin via REST",
RunE: serve,
}
const (
envPortFlag = "port"
defaultAlgo = "ed25519"
)
func init() {
_ = serveCmd.PersistentFlags().Int(envPortFlag, 8998, "the port to run the server on")
}
func serve(cmd *cobra.Command, args []string) error {
router := mux.NewRouter()
routeRegistrars := []func(*mux.Router) error{
// rest.Keys handlers
rest.NewDefaultKeysManager(defaultAlgo).RegisterAllCRUD,
// Coin send handler
coinrest.RegisterCoinSend,
// Coin query account handler
coinrest.RegisterQueryAccount,
// Roles createRole handler
rolerest.RegisterCreateRole,
// Basecoin sign transactions handler
rest.RegisterSignTx,
// Basecoin post transaction handler
rest.RegisterPostTx,
// Nonce query handler
noncerest.RegisterQueryNonce,
}
for _, routeRegistrar := range routeRegistrars {
if err := routeRegistrar(router); err != nil {
log.Fatal(err)
}
}
port := viper.GetInt(envPortFlag)
addr := fmt.Sprintf(":%d", port)
log.Printf("Serving on %q", addr)
return http.ListenAndServe(addr, router)
}
func main() {
commands.AddBasicFlags(srvCli)
srvCli.AddCommand(
commands.InitCmd,
commands.VersionCmd,
serveCmd,
)
// this should share the dir with basecli, so you can use the cli and
// the api interchangeably
cmd := cli.PrepareMainCmd(srvCli, "BC", os.ExpandEnv("$HOME/.basecli"))
if err := cmd.Execute(); err != nil {
log.Fatal(err)
}
}

View File

@ -1,122 +0,0 @@
#!/bin/bash
# These global variables are required for common.sh
SERVER_EXE=basecoin
CLIENT_EXE=basecli
ACCOUNTS=(jae ethan bucky rigel igor)
RICH=${ACCOUNTS[0]}
POOR=${ACCOUNTS[4]}
oneTimeSetUp() {
if ! quickSetup .basecoin_test_basictx basictx-chain; then
exit 1;
fi
}
oneTimeTearDown() {
quickTearDown
}
test00GetAccount() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
assertFalse "line=${LINENO}, requires arg" "${CLIENT_EXE} query account"
checkAccount $SENDER "9007199254740992"
ACCT2=$(${CLIENT_EXE} query account $RECV 2>/dev/null)
assertFalse "line=${LINENO}, has no genesis account" $?
}
test01SendTx() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
assertFalse "line=${LINENO}, missing dest" "${CLIENT_EXE} tx send --amount=992mycoin --sequence=1"
assertFalse "line=${LINENO}, bad password" "echo foo | ${CLIENT_EXE} tx send --amount=992mycoin --sequence=1 --to=$RECV --name=$RICH"
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=992mycoin --sequence=1 --to=$RECV --name=$RICH)
txSucceeded $? "$TX" "$RECV"
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
checkAccount $SENDER "9007199254740000" "$TX_HEIGHT"
# make sure 0x prefix also works
checkAccount "0x$SENDER" "9007199254740000" "$TX_HEIGHT"
checkAccount $RECV "992" "$TX_HEIGHT"
# Make sure tx is indexed
checkSendTx $HASH $TX_HEIGHT $SENDER "992"
}
test02SendTxWithFee() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
# Test to see if the auto-sequencing works, the sequence here should be calculated to be 2
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=90mycoin --fee=10mycoin --to=$RECV --name=$RICH)
txSucceeded $? "$TX" "$RECV"
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
# deduct 100 from sender, add 90 to receiver... fees "vanish"
checkAccount $SENDER "9007199254739900" "$TX_HEIGHT"
checkAccount $RECV "1082" "$TX_HEIGHT"
# Make sure tx is indexed
checkSendFeeTx $HASH $TX_HEIGHT $SENDER "90" "10"
# assert replay protection
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=90mycoin --fee=10mycoin --sequence=2 --to=$RECV --name=$RICH 2>/dev/null)
assertFalse "line=${LINENO}, replay: $TX" $?
# checking normally
checkAccount $SENDER "9007199254739900" "$TX_HEIGHT"
checkAccount $RECV "1082" "$TX_HEIGHT"
# make sure we can query the proper nonce
NONCE=$(${CLIENT_EXE} query nonce $SENDER)
if [ -n "$DEBUG" ]; then echo $NONCE; echo; fi
# TODO: note that cobra returns error code 0 on parse failure,
# so currently this check passes even if there is no nonce query command
if assertTrue "line=${LINENO}, no nonce query" $?; then
assertEquals "line=${LINENO}, proper nonce" "2" $(echo $NONCE | jq .data)
fi
# make sure this works without trust also
OLD_BC_HOME=$BC_HOME
export BC_HOME=/foo
export BC_TRUST_NODE=1
export BC_NODE=localhost:46657
checkSendFeeTx $HASH $TX_HEIGHT $SENDER "90" "10"
checkAccount $SENDER "9007199254739900" "$TX_HEIGHT"
checkAccount $RECV "1082" "$TX_HEIGHT"
unset BC_TRUST_NODE
unset BC_NODE
export BC_HOME=$OLD_BC_HOME
}
test03CreditTx() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
# make sure we are controlled by permissions (only rich can issue credit)
assertFalse "line=${LINENO}, bad password" "echo qwertyuiop | ${CLIENT_EXE} tx credit --amount=1000mycoin --sequence=1 --to=$RECV --name=$POOR"
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx credit --amount=1000mycoin --sequence=3 --to=$RECV --name=$RICH)
txSucceeded $? "$TX" "$RECV"
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
# receiver got cash, sender didn't lose any (1000 more than last check)
checkAccount $RECV "2082" "$TX_HEIGHT"
checkAccount $SENDER "9007199254739900" "$TX_HEIGHT"
}
# Load common then run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
CLI_DIR=$GOPATH/src/github.com/cosmos/cosmos-sdk/tests/cli
. $CLI_DIR/common.sh
. $CLI_DIR/shunit2

View File

@ -1,284 +0,0 @@
#!/bin/bash
#!/bin/bash
# These global variables are required for common.sh
SERVER_EXE=basecoin
CLIENT_EXE=basecli
ACCOUNTS=(jae ethan bucky rigel igor)
RICH=${ACCOUNTS[0]}
POOR=${ACCOUNTS[4]}
# For full stack traces in error output, run
# BC_TRACE=1 ./ibc.sh
oneTimeSetUp() {
# These are passed in as args
BASE_DIR_1=$HOME/.basecoin_test_ibc/chain1
CHAIN_ID_1=test-chain-1
CLIENT_1=${BASE_DIR_1}/client
PREFIX_1=1234
PORT_1=${PREFIX_1}7
BASE_DIR_2=$HOME/.basecoin_test_ibc/chain2
CHAIN_ID_2=test-chain-2
CLIENT_2=${BASE_DIR_2}/client
PREFIX_2=2345
PORT_2=${PREFIX_2}7
# Clean up and create the test dirs
rm -rf $BASE_DIR_1 $BASE_DIR_2 2>/dev/null
mkdir -p $BASE_DIR_1 $BASE_DIR_2
# Set up client for chain 1- make sure you use the proper prefix if you set
# a custom CLIENT_EXE
BC_HOME=${CLIENT_1} prepareClient
BC_HOME=${CLIENT_2} prepareClient
# Start basecoin server, giving money to the key in the first client
BC_HOME=${CLIENT_1} initServer $BASE_DIR_1 $CHAIN_ID_1 $PREFIX_1
if [ $? != 0 ]; then exit 1; fi
PID_SERVER_1=$PID_SERVER
# Start second basecoin server, giving money to the key in the second client
BC_HOME=${CLIENT_2} initServer $BASE_DIR_2 $CHAIN_ID_2 $PREFIX_2
if [ $? != 0 ]; then exit 1; fi
PID_SERVER_2=$PID_SERVER
# Connect both clients
BC_HOME=${CLIENT_1} initClient $CHAIN_ID_1 $PORT_1
if [ $? != 0 ]; then exit 1; fi
BC_HOME=${CLIENT_2} initClient $CHAIN_ID_2 $PORT_2
if [ $? != 0 ]; then exit 1; fi
printf "...Testing may begin!\n\n\n"
}
oneTimeTearDown() {
printf "\n\nstopping both $SERVER_EXE test servers... $PID_SERVER_1 $PID_SERVER_2"
kill -9 $PID_SERVER_1
kill -9 $PID_SERVER_2
sleep 1
}
test00GetAccount() {
export BC_HOME=${CLIENT_1}
SENDER_1=$(getAddr $RICH)
RECV_1=$(getAddr $POOR)
assertFalse "line=${LINENO}, requires arg" "${CLIENT_EXE} query account 2>/dev/null"
assertFalse "line=${LINENO}, has no genesis account" "${CLIENT_EXE} query account $RECV_1 2>/dev/null"
checkAccount $SENDER_1 "9007199254740992"
export BC_HOME=${CLIENT_2}
SENDER_2=$(getAddr $RICH)
RECV_2=$(getAddr $POOR)
assertFalse "line=${LINENO}, requires arg" "${CLIENT_EXE} query account 2>/dev/null"
assertFalse "line=${LINENO}, has no genesis account" "${CLIENT_EXE} query account $RECV_2 2>/dev/null"
checkAccount $SENDER_2 "9007199254740992"
# Make sure that they have different addresses on both chains (they are random keys)
assertNotEquals "line=${LINENO}, sender keys must be different" "$SENDER_1" "$SENDER_2"
assertNotEquals "line=${LINENO}, recipient keys must be different" "$RECV_1" "$RECV_2"
}
test01RegisterChains() {
# let's get the root commits to cross-register them
ROOT_1="$BASE_DIR_1/root_commit.json"
${CLIENT_EXE} commits export $ROOT_1 --home=${CLIENT_1}
assertTrue "line=${LINENO}, export commit failed" $?
ROOT_2="$BASE_DIR_2/root_commit.json"
${CLIENT_EXE} commits export $ROOT_2 --home=${CLIENT_2}
assertTrue "line=${LINENO}, export commit failed" $?
# register chain2 on chain1
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx ibc-register \
--sequence=1 --commit=${ROOT_2} --name=$POOR --home=${CLIENT_1})
txSucceeded $? "$TX" "register chain2 on chain 1"
# an example to quit early if there is no point in more tests
if [ $? != 0 ]; then echo "aborting!"; return 1; fi
# this is used later to check data
REG_HEIGHT=$(echo $TX | jq .height)
# register chain1 on chain2 (no money needed... yet)
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx ibc-register \
--sequence=1 --commit=${ROOT_1} --name=$POOR --home=${CLIENT_2})
txSucceeded $? "$TX" "register chain1 on chain 2"
# an example to quit early if there is no point in more tests
if [ $? != 0 ]; then echo "aborting!"; return 1; fi
}
test02UpdateChains() {
# let's get the root commits to cross-register them
UPDATE_1="$BASE_DIR_1/seed_1.json"
${CLIENT_EXE} commits update --home=${CLIENT_1} > /dev/null
${CLIENT_EXE} commits export $UPDATE_1 --home=${CLIENT_1}
assertTrue "line=${LINENO}, export commit failed" $?
# make sure it is newer than the other....
assertNewHeight "line=${LINENO}" $ROOT_1 $UPDATE_1
UPDATE_2="$BASE_DIR_2/seed_2.json"
${CLIENT_EXE} commits update --home=${CLIENT_2} > /dev/null
${CLIENT_EXE} commits export $UPDATE_2 --home=${CLIENT_2}
assertTrue "line=${LINENO}, export commit failed" $?
assertNewHeight "line=${LINENO}" $ROOT_2 $UPDATE_2
# this is used later to check query data
REGISTER_2_HEIGHT=$(cat $ROOT_2 | jq .commit.header.height)
UPDATE_2_HEIGHT=$(cat $UPDATE_2 | jq .commit.header.height)
# update chain2 on chain1
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx ibc-update \
--sequence=2 --commit=${UPDATE_2} --name=$POOR --home=${CLIENT_1})
txSucceeded $? "$TX" "update chain2 on chain 1"
# an example to quit early if there is no point in more tests
if [ $? != 0 ]; then echo "aborting!"; return 1; fi
# update chain1 on chain2 (no money needed... yet)
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx ibc-update \
--sequence=2 --commit=${UPDATE_1} --name=$POOR --home=${CLIENT_2})
txSucceeded $? "$TX" "update chain1 on chain 2"
# an example to quit early if there is no point in more tests
if [ $? != 0 ]; then echo "aborting!"; return 1; fi
}
# make sure all query commands about ibc work...
test03QueryIBC() {
# just test on one chain, as they are all symetrical
export BC_HOME=${CLIENT_1}
# make sure we can list all chains
CHAINS=$(${CLIENT_EXE} query ibc chains)
assertTrue "line=${LINENO}, cannot query chains" $?
assertEquals "1" $(echo $CHAINS | jq '.data | length')
assertEquals "line=${LINENO}" "\"$CHAIN_ID_2\"" $(echo $CHAINS | jq '.data[0]')
# error on unknown chain, data on proper chain
assertFalse "line=${LINENO}, unknown chain" "${CLIENT_EXE} query ibc chain random 2>/dev/null"
CHAIN_INFO=$(${CLIENT_EXE} query ibc chain $CHAIN_ID_2)
assertTrue "line=${LINENO}, cannot query chain $CHAIN_ID_2" $?
assertEquals "line=${LINENO}, register height" $REG_HEIGHT $(echo $CHAIN_INFO | jq .data.registered_at)
assertEquals "line=${LINENO}, tracked height" $UPDATE_2_HEIGHT $(echo $CHAIN_INFO | jq .data.remote_block)
}
# Trigger a cross-chain sendTx... from RICH on chain1 to POOR on chain2
# we make sure the money was reduced, but nothing arrived
test04SendIBCPacket() {
export BC_HOME=${CLIENT_1}
# make sure there are no packets yet
PACKETS=$(${CLIENT_EXE} query ibc packets --to=$CHAIN_ID_2 2>/dev/null)
assertFalse "line=${LINENO}, packet query" $?
SENDER=$(getAddr $RICH)
RECV=$(BC_HOME=${CLIENT_2} getAddr $POOR)
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=20002mycoin \
--to=${CHAIN_ID_2}::${RECV} --name=$RICH)
txSucceeded $? "$TX" "${CHAIN_ID_2}::${RECV}"
# quit early if there is no point in more tests
if [ $? != 0 ]; then echo "aborting!"; return 1; fi
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
# Make sure balance went down and tx is indexed
checkAccount $SENDER "9007199254720990" "$TX_HEIGHT"
checkSendTx $HASH $TX_HEIGHT $SENDER "20002"
# look, we wrote a packet
PACKETS=$(${CLIENT_EXE} query ibc packets --to=$CHAIN_ID_2 --height=$TX_HEIGHT)
assertTrue "line=${LINENO}, packets query" $?
assertEquals "line=${LINENO}, packet count" 1 $(echo $PACKETS | jq .data)
# and look at the packet itself
PACKET=$(${CLIENT_EXE} query ibc packet --to=$CHAIN_ID_2 --sequence=0 --height=$TX_HEIGHT)
assertTrue "line=${LINENO}, packet query" $?
assertEquals "line=${LINENO}, proper src" "\"$CHAIN_ID_1\"" $(echo $PACKET | jq .src_chain)
assertEquals "line=${LINENO}, proper dest" "\"$CHAIN_ID_2\"" $(echo $PACKET | jq .packet.dest_chain)
assertEquals "line=${LINENO}, proper sequence" "0" $(echo $PACKET | jq .packet.sequence)
if [ -n "$DEBUG" ]; then echo $PACKET; echo; fi
# nothing arrived
ARRIVED=$(${CLIENT_EXE} query ibc packets --from=$CHAIN_ID_1 --home=$CLIENT_2 2>/dev/null)
assertFalse "line=${LINENO}, packet query" $?
assertFalse "line=${LINENO}, no relay running" "BC_HOME=${CLIENT_2} ${CLIENT_EXE} query account $RECV"
}
test05ReceiveIBCPacket() {
export BC_HOME=${CLIENT_2}
RECV=$(getAddr $POOR)
# make some credit, so we can accept the packet
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx credit --amount=60006mycoin --to=$CHAIN_ID_1:: --name=$RICH)
txSucceeded $? "$TX" "${CHAIN_ID_1}::"
TX_HEIGHT=$(echo $TX | jq .height)
# make sure there is enough credit
checkAccount $CHAIN_ID_1:: "60006" "$TX_HEIGHT"
# and the poor guy doesn't have a penny to his name
ACCT2=$(${CLIENT_EXE} query account $RECV 2>/dev/null)
assertFalse "line=${LINENO}, has no genesis account" $?
# now, we try to post it.... (this is PACKET from last test)
# get the commit with the proof and post it
SRC_HEIGHT=$(echo $PACKET | jq .src_height)
PROOF_HEIGHT=$(expr $SRC_HEIGHT + 1)
# FIXME: this should auto-update on proofs...
${CLIENT_EXE} commits update --height=$PROOF_HEIGHT --home=${CLIENT_1} > /dev/null
assertTrue "line=${LINENO}, update commit failed" $?
PACKET_COMMIT="$BASE_DIR_1/packet_commit.json"
${CLIENT_EXE} commits export $PACKET_COMMIT --home=${CLIENT_1} --height=$PROOF_HEIGHT
assertTrue "line=${LINENO}, export commit failed" $?
if [ -n "$DEBUG" ]; then
echo "**** SEED ****"
cat $PACKET_COMMIT | jq .commit.header
echo
fi
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx ibc-update \
--commit=${PACKET_COMMIT} --name=$POOR --sequence=3)
txSucceeded $? "$TX" "prepare packet chain1 on chain 2"
# an example to quit early if there is no point in more tests
if [ $? != 0 ]; then echo "aborting!"; return 1; fi
TX_HEIGHT=$(echo $TX | jq .height)
# write the packet to the file
POST_PACKET="$BASE_DIR_1/post_packet.json"
echo $PACKET > $POST_PACKET
# post it as a tx (cross-fingers)
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx ibc-post \
--packet=${POST_PACKET} --name=$POOR --sequence=4)
txSucceeded $? "$TX" "post packet from chain1 on chain 2"
TX_HEIGHT=$(echo $TX | jq .height)
# ensure $POOR balance was incremented, and credit for CHAIN_1 decremented
checkAccount $CHAIN_ID_1:: "40004" "$TX_HEIGHT"
checkAccount $RECV "20002" "$TX_HEIGHT"
# look, we wrote a packet
PACKETS=$(${CLIENT_EXE} query ibc packets --height=$TX_HEIGHT --from=$CHAIN_ID_1)
assertTrue "line=${LINENO}, packets query" $?
assertEquals "line=${LINENO}, packet count" 1 $(echo $PACKETS | jq .data)
}
# XXX Ex Usage: assertNewHeight $MSG $SEED_1 $SEED_2
# Desc: Asserts that seed2 has a higher block height than commit 1
assertNewHeight() {
H1=$(cat $2 | jq .commit.header.height)
H2=$(cat $3 | jq .commit.header.height)
assertTrue "$1" "test $H2 -gt $H1"
return $?
}
# Load common then run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
CLI_DIR=$GOPATH/src/github.com/cosmos/cosmos-sdk/tests/cli
. $CLI_DIR/common.sh
. $CLI_DIR/shunit2

View File

@ -1,45 +0,0 @@
#!/bin/bash
CLIENT_EXE=basecli
SERVER_EXE=basecoin
test01initOption() {
BASE=~/.bc_init_test
rm -rf "$BASE"
mkdir -p "$BASE"
SERVE_DIR="${BASE}/server"
GENESIS_FILE=${SERVE_DIR}/genesis.json
HEX="deadbeef1234deadbeef1234deadbeef1234aaaa"
${SERVER_EXE} init ${HEX} --home="$SERVE_DIR" -p=eyes/key1/val1 -p='"eyes/key2/{""name"": ""joe"", ""age"": ""100""}"' >/dev/null
if ! assertTrue "line=${LINENO}" $?; then return 1; fi
OPTION1KEY=$(cat ${GENESIS_FILE} | jq '.app_options.plugin_options[2]')
OPTION1VAL=$(cat ${GENESIS_FILE} | jq '.app_options.plugin_options[3]')
OPTION2KEY=$(cat ${GENESIS_FILE} | jq '.app_options.plugin_options[4]')
OPTION2VAL=$(cat ${GENESIS_FILE} | jq '.app_options.plugin_options[5]')
OPTION2VALEXPECTED=$(echo '{"name": "joe", "age": "100"}' | jq '.')
assertEquals "line=${LINENO}" '"eyes/key1"' $OPTION1KEY
assertEquals "line=${LINENO}" '"val1"' $OPTION1VAL
assertEquals "line=${LINENO}" '"eyes/key2"' $OPTION2KEY
assertEquals "line=${LINENO}" "$OPTION2VALEXPECTED" "$OPTION2VAL"
}
test02runServer() {
# Attempt to begin the server with the custom genesis
SERVER_LOG=$BASE/${SERVER_EXE}.log
startServer $SERVE_DIR $SERVER_LOG
}
oneTimeTearDown() {
quickTearDown
}
# load and run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
CLI_DIR=$GOPATH/src/github.com/cosmos/cosmos-sdk/tests/cli
. $CLI_DIR/common.sh
. $CLI_DIR/shunit2

View File

@ -1,110 +0,0 @@
#!/bin/bash
CLIENT_EXE=basecli
SERVER_EXE=basecoin
oneTimeSetUp() {
BASE=~/.bc_init_test
rm -rf "$BASE"
mkdir -p "$BASE"
SERVER="${BASE}/server"
SERVER_LOG="${BASE}/${SERVER_EXE}.log"
HEX="deadbeef1234deadbeef1234deadbeef1234aaaa"
${SERVER_EXE} init ${HEX} --home="$SERVER" >> "$SERVER_LOG"
if ! assertTrue "line=${LINENO}" $?; then return 1; fi
GENESIS_FILE=${SERVER}/genesis.json
CHAIN_ID=$(cat ${GENESIS_FILE} | jq .chain_id | tr -d \")
printf "starting ${SERVER_EXE}...\n"
${SERVER_EXE} start --home="$SERVER" >> "$SERVER_LOG" 2>&1 &
sleep 5
PID_SERVER=$!
disown
if ! ps $PID_SERVER >/dev/null; then
echo "**STARTUP FAILED**"
cat $SERVER_LOG
return 1
fi
}
oneTimeTearDown() {
printf "\nstopping ${SERVER_EXE}..."
kill -9 $PID_SERVER >/dev/null 2>&1
sleep 1
}
test01goodInit() {
export BCHOME=${BASE}/client-01
assertFalse "line=${LINENO}" "ls ${BCHOME} 2>/dev/null >&2"
echo y | ${CLIENT_EXE} init --node=tcp://localhost:46657 --chain-id="${CHAIN_ID}" > /dev/null
assertTrue "line=${LINENO}, initialized light-client" $?
checkDir $BCHOME 3
}
test02badInit() {
export BCHOME=${BASE}/client-02
assertFalse "line=${LINENO}" "ls ${BCHOME} 2>/dev/null >&2"
# no node where we go
echo y | ${CLIENT_EXE} init --node=tcp://localhost:9999 --chain-id="${CHAIN_ID}" > /dev/null 2>&1
assertFalse "line=${LINENO}, invalid init" $?
# dir there, but empty...
checkDir $BCHOME 0
# try with invalid chain id
echo y | ${CLIENT_EXE} init --node=tcp://localhost:46657 --chain-id="bad-chain-id" > /dev/null 2>&1
assertFalse "line=${LINENO}, invalid init" $?
checkDir $BCHOME 0
# reject the response
echo n | ${CLIENT_EXE} init --node=tcp://localhost:46657 --chain-id="${CHAIN_ID}" > /dev/null 2>&1
assertFalse "line=${LINENO}, invalid init" $?
checkDir $BCHOME 0
}
test03noDoubleInit() {
export BCHOME=${BASE}/client-03
assertFalse "line=${LINENO}" "ls ${BCHOME} 2>/dev/null >&2"
# init properly
echo y | ${CLIENT_EXE} init --node=tcp://localhost:46657 --chain-id="${CHAIN_ID}" > /dev/null 2>&1
assertTrue "line=${LINENO}, initialized light-client" $?
checkDir $BCHOME 3
# try again, and we get an error
echo y | ${CLIENT_EXE} init --node=tcp://localhost:46657 --chain-id="${CHAIN_ID}" > /dev/null 2>&1
assertFalse "line=${LINENO}, warning on re-init" $?
checkDir $BCHOME 3
# unless we --force-reset
echo y | ${CLIENT_EXE} init --force-reset --node=tcp://localhost:46657 --chain-id="${CHAIN_ID}" > /dev/null 2>&1
assertTrue "line=${LINENO}, re-initialized light-client" $?
checkDir $BCHOME 3
}
test04acceptGenesisFile() {
export BCHOME=${BASE}/client-04
assertFalse "line=${LINENO}" "ls ${BCHOME} 2>/dev/null >&2"
# init properly
${CLIENT_EXE} init --node=tcp://localhost:46657 --genesis=${GENESIS_FILE} > /dev/null 2>&1
assertTrue "line=${LINENO}, initialized light-client" $?
checkDir $BCHOME 3
}
# XXX Ex: checkDir $DIR $FILES
# Makes sure directory exists and has the given number of files
checkDir() {
assertTrue "line=${LINENO}" "ls ${1} 2>/dev/null >&2"
assertEquals "line=${LINENO}, no files created" "$2" $(ls $1 | wc -l)
}
# load and run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
CLI_DIR=$GOPATH/src/github.com/cosmos/cosmos-sdk/tests/cli
. $CLI_DIR/shunit2

View File

@ -1,146 +0,0 @@
#!/bin/bash
EXE=basecli
oneTimeSetUp() {
PASS=qwertyuiop
export BCHOME=$HOME/.bc_keys_test
${EXE} reset_all
assertTrue "line ${LINENO}" $?
}
newKey(){
assertNotNull "keyname required" "$1"
KEYPASS=${2:-qwertyuiop}
KEY=$(echo $KEYPASS | ${EXE} keys new $1 -o json)
if ! assertTrue "line ${LINENO}: created $1" $?; then return 1; fi
assertEquals "$1" $(echo $KEY | jq .key.name | tr -d \")
return $?
}
# updateKey <name> <oldkey> <newkey>
updateKey() {
(echo $2; echo $3) | ${EXE} keys update $1 > /dev/null
return $?
}
test00MakeKeys() {
USER=demouser
assertFalse "line ${LINENO}: already user $USER" "${EXE} keys get $USER"
newKey $USER
assertTrue "line ${LINENO}: no user $USER" "${EXE} keys get $USER"
# make sure bad password not accepted
assertFalse "accepts short password" "echo 123 | ${EXE} keys new badpass"
}
test01ListKeys() {
# one line plus the number of keys
assertEquals "2" $(${EXE} keys list | wc -l)
newKey foobar
assertEquals "3" $(${EXE} keys list | wc -l)
# we got the proper name here...
assertEquals "foobar" $(${EXE} keys list -o json | jq .[1].name | tr -d \" )
# we get all names in normal output
EXPECTEDNAMES=$(echo demouser; echo foobar)
TEXTNAMES=$(${EXE} keys list | tail -n +2 | cut -f1)
assertEquals "$EXPECTEDNAMES" "$TEXTNAMES"
# let's make sure the addresses match!
assertEquals "line ${LINENO}: text and json addresses don't match" $(${EXE} keys list | tail -1 | cut -f3) $(${EXE} keys list -o json | jq .[1].address | tr -d \")
}
test02updateKeys() {
USER=changer
PASS1=awsedrftgyhu
PASS2=S4H.9j.D9S7hso
PASS3=h8ybO7GY6d2
newKey $USER $PASS1
assertFalse "line ${LINENO}: accepts invalid pass" "updateKey $USER $PASS2 $PASS2"
assertTrue "line ${LINENO}: doesn't update" "updateKey $USER $PASS1 $PASS2"
assertTrue "line ${LINENO}: takes new key after update" "updateKey $USER $PASS2 $PASS3"
}
test03recoverKeys() {
USER=sleepy
PASS1=S4H.9j.D9S7hso
USER2=easy
PASS2=1234567890
# make a user and check they exist
KEY=$(echo $PASS1 | ${EXE} keys new $USER -o json)
if ! assertTrue "created $USER" $?; then return 1; fi
if [ -n "$DEBUG" ]; then echo $KEY; echo; fi
SEED=$(echo $KEY | jq .seed | tr -d \")
ADDR=$(echo $KEY | jq .key.address | tr -d \")
PUBKEY=$(echo $KEY | jq .key.pubkey | tr -d \")
assertTrue "line ${LINENO}" "${EXE} keys get $USER > /dev/null"
# let's delete this key
assertFalse "line ${LINENO}" "echo foo | ${EXE} keys delete $USER > /dev/null"
assertTrue "line ${LINENO}" "echo $PASS1 | ${EXE} keys delete $USER > /dev/null"
assertFalse "line ${LINENO}" "${EXE} keys get $USER > /dev/null"
# fails on short password
assertFalse "line ${LINENO}" "echo foo; echo $SEED | ${EXE} keys recover $USER2 -o json > /dev/null"
# fails on bad seed
assertFalse "line ${LINENO}" "echo $PASS2; echo \"silly white whale tower bongo\" | ${EXE} keys recover $USER2 -o json > /dev/null"
# now we got it
KEY2=$((echo $PASS2; echo $SEED) | ${EXE} keys recover $USER2 -o json)
if ! assertTrue "recovery failed: $KEY2" $?; then return 1; fi
if [ -n "$DEBUG" ]; then echo $KEY2; echo; fi
# make sure it looks the same
NAME2=$(echo $KEY2 | jq .name | tr -d \")
ADDR2=$(echo $KEY2 | jq .address | tr -d \")
PUBKEY2=$(echo $KEY2 | jq .pubkey | tr -d \")
assertEquals "line ${LINENO}: wrong username" "$USER2" "$NAME2"
assertEquals "line ${LINENO}: address doesn't match" "$ADDR" "$ADDR2"
assertEquals "line ${LINENO}: pubkey doesn't match" "$PUBKEY" "$PUBKEY2"
# and we can find the info
assertTrue "line ${LINENO}" "${EXE} keys get $USER2 > /dev/null"
}
# try recovery with secp256k1 keys
test03recoverSecp() {
USER=dings
PASS1=Sbub-U9byS7hso
USER2=booms
PASS2=1234567890
KEY=$(echo $PASS1 | ${EXE} keys new $USER -o json -t secp256k1)
if ! assertTrue "created $USER" $?; then return 1; fi
if [ -n "$DEBUG" ]; then echo $KEY; echo; fi
SEED=$(echo $KEY | jq .seed | tr -d \")
ADDR=$(echo $KEY | jq .key.address | tr -d \")
PUBKEY=$(echo $KEY | jq .key.pubkey | tr -d \")
assertTrue "line ${LINENO}" "${EXE} keys get $USER > /dev/null"
# now we got it
KEY2=$((echo $PASS2; echo $SEED) | ${EXE} keys recover $USER2 -o json)
if ! assertTrue "recovery failed: $KEY2" $?; then return 1; fi
if [ -n "$DEBUG" ]; then echo $KEY2; echo; fi
# make sure it looks the same
NAME2=$(echo $KEY2 | jq .name | tr -d \")
ADDR2=$(echo $KEY2 | jq .address | tr -d \")
PUBKEY2=$(echo $KEY2 | jq .pubkey | tr -d \")
assertEquals "line ${LINENO}: wrong username" "$USER2" "$NAME2"
assertEquals "line ${LINENO}: address doesn't match" "$ADDR" "$ADDR2"
assertEquals "line ${LINENO}: pubkey doesn't match" "$PUBKEY" "$PUBKEY2"
# and we can find the info
assertTrue "line ${LINENO}" "${EXE} keys get $USER2 > /dev/null"
}
# load and run these tests with shunit2!
# load and run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
CLI_DIR=$GOPATH/src/github.com/cosmos/cosmos-sdk/tests/cli
. $CLI_DIR/shunit2

View File

@ -1,161 +0,0 @@
#!/bin/bash
# These global variables are required for common.sh
SERVER_EXE=basecoin
CLIENT_EXE=basecli
ACCOUNTS=(jae ethan bucky rigel igor)
RICH=${ACCOUNTS[0]}
POOR=${ACCOUNTS[4]}
BPORT=7000
URL="localhost:${BPORT}"
oneTimeSetUp() {
if ! quickSetup .basecoin_test_rest rest-chain; then
exit 1;
fi
baseserver serve --port $BPORT >/dev/null &
sleep 0.1 # for startup
PID_PROXY=$!
disown
}
oneTimeTearDown() {
quickTearDown
kill -9 $PID_PROXY
}
# XXX Ex Usage: restAddr $NAME
# Desc: Gets the address for a key name via rest
restAddr() {
assertNotNull "line=${LINENO}, keyname required" "$1"
ADDR=$(curl ${URL}/keys/${1} 2>/dev/null | jq .address | tr -d \")
assertNotEquals "line=${LINENO}, null key" "null" "$ADDR"
assertNotEquals "line=${LINENO}, no key" "" "$ADDR"
echo $ADDR
}
# XXX Ex Usage: restAccount $ADDR $AMOUNT [$HEIGHT]
# Desc: Assumes just one coin, checks the balance of first coin in any case
restAccount() {
assertNotNull "line=${LINENO}, address required" "$1"
QUERY=${URL}/query/account/sigs:$1
if [ -n "$3" ]; then
QUERY="${QUERY}?height=${3}"
fi
ACCT=$(curl ${QUERY} 2>/dev/null)
if [ -n "$DEBUG" ]; then echo $QUERY; echo $ACCT; echo; fi
assertEquals "line=${LINENO}, proper money" "$2" $(echo $ACCT | jq .data.coins[0].amount)
return $?
}
restNoAccount() {
ERROR=$(curl ${URL}/query/account/sigs:$1 2>/dev/null)
assertEquals "line=${LINENO}, should error" 400 $(echo $ERROR | jq .code)
}
test00GetAccount() {
RECV=$(restAddr $POOR)
SENDER=$(restAddr $RICH)
restNoAccount $RECV
restAccount $SENDER "9007199254740992"
}
test01SendTx() {
SENDER=$(restAddr $RICH)
RECV=$(restAddr $POOR)
CMD="{\"from\": {\"app\": \"sigs\", \"addr\": \"$SENDER\"}, \"to\": {\"app\": \"sigs\", \"addr\": \"$RECV\"}, \"amount\": [{\"denom\": \"mycoin\", \"amount\": 992}], \"sequence\": 1}"
UNSIGNED=$(curl -XPOST ${URL}/build/send -d "$CMD" 2>/dev/null)
if [ -n "$DEBUG" ]; then echo $UNSIGNED; echo; fi
TOSIGN="{\"name\": \"$RICH\", \"password\": \"qwertyuiop\", \"tx\": $UNSIGNED}"
SIGNED=$(curl -XPOST ${URL}/sign -d "$TOSIGN" 2>/dev/null)
TX=$(curl -XPOST ${URL}/tx -d "$SIGNED" 2>/dev/null)
if [ -n "$DEBUG" ]; then echo $TX; echo; fi
txSucceeded $? "$TX" "$RECV"
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
restAccount $SENDER "9007199254740000" "$TX_HEIGHT"
restAccount $RECV "992" "$TX_HEIGHT"
# Make sure tx is indexed
checkSendTx $HASH $TX_HEIGHT $SENDER "992"
}
# XXX Ex Usage: restCreateRole $PAYLOAD $EXPECTED
# Desc: Tests that the first returned signer.addr matches the expected
restCreateRole() {
assertNotNull "line=${LINENO}, data required" "$1"
ROLE=$(curl ${URL}/build/create_role --data "$1" 2>/dev/null)
if [ -n "$DEBUG" ]; then echo -e "$ROLE\n"; fi
assertEquals "line=${LINENO}, role required" "$2" $(echo $ROLE | jq .data.tx.data.signers[0].addr)
return $?
}
test03CreateRole() {
DATA="{\"role\": \"726f6c65\", \"seq\": 1, \"min_sigs\": 1, \"signers\": [{\"addr\": \"4FF759D47C81754D8F553DCCAC8651D0AF74C7F9\", \"app\": \"role\"}]}"
restCreateRole "$DATA" \""4FF759D47C81754D8F553DCCAC8651D0AF74C7F9"\"
}
test04CreateRoleInvalid() {
ERROR=$(curl ${URL}/build/create_role --data '{}' 2>/dev/null)
assertEquals "line=${LINENO}, should report validation failed" 0 $(echo $ERROR | grep "failed" > /dev/null && echo 0 || echo 1)
ERROR=$(curl ${URL}/build/create_role --data '{"role": "foo"}' 2>/dev/null)
assertEquals "line=${LINENO}, should report validation failed" 0 $(echo $ERROR | grep "failed" > /dev/null && echo 0 || echo 1)
ERROR=$(curl ${URL}/build/create_role --data '{"min_sigs": 2, "role": "abcdef"}' 2>/dev/null)
assertEquals "line=${LINENO}, should report validation failed" 0 $(echo $ERROR | grep "failed" > /dev/null && echo 0 || echo 1)
## Non-hex roles should be rejected
ERROR=$(curl ${URL}/build/create_role --data "{\"role\": \"foobar\", \"seq\": 2, \"signers\": [{\"addr\": \"4FF759D47C81754D8F553DCCAC8651D0AF74C7F9\", \"app\": \"role\"}], \"min_sigs\": 1}" 2>/dev/null)
assertEquals "line=${LINENO}, should report validation failed" 0 $(echo $ERROR | grep "invalid hex" > /dev/null && echo 0 || echo 1)
}
# test02SendTxWithFee() {
# SENDER=$(getAddr $RICH)
# RECV=$(getAddr $POOR)
# # Test to see if the auto-sequencing works, the sequence here should be calculated to be 2
# TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=90mycoin --fee=10mycoin --to=$RECV --name=$RICH)
# txSucceeded $? "$TX" "$RECV"
# HASH=$(echo $TX | jq .hash | tr -d \")
# TX_HEIGHT=$(echo $TX | jq .height)
# # deduct 100 from sender, add 90 to receiver... fees "vanish"
# checkAccount $SENDER "9007199254739900" "$TX_HEIGHT"
# checkAccount $RECV "1082" "$TX_HEIGHT"
# # Make sure tx is indexed
# checkSendFeeTx $HASH $TX_HEIGHT $SENDER "90" "10"
# # assert replay protection
# TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=90mycoin --fee=10mycoin --sequence=2 --to=$RECV --name=$RICH 2>/dev/null)
# assertFalse "line=${LINENO}, replay: $TX" $?
# checkAccount $SENDER "9007199254739900" "$TX_HEIGHT"
# checkAccount $RECV "1082" "$TX_HEIGHT"
# # make sure we can query the proper nonce
# NONCE=$(${CLIENT_EXE} query nonce $SENDER)
# if [ -n "$DEBUG" ]; then echo $NONCE; echo; fi
# # TODO: note that cobra returns error code 0 on parse failure,
# # so currently this check passes even if there is no nonce query command
# if assertTrue "line=${LINENO}, no nonce query" $?; then
# assertEquals "line=${LINENO}, proper nonce" "2" $(echo $NONCE | jq .data)
# fi
# }
# Load common then run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
CLI_DIR=$GOPATH/src/github.com/cosmos/cosmos-sdk/tests/cli
. $CLI_DIR/common.sh
. $CLI_DIR/shunit2

View File

@ -1,85 +0,0 @@
#!/bin/bash
# these are two globals to control all scripts (can use eg. counter instead)
SERVER_EXE=basecoin
CLIENT_EXE=basecli
ACCOUNTS=(jae ethan bucky rigel igor)
RICH=${ACCOUNTS[0]}
POOR=${ACCOUNTS[4]}
oneTimeSetUp() {
if ! quickSetup .basecoin_test_restart restart-chain; then
exit 1;
fi
}
oneTimeTearDown() {
quickTearDown
}
test00PreRestart() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=992mycoin --sequence=1 --to=$RECV --name=$RICH)
txSucceeded $? "$TX" "$RECV"
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
checkAccount $SENDER "9007199254740000" "$TX_HEIGHT"
checkAccount $RECV "992" "$TX_HEIGHT"
# make sure tx is indexed
checkSendTx $HASH $TX_HEIGHT $SENDER "992"
}
test01OnRestart() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=10000mycoin --sequence=2 --to=$RECV --name=$RICH)
txSucceeded $? "$TX" "$RECV"
if [ $? != 0 ]; then echo "can't make tx!"; return 1; fi
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
# wait til we have quite a few blocks... like at least 20,
# so the query command won't just wait for the next eg. 7 blocks to verify the result
echo "waiting to generate lots of blocks..."
sleep 5
echo "done waiting!"
# last minute tx just at the block cut-off...
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=20000mycoin --sequence=3 --to=$RECV --name=$RICH)
txSucceeded $? "$TX" "$RECV"
if [ $? != 0 ]; then echo "can't make second tx!"; return 1; fi
# now we do a restart...
quickTearDown
startServer $BASE_DIR/server $BASE_DIR/${SERVER_EXE}.log
if [ $? != 0 ]; then echo "can't restart server!"; return 1; fi
# make sure queries still work properly, with all 3 tx now executed
echo "Checking state after restart..."
checkAccount $SENDER "9007199254710000"
checkAccount $RECV "30992"
# make sure tx is indexed
checkSendTx $HASH $TX_HEIGHT $SENDER "10000"
# for double-check of logs
if [ -n "$DEBUG" ]; then
cat $BASE_DIR/${SERVER_EXE}.log;
fi
}
# Load common then run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
CLI_DIR=$GOPATH/src/github.com/cosmos/cosmos-sdk/tests/cli
. $CLI_DIR/common.sh
. $CLI_DIR/shunit2

View File

@ -1,97 +0,0 @@
#!/bin/bash
# These global variables are required for common.sh
SERVER_EXE=basecoin
CLIENT_EXE=basecli
ACCOUNTS=(jae ethan bucky rigel igor)
RICH=${ACCOUNTS[0]}
POOR=${ACCOUNTS[4]}
DUDE=${ACCOUNTS[2]}
ROLE="10CAFE4E"
oneTimeSetUp() {
if ! quickSetup .basecoin_test_roles roles-chain; then
exit 1;
fi
}
oneTimeTearDown() {
quickTearDown
}
test01SetupRole() {
ONE=$(getAddr $RICH)
TWO=$(getAddr $POOR)
THREE=$(getAddr $DUDE)
MEMBERS=${ONE},${TWO},${THREE}
SIGS=2
assertFalse "line=${LINENO}, missing min-sigs" "echo qwertyuiop | ${CLIENT_EXE} tx create-role --role=${ROLE} --members=${MEMBERS} --sequence=1 --name=$RICH"
assertFalse "line=${LINENO}, missing members" "echo qwertyuiop | ${CLIENT_EXE} tx create-role --role=${ROLE} --min-sigs=2 --sequence=1 --name=$RICH"
assertFalse "line=${LINENO}, missing role" "echo qwertyuiop | ${CLIENT_EXE} tx create-role --min-sigs=2 --members=${MEMBERS} --sequence=1 --name=$RICH"
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx create-role --role=${ROLE} --min-sigs=$SIGS --members=${MEMBERS} --sequence=1 --name=$RICH)
txSucceeded $? "$TX" "${ROLE}"
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
checkRole "${ROLE}" $SIGS 3 "$TX_HEIGHT"
# Make sure tx is indexed
checkRoleTx $HASH $TX_HEIGHT "${ROLE}" 3
}
test02SendTxToRole() {
SENDER=$(getAddr $RICH)
RECV=role:${ROLE}
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --fee=90mycoin --amount=10000mycoin --to=$RECV --sequence=2 --name=$RICH)
txSucceeded $? "$TX" "${ROLE}"
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
# reduce by 10090
checkAccount $SENDER "9007199254730902" "$TX_HEIGHT"
checkAccount $RECV "10000" "$TX_HEIGHT"
checkSendFeeTx $HASH $TX_HEIGHT $SENDER "10000" "90"
}
test03SendMultiFromRole() {
ONE=$(getAddr $RICH)
TWO=$(getAddr $POOR)
THREE=$(getAddr $DUDE)
BANK=role:${ROLE}
# no money to start mr. poor...
assertFalse "line=${LINENO}, has no money yet" "${CLIENT_EXE} query account $TWO 2>/dev/null"
# let's try to send money from the role directly without multisig
FAIL=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=6000mycoin --from=$BANK --to=$TWO --sequence=1 --name=$POOR 2>/dev/null)
assertFalse "need to assume role" $?
FAIL=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=6000mycoin --from=$BANK --to=$TWO --sequence=2 --assume-role=${ROLE} --name=$POOR 2>/dev/null)
assertFalse "need two signatures" $?
# okay, begin a multisig transaction mr. poor...
TX_FILE=$BASE_DIR/tx.json
echo qwertyuiop | ${CLIENT_EXE} tx send --amount=6000mycoin --from=$BANK --to=$TWO --sequence=1 --assume-role=${ROLE} --name=$POOR --multi --prepare=$TX_FILE
assertTrue "line=${LINENO}, successfully prepare tx" $?
# and get some dude to sign it
# FAIL=$(echo qwertyuiop | ${CLIENT_EXE} tx --in=$TX_FILE --name=$POOR 2>/dev/null)
# assertFalse "line=${LINENO}, double signing doesn't get bank" $?
# and get some dude to sign it for the full access
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx --in=$TX_FILE --name=$DUDE)
txSucceeded $? "$TX" "multi-bank"
TX_HEIGHT=$(echo $TX | jq .height)
checkAccount $TWO "6000" "$TX_HEIGHT"
checkAccount $BANK "4000" "$TX_HEIGHT"
}
# Load common then run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
CLI_DIR=$GOPATH/src/github.com/cosmos/cosmos-sdk/tests/cli
. $CLI_DIR/common.sh
. $CLI_DIR/shunit2

View File

@ -1,132 +0,0 @@
#!/bin/bash
CLIENT_EXE=basecli
SERVER_EXE=basecoin
oneTimeSetUp() {
BASE=~/.bc_init_test
rm -rf "$BASE"
mkdir -p "$BASE"
SERVER="${BASE}/server"
SERVER_LOG="${BASE}/${SERVER_EXE}.log"
HEX="deadbeef1234deadbeef1234deadbeef1234aaaa"
${SERVER_EXE} init ${HEX} --home="$SERVER" >> "$SERVER_LOG"
if ! assertTrue "line=${LINENO}" $?; then return 1; fi
GENESIS_FILE=${SERVER}/genesis.json
CHAIN_ID=$(cat ${GENESIS_FILE} | jq .chain_id | tr -d \")
printf "starting ${SERVER_EXE}...\n"
${SERVER_EXE} start --home="$SERVER" >> "$SERVER_LOG" 2>&1 &
sleep 5
PID_SERVER=$!
disown
if ! ps $PID_SERVER >/dev/null; then
echo "**STARTUP FAILED**"
cat $SERVER_LOG
return 1
fi
# this sets the base for all client queries in the tests
export BCHOME=${BASE}/client
${CLIENT_EXE} init --node=tcp://localhost:46657 --genesis=${GENESIS_FILE} > /dev/null 2>&1
if ! assertTrue "line=${LINENO}, initialized light-client" "$?"; then
return 1
fi
}
oneTimeTearDown() {
printf "\nstopping ${SERVER_EXE}..."
kill -9 $PID_SERVER >/dev/null 2>&1
sleep 1
}
test01GetInsecure() {
GENESIS=$(${CLIENT_EXE} rpc genesis)
assertTrue "line=${LINENO}, get genesis" "$?"
MYCHAIN=$(echo ${GENESIS} | jq .genesis.chain_id | tr -d \")
assertEquals "line=${LINENO}, genesis chain matches" "${CHAIN_ID}" "${MYCHAIN}"
STATUS=$(${CLIENT_EXE} rpc status)
assertTrue "line=${LINENO}, get status" "$?"
SHEIGHT=$(echo ${STATUS} | jq .latest_block_height)
assertTrue "line=${LINENO}, parsed status" "$?"
assertNotNull "line=${LINENO}, has a height" "${SHEIGHT}"
VALS=$(${CLIENT_EXE} rpc validators)
assertTrue "line=${LINENO}, get validators" "$?"
VHEIGHT=$(echo ${VALS} | jq .block_height)
assertTrue "line=${LINENO}, parsed validators" "$?"
assertTrue "line=${LINENO}, sensible heights: $SHEIGHT / $VHEIGHT" "test $VHEIGHT -ge $SHEIGHT"
VCNT=$(echo ${VALS} | jq '.validators | length')
assertEquals "line=${LINENO}, one validator" "1" "$VCNT"
INFO=$(${CLIENT_EXE} rpc info)
assertTrue "line=${LINENO}, get info" "$?"
DATA=$(echo $INFO | jq .response.data)
assertEquals "line=${LINENO}, basecoin info" '"basecoin v0.7.1"' "$DATA"
}
test02GetSecure() {
HEIGHT=$(${CLIENT_EXE} rpc status | jq .latest_block_height)
assertTrue "line=${LINENO}, get status" "$?"
# check block produces something reasonable
assertFalse "line=${LINENO}, missing height" "${CLIENT_EXE} rpc block"
BLOCK=$(${CLIENT_EXE} rpc block --height=$HEIGHT)
assertTrue "line=${LINENO}, get block" "$?"
MHEIGHT=$(echo $BLOCK | jq .block_meta.header.height)
assertEquals "line=${LINENO}, meta height" "${HEIGHT}" "${MHEIGHT}"
BHEIGHT=$(echo $BLOCK | jq .block.header.height)
assertEquals "line=${LINENO}, meta height" "${HEIGHT}" "${BHEIGHT}"
# check commit produces something reasonable
assertFalse "line=${LINENO}, missing height" "${CLIENT_EXE} rpc commit"
let "CHEIGHT = $HEIGHT - 1"
COMMIT=$(${CLIENT_EXE} rpc commit --height=$CHEIGHT)
assertTrue "line=${LINENO}, get commit" "$?"
HHEIGHT=$(echo $COMMIT | jq .header.height)
assertEquals "line=${LINENO}, commit height" "${CHEIGHT}" "${HHEIGHT}"
assertEquals "line=${LINENO}, canonical" "true" $(echo $COMMIT | jq .canonical)
BSIG=$(echo $BLOCK | jq .block.last_commit)
CSIG=$(echo $COMMIT | jq .commit)
assertEquals "line=${LINENO}, block and commit" "$BSIG" "$CSIG"
# now let's get some headers
# assertFalse "missing height" "${CLIENT_EXE} rpc headers"
HEADERS=$(${CLIENT_EXE} rpc headers --min=$CHEIGHT --max=$HEIGHT)
assertTrue "line=${LINENO}, get headers" "$?"
assertEquals "line=${LINENO}, proper height" "$HEIGHT" $(echo $HEADERS | jq '.block_metas[0].header.height')
assertEquals "line=${LINENO}, two headers" "2" $(echo $HEADERS | jq '.block_metas | length')
# should we check these headers?
CHEAD=$(echo $COMMIT | jq .header)
# most recent first, so the commit header is second....
HHEAD=$(echo $HEADERS | jq .block_metas[1].header)
assertEquals "line=${LINENO}, commit and header" "$CHEAD" "$HHEAD"
}
test03Waiting() {
START=$(${CLIENT_EXE} rpc status | jq .latest_block_height)
assertTrue "line=${LINENO}, get status" "$?"
let "NEXT = $START + 5"
assertFalse "line=${LINENO}, no args" "${CLIENT_EXE} rpc wait"
assertFalse "line=${LINENO}, too long" "${CLIENT_EXE} rpc wait --height=1234"
assertTrue "line=${LINENO}, normal wait" "${CLIENT_EXE} rpc wait --height=$NEXT"
STEP=$(${CLIENT_EXE} rpc status | jq .latest_block_height)
assertEquals "line=${LINENO}, wait until height" "$NEXT" "$STEP"
let "NEXT = $STEP + 3"
assertTrue "line=${LINENO}, ${CLIENT_EXE} rpc wait --delta=3"
STEP=$(${CLIENT_EXE} rpc status | jq .latest_block_height)
assertEquals "line=${LINENO}, wait for delta" "$NEXT" "$STEP"
}
# load and run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
CLI_DIR=$GOPATH/src/github.com/cosmos/cosmos-sdk/tests/cli
. $CLI_DIR/shunit2

View File

@ -1,14 +0,0 @@
LINKER_FLAGS:="-X github.com/cosmos/cosmos-sdk/client/commands.CommitHash=`git rev-parse --short HEAD`"
install:
@go install -ldflags $(LINKER_FLAGS) ./cmd/...
test: test_unit test_cli
test_unit:
@go test `glide novendor`
test_cli:
./tests/cli/counter.sh
.PHONY: install test test_unit test_cli

View File

@ -1,36 +0,0 @@
package main
import (
"os"
"github.com/spf13/cobra"
"github.com/tendermint/tmlibs/cli"
client "github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/examples/counter/plugins/counter"
"github.com/cosmos/cosmos-sdk/server/commands"
)
// RootCmd is the entry point for this binary
var RootCmd = &cobra.Command{
Use: "counter",
Short: "demo application for cosmos sdk",
}
func main() {
// TODO: register the counter here
commands.Handler = counter.NewHandler("mycoin")
RootCmd.AddCommand(
commands.InitCmd,
commands.StartCmd,
commands.UnsafeResetAllCmd,
client.VersionCmd,
)
commands.SetUpRoot(RootCmd)
cmd := cli.PrepareMainCmd(RootCmd, "CT", os.ExpandEnv("$HOME/.counter"))
cmd.Execute()
}

View File

@ -1,52 +0,0 @@
package commands
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
sdk "github.com/cosmos/cosmos-sdk"
txcmd "github.com/cosmos/cosmos-sdk/client/commands/txs"
"github.com/cosmos/cosmos-sdk/examples/counter/plugins/counter"
"github.com/cosmos/cosmos-sdk/modules/coin"
)
//CounterTxCmd is the CLI command to execute the counter
// through the appTx Command
var CounterTxCmd = &cobra.Command{
Use: "counter",
Short: "add a vote to the counter",
Long: `Add a vote to the counter.
You must pass --valid for it to count and the countfee will be added to the counter.`,
RunE: counterTx,
}
// nolint - flags names
const (
FlagCountFee = "countfee"
FlagValid = "valid"
)
func init() {
fs := CounterTxCmd.Flags()
fs.String(FlagCountFee, "", "Coins to send in the format <amt><coin>,<amt><coin>...")
fs.Bool(FlagValid, false, "Is count valid?")
}
func counterTx(cmd *cobra.Command, args []string) error {
tx, err := readCounterTxFlags()
if err != nil {
return err
}
return txcmd.DoTx(tx)
}
func readCounterTxFlags() (tx sdk.Tx, err error) {
feeCoins, err := coin.ParseCoins(viper.GetString(FlagCountFee))
if err != nil {
return tx, err
}
tx = counter.NewTx(viper.GetBool(FlagValid), feeCoins)
return tx, nil
}

View File

@ -1,32 +0,0 @@
package commands
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/client/commands/query"
"github.com/cosmos/cosmos-sdk/examples/counter/plugins/counter"
"github.com/cosmos/cosmos-sdk/stack"
)
//CounterQueryCmd - CLI command to query the counter state
var CounterQueryCmd = &cobra.Command{
Use: "counter",
Short: "Query counter state, with proof",
RunE: counterQueryCmd,
}
func counterQueryCmd(cmd *cobra.Command, args []string) error {
var cp counter.State
prove := !viper.GetBool(commands.FlagTrustNode)
key := stack.PrefixedKey(counter.NameCounter, counter.StateKey())
h, err := query.GetParsed(key, &cp, query.GetHeight(), prove)
if err != nil {
return err
}
return query.OutputProof(cp, h)
}

View File

@ -1,85 +0,0 @@
package main
import (
"os"
"github.com/spf13/cobra"
"github.com/tendermint/tmlibs/cli"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/client/commands/commits"
"github.com/cosmos/cosmos-sdk/client/commands/keys"
"github.com/cosmos/cosmos-sdk/client/commands/proxy"
"github.com/cosmos/cosmos-sdk/client/commands/query"
txcmd "github.com/cosmos/cosmos-sdk/client/commands/txs"
bcount "github.com/cosmos/cosmos-sdk/examples/counter/cmd/countercli/commands"
authcmd "github.com/cosmos/cosmos-sdk/modules/auth/commands"
basecmd "github.com/cosmos/cosmos-sdk/modules/base/commands"
coincmd "github.com/cosmos/cosmos-sdk/modules/coin/commands"
feecmd "github.com/cosmos/cosmos-sdk/modules/fee/commands"
noncecmd "github.com/cosmos/cosmos-sdk/modules/nonce/commands"
)
// CounterCli represents the base command when called without any subcommands
var CounterCli = &cobra.Command{
Use: "countercli",
Short: "Example app built using the Cosmos SDK",
Long: `Countercli is a demo app that includes custom logic to
present a formatted interface to a custom blockchain structure.
This is a useful tool and also serves to demonstrate how to configure
the Cosmos SDK to work for any custom ABCI app, see:
`,
}
func main() {
commands.AddBasicFlags(CounterCli)
// Prepare queries
query.RootCmd.AddCommand(
// These are default parsers, optional in your app
query.TxQueryCmd,
query.KeyQueryCmd,
coincmd.AccountQueryCmd,
noncecmd.NonceQueryCmd,
// XXX IMPORTANT: here is how you add custom query commands in your app
bcount.CounterQueryCmd,
)
// set up the middleware
txcmd.Middleware = txcmd.Wrappers{
feecmd.FeeWrapper{},
noncecmd.NonceWrapper{},
basecmd.ChainWrapper{},
authcmd.SigWrapper{},
}
txcmd.Middleware.Register(txcmd.RootCmd.PersistentFlags())
// Prepare transactions
txcmd.RootCmd.AddCommand(
// This is the default transaction, optional in your app
coincmd.SendTxCmd,
// XXX IMPORTANT: here is how you add custom tx construction for your app
bcount.CounterTxCmd,
)
// Set up the various commands to use
CounterCli.AddCommand(
commands.InitCmd,
commands.ResetCmd,
commands.VersionCmd,
keys.RootCmd,
commits.RootCmd,
query.RootCmd,
txcmd.RootCmd,
proxy.RootCmd,
)
cmd := cli.PrepareMainCmd(CounterCli, "CTL", os.ExpandEnv("$HOME/.countercli"))
cmd.Execute()
}

View File

@ -1,226 +0,0 @@
package counter
import (
"fmt"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-wire"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/errors"
"github.com/cosmos/cosmos-sdk/modules/auth"
"github.com/cosmos/cosmos-sdk/modules/base"
"github.com/cosmos/cosmos-sdk/modules/coin"
"github.com/cosmos/cosmos-sdk/modules/fee"
"github.com/cosmos/cosmos-sdk/modules/ibc"
"github.com/cosmos/cosmos-sdk/modules/nonce"
"github.com/cosmos/cosmos-sdk/modules/roles"
"github.com/cosmos/cosmos-sdk/stack"
"github.com/cosmos/cosmos-sdk/state"
)
// Tx
//--------------------------------------------------------------------------------
// register the tx type with it's validation logic
// make sure to use the name of the handler as the prefix in the tx type,
// so it gets routed properly
const (
NameCounter = "cntr"
ByteTx = 0x2F //TODO What does this byte represent should use typebytes probably
TypeTx = NameCounter + "/count"
)
func init() {
sdk.TxMapper.RegisterImplementation(Tx{}, TypeTx, ByteTx)
}
// Tx - struct for all counter transactions
type Tx struct {
Valid bool `json:"valid"`
Fee coin.Coins `json:"fee"`
}
// NewTx - return a new counter transaction struct wrapped as a basecoin transaction
func NewTx(valid bool, fee coin.Coins) sdk.Tx {
return Tx{
Valid: valid,
Fee: fee,
}.Wrap()
}
// Wrap - Wrap a Tx as a Basecoin Tx, used to satisfy the XXX interface
func (c Tx) Wrap() sdk.Tx {
return sdk.Tx{TxInner: c}
}
// ValidateBasic just makes sure the Fee is a valid, non-negative value
func (c Tx) ValidateBasic() error {
if !c.Fee.IsValid() {
return coin.ErrInvalidCoins()
}
if !c.Fee.IsNonnegative() {
return coin.ErrInvalidCoins()
}
return nil
}
// Custom errors
//--------------------------------------------------------------------------------
var (
errInvalidCounter = fmt.Errorf("Counter Tx marked invalid")
)
// ErrInvalidCounter - custom error class
func ErrInvalidCounter() error {
return errors.WithCode(errInvalidCounter, abci.CodeType_BaseInvalidInput)
}
// IsInvalidCounterErr - custom error class check
func IsInvalidCounterErr(err error) bool {
return errors.IsSameError(errInvalidCounter, err)
}
// ErrDecoding - This is just a helper function to return a generic "internal error"
func ErrDecoding() error {
return errors.ErrInternal("Error decoding state")
}
// Counter Handler
//--------------------------------------------------------------------------------
// NewHandler returns a new counter transaction processing handler
func NewHandler(feeDenom string) sdk.Handler {
return stack.New(
base.Logger{},
stack.Recovery{},
auth.Signatures{},
base.Chain{},
stack.Checkpoint{OnCheck: true},
nonce.ReplayCheck{},
).
IBC(ibc.NewMiddleware()).
Apps(
roles.NewMiddleware(),
fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank),
stack.Checkpoint{OnDeliver: true},
).
Dispatch(
coin.NewHandler(),
Handler{},
)
}
// Handler the counter transaction processing handler
type Handler struct {
stack.PassInitState
stack.PassInitValidate
}
var _ stack.Dispatchable = Handler{}
// Name - return counter namespace
func (Handler) Name() string {
return NameCounter
}
// AssertDispatcher - placeholder to satisfy XXX
func (Handler) AssertDispatcher() {}
// CheckTx checks if the tx is properly structured
func (h Handler) CheckTx(ctx sdk.Context, store state.SimpleDB, tx sdk.Tx, _ sdk.Checker) (res sdk.CheckResult, err error) {
_, err = checkTx(ctx, tx)
return
}
// DeliverTx executes the tx if valid
func (h Handler) DeliverTx(ctx sdk.Context, store state.SimpleDB, tx sdk.Tx, dispatch sdk.Deliver) (res sdk.DeliverResult, err error) {
ctr, err := checkTx(ctx, tx)
if err != nil {
return res, err
}
// note that we don't assert this on CheckTx (ValidateBasic),
// as we allow them to be writen to the chain
if !ctr.Valid {
return res, ErrInvalidCounter()
}
// handle coin movement.... like, actually decrement the other account
if !ctr.Fee.IsZero() {
// take the coins and put them in out account!
senders := ctx.GetPermissions("", auth.NameSigs)
if len(senders) == 0 {
return res, errors.ErrMissingSignature()
}
in := []coin.TxInput{{Address: senders[0], Coins: ctr.Fee}}
out := []coin.TxOutput{{Address: StoreActor(), Coins: ctr.Fee}}
send := coin.NewSendTx(in, out)
// if the deduction fails (too high), abort the command
_, err = dispatch.DeliverTx(ctx, store, send)
if err != nil {
return res, err
}
}
// update the counter
state, err := LoadState(store)
if err != nil {
return res, err
}
state.Counter++
state.TotalFees = state.TotalFees.Plus(ctr.Fee)
err = SaveState(store, state)
return res, err
}
func checkTx(ctx sdk.Context, tx sdk.Tx) (ctr Tx, err error) {
ctr, ok := tx.Unwrap().(Tx)
if !ok {
return ctr, errors.ErrInvalidFormat(TypeTx, tx)
}
err = ctr.ValidateBasic()
if err != nil {
return ctr, err
}
return ctr, nil
}
// CounterStore
//--------------------------------------------------------------------------------
// StoreActor - return the basecoin actor for the account
func StoreActor() sdk.Actor {
return sdk.Actor{App: NameCounter, Address: []byte{0x04, 0x20}} //XXX what do these bytes represent? - should use typebyte variables
}
// State - state of the counter applicaton
type State struct {
Counter int `json:"counter"`
TotalFees coin.Coins `json:"total_fees"`
}
// StateKey - store key for the counter state
func StateKey() []byte {
return []byte("state")
}
// LoadState - retrieve the counter state from the store
func LoadState(store state.SimpleDB) (state State, err error) {
bytes := store.Get(StateKey())
if len(bytes) > 0 {
err = wire.ReadBinaryBytes(bytes, &state)
if err != nil {
return state, errors.ErrDecoding()
}
}
return state, nil
}
// SaveState - save the counter state to the provided store
func SaveState(store state.SimpleDB, state State) error {
bytes := wire.BinaryBytes(state)
store.Set(StateKey(), bytes)
return nil
}

View File

@ -1,73 +0,0 @@
package counter
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-wire"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/app"
"github.com/cosmos/cosmos-sdk/modules/auth"
"github.com/cosmos/cosmos-sdk/modules/base"
"github.com/cosmos/cosmos-sdk/modules/coin"
"github.com/cosmos/cosmos-sdk/modules/nonce"
)
func TestCounterPlugin(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
// Basecoin initialization
chainID := "test_chain_id"
logger := log.TestingLogger()
// logger := log.NewTracingLogger(log.NewTMLogger(os.Stdout))
h := NewHandler("gold")
store, err := app.MockStoreApp("counter", logger)
require.Nil(err, "%+v", err)
bcApp := app.NewBaseApp(store, h, nil)
err = bcApp.InitState("base", "chain_id", chainID)
require.Nil(err, "%+v", err)
// Account initialization
bal := coin.Coins{{"", 1000}, {"gold", 1000}}
acct := coin.NewAccountWithKey(bal)
err = bcApp.InitState("coin", "account", acct.MakeOption())
require.Nil(err, "%+v", err)
// Deliver a CounterTx
DeliverCounterTx := func(valid bool, counterFee coin.Coins, sequence uint32) abci.Result {
tx := NewTx(valid, counterFee)
tx = nonce.NewTx(sequence, []sdk.Actor{acct.Actor()}, tx)
tx = base.NewChainTx(chainID, 0, tx)
stx := auth.NewSig(tx)
auth.Sign(stx, acct.Key)
txBytes := wire.BinaryBytes(stx.Wrap())
return bcApp.DeliverTx(txBytes)
}
// Test a basic send, no fee
res := DeliverCounterTx(true, nil, 1)
assert.True(res.IsOK(), res.String())
// Test an invalid send, no fee
res = DeliverCounterTx(false, nil, 2)
assert.True(res.IsErr(), res.String())
// Test an invalid sequence
res = DeliverCounterTx(true, nil, 2)
assert.True(res.IsErr(), res.String())
// Test an valid send, with supported fee
res = DeliverCounterTx(true, coin.Coins{{"gold", 100}}, 3)
assert.True(res.IsOK(), res.String())
// Test unsupported fee
res = DeliverCounterTx(true, coin.Coins{{"silver", 100}}, 4)
assert.True(res.IsErr(), res.String())
}

View File

@ -1,124 +0,0 @@
#!/bin/bash
# These global variables are required for common.sh
SERVER_EXE=counter
CLIENT_EXE=countercli
ACCOUNTS=(jae ethan bucky rigel igor)
RICH=${ACCOUNTS[0]}
POOR=${ACCOUNTS[4]}
oneTimeSetUp() {
if ! quickSetup .basecoin_test_counter counter-chain; then
exit 1;
fi
}
oneTimeTearDown() {
quickTearDown
}
test00GetAccount() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
assertFalse "Line=${LINENO}, requires arg" "${CLIENT_EXE} query account"
checkAccount $SENDER "9007199254740992"
ACCT2=$(${CLIENT_EXE} query account $RECV 2>/dev/null)
assertFalse "Line=${LINENO}, has no genesis account" $?
}
test01SendTx() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
# sequence should work well for first time also
assertFalse "Line=${LINENO}, missing dest" "${CLIENT_EXE} tx send --amount=992mycoin 2>/dev/null"
assertFalse "Line=${LINENO}, bad password" "echo foo | ${CLIENT_EXE} tx send --amount=992mycoin --to=$RECV --name=$RICH 2>/dev/null"
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=992mycoin --to=$RECV --name=$RICH)
txSucceeded $? "$TX" "$RECV"
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
checkAccount $SENDER "9007199254740000" "$TX_HEIGHT"
checkAccount $RECV "992" "$TX_HEIGHT"
# make sure tx is indexed
checkSendTx $HASH $TX_HEIGHT $SENDER "992"
}
test02GetCounter() {
COUNT=$(${CLIENT_EXE} query counter 2>/dev/null)
assertFalse "Line=${LINENO}, no default count" $?
}
# checkCounter $COUNT $BALANCE [$HEIGHT]
# Assumes just one coin, checks the balance of first coin in any case
# pass optional height to query which block to query
checkCounter() {
# default height of 0, but accept an argument
HEIGHT=${3:-0}
# make sure sender goes down
ACCT=$(${CLIENT_EXE} query counter --height=$HEIGHT)
if assertTrue "Line=${LINENO}, count is set" $?; then
assertEquals "Line=${LINENO}, proper count" "$1" $(echo $ACCT | jq .data.counter)
assertEquals "Line=${LINENO}, proper money" "$2" $(echo $ACCT | jq .data.total_fees[0].amount)
fi
}
test03AddCount() {
SENDER=$(getAddr $RICH)
assertFalse "Line=${LINENO}, bad password" "echo hi | ${CLIENT_EXE} tx counter --countfee=100mycoin --sequence=2 --name=${RICH} 2>/dev/null"
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx counter --countfee=10mycoin --sequence=2 --name=${RICH} --valid)
txSucceeded $? "$TX" "counter"
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
# make sure the counter was updated
checkCounter "1" "10" "$TX_HEIGHT"
# make sure the account was debited
checkAccount $SENDER "9007199254739990" "$TX_HEIGHT"
# make sure tx is indexed
TX=$(${CLIENT_EXE} query tx $HASH --trace)
if assertTrue "Line=${LINENO}, found tx" $?; then
assertEquals "Line=${LINENO}, proper height" $TX_HEIGHT $(echo $TX | jq .height)
assertEquals "Line=${LINENO}, type=sigs/one" '"sigs/one"' $(echo $TX | jq .data.type)
CTX=$(echo $TX | jq .data.data.tx)
assertEquals "Line=${LINENO}, type=chain/tx" '"chain/tx"' $(echo $CTX | jq .type)
NTX=$(echo $CTX | jq .data.tx)
assertEquals "line=${LINENO}, type=nonce" '"nonce"' $(echo $NTX | jq .type)
CNTX=$(echo $NTX | jq .data.tx)
assertEquals "Line=${LINENO}, type=cntr/count" '"cntr/count"' $(echo $CNTX | jq .type)
assertEquals "Line=${LINENO}, proper fee" "10" $(echo $CNTX | jq .data.fee[0].amount)
fi
# test again with fees...
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx counter --countfee=7mycoin --fee=4mycoin --sequence=3 --name=${RICH} --valid)
txSucceeded $? "$TX" "counter"
TX_HEIGHT=$(echo $TX | jq .height)
# make sure the counter was updated, added 7
checkCounter "2" "17" "$TX_HEIGHT"
# make sure the account was debited 11
checkAccount $SENDER "9007199254739979" "$TX_HEIGHT"
# make sure we cannot replay the counter, no state change
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx counter --countfee=10mycoin --sequence=2 --name=${RICH} --valid 2>/dev/null)
assertFalse "line=${LINENO}, replay: $TX" $?
TX_HEIGHT=$(echo $TX | jq .height)
checkCounter "2" "17" "$TX_HEIGHT"
checkAccount $SENDER "9007199254739979" "$TX_HEIGHT"
}
# Load common then run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
CLI_DIR=$GOPATH/src/github.com/cosmos/cosmos-sdk/tests/cli
. $CLI_DIR/common.sh
. $CLI_DIR/shunit2

View File

@ -1,14 +0,0 @@
LINKER_FLAGS:="-X github.com/cosmos/cosmos-sdk/client/commands.CommitHash=`git rev-parse --short HEAD`"
install:
@go install -ldflags $(LINKER_FLAGS) ./cmd/...
test: test_unit test_cli
test_unit:
@go test `glide novendor`
test_cli:
./tests/cli/eyes.sh
.PHONY: install test test_unit test_cli

View File

@ -1,58 +0,0 @@
package main
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/cosmos/cosmos-sdk/server/commands"
)
// InitCmd - node initialization command
var InitCmd = &cobra.Command{
Use: "init",
Short: "Initialize eyes abci server",
RunE: initCmd,
}
//nolint - flags
var (
FlagChainID = "chain-id" //TODO group with other flags or remove? is this already a flag here?
)
func init() {
InitCmd.Flags().String(FlagChainID, "eyes_test_id", "Chain ID")
}
func initCmd(cmd *cobra.Command, args []string) error {
// this will ensure that config.toml is there if not yet created, and create dir
cfg, err := tcmd.ParseConfig()
if err != nil {
return err
}
genesis := getGenesisJSON(viper.GetString(commands.FlagChainID))
return commands.CreateGenesisValidatorFiles(cfg, genesis, commands.StaticPrivValJSON, cmd.Root().Name())
}
// TODO: better, auto-generate validator...
func getGenesisJSON(chainID string) string {
return fmt.Sprintf(`{
"app_hash": "",
"chain_id": "%s",
"genesis_time": "0001-01-01T00:00:00.000Z",
"validators": [
{
"power": 10,
"name": "",
"pub_key": {
"type": "ed25519",
"data": "7B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
}
}
]
}`, chainID)
}

View File

@ -1,51 +0,0 @@
package main
import (
"os"
"github.com/spf13/cobra"
"github.com/tendermint/tmlibs/cli"
sdk "github.com/cosmos/cosmos-sdk"
client "github.com/cosmos/cosmos-sdk/client/commands"
eyesmod "github.com/cosmos/cosmos-sdk/modules/eyes"
"github.com/cosmos/cosmos-sdk/server/commands"
"github.com/cosmos/cosmos-sdk/util"
"github.com/cosmos/cosmos-sdk/examples/eyes"
)
// RootCmd is the entry point for this binary
var RootCmd = &cobra.Command{
Use: "eyes",
Short: "key-value store",
Long: "A demo app to show key-value store with proofs over abci",
}
// BuildApp constructs the stack we want to use for this app
func BuildApp() sdk.Handler {
return sdk.ChainDecorators(
util.Logger{},
util.Recovery{},
eyes.Parser{},
util.Chain{},
).WithHandler(
eyesmod.NewHandler(),
)
}
func main() {
commands.Handler = BuildApp()
RootCmd.AddCommand(
// out own init command to not require argument
InitCmd,
commands.StartCmd,
commands.UnsafeResetAllCmd,
client.VersionCmd,
)
commands.SetUpRoot(RootCmd)
cmd := cli.PrepareMainCmd(RootCmd, "EYE", os.ExpandEnv("$HOME/.eyes"))
cmd.Execute()
}

View File

@ -1,63 +0,0 @@
package main
import (
"os"
"github.com/spf13/cobra"
"github.com/tendermint/tmlibs/cli"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/client/commands/auto"
"github.com/cosmos/cosmos-sdk/client/commands/commits"
"github.com/cosmos/cosmos-sdk/client/commands/query"
rpccmd "github.com/cosmos/cosmos-sdk/client/commands/rpc"
txcmd "github.com/cosmos/cosmos-sdk/client/commands/txs"
eyescmd "github.com/cosmos/cosmos-sdk/modules/eyes/commands"
)
// EyesCli - main basecoin client command
var EyesCli = &cobra.Command{
Use: "eyescli",
Short: "Light client for Tendermint",
Long: `EyesCli is the light client for a merkle key-value store (eyes)`,
}
func main() {
commands.AddBasicFlags(EyesCli)
// Prepare queries
query.RootCmd.AddCommand(
// These are default parsers, but optional in your app (you can remove key)
query.TxQueryCmd,
query.KeyQueryCmd,
// this is our custom parser
eyescmd.EyesQueryCmd,
)
// no middleware wrapers
txcmd.Middleware = txcmd.Wrappers{}
// txcmd.Middleware.Register(txcmd.RootCmd.PersistentFlags())
// just the etc commands
txcmd.RootCmd.AddCommand(
eyescmd.SetTxCmd,
eyescmd.RemoveTxCmd,
)
// Set up the various commands to use
EyesCli.AddCommand(
// we use out own init command to not require address arg
commands.InitCmd,
commands.ResetCmd,
commits.RootCmd,
rpccmd.RootCmd,
query.RootCmd,
txcmd.RootCmd,
commands.VersionCmd,
auto.AutoCompleteCmd,
)
cmd := cli.PrepareMainCmd(EyesCli, "EYE", os.ExpandEnv("$HOME/.eyescli"))
cmd.Execute()
}

View File

@ -1,32 +0,0 @@
package eyes
import sdk "github.com/cosmos/cosmos-sdk"
// Parser converts bytes into a tx struct
type Parser struct{}
var _ sdk.Decorator = Parser{}
// CheckTx makes sure we are on the proper chain
// - fulfills Decorator interface
func (c Parser) CheckTx(ctx sdk.Context, store sdk.SimpleDB,
txBytes interface{}, next sdk.Checker) (res sdk.CheckResult, err error) {
tx, err := LoadTx(txBytes.([]byte))
if err != nil {
return res, err
}
return next.CheckTx(ctx, store, tx)
}
// DeliverTx makes sure we are on the proper chain
// - fulfills Decorator interface
func (c Parser) DeliverTx(ctx sdk.Context, store sdk.SimpleDB,
txBytes interface{}, next sdk.Deliverer) (res sdk.DeliverResult, err error) {
tx, err := LoadTx(txBytes.([]byte))
if err != nil {
return res, err
}
return next.DeliverTx(ctx, store, tx)
}

View File

@ -1,73 +0,0 @@
#!/bin/bash
# These global variables are required for common.sh
SERVER_EXE=eyes
CLIENT_EXE=eyescli
oneTimeSetUp() {
# These are passed in as args
BASE_DIR=$HOME/.test_eyes
CHAIN_ID="eyes-cli-test"
rm -rf $BASE_DIR 2>/dev/null
mkdir -p $BASE_DIR
echo "Setting up genesis..."
SERVE_DIR=${BASE_DIR}/server
SERVER_LOG=${BASE_DIR}/${SERVER_EXE}.log
echo "Starting ${SERVER_EXE} server..."
export EYE_HOME=${SERVE_DIR}
${SERVER_EXE} init --chain-id=$CHAIN_ID >>$SERVER_LOG
startServer $SERVE_DIR $SERVER_LOG
[ $? = 0 ] || return 1
# Set up client - make sure you use the proper prefix if you set
# a custom CLIENT_EXE
export EYE_HOME=${BASE_DIR}/client
initClient $CHAIN_ID
[ $? = 0 ] || return 1
printf "...Testing may begin!\n\n\n"
}
oneTimeTearDown() {
quickTearDown
}
test00SetGetRemove() {
KEY="CAFE6000"
VALUE="F00D4200"
assertFalse "line=${LINENO} data present" "${CLIENT_EXE} query eyes ${KEY}"
# set data
TXRES=$(${CLIENT_EXE} tx set --key=${KEY} --value=${VALUE})
txSucceeded $? "$TXRES" "set cafe"
HASH=$(echo $TXRES | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TXRES | jq .height)
# make sure it is set
DATA=$(${CLIENT_EXE} query eyes ${KEY} --height=$TX_HEIGHT)
assertTrue "line=${LINENO} data not set" $?
assertEquals "line=${LINENO}" "\"${VALUE}\"" $(echo $DATA | jq .data.value)
# query the tx
TX=$(${CLIENT_EXE} query tx $HASH)
assertTrue "line=${LINENO}, found tx" $?
if [ -n "$DEBUG" ]; then echo $TX; echo; fi
assertEquals "line=${LINENO}, proper type" "\"eyes/set\"" $(echo $TX | jq .data.type)
assertEquals "line=${LINENO}, proper key" "\"${KEY}\"" $(echo $TX | jq .data.data.key)
assertEquals "line=${LINENO}, proper value" "\"${VALUE}\"" $(echo $TX | jq .data.data.value)
}
# Load common then run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
CLI_DIR=$GOPATH/src/github.com/cosmos/cosmos-sdk/tests/cli
. $CLI_DIR/common.sh
. $CLI_DIR/shunit2

View File

@ -1,33 +0,0 @@
package eyes
import (
wire "github.com/tendermint/go-wire"
eyesmod "github.com/cosmos/cosmos-sdk/modules/eyes"
"github.com/cosmos/cosmos-sdk/util"
)
// Tx is what is submitted to the chain.
// This embeds the tx data along with any info we want for
// decorators (just chain for now to demo)
type Tx struct {
Tx eyesmod.EyesTx `json:"tx"`
Chain util.ChainData `json:"chain"`
}
// GetTx gets the tx info
func (e Tx) GetTx() interface{} {
return e.Tx
}
// GetChain gets the chain we wish to perform the tx on
// (info for decorators)
func (e Tx) GetChain() util.ChainData {
return e.Chain
}
// LoadTx parses the input data into our blockchain tx structure
func LoadTx(data []byte) (tx Tx, err error) {
err = wire.ReadBinaryBytes(data, &tx)
return
}

View File

@ -1,61 +0,0 @@
/*
Package genesis provides some utility functions for parsing
a standard genesis file to initialize your abci application.
We wish to support using one genesis file to initialize both
tendermint and the application, so this file format is designed
to be embedable in the tendermint genesis.json file. We reuse
the same chain_id field for tendermint, ignore the other fields,
and add a special app_options field that contains information just
for the abci app (and ignored by tendermint).
The use of this file format for your application is not required by
the sdk and is only used by default in the start command, if you wish
to write your own start command, you can use any other method to
store and parse options for your abci application. The important part is
that the same data is available on every node.
Example file format:
{
"chain_id": "foo_bar_chain",
"app_options": {
"accounts": [{
"address": "C471FB670E44D219EE6DF2FC284BE38793ACBCE1",
"pub_key": {
"type": "ed25519",
"data": "6880DB93598E283A67C4D88FC67A8858AA2DE70F713FE94A5109E29C137100C2"
},
"coins": [
{
"denom": "ETH",
"amount": 654321
}
]
}],
"plugin_options": [
"plugin1/key1", "value1",
"profile/set", {"name": "john", age: 37}
]
}
}
Note that there are two subfields under app_options. The first one "accounts"
is a special case for the coin module, which is assumed to be used by most
applications. It is simply a list of accounts with an identifier and their
initial balance. The account must be identified by EITHER an address
(20 bytes in hex) or a pubkey (in the go-crypto json format), not both as in
this example. "coins" defines the initial balance of the account.
Configuration options for every other module should be placed under
"plugin_options" as key value pairs (there must be an even number of items).
The first value must be "<module>/<key>" to define the option to be set.
The second value is parsed as raw json and is the value to pass to the
application. This may be a string, an array, a map or any other valid json
structure that the module can parse.
Note that we don't use a map for plugin_options, as we will often wish
to have many values for the same key, to run this setup many times,
just as we support setting many accounts.
*/
package genesis

View File

@ -1,153 +0,0 @@
package genesis
import (
"encoding/json"
"strings"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/pkg/errors"
cmn "github.com/tendermint/tmlibs/common"
)
// KeyDelimiter is used to separate module and key in
// the options
const KeyDelimiter = "/"
// Option just holds module/key/value triples from
// parsing the genesis file
type Option struct {
Module string
Key string
Value string
}
// InitStater is anything that can handle app options
// from genesis file. Setting the merkle store, config options,
// or anything else
type InitStater interface {
InitState(module, key, value string) error
}
// Load parses the genesis file and sets the initial
// state based on that
func Load(app InitStater, filePath string) error {
opts, err := GetOptions(filePath)
if err != nil {
return err
}
// execute all the genesis init options
// abort on any error
for _, opt := range opts {
err = app.InitState(opt.Module, opt.Key, opt.Value)
if err != nil {
return err
}
}
return nil
}
// GetOptions parses the genesis file in a format
// that can easily be handed into InitStaters
func GetOptions(path string) ([]Option, error) {
genDoc, err := load(path)
if err != nil {
return nil, err
}
opts := genDoc.AppOptions
cnt := 1 + len(opts.Accounts) + len(opts.pluginOptions)
res := make([]Option, 0, cnt)
res = append(res, Option{sdk.ModuleNameBase, sdk.ChainKey, genDoc.ChainID})
// set accounts
for _, acct := range opts.Accounts {
res = append(res, Option{"coin", "account", string(acct)})
}
// set plugin options
for _, kv := range opts.pluginOptions {
module, key := splitKey(kv.Key)
res = append(res, Option{module, key, kv.Value})
}
return res, nil
}
type keyValue struct {
Key string `json:"key"`
Value string `json:"value"`
}
// FullDoc - includes tendermint (in the json, we ignore here)
type FullDoc struct {
ChainID string `json:"chain_id"`
AppOptions *Doc `json:"app_options"`
}
// Doc - All genesis values
type Doc struct {
Accounts []json.RawMessage `json:"accounts"`
PluginOptions []json.RawMessage `json:"plugin_options"`
pluginOptions []keyValue // unmarshaled rawmessages
}
func load(filePath string) (*FullDoc, error) {
bytes, err := cmn.ReadFile(filePath)
if err != nil {
return nil, errors.Wrap(err, "loading genesis file")
}
// the basecoin genesis go-wire/data :)
genDoc := new(FullDoc)
err = json.Unmarshal(bytes, genDoc)
if err != nil {
return nil, errors.Wrap(err, "unmarshaling genesis file")
}
if genDoc.AppOptions == nil {
genDoc.AppOptions = new(Doc)
}
pluginOpts, err := parseList(genDoc.AppOptions.PluginOptions)
if err != nil {
return nil, err
}
genDoc.AppOptions.pluginOptions = pluginOpts
return genDoc, nil
}
func parseList(kvzIn []json.RawMessage) (kvz []keyValue, err error) {
if len(kvzIn)%2 != 0 {
return nil, errors.New("genesis cannot have an odd number of items. Format = [key1, value1, key2, value2, ...]")
}
for i := 0; i < len(kvzIn); i += 2 {
kv := keyValue{}
rawK := []byte(kvzIn[i])
err := json.Unmarshal(rawK, &(kv.Key))
if err != nil {
return nil, errors.Errorf("Non-string key: %s", string(rawK))
}
// convert value to string if possible (otherwise raw json)
rawV := kvzIn[i+1]
err = json.Unmarshal(rawV, &(kv.Value))
if err != nil {
kv.Value = string(rawV)
}
kvz = append(kvz, kv)
}
return kvz, nil
}
// Splits the string at the first '/'.
// if there are none, assign default module ("base").
func splitKey(key string) (string, string) {
if strings.Contains(key, KeyDelimiter) {
keyParts := strings.SplitN(key, KeyDelimiter, 2)
return keyParts[0], keyParts[1]
}
return sdk.ModuleNameBase, key
}

View File

@ -1,78 +0,0 @@
package genesis
import (
"encoding/json"
"testing"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
cmn "github.com/tendermint/tmlibs/common"
)
const genesisFilepath = "./testdata/genesis.json"
func TestParseList(t *testing.T) {
assert, require := assert.New(t), require.New(t)
bytes, err := cmn.ReadFile(genesisFilepath)
require.Nil(err, "loading genesis file %+v", err)
// the basecoin genesis go-wire/data :)
genDoc := new(FullDoc)
err = json.Unmarshal(bytes, genDoc)
require.Nil(err, "unmarshaling genesis file %+v", err)
pluginOpts, err := parseList(genDoc.AppOptions.PluginOptions)
require.Nil(err, "%+v", err)
genDoc.AppOptions.pluginOptions = pluginOpts
assert.Equal(genDoc.AppOptions.pluginOptions[0].Key, "plugin1/key1")
assert.Equal(genDoc.AppOptions.pluginOptions[1].Key, "plugin1/key2")
assert.Equal(genDoc.AppOptions.pluginOptions[0].Value, "value1")
assert.Equal(genDoc.AppOptions.pluginOptions[1].Value, "value2")
}
func TestGetOptions(t *testing.T) {
assert, require := assert.New(t), require.New(t)
opts, err := GetOptions(genesisFilepath)
require.Nil(err, "loading genesis file %+v", err)
require.Equal(4, len(opts))
chain := opts[0]
assert.Equal(sdk.ModuleNameBase, chain.Module)
assert.Equal(sdk.ChainKey, chain.Key)
assert.Equal("foo_bar_chain", chain.Value)
acct := opts[1]
assert.Equal("coin", acct.Module)
assert.Equal("account", acct.Key)
p1 := opts[2]
assert.Equal("plugin1", p1.Module)
assert.Equal("key1", p1.Key)
assert.Equal("value1", p1.Value)
p2 := opts[3]
assert.Equal("plugin1", p2.Module)
assert.Equal("key2", p2.Key)
assert.Equal("value2", p2.Value)
}
func TestSplitKey(t *testing.T) {
assert := assert.New(t)
prefix, suffix := splitKey("foo/bar")
assert.EqualValues("foo", prefix)
assert.EqualValues("bar", suffix)
prefix, suffix = splitKey("foobar")
assert.EqualValues("base", prefix)
assert.EqualValues("foobar", suffix)
prefix, suffix = splitKey("some/complex/issue")
assert.EqualValues("some", prefix)
assert.EqualValues("complex/issue", suffix)
}

View File

@ -1,22 +0,0 @@
{
"chain_id": "foo_bar_chain",
"app_options": {
"accounts": [{
"pub_key": {
"type": "ed25519",
"data": "6880db93598e283a67c4d88fc67a8858aa2de70f713fe94a5109e29c137100c2"
},
"coins": [
{
"denom": "blank",
"amount": 12345
},
{
"denom": "ETH",
"amount": 654321
}
]
}],
"plugin_options": ["plugin1/key1", "value1", "plugin1/key2", "value2"]
}
}

View File

@ -1,292 +0,0 @@
package app
// import (
// "encoding/hex"
// "testing"
// "github.com/stretchr/testify/assert"
// "github.com/stretchr/testify/require"
// sdk "github.com/cosmos/cosmos-sdk"
// "github.com/cosmos/cosmos-sdk/modules/auth"
// "github.com/cosmos/cosmos-sdk/modules/base"
// "github.com/cosmos/cosmos-sdk/modules/coin"
// "github.com/cosmos/cosmos-sdk/modules/fee"
// "github.com/cosmos/cosmos-sdk/modules/nonce"
// "github.com/cosmos/cosmos-sdk/stack"
// "github.com/cosmos/cosmos-sdk/state"
// "github.com/cosmos/cosmos-sdk/util"
// abci "github.com/tendermint/abci/types"
// wire "github.com/tendermint/go-wire"
// "github.com/tendermint/tmlibs/log"
// )
// // DefaultHandler for the tests (coin, roles, ibc)
// func DefaultHandler(feeDenom string) sdk.Handler {
// // use the default stack
// // r := roles.NewHandler()
// // i := ibc.NewHandler()
// return sdk.ChainDecorators(
// util.Logger{},
// util.Recovery{},
// auth.Signatures{},
// util.Chain{},
// // stack.Checkpoint{OnCheck: true},
// // nonce.ReplayCheck{},
// ).
// // IBC(ibc.NewMiddleware()).
// // Apps(
// // roles.NewMiddleware(),
// // fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank),
// // stack.Checkpoint{OnDeliver: true},
// // ).
// WithHandler(
// coin.NewHandler(),
// // stack.WrapHandler(r),
// // stack.WrapHandler(i),
// )
// }
// //--------------------------------------------------------
// // test environment is a list of input and output accounts
// type appTest struct {
// t *testing.T
// chainID string
// app *BaseApp
// acctIn *coin.AccountWithKey
// acctOut *coin.AccountWithKey
// }
// func newAppTest(t *testing.T) *appTest {
// at := &appTest{
// t: t,
// chainID: "test_chain_id",
// }
// at.reset()
// return at
// }
// // baseTx is the
// func (at *appTest) baseTx(coins coin.Coins) sdk.Tx {
// in := []coin.TxInput{{Address: at.acctIn.Actor(), Coins: coins}}
// out := []coin.TxOutput{{Address: at.acctOut.Actor(), Coins: coins}}
// tx := coin.NewSendTx(in, out)
// return tx
// }
// func (at *appTest) signTx(tx sdk.Tx) sdk.Tx {
// stx := auth.NewMulti(tx)
// auth.Sign(stx, at.acctIn.Key)
// return stx.Wrap()
// }
// func (at *appTest) getTx(coins coin.Coins, sequence uint32) sdk.Tx {
// tx := at.baseTx(coins)
// tx = nonce.NewTx(sequence, []sdk.Actor{at.acctIn.Actor()}, tx)
// tx = base.NewChainTx(at.chainID, 0, tx)
// return at.signTx(tx)
// }
// func (at *appTest) feeTx(coins coin.Coins, toll coin.Coin, sequence uint32) sdk.Tx {
// tx := at.baseTx(coins)
// tx = fee.NewFee(tx, toll, at.acctIn.Actor())
// tx = nonce.NewTx(sequence, []sdk.Actor{at.acctIn.Actor()}, tx)
// tx = base.NewChainTx(at.chainID, 0, tx)
// return at.signTx(tx)
// }
// // set the account on the app through InitState
// func (at *appTest) initAccount(acct *coin.AccountWithKey) {
// err := at.app.InitState("coin", "account", acct.MakeOption())
// require.Nil(at.t, err, "%+v", err)
// }
// // reset the in and out accs to be one account each with 7mycoin
// func (at *appTest) reset() {
// at.acctIn = coin.NewAccountWithKey(coin.Coins{{"mycoin", 7}})
// at.acctOut = coin.NewAccountWithKey(coin.Coins{{"mycoin", 7}})
// // Note: switch logger if you want to get more info
// logger := log.TestingLogger()
// // logger := log.NewTracingLogger(log.NewTMLogger(os.Stdout))
// store, err := NewStoreApp("app-test", "", 0, logger)
// require.Nil(at.t, err, "%+v", err)
// at.app = NewBaseApp(store, DefaultHandler("mycoin"), nil)
// err = at.app.InitState("base", "chain_id", at.chainID)
// require.Nil(at.t, err, "%+v", err)
// at.initAccount(at.acctIn)
// at.initAccount(at.acctOut)
// resabci := at.app.Commit()
// require.True(at.t, resabci.IsOK(), resabci)
// }
// func getBalance(key sdk.Actor, store state.SimpleDB) (coin.Coins, error) {
// cspace := stack.PrefixedStore(coin.NameCoin, store)
// acct, err := coin.GetAccount(cspace, key)
// return acct.Coins, err
// }
// func getAddr(addr []byte, state state.SimpleDB) (coin.Coins, error) {
// actor := auth.SigPerm(addr)
// return getBalance(actor, state)
// }
// // returns the final balance and expected balance for input and output accounts
// func (at *appTest) exec(t *testing.T, tx sdk.Tx, checkTx bool) (res abci.Result, diffIn, diffOut coin.Coins) {
// require := require.New(t)
// initBalIn, err := getBalance(at.acctIn.Actor(), at.app.Append())
// require.Nil(err, "%+v", err)
// initBalOut, err := getBalance(at.acctOut.Actor(), at.app.Append())
// require.Nil(err, "%+v", err)
// txBytes := wire.BinaryBytes(tx)
// if checkTx {
// res = at.app.CheckTx(txBytes)
// } else {
// res = at.app.DeliverTx(txBytes)
// }
// endBalIn, err := getBalance(at.acctIn.Actor(), at.app.Append())
// require.Nil(err, "%+v", err)
// endBalOut, err := getBalance(at.acctOut.Actor(), at.app.Append())
// require.Nil(err, "%+v", err)
// return res, endBalIn.Minus(initBalIn), endBalOut.Minus(initBalOut)
// }
// //--------------------------------------------------------
// func TestInitState(t *testing.T) {
// assert := assert.New(t)
// require := require.New(t)
// logger := log.TestingLogger()
// store, err := NewStoreApp("app-test", "", 0, logger)
// require.Nil(err, "%+v", err)
// app := NewBaseApp(store, DefaultHandler("atom"), nil)
// //testing ChainID
// chainID := "testChain"
// err = app.InitState("base", "chain_id", chainID)
// require.Nil(err, "%+v", err)
// assert.EqualValues(app.GetChainID(), chainID)
// // make a nice account...
// bal := coin.Coins{{"atom", 77}, {"eth", 12}}
// acct := coin.NewAccountWithKey(bal)
// err = app.InitState("coin", "account", acct.MakeOption())
// require.Nil(err, "%+v", err)
// // make sure it is set correctly, with some balance
// coins, err := getBalance(acct.Actor(), app.Append())
// require.Nil(err)
// assert.Equal(bal, coins)
// // let's parse an account with badly sorted coins...
// unsortAddr, err := hex.DecodeString("C471FB670E44D219EE6DF2FC284BE38793ACBCE1")
// require.Nil(err)
// unsortCoins := coin.Coins{{"BTC", 789}, {"eth", 123}}
// unsortAcc := `{
// "pub_key": {
// "type": "ed25519",
// "data": "AD084F0572C116D618B36F2EB08240D1BAB4B51716CCE0E7734B89C8936DCE9A"
// },
// "coins": [
// {
// "denom": "eth",
// "amount": 123
// },
// {
// "denom": "BTC",
// "amount": 789
// }
// ]
// }`
// err = app.InitState("coin", "account", unsortAcc)
// require.Nil(err, "%+v", err)
// coins, err = getAddr(unsortAddr, app.Append())
// require.Nil(err)
// assert.True(coins.IsValid())
// assert.Equal(unsortCoins, coins)
// err = app.InitState("base", "dslfkgjdas", "")
// require.Error(err)
// err = app.InitState("", "dslfkgjdas", "")
// require.Error(err)
// err = app.InitState("dslfkgjdas", "szfdjzs", "")
// require.Error(err)
// }
// // Test CheckTx and DeliverTx with insufficient and sufficient balance
// func TestTx(t *testing.T) {
// assert := assert.New(t)
// at := newAppTest(t)
// //Bad Balance
// at.acctIn.Coins = coin.Coins{{"mycoin", 2}}
// at.initAccount(at.acctIn)
// at.app.Commit()
// res, _, _ := at.exec(t, at.getTx(coin.Coins{{"mycoin", 5}}, 1), true)
// assert.True(res.IsErr(), "ExecTx/Bad CheckTx: Expected error return from ExecTx, returned: %v", res)
// res, diffIn, diffOut := at.exec(t, at.getTx(coin.Coins{{"mycoin", 5}}, 1), false)
// assert.True(res.IsErr(), "ExecTx/Bad DeliverTx: Expected error return from ExecTx, returned: %v", res)
// assert.True(diffIn.IsZero())
// assert.True(diffOut.IsZero())
// //Regular CheckTx
// at.reset()
// res, _, _ = at.exec(t, at.getTx(coin.Coins{{"mycoin", 5}}, 1), true)
// assert.True(res.IsOK(), "ExecTx/Good CheckTx: Expected OK return from ExecTx, Error: %v", res)
// //Regular DeliverTx
// at.reset()
// amt := coin.Coins{{"mycoin", 3}}
// res, diffIn, diffOut = at.exec(t, at.getTx(amt, 1), false)
// assert.True(res.IsOK(), "ExecTx/Good DeliverTx: Expected OK return from ExecTx, Error: %v", res)
// assert.Equal(amt.Negative(), diffIn)
// assert.Equal(amt, diffOut)
// //DeliverTx with fee.... 4 get to recipient, 1 extra taxed
// at.reset()
// amt = coin.Coins{{"mycoin", 4}}
// toll := coin.Coin{"mycoin", 1}
// res, diffIn, diffOut = at.exec(t, at.feeTx(amt, toll, 1), false)
// assert.True(res.IsOK(), "ExecTx/Good DeliverTx: Expected OK return from ExecTx, Error: %v", res)
// payment := amt.Plus(coin.Coins{toll}).Negative()
// assert.Equal(payment, diffIn)
// assert.Equal(amt, diffOut)
// }
// func TestQuery(t *testing.T) {
// assert := assert.New(t)
// at := newAppTest(t)
// res, _, _ := at.exec(t, at.getTx(coin.Coins{{"mycoin", 5}}, 1), false)
// assert.True(res.IsOK(), "Commit, DeliverTx: Expected OK return from DeliverTx, Error: %v", res)
// resQueryPreCommit := at.app.Query(abci.RequestQuery{
// Path: "/account",
// Data: at.acctIn.Address(),
// })
// res = at.app.Commit()
// assert.True(res.IsOK(), res)
// key := stack.PrefixedKey(coin.NameCoin, at.acctIn.Address())
// resQueryPostCommit := at.app.Query(abci.RequestQuery{
// Path: "/key",
// Data: key,
// })
// assert.NotEqual(resQueryPreCommit, resQueryPostCommit, "Query should change before/after commit")
// }

View File

@ -1,101 +0,0 @@
package app
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/genesis"
"github.com/cosmos/cosmos-sdk/modules/coin"
)
const genesisFilepath = "./testdata/genesis.json"
const genesisAcctFilepath = "./testdata/genesis2.json"
// 2b is just like 2, but add carl who has inconsistent
// pubkey and address
const genesisBadAcctFilepath = "./testdata/genesis2b.json"
func TestLoadGenesisDoNotFailIfAppOptionsAreMissing(t *testing.T) {
logger := log.TestingLogger()
store, err := MockStoreApp("genesis", logger)
require.Nil(t, err, "%+v", err)
app := NewBaseApp(store, DefaultHandler("mycoin"), nil)
err = genesis.Load(app, "./testdata/genesis3.json")
require.Nil(t, err, "%+v", err)
}
func TestLoadGenesisFailsWithUnknownOptions(t *testing.T) {
require := require.New(t)
logger := log.TestingLogger()
store, err := MockStoreApp("genesis", logger)
require.Nil(err, "%+v", err)
app := NewBaseApp(store, DefaultHandler("mycoin"), nil)
err = genesis.Load(app, genesisFilepath)
require.NotNil(err, "%+v", err)
}
// Fix for issue #89, change the parse format for accounts in genesis.json
func TestLoadGenesisAccountAddress(t *testing.T) {
assert, require := assert.New(t), require.New(t)
logger := log.TestingLogger()
store, err := MockStoreApp("genesis", logger)
require.Nil(err, "%+v", err)
app := NewBaseApp(store, DefaultHandler("mycoin"), nil)
err = genesis.Load(app, genesisAcctFilepath)
require.Nil(err, "%+v", err)
// check the chain id
assert.Equal("addr_accounts_chain", app.GetChainID())
// make sure the accounts were set properly
cases := []struct {
addr string
exists bool
hasPubkey bool
coins coin.Coins
}{
// this comes from a public key, should be stored proper (alice)
{"62035D628DE7543332544AA60D90D3693B6AD51B", true, true, coin.Coins{{"one", 111}}},
// this comes from an address, should be stored proper (bob)
{"C471FB670E44D219EE6DF2FC284BE38793ACBCE1", true, false, coin.Coins{{"two", 222}}},
// this comes from a secp256k1 public key, should be stored proper (sam)
{"979F080B1DD046C452C2A8A250D18646C6B669D4", true, true, coin.Coins{{"four", 444}}},
}
for i, tc := range cases {
addr, err := hex.DecodeString(tc.addr)
require.Nil(err, tc.addr)
coins, err := getAddr(addr, app.Append())
require.Nil(err, "%+v", err)
if !tc.exists {
assert.True(coins.IsZero(), "%d", i)
} else if assert.False(coins.IsZero(), "%d", i) {
// it should and does exist...
assert.True(coins.IsValid())
assert.Equal(tc.coins, coins)
}
}
}
// When you define an account in genesis with address
// and pubkey that don't match
func TestLoadGenesisAccountInconsistentAddress(t *testing.T) {
require := require.New(t)
logger := log.TestingLogger()
store, err := MockStoreApp("genesis", logger)
require.Nil(err, "%+v", err)
app := NewBaseApp(store, DefaultHandler("mycoin"), nil)
err = genesis.Load(app, genesisBadAcctFilepath)
require.NotNil(err)
}

View File

@ -1,6 +0,0 @@
/**
Package bonus is a temporary home for various functionalities
that were removed for complexity, but may be added back in
later.
**/
package bonus

View File

@ -1,118 +0,0 @@
package bonus
import (
abci "github.com/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/errors"
"github.com/cosmos/cosmos-sdk/state"
)
//nolint
const (
NameVal = "val"
NamePrice = "price"
TypeValChange = NameVal + "/change"
ByteValChange = 0xfe
TypePriceShow = NamePrice + "/show"
BytePriceShow = 0xfd
)
func init() {
sdk.TxMapper.
RegisterImplementation(ValChangeTx{}, TypeValChange, ByteValChange).
RegisterImplementation(PriceShowTx{}, TypePriceShow, BytePriceShow)
}
//--------------------------------
// Setup tx and handler for validation test cases
type ValSetHandler struct {
sdk.NopCheck
sdk.NopInitState
sdk.NopInitValidate
}
var _ sdk.Handler = ValSetHandler{}
func (ValSetHandler) Name() string {
return NameVal
}
func (ValSetHandler) DeliverTx(ctx sdk.Context, store state.SimpleDB,
tx sdk.Tx) (res sdk.DeliverResult, err error) {
change, ok := tx.Unwrap().(ValChangeTx)
if !ok {
return res, errors.ErrUnknownTxType(tx)
}
res.Diff = change.Diff
return
}
type ValChangeTx struct {
Diff []*abci.Validator
}
func (v ValChangeTx) Wrap() sdk.Tx {
return sdk.Tx{v}
}
func (v ValChangeTx) ValidateBasic() error { return nil }
//--------------------------------
// Setup tx and handler for testing checktx fees/gas
// PriceData is the data we ping back
var PriceData = []byte{0xCA, 0xFE}
// PriceHandler returns checktx results based on the input
type PriceHandler struct {
sdk.NopInitState
sdk.NopInitValidate
}
var _ sdk.Handler = PriceHandler{}
func (PriceHandler) Name() string {
return NamePrice
}
func (PriceHandler) CheckTx(ctx sdk.Context, store state.SimpleDB,
tx sdk.Tx) (res sdk.CheckResult, err error) {
price, ok := tx.Unwrap().(PriceShowTx)
if !ok {
return res, errors.ErrUnknownTxType(tx)
}
res.GasAllocated = price.GasAllocated
res.GasPayment = price.GasPayment
res.Data = PriceData
return
}
func (PriceHandler) DeliverTx(ctx sdk.Context, store state.SimpleDB,
tx sdk.Tx) (res sdk.DeliverResult, err error) {
_, ok := tx.Unwrap().(PriceShowTx)
if !ok {
return res, errors.ErrUnknownTxType(tx)
}
res.Data = PriceData
return
}
// PriceShowTx lets us bounce back a given fee/gas on CheckTx
type PriceShowTx struct {
GasAllocated uint64
GasPayment uint64
}
func NewPriceShowTx(gasAllocated, gasPayment uint64) sdk.Tx {
return PriceShowTx{GasAllocated: gasAllocated, GasPayment: gasPayment}.Wrap()
}
func (p PriceShowTx) Wrap() sdk.Tx {
return sdk.Tx{p}
}
func (v PriceShowTx) ValidateBasic() error { return nil }

View File

@ -1,116 +0,0 @@
package bonus
import (
"strings"
abci "github.com/tendermint/abci/types"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/go-wire/data"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/stack"
"github.com/cosmos/cosmos-sdk/state"
)
//nolint
const (
NameMultiplexer = "mplx"
)
// Multiplexer grabs a MultiTx and sends them sequentially down the line
type Multiplexer struct {
stack.PassInitState
stack.PassInitValidate
}
// Name of the module - fulfills Middleware interface
func (Multiplexer) Name() string {
return NameMultiplexer
}
var _ stack.Middleware = Multiplexer{}
// CheckTx splits the input tx and checks them all - fulfills Middlware interface
func (Multiplexer) CheckTx(ctx sdk.Context, store state.SimpleDB, tx sdk.Tx, next sdk.Checker) (res sdk.CheckResult, err error) {
if mtx, ok := tx.Unwrap().(MultiTx); ok {
return runAllChecks(ctx, store, mtx.Txs, next)
}
return next.CheckTx(ctx, store, tx)
}
// DeliverTx splits the input tx and checks them all - fulfills Middlware interface
func (Multiplexer) DeliverTx(ctx sdk.Context, store state.SimpleDB, tx sdk.Tx, next sdk.Deliver) (res sdk.DeliverResult, err error) {
if mtx, ok := tx.Unwrap().(MultiTx); ok {
return runAllDelivers(ctx, store, mtx.Txs, next)
}
return next.DeliverTx(ctx, store, tx)
}
func runAllChecks(ctx sdk.Context, store state.SimpleDB, txs []sdk.Tx, next sdk.Checker) (res sdk.CheckResult, err error) {
// store all results, unless anything errors
rs := make([]sdk.CheckResult, len(txs))
for i, stx := range txs {
rs[i], err = next.CheckTx(ctx, store, stx)
if err != nil {
return
}
}
// now combine the results into one...
return combineChecks(rs), nil
}
func runAllDelivers(ctx sdk.Context, store state.SimpleDB, txs []sdk.Tx, next sdk.Deliver) (res sdk.DeliverResult, err error) {
// store all results, unless anything errors
rs := make([]sdk.DeliverResult, len(txs))
for i, stx := range txs {
rs[i], err = next.DeliverTx(ctx, store, stx)
if err != nil {
return
}
}
// now combine the results into one...
return combineDelivers(rs), nil
}
// combines all data bytes as a go-wire array.
// joins all log messages with \n
func combineChecks(all []sdk.CheckResult) sdk.CheckResult {
datas := make([]data.Bytes, len(all))
logs := make([]string, len(all))
var allocated, payments uint64
for i, r := range all {
datas[i] = r.Data
logs[i] = r.Log
allocated += r.GasAllocated
payments += r.GasPayment
}
return sdk.CheckResult{
Data: wire.BinaryBytes(datas),
Log: strings.Join(logs, "\n"),
GasAllocated: allocated,
GasPayment: payments,
}
}
// combines all data bytes as a go-wire array.
// joins all log messages with \n
func combineDelivers(all []sdk.DeliverResult) sdk.DeliverResult {
datas := make([]data.Bytes, len(all))
logs := make([]string, len(all))
var used uint64
var diffs []*abci.Validator
for i, r := range all {
datas[i] = r.Data
logs[i] = r.Log
used += r.GasUsed
if len(r.Diff) > 0 {
diffs = append(diffs, r.Diff...)
}
}
return sdk.DeliverResult{
Data: wire.BinaryBytes(datas),
Log: strings.Join(logs, "\n"),
GasUsed: used,
Diff: diffs,
}
}

View File

@ -1,87 +0,0 @@
package bonus
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/stack"
"github.com/cosmos/cosmos-sdk/state"
"github.com/stretchr/testify/assert"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/tmlibs/log"
)
func TestMultiplexer(t *testing.T) {
assert := assert.New(t)
msg := "diddly"
chainID := "multi-verse"
height := uint64(100)
// Generic args here...
store := state.NewMemKVStore()
ctx := stack.NewContext(chainID, height, log.NewNopLogger())
// Build the stack
app := stack.
New(Multiplexer{}).
Dispatch(
stack.WrapHandler(stack.OKHandler{Log: msg}),
stack.WrapHandler(PriceHandler{}),
)
raw := stack.NewRawTx([]byte{1, 2, 3, 4})
fail := stack.NewFailTx()
price1 := NewPriceShowTx(123, 456)
price2 := NewPriceShowTx(1000, 2000)
price3 := NewPriceShowTx(11, 0)
join := func(data ...[]byte) []byte {
return wire.BinaryBytes(data)
}
cases := [...]struct {
tx sdk.Tx
valid bool
gasAllocated uint64
gasPayment uint64
log string
data data.Bytes
}{
// test the components without multiplexer (no effect)
0: {raw, true, 0, 0, msg, nil},
1: {price1, true, 123, 456, "", PriceData},
2: {fail, false, 0, 0, "", nil},
// test multiplexer on error
3: {NewMultiTx(raw, fail, price1), false, 0, 0, "", nil},
// test combining info on multiplexer
4: {NewMultiTx(price1, raw), true, 123, 456, "\n" + msg, join(PriceData, nil)},
// add lots of prices
5: {NewMultiTx(price1, price2, price3), true, 1134, 2456, "\n\n", join(PriceData, PriceData, PriceData)},
// combine multiple logs
6: {NewMultiTx(raw, price3, raw), true, 11, 0, msg + "\n\n" + msg, join(nil, PriceData, nil)},
}
for i, tc := range cases {
cres, err := app.CheckTx(ctx, store, tc.tx)
if tc.valid {
assert.Nil(err, "%d: %+v", i, err)
assert.Equal(tc.log, cres.Log, "%d", i)
assert.Equal(tc.data, cres.Data, "%d", i)
assert.Equal(tc.gasAllocated, cres.GasAllocated, "%d", i)
assert.Equal(tc.gasPayment, cres.GasPayment, "%d", i)
} else {
assert.NotNil(err, "%d", i)
}
// make sure deliver returns error, not a panic crash
dres, err := app.DeliverTx(ctx, store, tc.tx)
if tc.valid {
assert.Nil(err, "%d: %+v", i, err)
assert.Equal(tc.log, dres.Log, "%d", i)
assert.Equal(tc.data, dres.Data, "%d", i)
} else {
assert.NotNil(err, "%d", i)
}
}
}

View File

@ -1,20 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = python -msphinx
SPHINXPROJ = Cosmos-SDK
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

Some files were not shown because too many files have changed in this diff Show More