Add roles wrapper/tx/query command to basecoin

This commit is contained in:
Ethan Frey 2017-07-19 14:25:17 +02:00
parent 737e3740dd
commit 911dd1423e
12 changed files with 219 additions and 65 deletions

View File

@ -16,6 +16,7 @@ import (
"github.com/tendermint/basecoin/modules/coin" "github.com/tendermint/basecoin/modules/coin"
"github.com/tendermint/basecoin/modules/fee" "github.com/tendermint/basecoin/modules/fee"
"github.com/tendermint/basecoin/modules/nonce" "github.com/tendermint/basecoin/modules/nonce"
"github.com/tendermint/basecoin/modules/roles"
"github.com/tendermint/basecoin/stack" "github.com/tendermint/basecoin/stack"
sm "github.com/tendermint/basecoin/state" sm "github.com/tendermint/basecoin/state"
"github.com/tendermint/basecoin/version" "github.com/tendermint/basecoin/version"
@ -56,14 +57,19 @@ func NewBasecoin(handler basecoin.Handler, eyesCli *eyes.Client, logger log.Logg
// DefaultHandler - placeholder to just handle sendtx // DefaultHandler - placeholder to just handle sendtx
func DefaultHandler(feeDenom string) basecoin.Handler { func DefaultHandler(feeDenom string) basecoin.Handler {
// use the default stack // use the default stack
h := coin.NewHandler() c := coin.NewHandler()
d := stack.NewDispatcher(stack.WrapHandler(h)) r := roles.NewHandler()
d := stack.NewDispatcher(
stack.WrapHandler(c),
stack.WrapHandler(r),
)
return stack.New( return stack.New(
base.Logger{}, base.Logger{},
stack.Recovery{}, stack.Recovery{},
auth.Signatures{}, auth.Signatures{},
base.Chain{}, base.Chain{},
nonce.ReplayCheck{}, nonce.ReplayCheck{},
roles.NewMiddleware(),
fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank), fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank),
).Use(d) ).Use(d)
} }

View File

@ -83,11 +83,11 @@ func GetCertifier() (*certifiers.InquiringCertifier, error) {
return cert, nil return cert, nil
} }
// ParseAddress parses an address of form: // ParseActor parses an address of form:
// [<chain>:][<app>:]<hex address> // [<chain>:][<app>:]<hex address>
// into a basecoin.Actor. // into a basecoin.Actor.
// If app is not specified or "", then assume auth.NameSigs // If app is not specified or "", then assume auth.NameSigs
func ParseAddress(input string) (res basecoin.Actor, err error) { func ParseActor(input string) (res basecoin.Actor, err error) {
chain, app := "", auth.NameSigs chain, app := "", auth.NameSigs
input = strings.TrimSpace(input) input = strings.TrimSpace(input)
spl := strings.SplitN(input, ":", 3) spl := strings.SplitN(input, ":", 3)
@ -114,3 +114,17 @@ func ParseAddress(input string) (res basecoin.Actor, err error) {
} }
return return
} }
// ParseActors takes a comma-separated list of actors and parses them into
// a slice
func ParseActors(key string) (signers []basecoin.Actor, err error) {
var act basecoin.Actor
for _, k := range strings.Split(key, ",") {
act, err = ParseActor(k)
if err != nil {
return
}
signers = append(signers, act)
}
return
}

View File

@ -52,6 +52,37 @@ func GetSignerAct() (res basecoin.Actor) {
return res 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 basecoin.Tx) (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. // 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 // Modifies tx in place, and returns an error if it should sign but couldn't
func SignTx(tx basecoin.Tx) error { func SignTx(tx basecoin.Tx) error {

View File

@ -22,6 +22,7 @@ import (
coincmd "github.com/tendermint/basecoin/modules/coin/commands" coincmd "github.com/tendermint/basecoin/modules/coin/commands"
feecmd "github.com/tendermint/basecoin/modules/fee/commands" feecmd "github.com/tendermint/basecoin/modules/fee/commands"
noncecmd "github.com/tendermint/basecoin/modules/nonce/commands" noncecmd "github.com/tendermint/basecoin/modules/nonce/commands"
rolecmd "github.com/tendermint/basecoin/modules/roles/commands"
) )
// BaseCli - main basecoin client command // BaseCli - main basecoin client command
@ -61,6 +62,7 @@ func main() {
// set up the middleware // set up the middleware
txcmd.Middleware = txcmd.Wrappers{ txcmd.Middleware = txcmd.Wrappers{
feecmd.FeeWrapper{}, feecmd.FeeWrapper{},
rolecmd.RoleWrapper{},
noncecmd.NonceWrapper{}, noncecmd.NonceWrapper{},
basecmd.ChainWrapper{}, basecmd.ChainWrapper{},
authcmd.SigWrapper{}, authcmd.SigWrapper{},

View File

@ -33,32 +33,12 @@ func init() {
fs.Bool(FlagValid, false, "Is count valid?") fs.Bool(FlagValid, false, "Is count valid?")
} }
// TODO: counterTx is very similar to the sendtx one,
// maybe we can pull out some common patterns?
func counterTx(cmd *cobra.Command, args []string) error { func counterTx(cmd *cobra.Command, args []string) error {
tx, err := readCounterTxFlags() tx, err := readCounterTxFlags()
if err != nil { if err != nil {
return err return err
} }
return txcmd.DoTx(tx)
tx, err = txcmd.Middleware.Wrap(tx)
if err != nil {
return err
}
err = txcmd.SignTx(tx)
if err != nil {
return err
}
bres, err := txcmd.PrepareOrPostTx(tx)
if err != nil {
return err
}
if bres == nil {
return nil // successful prep, nothing left to do
}
return txcmd.OutputTx(bres) // print response of the post
} }
func readCounterTxFlags() (tx basecoin.Tx, err error) { func readCounterTxFlags() (tx basecoin.Tx, err error) {

View File

@ -37,31 +37,12 @@ func sendTxCmd(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return err return err
} }
return txcmd.DoTx(tx)
tx, err = txcmd.Middleware.Wrap(tx)
if err != nil {
return err
}
err = txcmd.SignTx(tx)
if err != nil {
return err
}
// otherwise, post it and display response
bres, err := txcmd.PrepareOrPostTx(tx)
if err != nil {
return err
}
if bres == nil {
return nil // successful prep, nothing left to do
}
return txcmd.OutputTx(bres) // print response of the post
} }
func readSendTxFlags() (tx basecoin.Tx, err error) { func readSendTxFlags() (tx basecoin.Tx, err error) {
// parse to address // parse to address
toAddr, err := commands.ParseAddress(viper.GetString(FlagTo)) toAddr, err := commands.ParseActor(viper.GetString(FlagTo))
if err != nil { if err != nil {
return tx, err return tx, err
} }
@ -94,5 +75,5 @@ func readFromAddr() (basecoin.Actor, error) {
if from == "" { if from == "" {
return txcmd.GetSignerAct(), nil return txcmd.GetSignerAct(), nil
} }
return commands.ParseAddress(from) return commands.ParseActor(from)
} }

View File

@ -55,5 +55,5 @@ func readPayer() (basecoin.Actor, error) {
if payer == "" { if payer == "" {
return txcmd.GetSignerAct(), nil return txcmd.GetSignerAct(), nil
} }
return commands.ParseAddress(payer) return commands.ParseActor(payer)
} }

View File

@ -9,7 +9,7 @@ import (
lc "github.com/tendermint/light-client" lc "github.com/tendermint/light-client"
"github.com/tendermint/basecoin" "github.com/tendermint/basecoin"
lcmd "github.com/tendermint/basecoin/client/commands" "github.com/tendermint/basecoin/client/commands"
proofcmd "github.com/tendermint/basecoin/client/commands/proofs" proofcmd "github.com/tendermint/basecoin/client/commands/proofs"
"github.com/tendermint/basecoin/modules/nonce" "github.com/tendermint/basecoin/modules/nonce"
"github.com/tendermint/basecoin/stack" "github.com/tendermint/basecoin/stack"
@ -19,7 +19,7 @@ import (
var NonceQueryCmd = &cobra.Command{ var NonceQueryCmd = &cobra.Command{
Use: "nonce [address]", Use: "nonce [address]",
Short: "Get details of a nonce sequence number, with proof", Short: "Get details of a nonce sequence number, with proof",
RunE: lcmd.RequireInit(nonceQueryCmd), RunE: commands.RequireInit(nonceQueryCmd),
} }
func nonceQueryCmd(cmd *cobra.Command, args []string) error { func nonceQueryCmd(cmd *cobra.Command, args []string) error {
@ -28,7 +28,7 @@ func nonceQueryCmd(cmd *cobra.Command, args []string) error {
} }
addr := strings.Join(args, ",") addr := strings.Join(args, ",")
signers, err := parseActors(addr) signers, err := commands.ParseActors(addr)
if err != nil { if err != nil {
return err return err
} }

View File

@ -2,7 +2,6 @@ package commands
import ( import (
"fmt" "fmt"
"strings"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -54,19 +53,7 @@ func readNonceKey() ([]basecoin.Actor, error) {
if nonce == "" { if nonce == "" {
return []basecoin.Actor{txcmd.GetSignerAct()}, nil return []basecoin.Actor{txcmd.GetSignerAct()}, nil
} }
return parseActors(nonce) return commands.ParseActors(nonce)
}
func parseActors(key string) (signers []basecoin.Actor, err error) {
var act basecoin.Actor
for _, k := range strings.Split(key, ",") {
act, err = commands.ParseAddress(k)
if err != nil {
return
}
signers = append(signers, act)
}
return
} }
// read the sequence from the flag or query for it if flag is -1 // read the sequence from the flag or query for it if flag is -1

View File

@ -0,0 +1,40 @@
package commands
import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
lcmd "github.com/tendermint/basecoin/client/commands"
proofcmd "github.com/tendermint/basecoin/client/commands/proofs"
"github.com/tendermint/basecoin/modules/roles"
"github.com/tendermint/basecoin/stack"
)
// RoleQueryCmd - command to query a role
var RoleQueryCmd = &cobra.Command{
Use: "role [name]",
Short: "Get details of a role, with proof",
RunE: lcmd.RequireInit(roleQueryCmd),
}
func roleQueryCmd(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("Missing required argument [name]")
} else if len(args) > 1 {
return errors.New("Command only supports one name")
}
role, err := parseRole(args[0])
if err != nil {
return err
}
var res roles.Role
key := stack.PrefixedKey(roles.NameRole, role)
proof, err := proofcmd.GetAndParseAppProof(key, &res)
if err != nil {
return err
}
return proofcmd.OutputProof(res, proof.BlockHeight())
}

View File

@ -0,0 +1,65 @@
package commands
import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/client/commands"
txcmd "github.com/tendermint/basecoin/client/commands/txs"
"github.com/tendermint/basecoin/modules/roles"
)
// CreateRoleTxCmd is CLI command to send tokens between basecoin accounts
var CreateRoleTxCmd = &cobra.Command{
Use: "send",
Short: "send tokens from one account to another",
RunE: commands.RequireInit(createRoleTxCmd),
}
//nolint
const (
FlagRole = "role"
FlagMembers = "members"
FlagMinSigs = "min-sigs"
)
func init() {
flags := CreateRoleTxCmd.Flags()
flags.String(FlagRole, "", "Name of the role to create")
flags.String(FlagMembers, "", "Set of comma-separated addresses for this role")
flags.Int(FlagMinSigs, 0, "Minimum number of signatures needed to assume this role")
}
// createRoleTxCmd is an example of how to make a tx
func createRoleTxCmd(cmd *cobra.Command, args []string) error {
tx, err := readCreateRoleTxFlags()
if err != nil {
return err
}
return txcmd.DoTx(tx)
}
func readCreateRoleTxFlags() (tx basecoin.Tx, err error) {
role, err := parseRole(viper.GetString(FlagRole))
if err != nil {
return tx, err
}
sigs := viper.GetInt(FlagMinSigs)
if sigs < 1 {
return tx, errors.Errorf("--%s must be at least 1", FlagMinSigs)
}
signers, err := commands.ParseActors(viper.GetString(FlagMembers))
if err != nil {
return tx, err
}
if len(signers) == 0 {
return tx, errors.New("must specify at least one member")
}
tx = roles.NewCreateRoleTx(role, uint32(sigs), signers)
return tx, nil
}

View File

@ -0,0 +1,48 @@
package commands
import (
"github.com/spf13/pflag"
"github.com/spf13/viper"
"github.com/tendermint/basecoin"
txcmd "github.com/tendermint/basecoin/client/commands/txs"
"github.com/tendermint/basecoin/modules/roles"
)
// nolint
const (
FlagAssumeRole = "assume-role"
)
// RoleWrapper wraps a tx with 0, 1, or more roles
type RoleWrapper struct{}
var _ txcmd.Wrapper = RoleWrapper{}
// Wrap grabs the sequence number from the flag and wraps
// the tx with this nonce. Grabs the permission from the signer,
// as we still only support single sig on the cli
func (RoleWrapper) Wrap(tx basecoin.Tx) (basecoin.Tx, error) {
assume := viper.GetStringSlice(FlagAssumeRole)
// we wrap from inside-out, so we must wrap them in the reverse order,
// so they are applied in the order the user intended
for i := len(assume) - 1; i >= 0; i-- {
r, err := parseRole(assume[i])
if err != nil {
return tx, err
}
tx = roles.NewAssumeRoleTx(r, tx)
}
return tx, nil
}
// Register adds the sequence flags to the cli
func (RoleWrapper) Register(fs *pflag.FlagSet) {
fs.StringSlice(FlagAssumeRole, nil, "Roles to assume (can use multiple times)")
}
// parse role turns the string->byte... todo: support hex?
func parseRole(role string) ([]byte, error) {
return []byte(role), nil
}