diff --git a/cmd/basecli/adapters.go b/cmd/basecli/adapters.go new file mode 100644 index 0000000000..b0b10d2fc4 --- /dev/null +++ b/cmd/basecli/adapters.go @@ -0,0 +1,156 @@ +package main + +import ( + "encoding/hex" + "encoding/json" + + "github.com/pkg/errors" + flag "github.com/spf13/pflag" + "github.com/spf13/viper" + btypes "github.com/tendermint/basecoin/types" + keycmd "github.com/tendermint/go-crypto/cmd" + wire "github.com/tendermint/go-wire" + lightclient "github.com/tendermint/light-client" + "github.com/tendermint/light-client/commands" + "github.com/tendermint/light-client/proofs" +) + +type AccountPresenter struct{} + +func (_ AccountPresenter) MakeKey(str string) ([]byte, error) { + res, err := hex.DecodeString(str) + if err == nil { + res = append([]byte("base/a/"), res...) + } + return res, err +} + +func (_ AccountPresenter) ParseData(raw []byte) (interface{}, error) { + var acc *btypes.Account + err := wire.ReadBinaryBytes(raw, &acc) + return acc, err +} + +type BaseTxPresenter struct { + proofs.RawPresenter // this handles MakeKey as hex bytes +} + +func (_ BaseTxPresenter) ParseData(raw []byte) (interface{}, error) { + var tx btypes.TxS + err := wire.ReadBinaryBytes(raw, &tx) + return tx, err +} + +// SendTXReader allows us to create SendTx +type SendTxReader struct { + ChainID string +} + +func (t SendTxReader) ReadTxJSON(data []byte) (interface{}, error) { + var tx btypes.SendTx + err := json.Unmarshal(data, &tx) + send := SendTx{ + chainID: t.ChainID, + Tx: &tx, + } + return &send, errors.Wrap(err, "parse sendtx") +} + +type SendTxMaker struct{} + +func (m SendTxMaker) MakeReader() (lightclient.TxReader, error) { + chainID := viper.GetString(commands.ChainFlag) + return SendTxReader{ChainID: chainID}, nil +} + +type SendFlags struct { + To string + Amount string + Fee string + Gas int64 + Sequence int +} + +func (m SendTxMaker) Flags() (*flag.FlagSet, interface{}) { + fs := flag.NewFlagSet("foobar", flag.ContinueOnError) + fs.String("to", "", "Destination address for the bits") + fs.String("amount", "", "Coins to send in the format ,...") + fs.String("fee", "", "Coins for the transaction fee of the format ") + fs.Int64("gas", 0, "Amount of gas for this transaction") + fs.Int("sequence", -1, "Sequence number for this transaction") + return fs, &SendFlags{} +} + +func (t SendTxReader) ReadTxFlags(flags interface{}) (interface{}, error) { + data := flags.(*SendFlags) + + // parse to and from addresses + to, err := hex.DecodeString(StripHex(data.To)) + if err != nil { + return nil, errors.Errorf("To address is invalid hex: %v\n", err) + } + + // TODO: figure out a cleaner way to do this... until then + // just close your eyes and continue... + manager := keycmd.GetKeyManager() + name := viper.GetString("name") + info, err := manager.Get(name) + + //parse the fee and amounts into coin types + feeCoin, err := btypes.ParseCoin(data.Fee) + if err != nil { + return nil, err + } + amountCoins, err := btypes.ParseCoins(data.Amount) + if err != nil { + return nil, err + } + + // craft the tx + input := btypes.TxInput{ + Address: info.Address, + Coins: amountCoins, + Sequence: data.Sequence, + } + if data.Sequence == 1 { + input.PubKey = info.PubKey + } + output := btypes.TxOutput{ + Address: to, + Coins: amountCoins, + } + tx := btypes.SendTx{ + Gas: data.Gas, + Fee: feeCoin, + Inputs: []btypes.TxInput{input}, + Outputs: []btypes.TxOutput{output}, + } + + // wrap it in the proper signer thing... + send := SendTx{ + chainID: t.ChainID, + Tx: &tx, + } + return &send, nil +} + +/** copied from basecoin cli - put in common somewhere? **/ + +// Returns true for non-empty hex-string prefixed with "0x" +func isHex(s string) bool { + if len(s) > 2 && s[:2] == "0x" { + _, err := hex.DecodeString(s[2:]) + if err != nil { + return false + } + return true + } + return false +} + +func StripHex(s string) string { + if isHex(s) { + return s[2:] + } + return s +} diff --git a/cmd/basecli/main.go b/cmd/basecli/main.go new file mode 100644 index 0000000000..2ecb8c75ea --- /dev/null +++ b/cmd/basecli/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "os" + + "github.com/spf13/cobra" + keycmd "github.com/tendermint/go-crypto/cmd" + "github.com/tendermint/light-client/commands" + "github.com/tendermint/light-client/commands/proofs" + "github.com/tendermint/light-client/commands/seeds" + "github.com/tendermint/light-client/commands/txs" +) + +// BaseCli represents the base command when called without any subcommands +var BaseCli = &cobra.Command{ + Use: "basecli", + Short: "Light client for tendermint", + Long: `Basecli is an version of tmcli including custom logic to +present a nice (not raw hex) interface to the basecoin blockchain structure. + +This is a useful tool, but also serves to demonstrate how one can configure +tmcli to work for any custom abci app. +`, +} + +func init() { + commands.AddBasicFlags(BaseCli) + + // set up the various commands to use + BaseCli.AddCommand(keycmd.RootCmd) + BaseCli.AddCommand(commands.InitCmd) + BaseCli.AddCommand(seeds.RootCmd) + proofs.StatePresenters.Register("account", AccountPresenter{}) + proofs.TxPresenters.Register("base", BaseTxPresenter{}) + BaseCli.AddCommand(proofs.RootCmd) + txs.Register("send", SendTxMaker{}) + BaseCli.AddCommand(txs.RootCmd) +} + +func main() { + keycmd.PrepareMainCmd(BaseCli, "BC", os.ExpandEnv("$HOME/.basecli")) + BaseCli.Execute() + // err := BaseCli.Execute() + // if err != nil { + // fmt.Printf("%+v\n", err) + // } +} diff --git a/cmd/basecli/sendtx.go b/cmd/basecli/sendtx.go new file mode 100644 index 0000000000..5f0ae78882 --- /dev/null +++ b/cmd/basecli/sendtx.go @@ -0,0 +1,60 @@ +package main + +import ( + "github.com/pkg/errors" + bc "github.com/tendermint/basecoin/types" + crypto "github.com/tendermint/go-crypto" + keys "github.com/tendermint/go-crypto/keys" + wire "github.com/tendermint/go-wire" +) + +type SendTx struct { + chainID string + signers []crypto.PubKey + Tx *bc.SendTx +} + +var _ keys.Signable = &SendTx{} + +// SignBytes returned the unsigned bytes, needing a signature +func (s *SendTx) SignBytes() []byte { + return s.Tx.SignBytes(s.chainID) +} + +// Sign will add a signature and pubkey. +// +// Depending on the Signable, one may be able to call this multiple times for multisig +// Returns error if called with invalid data or too many times +func (s *SendTx) Sign(pubkey crypto.PubKey, sig crypto.Signature) error { + addr := pubkey.Address() + set := s.Tx.SetSignature(addr, sig) + if !set { + return errors.Errorf("Cannot add signature for address %X", addr) + } + s.signers = append(s.signers, pubkey) + return nil +} + +// Signers will return the public key(s) that signed if the signature +// is valid, or an error if there is any issue with the signature, +// including if there are no signatures +func (s *SendTx) Signers() ([]crypto.PubKey, error) { + if len(s.signers) == 0 { + return nil, errors.New("No signatures on SendTx") + } + return s.signers, nil +} + +// TxBytes returns the transaction data as well as all signatures +// It should return an error if Sign was never called +func (s *SendTx) TxBytes() ([]byte, error) { + // TODO: verify it is signed + + // Code and comment from: basecoin/cmd/commands/tx.go + // Don't you hate having to do this? + // How many times have I lost an hour over this trick?! + txBytes := wire.BinaryBytes(struct { + bc.Tx `json:"unwrap"` + }{s.Tx}) + return txBytes, nil +} diff --git a/glide.lock b/glide.lock index f6772847ae..553f293582 100644 --- a/glide.lock +++ b/glide.lock @@ -1,6 +1,8 @@ -hash: ab7d4136802bfb9c56c25d6c384ce65891adfda2f2fc338fb4532ecc8e85ad40 -updated: 2017-04-27T12:49:42.595893036-04:00 +hash: f4077fecc95e11f007adea022a38441a2e1c5d51d75ebab0fc9a296052ee5a6b +updated: 2017-04-27T23:14:26.934716255+02:00 imports: +- name: github.com/bgentry/speakeasy + version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd - name: github.com/btcsuite/btcd version: 4b348c1d33373d672edd83fc576892d0e46686d2 subpackages: @@ -9,6 +11,12 @@ imports: version: 95f809107225be108efcf10a3509e4ea6ceef3c4 - name: github.com/fsnotify/fsnotify version: 4da3e2cfbabc9f751898f250b49f2439785783a1 +- name: github.com/go-playground/locales + version: 1e5f1161c6416a5ff48840eb8724a394e48cc534 + subpackages: + - currency +- name: github.com/go-playground/universal-translator + version: 71201497bace774495daed26a3874fd339e0b538 - name: github.com/go-stack/stack version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82 - name: github.com/golang/protobuf @@ -18,6 +26,12 @@ imports: - ptypes/any - name: github.com/golang/snappy version: 553a641470496b2327abcac10b36396bd98e45c9 +- name: github.com/gorilla/context + version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42 +- name: github.com/gorilla/handlers + version: 3a5767ca75ece5f7f1440b1d16975247f8d8b221 +- name: github.com/gorilla/mux + version: 392c28fe23e1c45ddba891b0320b3b5df220beea - name: github.com/gorilla/websocket version: 3ab3a8b8831546bd18fd182c20687ca853b2bb13 - name: github.com/hashicorp/hcl @@ -48,7 +62,7 @@ imports: - name: github.com/pelletier/go-toml version: 13d49d4606eb801b8f01ae542b4afc4c6ee3d84a - name: github.com/pkg/errors - version: ff09b135c25aae272398c51a07235b90a75aa4f0 + version: 645ef00459ed84a119197bfb8d8205042c6df63d - name: github.com/spf13/afero version: 9be650865eab0c12963d8753212f4f9c66cdcf12 subpackages: @@ -90,18 +104,46 @@ imports: subpackages: - edwards25519 - extra25519 +- name: github.com/tendermint/go-common + version: f9e3db037330c8a8d61d3966de8473eaf01154fa - name: github.com/tendermint/go-crypto - version: 9b95da8fa4187f6799558d89b271dc8ab6485615 + version: 197a2b270fd94ee03824b158e738fce62862d0b8 + subpackages: + - cmd + - keys + - keys/cmd + - keys/cryptostore + - keys/server + - keys/server/types + - keys/storage/filestorage + - keys/storage/memstorage +- name: github.com/tendermint/go-db + version: 9643f60bc2578693844aacf380a7c32e4c029fee +- name: github.com/tendermint/go-merkle + version: 714d4d04557fd068a7c2a1748241ce8428015a96 - name: github.com/tendermint/go-wire version: 334005c236d19c632fb5f073f9de3b0fab6a522b subpackages: - data + - data/base58 +- name: github.com/tendermint/light-client + version: d13e97e756e914fe9c6c1b23ea639b51f1d5bf40 + subpackages: + - certifiers + - certifiers/client + - certifiers/files + - commands + - commands/proofs + - commands/seeds + - commands/tx + - commands/txs + - proofs - name: github.com/tendermint/log15 version: ae0f3d6450da9eac7074b439c8e1c3cabf0d5ce6 subpackages: - term - name: github.com/tendermint/merkleeyes - version: 6fd69aa0871a4e685a5570aa7ab3d12e4068a722 + version: 0fab643ccac1a3f93b90e0e2682a5d1b9d17f8c4 subpackages: - app - client @@ -118,6 +160,7 @@ imports: - p2p - p2p/upnp - proxy + - rpc/client - rpc/core - rpc/core/types - rpc/grpc @@ -193,6 +236,8 @@ imports: - status - tap - transport +- name: gopkg.in/go-playground/validator.v9 + version: 6d8c18553ea1ac493d049edd6f102f52e618f085 - name: gopkg.in/yaml.v2 version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b testImports: diff --git a/glide.yaml b/glide.yaml index e232139b58..0abe9430da 100644 --- a/glide.yaml +++ b/glide.yaml @@ -11,10 +11,23 @@ import: - types - package: github.com/tendermint/go-crypto version: develop + subpackages: + - keys/cmd + - keys/cryptostore + - keys/server + - keys/storage/filestorage + - keys/storage/memstorage - package: github.com/tendermint/go-wire version: develop subpackages: - data +- package: github.com/tendermint/light-client + version: develop + subpackages: + - commands + - commands/proofs + - commands/seeds + - commands/tx - package: github.com/tendermint/merkleeyes version: develop subpackages: