Skip to content

Commit

Permalink
Merge pull request #295 from MaxMustermann2/data-signing
Browse files Browse the repository at this point in the history
[console/cmd] add support for `data` signing
  • Loading branch information
sunwavesun authored Dec 20, 2023
2 parents c38f4e4 + 24c5bf6 commit 178d050
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 44 deletions.
81 changes: 77 additions & 4 deletions cmd/subcommands/ethtransfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -43,9 +44,13 @@ func ethHandlerForShard(node string) (*rpc.HTTPMessenger, error) {
// Note that the vars need to be set before calling this handler.
func ethHandlerForTransaction(txLog *transactionLog) error {
from := fromAddress.String()
networkHandler, err := ethHandlerForShard(node)
if handlerForError(txLog, err) != nil {
return err
var networkHandler *rpc.HTTPMessenger
if !offlineSign {
var err error
networkHandler, err = ethHandlerForShard(node)
if handlerForError(txLog, err) != nil {
return err
}
}

var ctrlr *transaction.EthController
Expand Down Expand Up @@ -98,12 +103,17 @@ func ethHandlerForTransaction(txLog *transactionLog) error {
gLimit = uint64(tempLimit)
}

dataByte, err := transaction.StringToByte(data)
if err != nil {
return handlerForError(txLog, err)
}

txLog.TimeSigned = time.Now().UTC().Format(timeFormat) // Approximate time of signature
err = ctrlr.ExecuteEthTransaction(
nonce, gLimit,
toAddress.String(),
amt, gPrice,
[]byte{},
dataByte,
)

if dryRun {
Expand Down Expand Up @@ -187,6 +197,9 @@ func ethOpts(ctlr *transaction.EthController) {
if dryRun {
ctlr.Behavior.DryRun = true
}
if offlineSign {
ctlr.Behavior.OfflineSign = true
}
if useLedgerWallet {
ctlr.Behavior.SigningImpl = transaction.Ledger
}
Expand All @@ -204,6 +217,9 @@ func init() {
Create an Ethereum compatible transaction, sign it, and send off to the Harmony blockchain
`,
PreRunE: func(cmd *cobra.Command, args []string) error {
if offlineSign {
dryRun = true
}
if givenFilePath == "" {
for _, flagName := range [...]string{"from", "to", "amount", "chain-id"} {
_ = cmd.MarkFlagRequired(flagName)
Expand Down Expand Up @@ -269,15 +285,72 @@ Create an Ethereum compatible transaction, sign it, and send off to the Harmony
cmdEthTransfer.Flags().Var(&fromAddress, "from", "sender's one address, keystore must exist locally")
cmdEthTransfer.Flags().Var(&toAddress, "to", "the destination one address")
cmdEthTransfer.Flags().BoolVar(&dryRun, "dry-run", false, "do not send signed transaction")
cmdEthTransfer.Flags().BoolVar(&offlineSign, "offline-sign", false, "output offline signing")
cmdEthTransfer.Flags().BoolVar(&trueNonce, "true-nonce", false, "send transaction with on-chain nonce")
cmdEthTransfer.Flags().StringVar(&amount, "amount", "0", "amount to send (ONE)")
cmdEthTransfer.Flags().StringVar(&gasPrice, "gas-price", "100", "gas price to pay (NANO)")
cmdEthTransfer.Flags().StringVar(&gasLimit, "gas-limit", "", "gas limit")
cmdEthTransfer.Flags().StringVar(&inputNonce, "nonce", "", "set nonce for tx")
cmdEthTransfer.Flags().StringVar(&data, "data", "", "transaction data")
cmdEthTransfer.Flags().StringVar(&targetChain, "chain-id", "", "what chain ID to target")
cmdEthTransfer.Flags().Uint32Var(&timeout, "timeout", defaultTimeout, "set timeout in seconds. Set to 0 to not wait for confirm")
cmdEthTransfer.Flags().BoolVar(&userProvidesPassphrase, "passphrase", false, ppPrompt)
cmdEthTransfer.Flags().StringVar(&passphraseFilePath, "passphrase-file", "", "path to a file containing the passphrase")

RootCmd.AddCommand(cmdEthTransfer)

cmdOfflineSignEthTransfer := &cobra.Command{
Use: "offline-sign-eth-transfer",
Short: "Send a Offline Signed Ethereum transaction",
Args: cobra.ExactArgs(0),
Long: `
Send a offline signed transaction to the Harmony blockchain (on the same shard)
`,
PreRunE: func(cmd *cobra.Command, args []string) error {
if givenFilePath == "" {
return fmt.Errorf("must give a offline-signed file")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
var txLogs []*transactionLog

networkHandler := rpc.NewHTTPHandler(node)

openFile, err := os.Open(givenFilePath)
if err != nil {
return err
}
defer openFile.Close()

err = json.NewDecoder(openFile).Decode(&txLogs)
if err != nil {
return err
}

for _, txLog := range txLogs {
if len(txLog.Errors) > 0 {
continue
}

ctrlr := transaction.NewEthController(networkHandler, nil, nil, *chainName.chainID, ethOpts)
err := ctrlr.ExecuteRawTransaction(txLog.RawTxn)
if handlerForError(txLog, err) != nil {
txLog.Errors = append(txLog.Errors, err.Error())
continue
}

if txHash := ctrlr.TransactionHash(); txHash != nil {
txLog.TxHash = *txHash
}

txLog.Receipt = ctrlr.Receipt()["result"]
}

fmt.Println(common.ToJSONUnsafe(txLogs, true))
return nil
},
}

RootCmd.AddCommand(cmdOfflineSignEthTransfer)
}
11 changes: 9 additions & 2 deletions cmd/subcommands/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ var (
transferFileFlags []transferFlags
timeout uint32
timeFormat = "2006-01-02 15:04:05.000000"
data string
)

type transactionLog struct {
Expand Down Expand Up @@ -169,6 +170,11 @@ func handlerForTransaction(txLog *transactionLog) error {
gLimit = uint64(tempLimit)
}

dataByte, err := transaction.StringToByte(data)
if err != nil {
return handlerForError(txLog, err)
}

addr := toAddress.String()

txLog.TimeSigned = time.Now().UTC().Format(timeFormat) // Approximate time of signature
Expand All @@ -177,7 +183,7 @@ func handlerForTransaction(txLog *transactionLog) error {
&addr,
fromShardID, toShardID,
amt, gPrice,
[]byte{},
dataByte,
)

if dryRun {
Expand Down Expand Up @@ -414,6 +420,7 @@ Create a transaction, sign it, and send off to the Harmony blockchain
cmdTransfer.Flags().StringVar(&inputNonce, "nonce", "", "set nonce for tx")
cmdTransfer.Flags().Uint32Var(&fromShardID, "from-shard", 0, "source shard id")
cmdTransfer.Flags().Uint32Var(&toShardID, "to-shard", 0, "target shard id")
cmdTransfer.Flags().StringVar(&data, "data", "", "transaction data")
cmdTransfer.Flags().StringVar(&targetChain, "chain-id", "", "what chain ID to target")
cmdTransfer.Flags().Uint32Var(&timeout, "timeout", defaultTimeout, "set timeout in seconds. Set to 0 to not wait for confirm")
cmdTransfer.Flags().BoolVar(&userProvidesPassphrase, "passphrase", false, ppPrompt)
Expand Down Expand Up @@ -449,7 +456,7 @@ Get Nonce From a Account
Short: "Send a Offline Signed transaction",
Args: cobra.ExactArgs(0),
Long: `
Send a offline signed to the Harmony blockchain
Send a offline signed transaction to the Harmony blockchain
`,
PreRunE: func(cmd *cobra.Command, args []string) error {
if givenFilePath == "" {
Expand Down
37 changes: 23 additions & 14 deletions pkg/console/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,6 @@ import (
"encoding/hex"
"errors"
"fmt"
ethereum_rpc "github.com/ethereum/go-ethereum/rpc"
"github.com/harmony-one/go-sdk/pkg/account"
"github.com/harmony-one/go-sdk/pkg/address"
"github.com/harmony-one/go-sdk/pkg/common"
"github.com/harmony-one/go-sdk/pkg/console/jsre"
"github.com/harmony-one/go-sdk/pkg/console/jsre/deps"
"github.com/harmony-one/go-sdk/pkg/console/prompt"
"github.com/harmony-one/go-sdk/pkg/console/web3ext"
"github.com/harmony-one/go-sdk/pkg/rpc"
"github.com/harmony-one/go-sdk/pkg/store"
"github.com/harmony-one/go-sdk/pkg/transaction"
"github.com/harmony-one/harmony/accounts"
"io"
"io/ioutil"
"math/big"
Expand All @@ -29,6 +17,19 @@ import (
"syscall"
"time"

ethereum_rpc "github.com/ethereum/go-ethereum/rpc"
"github.com/harmony-one/go-sdk/pkg/account"
"github.com/harmony-one/go-sdk/pkg/address"
"github.com/harmony-one/go-sdk/pkg/common"
"github.com/harmony-one/go-sdk/pkg/console/jsre"
"github.com/harmony-one/go-sdk/pkg/console/jsre/deps"
"github.com/harmony-one/go-sdk/pkg/console/prompt"
"github.com/harmony-one/go-sdk/pkg/console/web3ext"
"github.com/harmony-one/go-sdk/pkg/rpc"
"github.com/harmony-one/go-sdk/pkg/store"
"github.com/harmony-one/go-sdk/pkg/transaction"
"github.com/harmony-one/harmony/accounts"

"github.com/dop251/goja"
"github.com/mattn/go-colorable"
"github.com/peterh/liner"
Expand Down Expand Up @@ -553,6 +554,10 @@ func (b *bridge) HmySignTransaction(call jsre.Call) (goja.Value, error) {
gasLimit := getStringFromJsObjWithDefault(txObj, "gas", "1000000")
amount := getStringFromJsObjWithDefault(txObj, "value", "0")
gasPrice := getStringFromJsObjWithDefault(txObj, "gasPrice", "1")
input, err := transaction.StringToByte(getStringFromJsObjWithDefault(txObj, "data", ""))
if err != nil {
return nil, err
}

networkHandler := rpc.NewHTTPHandler(b.console.nodeUrl)
chanId, err := common.StringToChainID(b.console.net)
Expand Down Expand Up @@ -598,7 +603,7 @@ func (b *bridge) HmySignTransaction(call jsre.Call) (goja.Value, error) {
toP,
uint32(b.console.shardId), uint32(b.console.shardId),
amt, gPrice,
[]byte{},
input,
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -635,6 +640,10 @@ func (b *bridge) HmySendTransaction(call jsre.Call) (goja.Value, error) {
gasLimit := getStringFromJsObjWithDefault(txObj, "gas", "1000000")
amount := getStringFromJsObjWithDefault(txObj, "value", "0")
gasPrice := getStringFromJsObjWithDefault(txObj, "gasPrice", "1")
input, err := transaction.StringToByte(getStringFromJsObjWithDefault(txObj, "data", ""))
if err != nil {
return nil, err
}

networkHandler := rpc.NewHTTPHandler(b.console.nodeUrl)
chanId, err := common.StringToChainID(b.console.net)
Expand Down Expand Up @@ -680,7 +689,7 @@ func (b *bridge) HmySendTransaction(call jsre.Call) (goja.Value, error) {
toP,
uint32(b.console.shardId), uint32(b.console.shardId),
amt, gPrice,
[]byte{},
input,
)
if err != nil {
return nil, err
Expand Down
1 change: 0 additions & 1 deletion pkg/transaction/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ func (C *Controller) setAmount(amount numeric.Dec) {
return
}
}

C.transactionForRPC.params["transfer-amount"] = amountInAtto
}

Expand Down
55 changes: 32 additions & 23 deletions pkg/transaction/ethcontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,34 +134,37 @@ func (C *EthController) setAmount(amount numeric.Dec) {
})
return
}
balanceRPCReply, err := C.messenger.SendRPC(
rpc.Method.GetBalance,
p{address.ToBech32(C.sender.account.Address), "latest"},
)
if err != nil {
C.executionError = err
return
}
currentBalance, _ := balanceRPCReply["result"].(string)
bal, _ := new(big.Int).SetString(currentBalance[2:], 16)
balance := numeric.NewDecFromBigInt(bal)

gasAsDec := C.transactionForRPC.params["gas-price"].(numeric.Dec)
gasAsDec = gasAsDec.Mul(numeric.NewDec(int64(C.transactionForRPC.params["gas-limit"].(uint64))))
amountInAtto := amount.Mul(oneAsDec)
total := amountInAtto.Add(gasAsDec)

if total.GT(balance) {
balanceInOne := balance.Quo(oneAsDec)
C.executionError = ErrBadTransactionParam
errorMsg := fmt.Sprintf(
"insufficient balance of %s in shard %d for the requested transfer of %s",
balanceInOne.String(), C.transactionForRPC.params["from-shard"].(uint32), amount.String(),
if !C.Behavior.OfflineSign {
balanceRPCReply, err := C.messenger.SendRPC(
rpc.Method.GetBalance,
p{address.ToBech32(C.sender.account.Address), "latest"},
)
C.transactionErrors = append(C.transactionErrors, &Error{
ErrMessage: &errorMsg,
TimestampOfRejection: time.Now().Unix(),
})
return
if err != nil {
C.executionError = err
return
}
currentBalance, _ := balanceRPCReply["result"].(string)
bal, _ := new(big.Int).SetString(currentBalance[2:], 16)
balance := numeric.NewDecFromBigInt(bal)
if total.GT(balance) {
balanceInOne := balance.Quo(oneAsDec)
C.executionError = ErrBadTransactionParam
errorMsg := fmt.Sprintf(
"insufficient balance of %s in shard %d for the requested transfer of %s",
balanceInOne.String(), C.transactionForRPC.params["from-shard"].(uint32), amount.String(),
)
C.transactionErrors = append(C.transactionErrors, &Error{
ErrMessage: &errorMsg,
TimestampOfRejection: time.Now().Unix(),
})
return
}
}
C.transactionForRPC.params["transfer-amount"] = amountInAtto
}
Expand Down Expand Up @@ -304,4 +307,10 @@ func (C *EthController) ExecuteEthTransaction(
return C.executionError
}

// TODO: add logic to create staking transactions in the SDK.
func (C *EthController) ExecuteRawTransaction(txn string) error {
C.transactionForRPC.signature = &txn

C.sendSignedTx()
C.txConfirmation()
return C.executionError
}
17 changes: 17 additions & 0 deletions pkg/transaction/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package transaction

import (
"encoding/hex"
"fmt"
"strings"
)

func StringToByte(dataStr string) ([]byte, error) {
if len(dataStr) == 0 {
return []byte{}, nil
}
if !strings.HasPrefix(dataStr, "0x") {
return nil, fmt.Errorf("invalid data literal: %q", dataStr)
}
return hex.DecodeString(dataStr[2:])
}

0 comments on commit 178d050

Please sign in to comment.