[cmd][pkg/transaction] Refactor merged code for ledger, provide more information in cookbook

pull/24/head
Edgar Aroutiounian 5 years ago
parent 9639ec546c
commit d67dc8984e
  1. 17
      README.md
  2. 4
      cmd/subcommands/keys.go
  3. 14
      cmd/subcommands/root.go
  4. 141
      cmd/subcommands/transfer.go
  5. 8
      cmd/subcommands/values.go
  6. 41
      pkg/transaction/controller.go
  7. 8
      pkg/transaction/signer.go

@ -15,6 +15,23 @@ invoke the following command to see the most command usages of `hmy`
```
$ hmy cookbook
Cookbook of usage, note that every subcommand recognizes a '--help' flag
1. Check Balances
hmy --node="https://api.s1.b.hmny.io/" balance <SOME_ONE_ADDRESS>
2. Check completed transaction
hmy --node="https://api.s1.b.hmny.io" blockchain transaction-by-hash <SOME_TRANSACTION_HASH>
3. List local keys
hmy keys list
4. Sending a transaction
./hmy --node="https://api.s0.b.hmny.io/" transfer \
--from one1yc06ghr2p8xnl2380kpfayweguuhxdtupkhqzw \
--to one1q6gkzcap0uruuu8r6sldxuu47pd4ww9w9t7tg6 \
--from-shard 0 --to-shard 1 --amount 200
```
# Debugging

@ -9,9 +9,9 @@ import (
"github.com/harmony-one/go-sdk/pkg/account"
c "github.com/harmony-one/go-sdk/pkg/common"
"github.com/harmony-one/go-sdk/pkg/ledger"
"github.com/harmony-one/go-sdk/pkg/mnemonic"
"github.com/harmony-one/go-sdk/pkg/store"
"github.com/harmony-one/go-sdk/pkg/ledger"
"github.com/spf13/cobra"
"github.com/tyler-smith/go-bip39"
"golang.org/x/crypto/ssh/terminal"
@ -25,7 +25,6 @@ const (
var (
recoverFromMnemonic bool
userProvidesPassphrase bool
useLedgerWallet bool
)
func doubleTakePhrase() string {
@ -109,7 +108,6 @@ Manage your local keys
},
}
RootCmd.PersistentFlags().BoolVarP(&useLedgerWallet, "ledger", "e", false, "Use ledger hardware wallet")
cmdKeys.AddCommand(keysSub()...)
RootCmd.AddCommand(cmdKeys)
}

@ -14,6 +14,7 @@ import (
)
var (
useLedgerWallet bool
useLatestInParamsForRPC bool
noPrettyOutput bool
node string
@ -49,17 +50,10 @@ See "hmy cookbook" for examples of the most common, important usages`,
)
func init() {
RootCmd.PersistentFlags().StringVarP(
&node,
"node",
"n",
defaultNodeAddr,
"<host>:<port>",
)
RootCmd.PersistentFlags().StringVarP(&node, "node", "n", defaultNodeAddr, "<host>")
RootCmd.PersistentFlags().BoolVarP(&useLatestInParamsForRPC, "latest", "l", false, "Add 'latest' to RPC params")
RootCmd.PersistentFlags().BoolVar(&noPrettyOutput, "no-pretty", false, "disable pretty print JSON outputs")
RootCmd.PersistentFlags().StringVar(&keyStoreDir, "key-store-dir", "k", "What directory to use as the keystore")
RootCmd.PersistentFlags().StringVar(&keyStoreDir, "key-store-dir", "k", "what directory to use as the keystore")
RootCmd.AddCommand(&cobra.Command{
Use: "cookbook",
Short: "Example usages of the most important, frequently used commands",
@ -67,7 +61,7 @@ func init() {
fmt.Print(cookbookDoc)
},
})
RootCmd.PersistentFlags().BoolVarP(&useLedgerWallet, "ledger", "e", false, "Use ledger hardware wallet")
RootCmd.AddCommand(&cobra.Command{
Use: "docs",
Short: fmt.Sprintf("Generate docs to a local %s directory", defaultNodeAddr),

@ -46,83 +46,57 @@ Create a transaction, sign it, and send off to the Harmony blockchain
`,
RunE: func(cmd *cobra.Command, args []string) error {
networkHandler := handlerForShard(fromShardID, node)
dryRunOpt := func(ctlr *transaction.Controller) {
if dryRun {
ctlr.Behavior.DryRun = true
}
ks := store.FromAddress(fromAddress.String())
if ks == nil {
return fmt.Errorf("could not open local keystore for %s", fromAddress)
}
sender := address.Parse(fromAddress.String())
var ctrlr *transaction.Controller
if useLedgerWallet {
account := accounts.Account{Address: sender}
ctrlr = transaction.NewController(
networkHandler, nil, &account,
*chainName.chainID,
dryRunOpt,
)
if transactionFailure := ctrlr.ExecuteHardwareTransaction(
toAddress.String(),
"",
amount, gasPrice,
fromShardID,
toShardID,
); transactionFailure != nil {
return transactionFailure
}
} else {
ks := store.FromAddress(fromAddress.String())
if ks == nil {
return fmt.Errorf("could not open local keystore for %s", fromAddress.String())
}
account, lookupErr := ks.Find(accounts.Account{Address: sender})
if lookupErr != nil {
return fmt.Errorf("could not find %s in keystore", fromAddress.String())
}
if unlockError := ks.Unlock(account, unlockP); unlockError != nil {
return errors.New("could not unlock account with passphrase, perhaps need different phrase")
}
dryRunOpt := func(ctlr *transaction.Controller) {
if dryRun {
ctlr.Behavior.DryRun = true
}
account, lookupErr := ks.Find(accounts.Account{Address: sender})
if lookupErr != nil {
return fmt.Errorf("could not find %s in keystore", fromAddress)
}
if unlockError := ks.Unlock(account, unlockP); unlockError != nil {
return errors.New("could not unlock account with passphrase, perhaps need different phrase")
}
opts := func(ctlr *transaction.Controller) {
if dryRun {
ctlr.Behavior.DryRun = true
}
ctrlr = transaction.NewController(
networkHandler, ks, &account,
*chainName.chainID,
dryRunOpt,
)
if transactionFailure := ctrlr.ExecuteTransaction(
toAddress.String(),
"",
amount, gasPrice,
fromShardID,
toShardID,
); transactionFailure != nil {
return transactionFailure
if useLedgerWallet {
ctlr.Behavior.SigningImpl = transaction.Ledger
}
}
ctrlr :=
transaction.NewController(networkHandler, ks, &account, *chainName.chainID, opts)
if transactionFailure := ctrlr.ExecuteTransaction(
toAddress.String(),
"",
amount, gasPrice,
fromShardID,
toShardID,
); transactionFailure != nil {
return transactionFailure
}
if !dryRun {
fmt.Println(fmt.Sprintf(`{"transaction-receipt":"%s"}`, ctrlr.Receipt()))
fmt.Println(fmt.Sprintf(`{"transaction-receipt":"%s"}`, *ctrlr.Receipt()))
}
return nil
},
}
cmdTransfer.Flags().Var(&fromAddress, "from", "From can be an account alias or a one address")
cmdTransfer.Flags().Var(&toAddress, "to", "the to address")
cmdTransfer.Flags().BoolVar(&dryRun, "dry-run", false, "Do not send signed transaction")
cmdTransfer.Flags().Var(&fromAddress, "from", "sender's one address, keystore must exist locally")
cmdTransfer.Flags().Var(&toAddress, "to", "the destination one address")
cmdTransfer.Flags().BoolVar(&dryRun, "dry-run", false, "do not send signed transaction")
cmdTransfer.Flags().Float64Var(&amount, "amount", 0.0, "amount")
cmdTransfer.Flags().Float64Var(&gasPrice, "gas-price", 0.0, "gas price to pay")
cmdTransfer.Flags().IntVar(&fromShardID, "from-shard", -1, "source shard id")
cmdTransfer.Flags().IntVar(&toShardID, "to-shard", -1, "target shard id")
cmdTransfer.Flags().Var(&chainName, "chain-id", "What chain ID to target")
cmdTransfer.Flags().Uint32Var(&confirmWait, "wait-for-confirm", 0, "Only waits if non-zero value, in seconds")
cmdTransfer.Flags().StringVar(&unlockP, "passphrase", common.DefaultPassphrase, "Passphrase to unlock `from`")
cmdTransfer.Flags().Var(&chainName, "chain-id", "what chain ID to target")
cmdTransfer.Flags().Uint32Var(&confirmWait, "wait-for-confirm", 0, "only waits if non-zero value, in seconds")
cmdTransfer.Flags().StringVar(&unlockP,
"passphrase", common.DefaultPassphrase,
"passphrase to unlock sender's keystore",
)
for _, flagName := range [...]string{"from", "to", "amount", "from-shard", "to-shard"} {
cmdTransfer.MarkFlagRequired(flagName)
@ -130,46 +104,3 @@ Create a transaction, sign it, and send off to the Harmony blockchain
RootCmd.AddCommand(cmdTransfer)
}
// implemets pflag.Value interface
type oneAddress struct {
address string
}
func (oneAddress oneAddress) String() string {
return oneAddress.address
}
func (oneAddress *oneAddress) Set(s string) error {
_, err := address.Bech32ToAddress(s)
if err != nil {
return err
}
oneAddress.address = s
return nil
}
func (oneAddress oneAddress) Type() string {
return "OneAddress"
}
// implements pflag.Value interface
type chainIDWrapper struct {
chainID *common.ChainID
}
func (chainIDWrapper chainIDWrapper) String() string {
return chainIDWrapper.chainID.Name
}
func (chainIDWrapper *chainIDWrapper) Set(s string) error {
chainIDWrapper.chainID = common.StringToChainID(s)
if chainIDWrapper.chainID == nil {
return errors.New("ChainID \"" + s + "\" is invalid")
}
return nil
}
func (chainIDWrapper chainIDWrapper) Type() string {
return "ChainID"
}

@ -7,7 +7,7 @@ import (
)
const (
hmyDocsDir = "hmycli-docs"
hmyDocsDir = "hmy-docs"
defaultNodeAddr = "http://localhost:9500"
)
@ -20,7 +20,7 @@ Cookbook of usage, note that every subcommand recognizes a '--help' flag
hmy --node="https://api.s1.b.hmny.io/" balance <SOME_ONE_ADDRESS>
%s
hmy --node="https://api.s1.b.hmny.io" blockchain transaction-by-hash <SOME_TRANSACTION_HASH>
hmy --node="https://api.s1.b.hmny.io" blockchain transaction-by-hash <SOME_TX_HASH>
%s
hmy keys list
@ -30,10 +30,14 @@ hmy keys list
--from one1yc06ghr2p8xnl2380kpfayweguuhxdtupkhqzw \
--to one1q6gkzcap0uruuu8r6sldxuu47pd4ww9w9t7tg6 \
--from-shard 0 --to-shard 1 --amount 200
%s
./hmy --node="https://api.s0.b.hmny.io" blockchain transaction-receipt <SOME_TX_HASH>
`,
g("1. Check Balances"),
g("2. Check completed transaction"),
g("3. List local keys"),
g("4. Sending a transaction"),
g("5. Check a completed transaction receipt"),
)
)

@ -12,8 +12,8 @@ import (
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/go-sdk/pkg/address"
"github.com/harmony-one/go-sdk/pkg/common"
"github.com/harmony-one/go-sdk/pkg/rpc"
"github.com/harmony-one/go-sdk/pkg/ledger"
"github.com/harmony-one/go-sdk/pkg/rpc"
"github.com/harmony-one/harmony/accounts"
"github.com/harmony-one/harmony/accounts/keystore"
"github.com/harmony-one/harmony/common/denominations"
@ -45,7 +45,8 @@ type Controller struct {
}
type behavior struct {
DryRun bool
DryRun bool
SigningImpl SignerImpl
}
func NewController(
@ -69,7 +70,7 @@ func NewController(
receipt: nil,
},
chain: chain,
Behavior: behavior{false},
Behavior: behavior{false, Software},
}
for _, option := range options {
option(ctrlr)
@ -210,25 +211,6 @@ func (C *Controller) Receipt() *string {
return C.transactionForRPC.receipt
}
func (C *Controller) ExecuteTransaction(
to, inputData string,
amount, gPrice float64,
fromShard, toShard int,
) error {
// WARNING Order of execution matters
C.setShardIDs(fromShard, toShard)
C.setIntrinsicGas(inputData)
C.setAmount(amount)
C.verifyBalance(amount)
C.setReceiver(to)
C.setGasPrice()
C.setNextNonce()
C.setNewTransactionWithDataAndGas(inputData, amount, gPrice)
C.signAndPrepareTxEncodedForSending()
C.sendSignedTx()
return C.failure
}
func (C *Controller) hardwareSignAndPrepareTxEncodedForSending() {
if C.failure != nil {
return
@ -240,7 +222,7 @@ func (C *Controller) hardwareSignAndPrepareTxEncodedForSending() {
os.Exit(-1)
}
if strings.Compare(signerAddr, address.ToBech32(C.sender.account.Address)) != 0 {
if strings.Compare(signerAddr, address.ToBech32(C.sender.account.Address)) != 0 {
fmt.Println("signature verification failed : sender address doesn't match with ledger hardware addresss")
os.Exit(-1)
}
@ -249,15 +231,11 @@ func (C *Controller) hardwareSignAndPrepareTxEncodedForSending() {
C.transactionForRPC.signature = &hexSignature
}
func (C *Controller) ExecuteHardwareTransaction(
func (C *Controller) ExecuteTransaction(
to, inputData string,
amount, gPrice float64,
fromShard, toShard int,
) error {
C.transactionForRPC.params["gas-price"] = big.NewInt(0)
fmt.Println(to, inputData, amount, fromShard, toShard)
// WARNING Order of execution matters
C.setShardIDs(fromShard, toShard)
C.setIntrinsicGas(inputData)
@ -267,7 +245,12 @@ func (C *Controller) ExecuteHardwareTransaction(
C.setGasPrice()
C.setNextNonce()
C.setNewTransactionWithDataAndGas(inputData, amount, gPrice)
C.hardwareSignAndPrepareTxEncodedForSending()
switch C.Behavior.SigningImpl {
case Software:
C.signAndPrepareTxEncodedForSending()
case Ledger:
C.hardwareSignAndPrepareTxEncodedForSending()
}
C.sendSignedTx()
return C.failure
}

@ -0,0 +1,8 @@
package transaction
type SignerImpl int
const (
Software SignerImpl = iota
Ledger
)
Loading…
Cancel
Save