├── .gitignore
├── pkg
├── transaction
│ ├── signer.go
│ ├── util.go
│ ├── transaction.go
│ ├── errors.go
│ └── ethcontroller.go
├── rpc
│ ├── query_test.go
│ ├── handler.go
│ ├── query.go
│ ├── common
│ │ └── methods.go
│ ├── eth
│ │ └── methods.go
│ ├── v1
│ │ └── methods.go
│ └── methods.go
├── governance
│ ├── config.go
│ ├── util.go
│ ├── cli.go
│ ├── api.go
│ ├── request.go
│ ├── signing.go
│ ├── signing_test.go
│ ├── eip712.go
│ └── types.go
├── common
│ ├── store.go
│ ├── presentation.go
│ ├── values.go
│ ├── chain-id.go
│ └── numeric.go
├── keys
│ ├── keystore_test.go
│ ├── encoding.go
│ ├── mnemonic.go
│ ├── mnemonic_test.go
│ ├── keys.go
│ └── bls_test.go
├── mnemonic
│ └── mnemonic.go
├── account
│ ├── removal.go
│ ├── account_test.go
│ ├── creation.go
│ ├── export.go
│ └── import.go
├── console
│ ├── hmy.go
│ ├── jsre
│ │ ├── deps
│ │ │ └── deps.go
│ │ ├── completion_test.go
│ │ ├── completion.go
│ │ ├── jsre_test.go
│ │ ├── pretty.go
│ │ └── jsre.go
│ └── prompt
│ │ └── prompter.go
├── validation
│ ├── validation_test.go
│ └── validator.go
├── sharding
│ └── structure.go
├── address
│ └── address.go
├── store
│ └── local.go
└── ledger
│ ├── hw_wallet.go
│ └── nano_S_driver.go
├── e2e
└── keystore_test.go
├── cmd
├── subcommands
│ ├── completion.go
│ ├── delegation.go
│ ├── failures.go
│ ├── custom-flags.go
│ ├── balance.go
│ ├── command.go
│ ├── validator.go
│ ├── governance.go
│ ├── utility.go
│ ├── values.go
│ ├── blockchain.go
│ └── root.go
└── main.go
├── scripts
├── stress-test
└── hmy.sh
├── Makefile
├── .github
└── workflows
│ ├── test-build.yml
│ └── hmybuild.yml
└── go.mod
/.gitignore:
--------------------------------------------------------------------------------
1 | hmy
2 | hmy-docs
3 | dist
4 | .idea/
5 | *.key
6 | hmy_version
7 |
--------------------------------------------------------------------------------
/pkg/transaction/signer.go:
--------------------------------------------------------------------------------
1 | package transaction
2 |
3 | type SignerImpl int
4 |
5 | const (
6 | Software SignerImpl = iota
7 | Ledger
8 | )
9 |
--------------------------------------------------------------------------------
/pkg/rpc/query_test.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestRPCRequest(t *testing.T) {
9 | fmt.Println("hell rpc?")
10 | }
11 |
--------------------------------------------------------------------------------
/e2e/keystore_test.go:
--------------------------------------------------------------------------------
1 | package keys
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestKeyStore(t *testing.T) {
9 | fmt.Println("Hello world")
10 | //t.Errorf("Testing pipeline")
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/governance/config.go:
--------------------------------------------------------------------------------
1 | package governance
2 |
3 | const (
4 | backendAddress = "https://hub.snapshot.org/api/"
5 | urlMessage = backendAddress + "msg"
6 | version = "0.1.4"
7 | name = "snapshot"
8 | )
9 |
--------------------------------------------------------------------------------
/pkg/common/store.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "github.com/harmony-one/harmony/accounts/keystore"
5 | )
6 |
7 | func KeyStoreForPath(p string) *keystore.KeyStore {
8 | return keystore.NewKeyStore(p, ScryptN, ScryptP)
9 | }
10 |
--------------------------------------------------------------------------------
/pkg/governance/util.go:
--------------------------------------------------------------------------------
1 | package governance
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | )
7 |
8 | func indent(v interface{}) string {
9 | b, err := json.MarshalIndent(v, "", " ")
10 | if err != nil {
11 | return fmt.Sprintf("%#v", v)
12 | }
13 | return string(b)
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/keys/keystore_test.go:
--------------------------------------------------------------------------------
1 | package keys
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestKeyStore(t *testing.T) {
9 | fmt.Println("Hello world 3")
10 | // t.Errorf("Testing pipeline")
11 | }
12 |
13 | func TestAnother(t *testing.T) {
14 | fmt.Println("Another test run")
15 | }
16 |
--------------------------------------------------------------------------------
/pkg/mnemonic/mnemonic.go:
--------------------------------------------------------------------------------
1 | package mnemonic
2 |
3 | import (
4 | "errors"
5 |
6 | "github.com/tyler-smith/go-bip39"
7 | )
8 |
9 | var (
10 | InvalidMnemonic = errors.New("invalid mnemonic given")
11 | )
12 |
13 | func Generate() string {
14 | entropy, _ := bip39.NewEntropy(256)
15 | mnemonic, _ := bip39.NewMnemonic(entropy)
16 | return mnemonic
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/transaction/util.go:
--------------------------------------------------------------------------------
1 | package transaction
2 |
3 | import (
4 | "encoding/hex"
5 | "fmt"
6 | "strings"
7 | )
8 |
9 | func StringToByte(dataStr string) ([]byte, error) {
10 | if len(dataStr) == 0 {
11 | return []byte{}, nil
12 | }
13 | if !strings.HasPrefix(dataStr, "0x") {
14 | return nil, fmt.Errorf("invalid data literal: %q", dataStr)
15 | }
16 | return hex.DecodeString(dataStr[2:])
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/rpc/handler.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | type Reply map[string]interface{}
4 |
5 | type T interface {
6 | SendRPC(string, []interface{}) (Reply, error)
7 | }
8 |
9 | type HTTPMessenger struct {
10 | node string
11 | }
12 |
13 | func (M *HTTPMessenger) SendRPC(meth string, params []interface{}) (Reply, error) {
14 | return Request(meth, M.node, params)
15 | }
16 |
17 | func NewHTTPHandler(node string) *HTTPMessenger {
18 | // TODO Sanity check the URL for HTTP
19 | return &HTTPMessenger{node}
20 | }
21 |
--------------------------------------------------------------------------------
/pkg/keys/encoding.go:
--------------------------------------------------------------------------------
1 | package keys
2 |
3 | import (
4 | secp256k1 "github.com/btcsuite/btcd/btcec"
5 | "github.com/ethereum/go-ethereum/common/hexutil"
6 | )
7 |
8 | type Dump struct {
9 | PrivateKey, PublicKeyCompressed, PublicKey string
10 | }
11 |
12 | func EncodeHex(sk *secp256k1.PrivateKey, pk *secp256k1.PublicKey) *Dump {
13 | p0 := sk.Serialize()
14 | p1 := pk.SerializeCompressed()
15 | p2 := pk.SerializeUncompressed()
16 | return &Dump{hexutil.Encode(p0), hexutil.Encode(p1), hexutil.Encode(p2)}
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/common/presentation.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | )
7 |
8 | func JSONPrettyFormat(in string) string {
9 | var out bytes.Buffer
10 | err := json.Indent(&out, []byte(in), "", " ")
11 | if err != nil {
12 | return in
13 | }
14 | return out.String()
15 | }
16 |
17 | // returns "{}" on failure case
18 | func ToJSONUnsafe(payload interface{}, pretty bool) string {
19 | j, err := json.Marshal(payload)
20 | if err != nil {
21 | return "{}"
22 | }
23 | if pretty {
24 | return JSONPrettyFormat(string(j))
25 | }
26 | return string(j)
27 | }
28 |
--------------------------------------------------------------------------------
/cmd/subcommands/completion.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "os"
5 |
6 | "github.com/spf13/cobra"
7 | )
8 |
9 | func init() {
10 | cmdStaking := &cobra.Command{
11 | Use: "completion",
12 | Short: "Generates bash completion scripts",
13 | Long: `To load completion, run:
14 |
15 | . <(hmy completion)
16 |
17 | Add the line to your ~/.bashrc to enable completiony for each bash session.
18 | `,
19 | RunE: func(cmd *cobra.Command, args []string) error {
20 | RootCmd.GenBashCompletion(os.Stdout)
21 | return nil
22 | },
23 | }
24 |
25 | cmdStaking.AddCommand(stakingSubCommands()...)
26 | RootCmd.AddCommand(cmdStaking)
27 | }
28 |
--------------------------------------------------------------------------------
/pkg/governance/cli.go:
--------------------------------------------------------------------------------
1 | package governance
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/harmony-one/harmony/accounts"
7 | "github.com/harmony-one/harmony/accounts/keystore"
8 | )
9 |
10 | func DoVote(keyStore *keystore.KeyStore, account accounts.Account, vote Vote) error {
11 | typedData, err := vote.ToEIP712()
12 | if err != nil {
13 | return err
14 | }
15 | sig, err := signTypedData(keyStore, account, typedData)
16 | if err != nil {
17 | return err
18 | }
19 |
20 | result, err := submitMessage(account.Address.String(), typedData, sig)
21 | if err != nil {
22 | return err
23 | }
24 |
25 | fmt.Println(indent(result))
26 | return nil
27 | }
28 |
--------------------------------------------------------------------------------
/pkg/account/removal.go:
--------------------------------------------------------------------------------
1 | package account
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path"
7 |
8 | "github.com/harmony-one/go-sdk/pkg/common"
9 | "github.com/harmony-one/go-sdk/pkg/store"
10 | "github.com/mitchellh/go-homedir"
11 | )
12 |
13 | // RemoveAccount - removes an account from the keystore
14 | func RemoveAccount(name string) error {
15 | accountExists := store.DoesNamedAccountExist(name)
16 |
17 | if !accountExists {
18 | return fmt.Errorf("account %s doesn't exist", name)
19 | }
20 |
21 | uDir, _ := homedir.Dir()
22 | hmyCLIDir := path.Join(uDir, common.DefaultConfigDirName, common.DefaultConfigAccountAliasesDirName)
23 | accountDir := fmt.Sprintf("%s/%s", hmyCLIDir, name)
24 | os.RemoveAll(accountDir)
25 |
26 | return nil
27 | }
28 |
--------------------------------------------------------------------------------
/pkg/governance/api.go:
--------------------------------------------------------------------------------
1 | package governance
2 |
3 | import (
4 | "encoding/json"
5 |
6 | "github.com/pkg/errors"
7 | )
8 |
9 | func submitMessage(address string, typedData *TypedData, sign string) (map[string]interface{}, error) {
10 | data, err := typedData.String()
11 | if err != nil {
12 | return nil, errors.Wrapf(err, "could not encode EIP712 data")
13 | }
14 |
15 | type body struct {
16 | Address string `json:"address"`
17 | Sig string `json:"sig"`
18 | JsonData json.RawMessage `json:"data"`
19 | }
20 |
21 | message, err := json.Marshal(body{
22 | Address: address,
23 | Sig: sign,
24 | JsonData: json.RawMessage(data),
25 | })
26 | if err != nil {
27 | return nil, errors.Wrapf(err, "could not encode body")
28 | }
29 | return postAndParse(urlMessage, message)
30 | }
31 |
--------------------------------------------------------------------------------
/scripts/stress-test:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -ieu
4 |
5 | source ../harmony/scripts/setup_bls_build_flags.sh
6 |
7 | sender='one1yc06ghr2p8xnl2380kpfayweguuhxdtupkhqzw'
8 | receiver='one1q6gkzcap0uruuu8r6sldxuu47pd4ww9w9t7tg6'
9 | shard_zero='https://api.s0.b.hmny.io/'
10 | shard_one='https://api.s0.b.hmny.io/'
11 |
12 | direct_node='http://52.27.34.100:9500'
13 |
14 | function c {
15 | printf "%s\n" "$*" | bc
16 | }
17 |
18 | # Shard 0 to 0
19 | for iter in $(seq 100); do
20 | rand=$(grep -m1 -ao '[0-9]' /dev/urandom | sed s/0/3/ | head -n1)
21 | value=$(c "${iter}/100")
22 | bump=$(c "${value}+${rand}")
23 | amount=$(printf "%.2f" ${bump})
24 | ./hmy --node=${shard_zero} \
25 | transfer --from ${sender} --to ${receiver} \
26 | --from-shard 0 --to-shard 0 --amount ${amount} \
27 | --passphrase='' &
28 | done
29 |
--------------------------------------------------------------------------------
/cmd/subcommands/delegation.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "github.com/harmony-one/go-sdk/pkg/rpc"
5 | "github.com/spf13/cobra"
6 | )
7 |
8 | var (
9 | delegationSubCmds = []*cobra.Command{{
10 | Use: "by-delegator",
11 | Short: "Get all delegations by a delegator",
12 | Args: cobra.ExactArgs(1),
13 | PreRunE: validateAddress,
14 | RunE: func(cmd *cobra.Command, args []string) error {
15 | noLatest = true
16 | return request(rpc.Method.GetDelegationsByDelegator, []interface{}{addr.address})
17 | },
18 | }, {
19 | Use: "by-validator",
20 | Short: "Get all delegations for a validator",
21 | Args: cobra.ExactArgs(1),
22 | PreRunE: validateAddress,
23 | RunE: func(cmd *cobra.Command, args []string) error {
24 | noLatest = true
25 | return request(rpc.Method.GetDelegationsByValidator, []interface{}{addr.address})
26 | },
27 | }}
28 | )
29 |
--------------------------------------------------------------------------------
/pkg/keys/mnemonic.go:
--------------------------------------------------------------------------------
1 | package keys
2 |
3 | import (
4 | "fmt"
5 |
6 | secp256k1 "github.com/btcsuite/btcd/btcec"
7 | "github.com/cosmos/cosmos-sdk/crypto/keys/hd"
8 | "github.com/tyler-smith/go-bip39"
9 | )
10 |
11 | // FromMnemonicSeedAndPassphrase mimics the Harmony JS sdk in deriving the
12 | // private, public key pair from the mnemonic, its index, and empty string password.
13 | // Note that an index k would be the k-th key generated using the same mnemonic.
14 | func FromMnemonicSeedAndPassphrase(mnemonic string, index int, coinType int) (*secp256k1.PrivateKey, *secp256k1.PublicKey) {
15 | seed := bip39.NewSeed(mnemonic, "")
16 | master, ch := hd.ComputeMastersFromSeed(seed)
17 | private, _ := hd.DerivePrivateKeyForPath(
18 | master,
19 | ch,
20 | fmt.Sprintf("44'/%d'/0'/0/%d", coinType, index),
21 | )
22 |
23 | return secp256k1.PrivKeyFromBytes(secp256k1.S256(), private[:])
24 | }
25 |
--------------------------------------------------------------------------------
/pkg/keys/mnemonic_test.go:
--------------------------------------------------------------------------------
1 | package keys
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | const (
8 | phrase = "crouch embrace tree can horn decrease until boil ice edit eagle chimney"
9 | index = 0
10 | publicKey = "0x030b64624e60c6e6758711fdf00f7e873f40a22e647a6918ba73807fab194d09ba"
11 | privateKey = "0xda4bc68857640103942ce7dd22a9fcdb96f3cfe0380254e81352a94ac8262ed2"
12 | coinType = 1023
13 | )
14 |
15 | func TestMnemonic(t *testing.T) {
16 | private, public := FromMnemonicSeedAndPassphrase(phrase, index, coinType)
17 | sk, pkCompressed := func() (string, string) {
18 | dump := EncodeHex(private, public)
19 | return dump.PrivateKey, dump.PublicKeyCompressed
20 | }()
21 | if sk != privateKey {
22 | t.Errorf("Private key mismatch %s != %s", sk, privateKey)
23 | }
24 | if pkCompressed != publicKey {
25 | t.Errorf("Public Compressed key mismatch %s != %s", pkCompressed, publicKey)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/pkg/console/hmy.go:
--------------------------------------------------------------------------------
1 | package console
2 |
3 | import (
4 | "fmt"
5 | "github.com/dop251/goja"
6 | "github.com/harmony-one/harmony/accounts"
7 | "github.com/harmony-one/harmony/accounts/keystore"
8 | "github.com/harmony-one/harmony/crypto/hash"
9 | "strconv"
10 | )
11 |
12 | func signMessageWithPassword(keyStore *keystore.KeyStore, account accounts.Account, password string, data []byte) (sign []byte, err error) {
13 | signData := append([]byte("\x19Ethereum Signed Message:\n" + strconv.Itoa(len(data))))
14 | msgHash := hash.Keccak256(append(signData, data...))
15 |
16 | sign, err = keyStore.SignHashWithPassphrase(account, password, msgHash)
17 | if err != nil {
18 | return nil, err
19 | }
20 |
21 | if len(sign) != 65 {
22 | return nil, fmt.Errorf("sign error")
23 | }
24 |
25 | sign[64] += 0x1b
26 | return sign, nil
27 | }
28 |
29 | func getStringFromJsObjWithDefault(o *goja.Object, key string, def string) string {
30 | get := o.Get(key)
31 | if get == nil {
32 | return def
33 | } else {
34 | return get.String()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/cmd/subcommands/failures.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "github.com/harmony-one/go-sdk/pkg/rpc"
5 | "github.com/spf13/cobra"
6 | )
7 |
8 | func init() {
9 | cmdFailures := &cobra.Command{
10 | Use: "failures",
11 | Short: "Check in-memory record of failed transactions",
12 | Long: `Check node for its in-memory record of failed transactions`,
13 | RunE: func(cmd *cobra.Command, args []string) error {
14 | cmd.Help()
15 | return nil
16 | },
17 | }
18 | cmdFailures.AddCommand([]*cobra.Command{{
19 | Use: "plain",
20 | Short: "plain transaction failures",
21 | RunE: func(cmd *cobra.Command, args []string) error {
22 | noLatest = true
23 | return request(rpc.Method.GetCurrentTransactionErrorSink, []interface{}{})
24 | },
25 | }, {
26 | Use: "staking",
27 | Short: "staking transaction failures",
28 | RunE: func(cmd *cobra.Command, args []string) error {
29 | noLatest = true
30 | return request(rpc.Method.GetCurrentStakingErrorSink, []interface{}{})
31 | },
32 | },
33 | }...)
34 | RootCmd.AddCommand(cmdFailures)
35 | }
36 |
--------------------------------------------------------------------------------
/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path"
7 |
8 | cmd "github.com/harmony-one/go-sdk/cmd/subcommands"
9 | // Need this side effect
10 | _ "github.com/harmony-one/go-sdk/pkg/store"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | var (
15 | version string
16 | commit string
17 | builtAt string
18 | builtBy string
19 | )
20 |
21 | func main() {
22 | // HACK Force usage of go implementation rather than the C based one. Do the right way, see the
23 | // notes one line 66,67 of https://golang.org/src/net/net.go that say can make the decision at
24 | // build time.
25 | os.Setenv("GODEBUG", "netdns=go")
26 | cmd.VersionWrapDump = version + "-" + commit
27 | cmd.RootCmd.AddCommand(&cobra.Command{
28 | Use: "version",
29 | Short: "Show version",
30 | RunE: func(cmd *cobra.Command, args []string) error {
31 | fmt.Fprintf(os.Stderr,
32 | "Harmony (C) 2020. %v, version %v-%v (%v %v)\n",
33 | path.Base(os.Args[0]), version, commit, builtBy, builtAt)
34 | os.Exit(0)
35 | return nil
36 | },
37 | })
38 | cmd.Execute()
39 | }
40 |
--------------------------------------------------------------------------------
/pkg/console/jsre/deps/deps.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | // Package deps contains the console JavaScript dependencies Go embedded.
18 | package deps
19 |
20 | //go:generate go-bindata -pkg deps -o bindata.go bignumber.js web3.js
21 | //go:generate gofmt -w -s bindata.go
22 |
--------------------------------------------------------------------------------
/pkg/account/account_test.go:
--------------------------------------------------------------------------------
1 | package account
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/harmony-one/go-sdk/pkg/store"
7 | )
8 |
9 | func TestAccountGetsRemoved(t *testing.T) {
10 | tests := []struct {
11 | Name string
12 | Expected bool
13 | }{
14 | {"foobar", false},
15 | }
16 |
17 | for _, test := range tests {
18 | acc := Creation{
19 | Name: test.Name,
20 | Passphrase: "",
21 | Mnemonic: "",
22 | HdAccountNumber: nil,
23 | HdIndexNumber: nil,
24 | }
25 |
26 | err := CreateNewLocalAccount(&acc)
27 |
28 | if err != nil {
29 | t.Errorf(`RemoveAccount("%s") failed to create keystore account %v`, test.Name, err)
30 | }
31 |
32 | exists := store.DoesNamedAccountExist(test.Name)
33 |
34 | if !exists {
35 | t.Errorf(`RemoveAccount("%s") account should exist - but it can't be found`, test.Name)
36 | }
37 |
38 | RemoveAccount(test.Name)
39 | exists = store.DoesNamedAccountExist(test.Name)
40 |
41 | if exists != test.Expected {
42 | t.Errorf(`RemoveAccount("%s") returned %v, expected %v`, test.Name, exists, test.Expected)
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/cmd/subcommands/custom-flags.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "github.com/harmony-one/go-sdk/pkg/address"
5 | "github.com/harmony-one/go-sdk/pkg/common"
6 | "github.com/harmony-one/go-sdk/pkg/validation"
7 | "github.com/pkg/errors"
8 | )
9 |
10 | type oneAddress struct {
11 | address string
12 | }
13 |
14 | func (oneAddress oneAddress) String() string {
15 | return oneAddress.address
16 | }
17 |
18 | func (oneAddress *oneAddress) Set(s string) error {
19 | err := validation.ValidateAddress(s)
20 | if err != nil {
21 | return err
22 | }
23 | _, err = address.Bech32ToAddress(s)
24 | if err != nil {
25 | return errors.Wrap(err, "not a valid one address")
26 | }
27 | oneAddress.address = s
28 | return nil
29 | }
30 |
31 | func (oneAddress oneAddress) Type() string {
32 | return "one-address"
33 | }
34 |
35 | type chainIDWrapper struct {
36 | chainID *common.ChainID
37 | }
38 |
39 | func (chainIDWrapper chainIDWrapper) String() string {
40 | return chainIDWrapper.chainID.Name
41 | }
42 |
43 | func (chainIDWrapper *chainIDWrapper) Set(s string) error {
44 | chain, err := common.StringToChainID(s)
45 | chainIDWrapper.chainID = chain
46 | if err != nil {
47 | return err
48 | }
49 | return nil
50 | }
51 |
52 | func (chainIDWrapper chainIDWrapper) Type() string {
53 | return "chain-id"
54 | }
55 |
--------------------------------------------------------------------------------
/pkg/common/values.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "errors"
5 | "os"
6 |
7 | "github.com/harmony-one/harmony/accounts/keystore"
8 | )
9 |
10 | const (
11 | DefaultConfigDirName = ".hmy_cli"
12 | DefaultConfigAccountAliasesDirName = "account-keys"
13 | DefaultCommandAliasesDirName = "command"
14 | DefaultPassphrase = ""
15 | JSONRPCVersion = "2.0"
16 | Secp256k1PrivateKeyBytesLength = 32
17 | )
18 |
19 | var (
20 | ScryptN = keystore.StandardScryptN
21 | ScryptP = keystore.StandardScryptP
22 | DebugRPC = false
23 | DebugTransaction = false
24 | ErrNotAbsPath = errors.New("keypath is not absolute path")
25 | ErrBadKeyLength = errors.New("Invalid private key (wrong length)")
26 | ErrFoundNoKey = errors.New("found no bls key file")
27 | ErrFoundNoPass = errors.New("found no passphrase file")
28 | )
29 |
30 | func init() {
31 | if _, enabled := os.LookupEnv("HMY_RPC_DEBUG"); enabled != false {
32 | DebugRPC = true
33 | }
34 | if _, enabled := os.LookupEnv("HMY_TX_DEBUG"); enabled != false {
35 | DebugTransaction = true
36 | }
37 | if _, enabled := os.LookupEnv("HMY_ALL_DEBUG"); enabled != false {
38 | EnableAllVerbose()
39 | }
40 | }
41 |
42 | // EnableAllVerbose sets debug vars to true
43 | func EnableAllVerbose() {
44 | DebugRPC = true
45 | DebugTransaction = true
46 | }
47 |
--------------------------------------------------------------------------------
/pkg/account/creation.go:
--------------------------------------------------------------------------------
1 | package account
2 |
3 | import (
4 | "errors"
5 |
6 | "github.com/harmony-one/go-sdk/pkg/keys"
7 | "github.com/harmony-one/go-sdk/pkg/mnemonic"
8 | "github.com/harmony-one/go-sdk/pkg/store"
9 | )
10 |
11 | var (
12 | AccountByNameExists = errors.New("name chosen for account already exists")
13 | )
14 |
15 | type Creation struct {
16 | Name string
17 | Passphrase string
18 | Mnemonic string
19 | HdAccountNumber *uint32
20 | HdIndexNumber *uint32
21 | CoinType *uint32
22 | }
23 |
24 | func New() string {
25 | return "New Account"
26 | }
27 |
28 | func IsValidPassphrase(pass string) bool {
29 | return true
30 | }
31 |
32 | // CreateNewLocalAccount assumes all the inputs are valid, legitmate
33 | func CreateNewLocalAccount(candidate *Creation) error {
34 | ks := store.FromAccountName(candidate.Name)
35 | if candidate.Mnemonic == "" {
36 | candidate.Mnemonic = mnemonic.Generate()
37 | }
38 |
39 | index := uint32(0)
40 | if candidate.HdIndexNumber != nil {
41 | index = *candidate.HdIndexNumber
42 | }
43 |
44 | coinType := uint32(1023)
45 | if candidate.CoinType != nil {
46 | coinType = *candidate.CoinType
47 | }
48 |
49 | private, _ := keys.FromMnemonicSeedAndPassphrase(candidate.Mnemonic, int(index), int(coinType))
50 | _, err := ks.ImportECDSA(private.ToECDSA(), candidate.Passphrase)
51 | if err != nil {
52 | return err
53 | }
54 | return nil
55 | }
56 |
--------------------------------------------------------------------------------
/pkg/validation/validation_test.go:
--------------------------------------------------------------------------------
1 | package validation
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/harmony-one/go-sdk/pkg/sharding"
7 | )
8 |
9 | func TestIsValidAddress(t *testing.T) {
10 | tests := []struct {
11 | str string
12 | exp bool
13 | }{
14 | {"one1ay37rp2pc3kjarg7a322vu3sa8j9puahg679z3", true},
15 | {"0x7c41E0668B551f4f902cFaec05B5Bdca68b124CE", true},
16 | {"onefoofoo", false},
17 | {"0xbarbar", false},
18 | {"dsasdadsasaadsas", false},
19 | {"32312123213213212321", false},
20 | }
21 |
22 | for _, test := range tests {
23 | err := ValidateAddress(test.str)
24 | valid := false
25 |
26 | if err == nil {
27 | valid = true
28 | }
29 |
30 | if valid != test.exp {
31 | t.Errorf(`ValidateAddress("%s") returned %v, expected %v`, test.str, valid, test.exp)
32 | }
33 | }
34 | }
35 |
36 | func TestIsValidShard(t *testing.T) {
37 | if err := ValidateNodeConnection("http://localhost:9500"); err != nil {
38 | t.Skip()
39 | }
40 | s, _ := sharding.Structure("http://localhost:9500")
41 |
42 | tests := []struct {
43 | shardID uint32
44 | exp bool
45 | }{
46 | {0, true},
47 | {1, true},
48 | {98, false},
49 | {99, false},
50 | }
51 |
52 | for _, test := range tests {
53 | valid := ValidShardID(test.shardID, uint32(len(s)))
54 |
55 | if valid != test.exp {
56 | t.Errorf("ValidShardID(%d) returned %v, expected %v", test.shardID, valid, test.exp)
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/pkg/governance/request.go:
--------------------------------------------------------------------------------
1 | package governance
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "fmt"
7 | "io/ioutil"
8 | "net/http"
9 |
10 | "github.com/pkg/errors"
11 | "github.com/valyala/fastjson"
12 | )
13 |
14 | func postAndParse(url string, postData []byte) (map[string]interface{}, error) {
15 | resp, err := http.Post(string(url), "application/json", bytes.NewReader(postData))
16 | if err != nil {
17 | return nil, errors.Wrapf(err, "could not send post request")
18 | }
19 | defer resp.Body.Close()
20 | return parseAndUnmarshal(resp)
21 | }
22 |
23 | func parseAndUnmarshal(resp *http.Response) (map[string]interface{}, error) {
24 | bodyData, err := ioutil.ReadAll(resp.Body)
25 | if err != nil {
26 | return nil, err
27 | }
28 | // the 500 is used for errors like `invalid signature`, `not in voting window`, etc.
29 | if resp.StatusCode != 200 && resp.StatusCode != 500 {
30 | return nil, errors.Errorf("unexpected response code %s\nbody: %s", resp.Status, bodyData)
31 | }
32 |
33 | if fastjson.GetString(bodyData, "error") != "" {
34 | return nil, fmt.Errorf("error: %s, %s", fastjson.GetString(bodyData, "error"), fastjson.GetString(bodyData, "error_description"))
35 | }
36 |
37 | var result map[string]interface{}
38 | if err := json.Unmarshal(bodyData, &result); err != nil {
39 | return nil, errors.Wrapf(err, "could not decode result from %s", string(bodyData))
40 | } else {
41 | return result, nil
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/pkg/validation/validator.go:
--------------------------------------------------------------------------------
1 | package validation
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "regexp"
7 | "time"
8 | )
9 |
10 | var (
11 | addressValidationRegexp = regexp.MustCompile(`^(one[a-zA-Z0-9]{39})|(0x[a-fA-F0-9]{40})`)
12 | )
13 |
14 | // ValidateAddress validates that an address is a valid bech32 address (one...) or a valid base16 address (0x...)
15 | func ValidateAddress(address string) error {
16 | matches := addressValidationRegexp.FindAllStringSubmatch(address, -1)
17 | if len(matches) == 0 {
18 | return fmt.Errorf("address supplied (%s) is not valid", address)
19 | }
20 |
21 | return nil
22 | }
23 |
24 | // ValidShardIDs validates senderShard and receiverShard against the shardCount
25 | func ValidShardIDs(senderShard uint32, receiverShard uint32, shardCount uint32) error {
26 | if !ValidShardID(senderShard, shardCount) {
27 | return fmt.Errorf(`invalid argument "%d" for "--from-shard"`, senderShard)
28 | }
29 |
30 | if !ValidShardID(receiverShard, shardCount) {
31 | return fmt.Errorf(`invalid argument "%d" for "--to-shard"`, receiverShard)
32 | }
33 |
34 | return nil
35 | }
36 |
37 | // ValidShardID validates that a shardID is within the bounds of the shardCount
38 | func ValidShardID(shardID uint32, shardCount uint32) bool {
39 | if shardID > (shardCount - 1) {
40 | return false
41 | }
42 |
43 | return true
44 | }
45 |
46 | // ValidateNodeConnection validates that the node can be connected to
47 | func ValidateNodeConnection(node string) error {
48 | timeout := time.Duration(1 * time.Second)
49 | _, err := net.DialTimeout("tcp", node, timeout)
50 | return err
51 | }
52 |
--------------------------------------------------------------------------------
/scripts/hmy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | BUCKET='pub.harmony.one'
4 | OS="$(uname -s)"
5 |
6 | usage () {
7 | cat << EOT
8 | Usage: $0 [option] command
9 |
10 | Options:
11 | -d download all the binaries
12 | -h print this help
13 | Note: Arguments must be passed at the end for ./hmy to work correctly.
14 | For instance: ./hmy.sh balances --node=https://api.s0.p.hmny.io/
15 |
16 | EOT
17 | }
18 |
19 | set_download () {
20 | local rel='mainnet'
21 | case "$OS" in
22 | Darwin)
23 | FOLDER=release/darwin-x86_64/${rel}
24 | URL=http://${BUCKET}.s3.amazonaws.com/${FOLDER}
25 | BIN=( hmy libbls384_256.dylib libcrypto.1.0.0.dylib libgmp.10.dylib libgmpxx.4.dylib libmcl.dylib )
26 | NAMES=("${BIN[@]}")
27 | ;;
28 | Linux)
29 | URL=https://harmony.one
30 | BIN=( hmycli )
31 | NAMES=( hmy )
32 | ;;
33 | *)
34 | echo "${OS} not supported."
35 | exit 2
36 | ;;
37 | esac
38 | }
39 |
40 | do_download () {
41 | # download all the binaries
42 | for i in "${!BIN[@]}"; do
43 | rm -f ${NAMES[i]}
44 | curl -L ${URL}/${BIN[i]} -o ${NAMES[i]}
45 | done
46 | chmod +x hmy
47 | }
48 |
49 | while getopts "dh" opt; do
50 | case ${opt} in
51 | d)
52 | set_download
53 | do_download
54 | exit 0
55 | ;;
56 | h|*)
57 | usage
58 | exit 1
59 | ;;
60 | esac
61 | done
62 |
63 | shift $((OPTIND-1))
64 |
65 | if [ "$OS" = "Linux" ]; then
66 | ./hmy "$@"
67 | else
68 | DYLD_FALLBACK_LIBRARY_PATH="$(pwd)" ./hmy "$@"
69 | fi
70 |
--------------------------------------------------------------------------------
/pkg/keys/keys.go:
--------------------------------------------------------------------------------
1 | package keys
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path"
7 | "strings"
8 |
9 | ethCommon "github.com/ethereum/go-ethereum/common"
10 | "github.com/harmony-one/go-sdk/pkg/address"
11 | "github.com/harmony-one/harmony/accounts/keystore"
12 |
13 | // "github.com/ethereum/go-ethereum/crypto"
14 |
15 | homedir "github.com/mitchellh/go-homedir"
16 | )
17 |
18 | func checkAndMakeKeyDirIfNeeded() string {
19 | userDir, _ := homedir.Dir()
20 | hmyCLIDir := path.Join(userDir, ".hmy_cli", "keystore")
21 | if _, err := os.Stat(hmyCLIDir); os.IsNotExist(err) {
22 | // Double check with Leo what is right file persmission
23 | os.Mkdir(hmyCLIDir, 0700)
24 | }
25 |
26 | return hmyCLIDir
27 | }
28 |
29 | func ListKeys(keystoreDir string) {
30 | hmyCLIDir := checkAndMakeKeyDirIfNeeded()
31 | scryptN := keystore.StandardScryptN
32 | scryptP := keystore.StandardScryptP
33 | ks := keystore.NewKeyStore(hmyCLIDir, scryptN, scryptP)
34 | // keystore.KeyStore
35 | allAccounts := ks.Accounts()
36 | fmt.Printf("Harmony Address:%s File URL:\n", strings.Repeat(" ", ethCommon.AddressLength*2))
37 | for _, account := range allAccounts {
38 | fmt.Printf("%s\t\t %s\n", address.ToBech32(account.Address), account.URL)
39 | }
40 | }
41 |
42 | func AddNewKey(password string) {
43 | hmyCLIDir := checkAndMakeKeyDirIfNeeded()
44 | scryptN := keystore.StandardScryptN
45 | scryptP := keystore.StandardScryptP
46 | ks := keystore.NewKeyStore(hmyCLIDir, scryptN, scryptP)
47 | account, err := ks.NewAccount(password)
48 | if err != nil {
49 | fmt.Printf("new account error: %v\n", err)
50 | }
51 | fmt.Printf("account: %s\n", address.ToBech32(account.Address))
52 | fmt.Printf("URL: %s\n", account.URL)
53 | }
54 |
--------------------------------------------------------------------------------
/pkg/account/export.go:
--------------------------------------------------------------------------------
1 | package account
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "path/filepath"
7 |
8 | "github.com/harmony-one/go-sdk/pkg/store"
9 | "github.com/harmony-one/harmony/accounts"
10 | )
11 |
12 | var (
13 | ErrAddressNotFound = errors.New("account was not found in keystore")
14 | )
15 |
16 | func ExportPrivateKey(address, passphrase string) error {
17 | ks := store.FromAddress(address)
18 | if ks == nil {
19 | return ErrAddressNotFound
20 | }
21 | account := ks.Accounts()[0]
22 | _, key, err := ks.GetDecryptedKey(accounts.Account{Address: account.Address}, passphrase)
23 | if err != nil {
24 | return err
25 | }
26 | fmt.Printf("%064x\n", key.PrivateKey.D)
27 | return nil
28 | }
29 |
30 | func VerifyPassphrase(address, passphrase string) (bool, error) {
31 | ks := store.FromAddress(address)
32 | if ks == nil {
33 | return false, ErrAddressNotFound
34 | }
35 | account := ks.Accounts()[0]
36 | _, _, err := ks.GetDecryptedKey(accounts.Account{Address: account.Address}, passphrase)
37 | if err != nil {
38 | return false, err
39 | }
40 | return true, nil
41 | }
42 |
43 | func ExportKeystore(address, path, passphrase string) (string, error) {
44 | ks := store.FromAddress(address)
45 | if ks == nil {
46 | return "", ErrAddressNotFound
47 | }
48 | account := ks.Accounts()[0]
49 | dirPath, err := filepath.Abs(path)
50 | if err != nil {
51 | return "", err
52 | }
53 | outFile := filepath.Join(dirPath, fmt.Sprintf("%s.key", address))
54 | keyFile, err := ks.Export(accounts.Account{Address: account.Address}, passphrase, passphrase)
55 | if err != nil {
56 | return "", err
57 | }
58 | e := writeToFile(outFile, string(keyFile))
59 | if e != nil {
60 | return "", e
61 | }
62 | return outFile, nil
63 | }
64 |
--------------------------------------------------------------------------------
/pkg/common/chain-id.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "math/big"
7 | "strconv"
8 | )
9 |
10 | // ChainID is a wrapper around the human name for a chain and the actual Big.Int used
11 | type ChainID struct {
12 | Name string `json:"-"`
13 | Value *big.Int `json:"chain-as-number"`
14 | }
15 |
16 | type chainIDList struct {
17 | MainNet ChainID `json:"mainnet"`
18 | TestNet ChainID `json:"testnet"`
19 | PangaeaNet ChainID `json:"pangaea"`
20 | PartnerNet ChainID `json:"partner"`
21 | StressNet ChainID `json:"stress"`
22 | }
23 |
24 | // Chain is an enumeration of the known Chain-IDs
25 | var Chain = chainIDList{
26 | MainNet: ChainID{"mainnet", big.NewInt(1)},
27 | TestNet: ChainID{"testnet", big.NewInt(2)},
28 | PangaeaNet: ChainID{"pangaea", big.NewInt(3)},
29 | PartnerNet: ChainID{"partner", big.NewInt(4)},
30 | StressNet: ChainID{"stress", big.NewInt(5)},
31 | }
32 |
33 | func (c chainIDList) String() string {
34 | s, _ := json.MarshalIndent(c, "", " ")
35 | return string(s)
36 | }
37 |
38 | // StringToChainID returns the ChainID wrapper for the given human name of a chain-id
39 | func StringToChainID(name string) (*ChainID, error) {
40 | switch name {
41 | case "mainnet":
42 | return &Chain.MainNet, nil
43 | case "testnet":
44 | return &Chain.TestNet, nil
45 | case "pangaea":
46 | return &Chain.PangaeaNet, nil
47 | case "devnet":
48 | return &Chain.PartnerNet, nil
49 | case "partner":
50 | return &Chain.PartnerNet, nil
51 | case "stressnet":
52 | return &Chain.StressNet, nil
53 | case "dryrun":
54 | return &Chain.MainNet, nil
55 | default:
56 | if chainID, err := strconv.Atoi(name); err == nil && chainID >= 0 {
57 | return &ChainID{Name: fmt.Sprintf("%d", chainID), Value: big.NewInt(int64(chainID))}, nil
58 | }
59 | return nil, fmt.Errorf("unknown chain-id: %s", name)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/pkg/common/numeric.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "math/big"
7 | "regexp"
8 | "strconv"
9 | "strings"
10 |
11 | "github.com/harmony-one/harmony/numeric"
12 | )
13 |
14 | var (
15 | pattern, _ = regexp.Compile("[0-9]+\\.{0,1}[0-9]*[eE][-+]{0,1}[0-9]+")
16 | )
17 |
18 | func Pow(base numeric.Dec, exp int) numeric.Dec {
19 | if exp < 0 {
20 | return Pow(numeric.NewDec(1).Quo(base), -exp)
21 | }
22 | result := numeric.NewDec(1)
23 | for {
24 | if exp%2 == 1 {
25 | result = result.Mul(base)
26 | }
27 | exp = exp >> 1
28 | if exp == 0 {
29 | break
30 | }
31 | base = base.Mul(base)
32 | }
33 | return result
34 | }
35 |
36 | func NewDecFromString(i string) (numeric.Dec, error) {
37 | if strings.HasPrefix(i, "-") {
38 | return numeric.ZeroDec(), errors.New(fmt.Sprintf("can not be negative: %s", i))
39 | }
40 | if pattern.FindString(i) != "" {
41 | if tokens := strings.Split(i, "e"); len(tokens) > 1 {
42 | a, _ := numeric.NewDecFromStr(tokens[0])
43 | b, _ := strconv.Atoi(tokens[1])
44 | return a.Mul(Pow(numeric.NewDec(10), b)), nil
45 | }
46 | tokens := strings.Split(i, "E")
47 | a, _ := numeric.NewDecFromStr(tokens[0])
48 | b, _ := strconv.Atoi(tokens[1])
49 | return a.Mul(Pow(numeric.NewDec(10), b)), nil
50 | } else {
51 | if strings.HasPrefix(i, ".") {
52 | i = "0" + i
53 | }
54 | return numeric.NewDecFromStr(i)
55 | }
56 | }
57 |
58 | // Assumes Hex string input
59 | // Split into 2 64 bit integers to guarentee 128 bit precision
60 | func NewDecFromHex(str string) numeric.Dec {
61 | str = strings.TrimPrefix(str, "0x")
62 | half := len(str) / 2
63 | right := str[half:]
64 | r, _ := big.NewInt(0).SetString(right, 16)
65 | if half == 0 {
66 | return numeric.NewDecFromBigInt(r)
67 | }
68 | left := str[:half]
69 | l, _ := big.NewInt(0).SetString(left, 16)
70 | return numeric.NewDecFromBigInt(l).Mul(
71 | Pow(numeric.NewDec(16), len(right)),
72 | ).Add(numeric.NewDecFromBigInt(r))
73 | }
74 |
--------------------------------------------------------------------------------
/cmd/subcommands/balance.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "net"
7 | "strings"
8 |
9 | "github.com/harmony-one/go-sdk/pkg/common"
10 | "github.com/harmony-one/go-sdk/pkg/rpc"
11 | "github.com/harmony-one/go-sdk/pkg/sharding"
12 | "github.com/spf13/cobra"
13 | )
14 |
15 | func init() {
16 | cmdQuery := &cobra.Command{
17 | Use: "balances",
18 | Short: "Check account balance on all shards",
19 | Long: "Query for the latest account balance given a Harmony Address",
20 | Args: cobra.ExactArgs(1),
21 | PreRunE: validateAddress,
22 | RunE: func(cmd *cobra.Command, args []string) error {
23 | if checkNodeInput(node) {
24 | balanceRPCReply, err := rpc.Request(rpc.Method.GetBalance, node, []interface{}{addr.address, "latest"})
25 | if err != nil {
26 | return err
27 | }
28 | nodeRPCReply, err := rpc.Request(rpc.Method.GetShardID, node, []interface{}{})
29 | if err != nil {
30 | return err
31 | }
32 | balance, _ := balanceRPCReply["result"].(string)
33 | bln := common.NewDecFromHex(balance)
34 | bln = bln.Quo(oneAsDec)
35 | var out bytes.Buffer
36 | out.WriteString("[")
37 | out.WriteString(fmt.Sprintf(`{"shard":%d, "amount":%s}`,
38 | uint32(nodeRPCReply["result"].(float64)),
39 | bln.String(),
40 | ))
41 | out.WriteString("]")
42 | fmt.Println(common.JSONPrettyFormat(out.String()))
43 | return nil
44 | }
45 | r, err := sharding.CheckAllShards(node, addr.String(), noPrettyOutput)
46 | if err != nil {
47 | return err
48 | }
49 | fmt.Println(r)
50 | return nil
51 | },
52 | }
53 |
54 | RootCmd.AddCommand(cmdQuery)
55 | }
56 |
57 | // Check if input for --node is an IP address
58 | func checkNodeInput(node string) bool {
59 | removePrefix := strings.TrimPrefix(node, "http://")
60 | removePrefix = strings.TrimPrefix(removePrefix, "https://")
61 | possibleIP := strings.Split(removePrefix, ":")[0]
62 | return net.ParseIP(string(possibleIP)) != nil
63 | }
64 |
--------------------------------------------------------------------------------
/pkg/sharding/structure.go:
--------------------------------------------------------------------------------
1 | package sharding
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "fmt"
7 |
8 | "github.com/harmony-one/go-sdk/pkg/common"
9 | "github.com/harmony-one/go-sdk/pkg/rpc"
10 | "github.com/harmony-one/harmony/common/denominations"
11 | "github.com/harmony-one/harmony/numeric"
12 | )
13 |
14 | var (
15 | nanoAsDec = numeric.NewDec(denominations.Nano)
16 | oneAsDec = numeric.NewDec(denominations.One)
17 | )
18 |
19 | // RPCRoutes reflects the RPC endpoints of the target network across shards
20 | type RPCRoutes struct {
21 | HTTP string `json:"http"`
22 | ShardID int `json:"shardID"`
23 | WS string `json:"ws"`
24 | }
25 |
26 | // Structure produces a slice of RPCRoutes for the network across shards
27 | func Structure(node string) ([]RPCRoutes, error) {
28 | type r struct {
29 | Result []RPCRoutes `json:"result"`
30 | }
31 | p, e := rpc.RawRequest(rpc.Method.GetShardingStructure, node, []interface{}{})
32 | if e != nil {
33 | return nil, e
34 | }
35 | result := r{}
36 | if err := json.Unmarshal(p, &result); err != nil {
37 | return nil, err
38 | }
39 | return result.Result, nil
40 | }
41 |
42 | func CheckAllShards(node, oneAddr string, noPretty bool) (string, error) {
43 | var out bytes.Buffer
44 | out.WriteString("[")
45 | params := []interface{}{oneAddr, "latest"}
46 | s, err := Structure(node)
47 | if err != nil {
48 | return "", err
49 | }
50 | for i, shard := range s {
51 | balanceRPCReply, err := rpc.Request(rpc.Method.GetBalance, shard.HTTP, params)
52 | if err != nil {
53 | if common.DebugRPC {
54 | fmt.Printf("NOTE: Route %s failed.", shard.HTTP)
55 | }
56 | continue
57 | }
58 | if i != 0 {
59 | out.WriteString(",")
60 | }
61 | balance, _ := balanceRPCReply["result"].(string)
62 | bln := common.NewDecFromHex(balance)
63 | bln = bln.Quo(oneAsDec)
64 | out.WriteString(fmt.Sprintf(`{"shard":%d, "amount":%s}`,
65 | shard.ShardID,
66 | bln.String(),
67 | ))
68 | }
69 | out.WriteString("]")
70 | if noPretty {
71 | return out.String(), nil
72 | }
73 | return common.JSONPrettyFormat(out.String()), nil
74 | }
75 |
--------------------------------------------------------------------------------
/pkg/transaction/transaction.go:
--------------------------------------------------------------------------------
1 | package transaction
2 |
3 | import (
4 | "math/big"
5 |
6 | "github.com/harmony-one/go-sdk/pkg/address"
7 | "github.com/harmony-one/go-sdk/pkg/rpc"
8 | "github.com/harmony-one/harmony/core/types"
9 | "github.com/harmony-one/harmony/numeric"
10 | )
11 |
12 | // NewTransaction - create a new Transaction based on supplied params
13 | func NewTransaction(
14 | nonce, gasLimit uint64,
15 | to *address.T,
16 | shardID, toShardID uint32,
17 | amount, gasPrice numeric.Dec,
18 | data []byte) *types.Transaction {
19 | return types.NewCrossShardTransaction(nonce, to, shardID, toShardID, amount.TruncateInt(), gasLimit, gasPrice.TruncateInt(), data[:])
20 | }
21 |
22 | // NewEthTransaction - create a new Transaction based on supplied params
23 | func NewEthTransaction(
24 | nonce, gasLimit uint64,
25 | to address.T,
26 | amount, gasPrice numeric.Dec,
27 | data []byte) *types.EthTransaction {
28 | return types.NewEthTransaction(nonce, to, amount.TruncateInt(), gasLimit, gasPrice.TruncateInt(), data[:])
29 | }
30 |
31 | // GetNextNonce returns the nonce on-chain (finalized transactions)
32 | func GetNextNonce(addr string, messenger rpc.T) uint64 {
33 | transactionCountRPCReply, err :=
34 | messenger.SendRPC(rpc.Method.GetTransactionCount, []interface{}{address.Parse(addr), "latest"})
35 |
36 | if err != nil {
37 | return 0
38 | }
39 |
40 | transactionCount, _ := transactionCountRPCReply["result"].(string)
41 | n, _ := big.NewInt(0).SetString(transactionCount[2:], 16)
42 | return n.Uint64()
43 | }
44 |
45 | // GetNextPendingNonce returns the nonce from the tx-pool (un-finalized transactions)
46 | func GetNextPendingNonce(addr string, messenger rpc.T) uint64 {
47 | transactionCountRPCReply, err :=
48 | messenger.SendRPC(rpc.Method.GetTransactionCount, []interface{}{address.Parse(addr), "pending"})
49 |
50 | if err != nil {
51 | return 0
52 | }
53 |
54 | transactionCount, _ := transactionCountRPCReply["result"].(string)
55 | n, _ := big.NewInt(0).SetString(transactionCount[2:], 16)
56 | return n.Uint64()
57 | }
58 |
59 | // IsValid - whether or not a tx is valid
60 | func IsValid(tx *types.Transaction) bool {
61 | return true
62 | }
63 |
--------------------------------------------------------------------------------
/pkg/governance/signing.go:
--------------------------------------------------------------------------------
1 | package governance
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/ethereum/go-ethereum/common/hexutil"
7 | "github.com/harmony-one/harmony/accounts"
8 | "github.com/harmony-one/harmony/accounts/keystore"
9 | "github.com/harmony-one/harmony/crypto/hash"
10 | "github.com/pkg/errors"
11 | )
12 |
13 | func encodeForSigning(typedData *TypedData) ([]byte, error) {
14 | domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
15 | if err != nil {
16 | return nil, errors.Wrapf(
17 | err,
18 | "cannot hash the domain structure",
19 | )
20 | }
21 |
22 | typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
23 | if err != nil {
24 | return nil, errors.Wrapf(
25 | err,
26 | "cannot hash the structure",
27 | )
28 | }
29 |
30 | rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
31 | return rawData, nil
32 | }
33 |
34 | // signTypedData encodes and signs EIP-712 data
35 | // it is copied over here from Geth to use our own keystore implementation
36 | func signTypedData(keyStore *keystore.KeyStore, account accounts.Account, typedData *TypedData) (string, error) {
37 | rawData, err := encodeForSigning(typedData)
38 | if err != nil {
39 | return "", errors.Wrapf(
40 | err,
41 | "cannot encode for signing",
42 | )
43 | }
44 |
45 | msgHash := hash.Keccak256Hash(rawData)
46 | sign, err := keyStore.SignHash(account, msgHash.Bytes())
47 | if err != nil {
48 | return "", err
49 | }
50 | if len(sign) != 65 {
51 | return "", fmt.Errorf("sign error")
52 | }
53 | sign[64] += 0x1b
54 | return hexutil.Encode(sign), nil
55 | }
56 |
57 | // func signMessage(keyStore *keystore.KeyStore, account accounts.Account, data []byte) (string, error) {
58 | // fullMessage := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
59 | // msgHash := hash.Keccak256Hash([]byte(fullMessage))
60 | // sign, err := keyStore.SignHash(account, msgHash.Bytes())
61 | // if err != nil {
62 | // return "", err
63 | // }
64 | // if len(sign) != 65 {
65 | // return "", fmt.Errorf("sign error")
66 | // }
67 | // sign[64] += 0x1b
68 | // return hexutil.Encode(sign), nil
69 | // }
70 |
--------------------------------------------------------------------------------
/pkg/transaction/errors.go:
--------------------------------------------------------------------------------
1 | package transaction
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "time"
7 |
8 | "github.com/harmony-one/go-sdk/pkg/rpc"
9 | )
10 |
11 | var (
12 | errorSinkRPCs = []string{
13 | rpc.Method.GetCurrentTransactionErrorSink,
14 | rpc.Method.GetCurrentStakingErrorSink,
15 | }
16 | )
17 |
18 | // Error is the struct for all errors in the error sinks.
19 | // Note that StakingDirective is non-nil for staking txn errors.
20 | type Error struct {
21 | TxHashID *string `json:"tx-hash-id"`
22 | StakingDirective *string `json:"directive-kind"` // optional error field for staking
23 | ErrMessage *string `json:"error-message"`
24 | TimestampOfRejection int64 `json:"time-at-rejection"`
25 | }
26 |
27 | // Error an error with the ErrMessage and TimestampOfRejection as the message.
28 | func (e *Error) Error() error {
29 | if e.StakingDirective != nil {
30 | return fmt.Errorf("[%s] %s -- %s",
31 | *e.StakingDirective, *e.ErrMessage, time.Unix(e.TimestampOfRejection, 0).String(),
32 | )
33 | } else {
34 | return fmt.Errorf("[Plain transaction] %s -- %s",
35 | *e.ErrMessage, time.Unix(e.TimestampOfRejection, 0).String(),
36 | )
37 | }
38 | }
39 |
40 | // Errors ...
41 | type Errors []*Error
42 |
43 | func getTxErrorBySink(txHash, errorSinkRPC string, messenger rpc.T) (Errors, error) {
44 | var txErrors Errors
45 | response, err := messenger.SendRPC(errorSinkRPC, []interface{}{})
46 | if err != nil {
47 | return nil, err
48 | }
49 | var allErrors []Error
50 | asJSON, _ := json.Marshal(response["result"])
51 | _ = json.Unmarshal(asJSON, &allErrors)
52 | for i := range allErrors {
53 | txError := allErrors[i]
54 | if *txError.TxHashID == txHash {
55 | txErrors = append(txErrors, &txError)
56 | }
57 | }
58 | return txErrors, nil
59 | }
60 |
61 | // GetError returns all errors for a given (staking or plain) transaction hash.
62 | func GetError(txHash string, messenger rpc.T) (Errors, error) {
63 | for _, errorSinkRpc := range errorSinkRPCs {
64 | txErrors, err := getTxErrorBySink(txHash, errorSinkRpc, messenger)
65 | if err != nil {
66 | return Errors{}, err
67 | }
68 | if txErrors != nil {
69 | return txErrors, nil
70 | }
71 | }
72 | return Errors{}, nil
73 | }
74 |
--------------------------------------------------------------------------------
/cmd/subcommands/command.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | ethereum_rpc "github.com/ethereum/go-ethereum/rpc"
5 | "github.com/harmony-one/go-sdk/pkg/common"
6 | "github.com/harmony-one/go-sdk/pkg/console"
7 | "github.com/harmony-one/go-sdk/pkg/rpc"
8 | "github.com/mitchellh/go-homedir"
9 | "github.com/spf13/cobra"
10 | "log"
11 | "os"
12 | "path"
13 | )
14 |
15 | func init() {
16 | net := "mainnet"
17 |
18 | cmdCommand := &cobra.Command{
19 | Use: "command",
20 | Short: "Start an interactive JavaScript environment (connect to node)",
21 | RunE: func(cmd *cobra.Command, args []string) error {
22 | return openConsole(net)
23 | },
24 | }
25 |
26 | cmdCommand.Flags().StringVar(&net, "net", "mainnet", "used net(default: mainnet, eg: mainnet, testnet ...)")
27 |
28 | RootCmd.AddCommand(cmdCommand)
29 | }
30 |
31 | func checkAndMakeDirIfNeeded() string {
32 | userDir, _ := homedir.Dir()
33 | hmyCLIDir := path.Join(userDir, common.DefaultConfigDirName, common.DefaultCommandAliasesDirName)
34 | if _, err := os.Stat(hmyCLIDir); os.IsNotExist(err) {
35 | // Double check with Leo what is right file persmission
36 | os.Mkdir(hmyCLIDir, 0700)
37 | }
38 |
39 | return hmyCLIDir
40 | }
41 |
42 | // remoteConsole will connect to a remote node instance, attaching a JavaScript
43 | // console to it.
44 | func openConsole(net string) error {
45 | client, err := ethereum_rpc.Dial(node)
46 | if err != nil {
47 | log.Fatalf("Unable to attach to remote node: %v", err)
48 | }
49 |
50 | // check net type
51 | _, err = common.StringToChainID(net)
52 | if err != nil {
53 | return err
54 | }
55 |
56 | // get shard id
57 | nodeRPCReply, err := rpc.Request(rpc.Method.GetShardID, node, []interface{}{})
58 | if err != nil {
59 | return err
60 | }
61 |
62 | shard := int(nodeRPCReply["result"].(float64))
63 |
64 | config := console.Config{
65 | DataDir: checkAndMakeDirIfNeeded(),
66 | DocRoot: ".",
67 | Client: client,
68 | Preload: nil,
69 | NodeUrl: node,
70 | ShardId: shard,
71 | Net: net,
72 | }
73 |
74 | consoleInstance, err := console.New(config)
75 | if err != nil {
76 | log.Fatalf("Failed to start the JavaScript console insatnce: %v", err)
77 | }
78 | defer consoleInstance.Stop(false)
79 |
80 | // Otherwise print the welcome screen and enter interactive mode
81 | consoleInstance.Welcome()
82 | consoleInstance.Interactive()
83 |
84 | return nil
85 | }
86 |
--------------------------------------------------------------------------------
/pkg/keys/bls_test.go:
--------------------------------------------------------------------------------
1 | package keys
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path/filepath"
7 | "testing"
8 | )
9 |
10 | func TestBlsKeyGeneration(t *testing.T) {
11 | valid := true
12 | passphrase := ""
13 | folderPath := "TestBlsKeyGeneration"
14 | absFolderPath, err := filepath.Abs(fmt.Sprintf("./%s", folderPath))
15 | if err != nil {
16 | t.Errorf("TestBlsKeyGeneration - failed to convert relative path to absolute path")
17 | }
18 |
19 | absFilePath := fmt.Sprintf("%s/TestBlsKeyGeneration.key", absFolderPath)
20 |
21 | err = os.MkdirAll(absFolderPath, 0755)
22 | if err != nil {
23 | t.Errorf("TestBlsKeyGeneration - failed to make test key folder")
24 | }
25 |
26 | if err := GenBlsKey(&BlsKey{Passphrase: passphrase, FilePath: absFilePath}); err != nil {
27 | t.Errorf("TestBlsKeyGeneration - failed to generate bls key using passphrase %s and path %s", passphrase, absFilePath)
28 | }
29 |
30 | if _, err = os.Stat(absFilePath); err != nil {
31 | t.Errorf("TestBlsKeyGeneration - failed to check if file %s exists", absFilePath)
32 | }
33 |
34 | valid = !os.IsNotExist(err)
35 |
36 | if !valid {
37 | t.Errorf("GenBlsKey - failed to generate a bls key using passphrase %s", "")
38 | }
39 |
40 | os.RemoveAll(absFolderPath)
41 | }
42 |
43 | func TestMultiBlsKeyGeneration(t *testing.T) {
44 | tests := []struct {
45 | node string
46 | count uint32
47 | shardID uint32
48 | filePath string
49 | expected bool
50 | }{
51 | {node: "https://api.s0.b.hmny.io", count: 3, shardID: 0, expected: true},
52 | {node: "https://api.s0.b.hmny.io", count: 3, shardID: 4, expected: false},
53 | }
54 |
55 | for _, test := range tests {
56 | valid := false
57 | blsKeys := []*BlsKey{}
58 | for i := uint32(0); i < test.count; i++ {
59 | blsKeys = append(blsKeys, &BlsKey{Passphrase: "", FilePath: ""})
60 | }
61 |
62 | blsKeys, shardCount, err := genBlsKeyForNode(blsKeys, test.node, test.shardID)
63 | if err != nil {
64 | valid = false
65 | }
66 |
67 | successCount := 0
68 |
69 | for _, blsKey := range blsKeys {
70 | success := (blsKey.ShardPublicKey != nil && blsKeyMatchesShardID(blsKey.ShardPublicKey, test.shardID, shardCount))
71 | if success {
72 | successCount++
73 | }
74 | }
75 |
76 | valid = (successCount == int(test.count))
77 |
78 | if valid != test.expected {
79 | t.Errorf("genBlsKeyForNode - failed to generate %d keys for shard %d using node %s", test.count, test.shardID, test.node)
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/pkg/console/jsre/completion_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package jsre
18 |
19 | import (
20 | "os"
21 | "reflect"
22 | "testing"
23 | )
24 |
25 | func TestCompleteKeywords(t *testing.T) {
26 | re := New("", os.Stdout)
27 | re.Run(`
28 | function theClass() {
29 | this.foo = 3;
30 | this.gazonk = {xyz: 4};
31 | }
32 | theClass.prototype.someMethod = function () {};
33 | var x = new theClass();
34 | var y = new theClass();
35 | y.someMethod = function override() {};
36 | `)
37 |
38 | var tests = []struct {
39 | input string
40 | want []string
41 | }{
42 | {
43 | input: "St",
44 | want: []string{"String"},
45 | },
46 | {
47 | input: "x",
48 | want: []string{"x."},
49 | },
50 | {
51 | input: "x.someMethod",
52 | want: []string{"x.someMethod("},
53 | },
54 | {
55 | input: "x.",
56 | want: []string{
57 | "x.constructor",
58 | "x.foo",
59 | "x.gazonk",
60 | "x.someMethod",
61 | },
62 | },
63 | {
64 | input: "y.",
65 | want: []string{
66 | "y.constructor",
67 | "y.foo",
68 | "y.gazonk",
69 | "y.someMethod",
70 | },
71 | },
72 | {
73 | input: "x.gazonk.",
74 | want: []string{
75 | "x.gazonk.constructor",
76 | "x.gazonk.hasOwnProperty",
77 | "x.gazonk.isPrototypeOf",
78 | "x.gazonk.propertyIsEnumerable",
79 | "x.gazonk.toLocaleString",
80 | "x.gazonk.toString",
81 | "x.gazonk.valueOf",
82 | "x.gazonk.xyz",
83 | },
84 | },
85 | }
86 | for _, test := range tests {
87 | cs := re.CompleteKeywords(test.input)
88 | if !reflect.DeepEqual(cs, test.want) {
89 | t.Errorf("wrong completions for %q\ngot %v\nwant %v", test.input, cs, test.want)
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/pkg/rpc/query.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "strconv"
7 | "time"
8 |
9 | "github.com/valyala/fasthttp"
10 |
11 | "github.com/harmony-one/go-sdk/pkg/common"
12 | )
13 |
14 | var (
15 | queryID = 0
16 | post = []byte("POST")
17 | )
18 |
19 | func baseRequest(method string, node string, params interface{}) ([]byte, error) {
20 | requestBody, _ := json.Marshal(map[string]interface{}{
21 | "jsonrpc": common.JSONRPCVersion,
22 | "id": strconv.Itoa(queryID),
23 | "method": method,
24 | "params": params,
25 | })
26 | const contentType = "application/json"
27 | req := fasthttp.AcquireRequest()
28 | req.SetBody(requestBody)
29 | req.Header.SetMethodBytes(post)
30 | req.Header.SetContentType(contentType)
31 | req.SetRequestURIBytes([]byte(node))
32 | res := fasthttp.AcquireResponse()
33 | if err := fasthttp.Do(req, res); err != nil {
34 | return nil, err
35 | }
36 | c := res.StatusCode()
37 | if c != 200 {
38 | return nil, fmt.Errorf("http status code not 200, received: %d", c)
39 | }
40 | fasthttp.ReleaseRequest(req)
41 | body := res.Body()
42 | result := make([]byte, len(body))
43 | copy(result, body)
44 | fasthttp.ReleaseResponse(res)
45 | if common.DebugRPC {
46 | reqB := common.JSONPrettyFormat(string(requestBody))
47 | respB := common.JSONPrettyFormat(string(body))
48 | fmt.Printf("Response Timestamp: %s\n", time.Now().String())
49 | fmt.Printf("URL: %s, Request Body: %s\n\n", node, reqB)
50 | fmt.Printf("URL: %s, Response Body: %s\n\n", node, respB)
51 | }
52 | queryID++
53 | return result, nil
54 | }
55 |
56 | // TODO Check if Method known, return error when not known, good intern task
57 |
58 | // Request processes
59 | func Request(method string, node string, params interface{}) (Reply, error) {
60 | rpcJSON := make(map[string]interface{})
61 | rawReply, err := baseRequest(method, node, params)
62 | if err != nil {
63 | return nil, err
64 | }
65 | json.Unmarshal(rawReply, &rpcJSON)
66 | if oops := rpcJSON["error"]; oops != nil {
67 | errNo := oops.(map[string]interface{})["code"].(float64)
68 | errMessage := ""
69 | if oops.(map[string]interface{})["message"] != nil {
70 | errMessage = oops.(map[string]interface{})["message"].(string)
71 | }
72 | return nil, ErrorCodeToError(errMessage, errNo)
73 | }
74 | return rpcJSON, nil
75 | }
76 |
77 | // RawRequest is to sidestep the lifting done by Request
78 | func RawRequest(method string, node string, params interface{}) ([]byte, error) {
79 | return baseRequest(method, node, params)
80 | }
81 |
--------------------------------------------------------------------------------
/pkg/governance/signing_test.go:
--------------------------------------------------------------------------------
1 | package governance
2 |
3 | import (
4 | "encoding/hex"
5 | "os"
6 | "path"
7 | "testing"
8 |
9 | "github.com/btcsuite/btcd/btcec"
10 | "github.com/harmony-one/go-sdk/pkg/common"
11 | "github.com/harmony-one/harmony/accounts"
12 | )
13 |
14 | func TestSigning(t *testing.T) {
15 | // make the EIP712 data structure
16 | vote := Vote{
17 | Space: "yam.eth",
18 | Proposal: "0x21ea31e896ec5b5a49a3653e51e787ee834aaf953263144ab936ed756f36609f",
19 | ProposalType: "single-choice",
20 | Choice: "1",
21 | App: "my-app",
22 | Timestamp: 1660909056,
23 | From: "0x9E713963a92c02317A681b9bB3065a8249DE124F",
24 | }
25 | typedData, err := vote.ToEIP712()
26 | if err != nil {
27 | t.Fatal(err)
28 | }
29 |
30 | // add a temporary key store with the below private key
31 | location := path.Join(os.TempDir(), "hmy-test")
32 | keyStore := common.KeyStoreForPath(location)
33 | privateKeyBytes, _ := hex.DecodeString("91c8360c4cb4b5fac45513a7213f31d4e4a7bfcb4630e9fbf074f42a203ac0b9")
34 | sk, _ := btcec.PrivKeyFromBytes(btcec.S256(), privateKeyBytes)
35 | passphrase := ""
36 | keyStore.ImportECDSA(sk.ToECDSA(), passphrase)
37 | keyStore.Unlock(accounts.Account{Address: keyStore.Accounts()[0].Address}, passphrase)
38 | account := accounts.Account{
39 | Address: keyStore.Accounts()[0].Address,
40 | }
41 |
42 | sign, err := signTypedData(keyStore, account, typedData)
43 | if err != nil {
44 | t.Fatal(err)
45 | }
46 | expectedSig := "0x6b572bacbb44efe75cad5b938a5d4fe64c3495bec28807e78989e3159e11d21d5d5568ffabcf274194830b6cb375355af423995afc7ee290e4a632b12bdbe0cc1b"
47 | if sign != expectedSig {
48 | t.Errorf("invalid sig: got %s but expected %s", sign, expectedSig)
49 | }
50 |
51 | os.RemoveAll(location)
52 | }
53 |
54 | // The below NodeJS code was used to generate the above signature
55 | // import snapshot from '@snapshot-labs/snapshot.js';
56 | // import { Wallet } from "ethers";
57 | // const hub = 'https://hub.snapshot.org';
58 | // const client = new snapshot.Client712(hub);
59 | // const wallet = new Wallet("91c8360c4cb4b5fac45513a7213f31d4e4a7bfcb4630e9fbf074f42a203ac0b9");
60 | // const receipt = await client.vote(wallet, await wallet.getAddress(), {
61 | // space: 'yam.eth',
62 | // proposal: '0x21ea31e896ec5b5a49a3653e51e787ee834aaf953263144ab936ed756f36609f',
63 | // type: 'single-choice',
64 | // choice: 1,
65 | // app: 'my-app',
66 | // timestamp: 1660909056,
67 | // });
68 |
69 | // package.json
70 | // "@snapshot-labs/snapshot.js": "^0.4.18"
71 | // "ethers": "^5.6.9"
72 |
--------------------------------------------------------------------------------
/cmd/subcommands/validator.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 |
7 | "github.com/harmony-one/go-sdk/pkg/rpc"
8 | "github.com/pkg/errors"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | var (
13 | validatorSubCmds = []*cobra.Command{{
14 | Use: "elected",
15 | Short: "elected validators",
16 | RunE: func(cmd *cobra.Command, args []string) error {
17 | noLatest = true
18 | return request(rpc.Method.GetElectedValidatorAddresses, []interface{}{})
19 | },
20 | }, {
21 | Use: "all",
22 | Short: "all validator addresses",
23 | RunE: func(cmd *cobra.Command, args []string) error {
24 | return request(rpc.Method.GetAllValidatorAddresses, []interface{}{})
25 | },
26 | }, {
27 | Use: "information",
28 | Short: "original creation record of a validator",
29 | Args: cobra.ExactArgs(1),
30 | PreRunE: validateAddress,
31 | RunE: func(cmd *cobra.Command, args []string) error {
32 | noLatest = true
33 | e := request(rpc.Method.GetValidatorInformation, []interface{}{addr.address})
34 | if e != nil {
35 | return fmt.Errorf("validator address not found: %s", addr.address)
36 | }
37 | return e
38 | },
39 | }, {
40 | Use: "information-by-block-number",
41 | Short: "original creation record of a validator by block number",
42 | Args: cobra.ExactArgs(2),
43 | PreRunE: validateAddress,
44 | RunE: func(cmd *cobra.Command, args []string) error {
45 | noLatest = true
46 | return request(rpc.Method.GetValidatorInformationByBlockNumber, []interface{}{addr.address, args[1]})
47 | },
48 | }, {
49 | Use: "all-information",
50 | Short: "all validators information",
51 | Args: cobra.ExactArgs(1),
52 | RunE: func(cmd *cobra.Command, args []string) error {
53 | noLatest = true
54 | page, err := strconv.ParseInt(args[0], 10, 64)
55 | if err != nil {
56 | return errors.Wrapf(err, "the page argument must be integer, supplied %v", args[0])
57 | }
58 | return request(rpc.Method.GetAllValidatorInformation, []interface{}{page})
59 | },
60 | }, {
61 | Use: "all-information-by-block-number",
62 | Short: "all validators information by block number",
63 | Args: cobra.ExactArgs(2),
64 | RunE: func(cmd *cobra.Command, args []string) error {
65 | noLatest = true
66 | page, err := strconv.ParseInt(args[0], 10, 64)
67 | if err != nil {
68 | return errors.Wrapf(err, "the page argument must be integer, supplied %v", args[0])
69 | }
70 | return request(rpc.Method.GetAllValidatorInformationByBlockNumber, []interface{}{page, args[1]})
71 | },
72 | },
73 | }
74 | )
75 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | SHELL := /bin/bash
2 | TOP:=$(realpath ..)
3 | export LD_LIBRARY_PATH:=$(TOP)/bls/lib:$(TOP)/mcl/lib:/usr/local/opt/openssl/lib:/opt/homebrew/opt/gmp/lib/:/opt/homebrew/opt/openssl/lib
4 | export LIBRARY_PATH:=$(LD_LIBRARY_PATH)
5 | version := $(shell git rev-list --count HEAD)
6 | commit := $(shell git describe --always --long --dirty)
7 | built_at := $(shell date +%FT%T%z)
8 | built_by := ${USER}@harmony.one
9 |
10 | flags := -gcflags="all=-N -l"
11 | ldflags := -X main.version=v${version} -X main.commit=${commit}
12 | ldflags += -X main.builtAt=${built_at} -X main.builtBy=${built_by}
13 | cli := ./dist/hmy
14 | upload-path-darwin := 's3://pub.harmony.one/release/darwin-x86_64/mainnet/hmy'
15 | upload-path-linux := 's3://pub.harmony.one/release/linux-x86_64/mainnet/hmy'
16 | upload-path-linux-version := 's3://pub.harmony.one/release/linux-x86_64/mainnet/hmy_version'
17 | uname := $(shell uname)
18 | GOPATH := $(shell go env GOPATH)
19 |
20 | env := GO111MODULE=on
21 |
22 | DIR := ${CURDIR}
23 | export CGO_LDFLAGS=-L$(DIR)/dist/lib -Wl,-rpath -Wl,\$ORIGIN/lib
24 |
25 | all:
26 | source ${GOPATH}/src/github.com/harmony-one/harmony/scripts/setup_bls_build_flags.sh && $(env) go build -o $(cli) -ldflags="$(ldflags)" cmd/main.go
27 | cp $(cli) hmy
28 |
29 | static:
30 | make -C ${GOPATH}/src/github.com/harmony-one/mcl
31 | make -C ${GOPATH}/src/github.com/harmony-one/bls minimised_static BLS_SWAP_G=1
32 | source ${GOPATH}/src/github.com/harmony-one/harmony/scripts/setup_bls_build_flags.sh && $(env) go build -o $(cli) -ldflags="$(ldflags) -w -extldflags \"-static\"" cmd/main.go
33 | cp $(cli) hmy
34 |
35 | debug:
36 | source ${GOPATH}/src/github.com/harmony-one/harmony/scripts/setup_bls_build_flags.sh && $(env) go build $(flags) -o $(cli) -ldflags="$(ldflags)" cmd/main.go
37 | cp $(cli) hmy
38 |
39 | install:all
40 | cp $(cli) ~/.local/bin
41 |
42 | run-tests: test-rpc test-key;
43 |
44 | test-key:
45 | go test ./pkg/keys -cover -v
46 |
47 | test-rpc:
48 | go test ./pkg/rpc -cover -v
49 |
50 | # Notice assumes you have correct uploading credentials
51 | upload-darwin:all
52 | ifeq (${uname}, Darwin)
53 | aws --profile upload s3 cp ./hmy ${upload-path-darwin}
54 | else
55 | @echo "Wrong operating system for target upload"
56 | endif
57 |
58 | # Only the linux build will upload the CLI version
59 | upload-linux:static
60 | ifeq (${uname}, Linux)
61 | aws --profile upload s3 cp ./hmy ${upload-path-linux}
62 | ./hmy version &> ./hmy_version
63 | aws --profile upload s3 cp ./hmy_version ${upload-path-linux-version}
64 | else
65 | @echo "Wrong operating system for target upload"
66 | endif
67 |
68 | .PHONY:clean run-tests upload-darwin upload-linux
69 |
70 | clean:
71 | @rm -f $(cli)
72 | @rm -rf ./dist
73 |
--------------------------------------------------------------------------------
/pkg/address/address.go:
--------------------------------------------------------------------------------
1 | package address
2 |
3 | import (
4 | "github.com/btcsuite/btcutil/bech32"
5 | ethCommon "github.com/ethereum/go-ethereum/common"
6 | "github.com/pkg/errors"
7 | )
8 |
9 | const (
10 | // HashLength is the expected length of the hash
11 | HashLength = ethCommon.HashLength
12 | // AddressLength is the expected length of the address
13 | AddressLength = ethCommon.AddressLength
14 | Bech32AddressHRP = "one"
15 | )
16 |
17 | type T = ethCommon.Address
18 |
19 | func decodeAndConvert(bech string) (string, []byte, error) {
20 | hrp, data, err := bech32.Decode(bech)
21 | if err != nil {
22 | return "", nil, errors.Wrap(err, "decoding bech32 failed")
23 | }
24 | converted, err := bech32.ConvertBits(data, 5, 8, false)
25 | if err != nil {
26 | return "", nil, errors.Wrap(err, "decoding bech32 failed")
27 | }
28 | return hrp, converted, nil
29 | }
30 |
31 | // TODO ek – the following functions use Ethereum addresses until we have a
32 | // proper abstraction set in place.
33 |
34 | // ParseBech32Addr decodes the given bech32 address and populates the given
35 | // human-readable-part string and address with the decoded result.
36 | func parseBech32Addr(b32 string, hrp *string, addr *T) error {
37 | h, b, err := decodeAndConvert(b32)
38 | if err != nil {
39 | return errors.Wrapf(err, "cannot decode %#v as bech32 address", b32)
40 | }
41 | if len(b) != AddressLength {
42 | return errors.Errorf("decoded bech32 %#v has invalid length %d",
43 | b32, len(b))
44 | }
45 | *hrp = h
46 | addr.SetBytes(b)
47 | return nil
48 | }
49 |
50 | func Bech32ToAddress(b32 string) (addr T, err error) {
51 | var hrp string
52 | err = parseBech32Addr(b32, &hrp, &addr)
53 | if err == nil && hrp != Bech32AddressHRP {
54 | err = errors.Errorf("%#v is not a %#v address", b32, Bech32AddressHRP)
55 | }
56 | return
57 | }
58 |
59 | // ParseAddr parses the given address, either as bech32 or as hex.
60 | // The result can be 0x00..00 if the passing param is not a correct address.
61 | func Parse(s string) T {
62 | if addr, err := Bech32ToAddress(s); err == nil {
63 | return addr
64 | }
65 | // The result can be 0x00...00 if the passing param is not a correct address.
66 | return ethCommon.HexToAddress(s)
67 | }
68 |
69 | func ToBech32(addr T) string {
70 | b32, err := BuildBech32Addr(Bech32AddressHRP, addr)
71 | if err != nil {
72 | panic(err)
73 | }
74 | return b32
75 | }
76 |
77 | func BuildBech32Addr(hrp string, addr T) (string, error) {
78 | return ConvertAndEncode(hrp, addr.Bytes())
79 | }
80 |
81 | func ConvertAndEncode(hrp string, data []byte) (string, error) {
82 | converted, err := bech32.ConvertBits(data, 8, 5, true)
83 | if err != nil {
84 | return "", errors.Wrap(err, "encoding bech32 failed")
85 | }
86 | return bech32.Encode(hrp, converted)
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/pkg/console/jsre/completion.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package jsre
18 |
19 | import (
20 | "sort"
21 | "strings"
22 |
23 | "github.com/dop251/goja"
24 | )
25 |
26 | // CompleteKeywords returns potential continuations for the given line. Since line is
27 | // evaluated, callers need to make sure that evaluating line does not have side effects.
28 | func (jsre *JSRE) CompleteKeywords(line string) []string {
29 | var results []string
30 | jsre.Do(func(vm *goja.Runtime) {
31 | results = getCompletions(vm, line)
32 | })
33 | return results
34 | }
35 |
36 | func getCompletions(vm *goja.Runtime, line string) (results []string) {
37 | parts := strings.Split(line, ".")
38 | if len(parts) == 0 {
39 | return nil
40 | }
41 |
42 | // Find the right-most fully named object in the line. e.g. if line = "x.y.z"
43 | // and "x.y" is an object, obj will reference "x.y".
44 | obj := vm.GlobalObject()
45 | for i := 0; i < len(parts)-1; i++ {
46 | v := obj.Get(parts[i])
47 | if v == nil {
48 | return nil // No object was found
49 | }
50 | obj = v.ToObject(vm)
51 | }
52 |
53 | // Go over the keys of the object and retain the keys matching prefix.
54 | // Example: if line = "x.y.z" and "x.y" exists and has keys "zebu", "zebra"
55 | // and "platypus", then "x.y.zebu" and "x.y.zebra" will be added to results.
56 | prefix := parts[len(parts)-1]
57 | iterOwnAndConstructorKeys(vm, obj, func(k string) {
58 | if strings.HasPrefix(k, prefix) {
59 | if len(parts) == 1 {
60 | results = append(results, k)
61 | } else {
62 | results = append(results, strings.Join(parts[:len(parts)-1], ".")+"."+k)
63 | }
64 | }
65 | })
66 |
67 | // Append opening parenthesis (for functions) or dot (for objects)
68 | // if the line itself is the only completion.
69 | if len(results) == 1 && results[0] == line {
70 | obj := obj.Get(parts[len(parts)-1])
71 | if obj != nil {
72 | if _, isfunc := goja.AssertFunction(obj); isfunc {
73 | results[0] += "("
74 | } else {
75 | results[0] += "."
76 | }
77 | }
78 | }
79 |
80 | sort.Strings(results)
81 | return results
82 | }
83 |
--------------------------------------------------------------------------------
/cmd/subcommands/governance.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/harmony-one/go-sdk/pkg/governance"
7 | "github.com/harmony-one/go-sdk/pkg/store"
8 | "github.com/harmony-one/harmony/accounts"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func init() {
13 | cmdGovernance := &cobra.Command{
14 | Use: "governance",
15 | Short: "Interact with the Harmony spaces on https://snapshot.org",
16 | Long: `
17 | Support interaction with the Harmony governance space on Snapshot, especially for validators that do not want to import their account private key into either metamask or onewallet.
18 | `,
19 | RunE: func(cmd *cobra.Command, args []string) error {
20 | cmd.Help()
21 | return nil
22 | },
23 | }
24 |
25 | cmdGovernance.AddCommand([]*cobra.Command{
26 | commandVote(),
27 | }...)
28 |
29 | RootCmd.AddCommand(cmdGovernance)
30 | }
31 |
32 | func commandVote() (cmd *cobra.Command) {
33 | var space string
34 | var proposal string
35 | var choice string
36 | var key string
37 | var proposalType string
38 | // var privacy string
39 | var app string
40 | var reason string
41 |
42 | cmd = &cobra.Command{
43 | Use: "vote-proposal",
44 | Short: "Vote on a proposal",
45 | RunE: func(cmd *cobra.Command, args []string) error {
46 | keyStore := store.FromAccountName(key)
47 | passphrase, err := getPassphrase()
48 | if err != nil {
49 | return err
50 | }
51 |
52 | if len(keyStore.Accounts()) <= 0 {
53 | return fmt.Errorf("couldn't find address from the key")
54 | }
55 |
56 | account := accounts.Account{Address: keyStore.Accounts()[0].Address}
57 | err = keyStore.Unlock(accounts.Account{Address: keyStore.Accounts()[0].Address}, passphrase)
58 | if err != nil {
59 | return err
60 | }
61 |
62 | return governance.DoVote(keyStore, account, governance.Vote{
63 | Space: space,
64 | Proposal: proposal,
65 | ProposalType: proposalType,
66 | Choice: choice,
67 | // Privacy: privacy,
68 | App: app,
69 | From: account.Address.Hex(),
70 | Reason: reason,
71 | })
72 | },
73 | }
74 |
75 | cmd.Flags().StringVar(&key, "key", "", "Account name. Must first use (hmy keys import-private-key) to import.")
76 | cmd.Flags().StringVar(&space, "space", "harmony-mainnet.eth", "Snapshot space")
77 | cmd.Flags().StringVar(&proposal, "proposal", "", "Proposal hash")
78 | cmd.Flags().StringVar(&proposalType, "proposal-type", "single-choice", "Proposal type like single-choice, approval, quadratic, etc.")
79 | cmd.Flags().StringVar(&choice, "choice", "", "Vote choice either as integer, list of integers (e.x. when using ranked choice voting), or string")
80 | // cmd.Flags().StringVar(&privacy, "privacy", "", "Vote privacy e.x. shutter")
81 | cmd.Flags().StringVar(&app, "app", "snapshot", "Voting app")
82 | cmd.Flags().StringVar(&reason, "reason", "", "Reason for your choice")
83 | cmd.Flags().BoolVar(&userProvidesPassphrase, "passphrase", false, ppPrompt)
84 |
85 | cmd.MarkFlagRequired("key")
86 | cmd.MarkFlagRequired("proposal")
87 | cmd.MarkFlagRequired("choice")
88 | return
89 | }
90 |
--------------------------------------------------------------------------------
/pkg/rpc/common/methods.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | type RpcMethod = string
4 |
5 | type RpcEnumList struct {
6 | GetShardingStructure RpcMethod
7 | GetBlockByHash RpcMethod
8 | GetBlockByNumber RpcMethod
9 | GetBlockTransactionCountByHash RpcMethod
10 | GetBlockTransactionCountByNumber RpcMethod
11 | GetCode RpcMethod
12 | GetTransactionByBlockHashAndIndex RpcMethod
13 | GetTransactionByBlockNumberAndIndex RpcMethod
14 | GetTransactionByHash RpcMethod
15 | GetStakingTransactionByHash RpcMethod
16 | GetTransactionReceipt RpcMethod
17 | Syncing RpcMethod
18 | PeerCount RpcMethod
19 | GetBalance RpcMethod
20 | GetStorageAt RpcMethod
21 | GetTransactionCount RpcMethod
22 | SendTransaction RpcMethod
23 | SendRawTransaction RpcMethod
24 | Subscribe RpcMethod
25 | GetPastLogs RpcMethod
26 | GetWork RpcMethod
27 | GetProof RpcMethod
28 | GetFilterChanges RpcMethod
29 | NewPendingTransactionFilter RpcMethod
30 | NewBlockFilter RpcMethod
31 | NewFilter RpcMethod
32 | Call RpcMethod
33 | EstimateGas RpcMethod
34 | GasPrice RpcMethod
35 | BlockNumber RpcMethod
36 | UnSubscribe RpcMethod
37 | NetVersion RpcMethod
38 | ProtocolVersion RpcMethod
39 | GetNodeMetadata RpcMethod
40 | GetLatestBlockHeader RpcMethod
41 | SendRawStakingTransaction RpcMethod
42 | GetElectedValidatorAddresses RpcMethod
43 | GetAllValidatorAddresses RpcMethod
44 | GetValidatorInformation RpcMethod
45 | GetAllValidatorInformation RpcMethod
46 | GetValidatorInformationByBlockNumber RpcMethod
47 | GetAllValidatorInformationByBlockNumber RpcMethod
48 | GetDelegationsByDelegator RpcMethod
49 | GetDelegationsByValidator RpcMethod
50 | GetCurrentTransactionErrorSink RpcMethod
51 | GetMedianRawStakeSnapshot RpcMethod
52 | GetCurrentStakingErrorSink RpcMethod
53 | GetTransactionsHistory RpcMethod
54 | GetPendingTxnsInPool RpcMethod
55 | GetPendingCrosslinks RpcMethod
56 | GetPendingCXReceipts RpcMethod
57 | GetCurrentUtilityMetrics RpcMethod
58 | ResendCX RpcMethod
59 | GetSuperCommmittees RpcMethod
60 | GetCurrentBadBlocks RpcMethod
61 | GetShardID RpcMethod
62 | GetLastCrossLinks RpcMethod
63 | GetLatestChainHeaders RpcMethod
64 | }
65 |
66 | // ValidatedMethod checks if given method is known
67 | func ValidatedMethod(m RpcMethod) string {
68 | switch m := RpcMethod(m); m {
69 | default:
70 | return string(m)
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/.github/workflows/test-build.yml:
--------------------------------------------------------------------------------
1 | name: test build
2 |
3 | on:
4 | push:
5 |
6 | env:
7 | GOPATH: ${{ github.workspace }}
8 | GOBIN: ${{ github.workspace }}/bin
9 |
10 | jobs:
11 | build-x86_64:
12 | name: Build hmy binary for x86_64
13 | runs-on: ${{ matrix.os }}
14 | strategy:
15 | matrix:
16 | os: [ ubuntu-22.04, macos-latest ]
17 |
18 | steps:
19 | - name: Checkout hmy code
20 | uses: actions/checkout@v4
21 | with:
22 | path: go-sdk
23 |
24 | - name: Set up Go
25 | uses: actions/setup-go@v4
26 | with:
27 | go-version-file: go-sdk/go.mod
28 |
29 | - name: Checkout dependence repo
30 | uses: actions/checkout@v4
31 | with:
32 | repository: harmony-one/mcl
33 | path: ${{ github.workspace }}/src/github.com/harmony-one/mcl
34 | ref: master
35 | fetch-depth: 0
36 |
37 | - name: Checkout dependence repo
38 | uses: actions/checkout@v4
39 | with:
40 | repository: harmony-one/bls
41 | path: ${{ github.workspace }}/src/github.com/harmony-one/bls
42 | ref: master
43 | fetch-depth: 0
44 |
45 | - name: Checkout dependence code
46 | uses: actions/checkout@v4
47 | with:
48 | repository: harmony-one/harmony
49 | path: ${{ github.workspace }}/src/github.com/harmony-one/harmony
50 | ref: main
51 | fetch-depth: 0
52 |
53 | - name: Get latest version and release
54 | run: |
55 | VERSION=$(git tag -l --sort=-v:refname | head -n 1 | tr -d v)
56 | RELEASE=$(git describe --long | cut -f2 -d-)
57 | echo "build_version=$VERSION" >> $GITHUB_ENV
58 | echo "build_release=$RELEASE" >> $GITHUB_ENV
59 | working-directory: go-sdk
60 |
61 | - name: Debug
62 | run: |
63 | pwd
64 | echo ${HOME}
65 | echo ${GITHUB_WORKSPACE}
66 | echo ${GOPATH}
67 | echo ${GOROOT}
68 | ls ${{ github.workspace }}/src/github.com/harmony-one/
69 |
70 | - name: Build hmy binary for linux ubuntu
71 | if: matrix.os == 'ubuntu-22.04'
72 | run: |
73 | make static
74 | working-directory: go-sdk
75 |
76 | - name: Build libs for macos-latest
77 | if: matrix.os == 'macos-latest'
78 | run: |
79 | brew install gmp
80 | brew install openssl
81 | sudo mkdir -p /opt/homebrew/opt/
82 | sudo ln -sf /usr/local/opt/openssl@1.1 /opt/homebrew/opt/openssl@1.1
83 | echo "ls -l /opt/homebrew/opt/openssl@1.1"; ls -l /opt/homebrew/opt/openssl@1.1
84 | make libs
85 | working-directory: ${{ github.workspace }}/src/github.com/harmony-one/harmony
86 |
87 | - name: Build hmy binary for macos-latest x86_64
88 | if: matrix.os == 'macos-latest'
89 | run: |
90 | make all
91 | working-directory: go-sdk
92 |
93 | - name: Upload artifact for linux
94 | uses: actions/upload-artifact@v4
95 | if: matrix.os == 'ubuntu-22.04'
96 | with:
97 | name: hmy-linux
98 | path: ${{ github.workspace }}/go-sdk/dist/*
99 | retention-days: 1
100 |
101 | - name: Upload artifact for darwin
102 | uses: actions/upload-artifact@v4
103 | if: matrix.os == 'macos-latest'
104 | with:
105 | name: hmy-darwin
106 | path: ${{ github.workspace }}/go-sdk/dist/*
107 | retention-days: 1
108 |
--------------------------------------------------------------------------------
/pkg/console/jsre/jsre_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package jsre
18 |
19 | import (
20 | "io/ioutil"
21 | "os"
22 | "path"
23 | "reflect"
24 | "testing"
25 | "time"
26 |
27 | "github.com/dop251/goja"
28 | )
29 |
30 | type testNativeObjectBinding struct {
31 | vm *goja.Runtime
32 | }
33 |
34 | type msg struct {
35 | Msg string
36 | }
37 |
38 | func (no *testNativeObjectBinding) TestMethod(call goja.FunctionCall) goja.Value {
39 | m := call.Argument(0).ToString().String()
40 | return no.vm.ToValue(&msg{m})
41 | }
42 |
43 | func newWithTestJS(t *testing.T, testjs string) (*JSRE, string) {
44 | dir, err := ioutil.TempDir("", "jsre-test")
45 | if err != nil {
46 | t.Fatal("cannot create temporary directory:", err)
47 | }
48 | if testjs != "" {
49 | if err := ioutil.WriteFile(path.Join(dir, "test.js"), []byte(testjs), os.ModePerm); err != nil {
50 | t.Fatal("cannot create test.js:", err)
51 | }
52 | }
53 | jsre := New(dir, os.Stdout)
54 | return jsre, dir
55 | }
56 |
57 | func TestExec(t *testing.T) {
58 | jsre, dir := newWithTestJS(t, `msg = "testMsg"`)
59 | defer os.RemoveAll(dir)
60 |
61 | err := jsre.Exec("test.js")
62 | if err != nil {
63 | t.Errorf("expected no error, got %v", err)
64 | }
65 | val, err := jsre.Run("msg")
66 | if err != nil {
67 | t.Errorf("expected no error, got %v", err)
68 | }
69 | if val.ExportType().Kind() != reflect.String {
70 | t.Errorf("expected string value, got %v", val)
71 | }
72 | exp := "testMsg"
73 | got := val.ToString().String()
74 | if exp != got {
75 | t.Errorf("expected '%v', got '%v'", exp, got)
76 | }
77 | jsre.Stop(false)
78 | }
79 |
80 | func TestNatto(t *testing.T) {
81 | jsre, dir := newWithTestJS(t, `setTimeout(function(){msg = "testMsg"}, 1);`)
82 | defer os.RemoveAll(dir)
83 |
84 | err := jsre.Exec("test.js")
85 | if err != nil {
86 | t.Errorf("expected no error, got %v", err)
87 | }
88 | time.Sleep(100 * time.Millisecond)
89 | val, err := jsre.Run("msg")
90 | if err != nil {
91 | t.Errorf("expected no error, got %v", err)
92 | }
93 | if val.ExportType().Kind() != reflect.String {
94 | t.Errorf("expected string value, got %v", val)
95 | }
96 | exp := "testMsg"
97 | got := val.ToString().String()
98 | if exp != got {
99 | t.Errorf("expected '%v', got '%v'", exp, got)
100 | }
101 | jsre.Stop(false)
102 | }
103 |
104 | func TestBind(t *testing.T) {
105 | jsre := New("", os.Stdout)
106 | defer jsre.Stop(false)
107 |
108 | jsre.Set("no", &testNativeObjectBinding{vm: jsre.vm})
109 |
110 | _, err := jsre.Run(`no.TestMethod("testMsg")`)
111 | if err != nil {
112 | t.Errorf("expected no error, got %v", err)
113 | }
114 | }
115 |
116 | func TestLoadScript(t *testing.T) {
117 | jsre, dir := newWithTestJS(t, `msg = "testMsg"`)
118 | defer os.RemoveAll(dir)
119 |
120 | _, err := jsre.Run(`loadScript("test.js")`)
121 | if err != nil {
122 | t.Errorf("expected no error, got %v", err)
123 | }
124 | val, err := jsre.Run("msg")
125 | if err != nil {
126 | t.Errorf("expected no error, got %v", err)
127 | }
128 | if val.ExportType().Kind() != reflect.String {
129 | t.Errorf("expected string value, got %v", val)
130 | }
131 | exp := "testMsg"
132 | got := val.ToString().String()
133 | if exp != got {
134 | t.Errorf("expected '%v', got '%v'", exp, got)
135 | }
136 | jsre.Stop(false)
137 | }
138 |
--------------------------------------------------------------------------------
/pkg/account/import.go:
--------------------------------------------------------------------------------
1 | package account
2 |
3 | import (
4 | "encoding/hex"
5 | "fmt"
6 | "github.com/ethereum/go-ethereum/crypto"
7 | "github.com/mitchellh/go-homedir"
8 | "io"
9 | "io/ioutil"
10 | "os"
11 | "path/filepath"
12 | "strings"
13 |
14 | "github.com/btcsuite/btcd/btcec"
15 | mapset "github.com/deckarep/golang-set"
16 | "github.com/harmony-one/go-sdk/pkg/address"
17 | "github.com/harmony-one/go-sdk/pkg/common"
18 | "github.com/harmony-one/go-sdk/pkg/mnemonic"
19 | "github.com/harmony-one/go-sdk/pkg/store"
20 | "github.com/harmony-one/harmony/accounts/keystore"
21 | )
22 |
23 | // ImportFromPrivateKey allows import of an ECDSA private key
24 | func ImportFromPrivateKey(privateKey, name, passphrase string) (string, error) {
25 | privateKey = strings.TrimPrefix(privateKey, "0x")
26 |
27 | if name == "" {
28 | name = generateName() + "-imported"
29 | for store.DoesNamedAccountExist(name) {
30 | name = generateName() + "-imported"
31 | }
32 | } else if store.DoesNamedAccountExist(name) {
33 | return "", fmt.Errorf("account %s already exists", name)
34 | }
35 |
36 | privateKeyBytes, err := hex.DecodeString(privateKey)
37 | if err != nil {
38 | return "", err
39 | }
40 | if len(privateKeyBytes) != common.Secp256k1PrivateKeyBytesLength {
41 | return "", common.ErrBadKeyLength
42 | }
43 |
44 | // btcec.PrivKeyFromBytes only returns a secret key and public key
45 | sk, _ := btcec.PrivKeyFromBytes(btcec.S256(), privateKeyBytes)
46 | oneAddress := address.ToBech32(crypto.PubkeyToAddress(sk.PublicKey))
47 |
48 | if store.FromAddress(oneAddress) != nil {
49 | return "", fmt.Errorf("address %s already exists", oneAddress)
50 | }
51 |
52 | ks := store.FromAccountName(name)
53 | _, err = ks.ImportECDSA(sk.ToECDSA(), passphrase)
54 | return name, err
55 | }
56 |
57 | func generateName() string {
58 | words := strings.Split(mnemonic.Generate(), " ")
59 | existingAccounts := mapset.NewSet()
60 | for a := range store.LocalAccounts() {
61 | existingAccounts.Add(a)
62 | }
63 | foundName := false
64 | acct := ""
65 | i := 0
66 | for {
67 | if foundName {
68 | break
69 | }
70 | if i == len(words)-1 {
71 | words = strings.Split(mnemonic.Generate(), " ")
72 | }
73 | candidate := words[i]
74 | if !existingAccounts.Contains(candidate) {
75 | foundName = true
76 | acct = candidate
77 | break
78 | }
79 | }
80 | return acct
81 | }
82 |
83 | func writeToFile(path string, data string) error {
84 | currDir, _ := os.Getwd()
85 | path, err := filepath.Abs(path)
86 | if err != nil {
87 | return err
88 | }
89 | os.MkdirAll(filepath.Dir(path), 0777)
90 | os.Chdir(filepath.Dir(path))
91 | file, err := os.Create(filepath.Base(path))
92 | if err != nil {
93 | return err
94 | }
95 | defer file.Close()
96 |
97 | _, err = io.WriteString(file, data)
98 | if err != nil {
99 | return err
100 | }
101 | os.Chdir(currDir)
102 | return file.Sync()
103 | }
104 |
105 | // ImportKeyStore imports a keystore along with a password
106 | func ImportKeyStore(keyPath, name, passphrase string) (string, error) {
107 | keyPath, err := filepath.Abs(keyPath)
108 | if err != nil {
109 | return "", err
110 | }
111 | keyJSON, readError := ioutil.ReadFile(keyPath)
112 | if readError != nil {
113 | return "", readError
114 | }
115 | if name == "" {
116 | name = generateName() + "-imported"
117 | for store.DoesNamedAccountExist(name) {
118 | name = generateName() + "-imported"
119 | }
120 | } else if store.DoesNamedAccountExist(name) {
121 | return "", fmt.Errorf("account %s already exists", name)
122 | }
123 | key, err := keystore.DecryptKey(keyJSON, passphrase)
124 | if err != nil {
125 | return "", err
126 | }
127 | b32 := address.ToBech32(key.Address)
128 | hasAddress := store.FromAddress(b32) != nil
129 | if hasAddress {
130 | return "", fmt.Errorf("address %s already exists in keystore", b32)
131 | }
132 | uDir, _ := homedir.Dir()
133 | newPath := filepath.Join(uDir, common.DefaultConfigDirName, common.DefaultConfigAccountAliasesDirName, name, filepath.Base(keyPath))
134 | err = writeToFile(newPath, string(keyJSON))
135 | if err != nil {
136 | return "", err
137 | }
138 | return name, nil
139 | }
140 |
--------------------------------------------------------------------------------
/cmd/subcommands/utility.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "math/big"
7 | "strings"
8 |
9 | bls_core "github.com/harmony-one/bls/ffi/go/bls"
10 | "github.com/harmony-one/go-sdk/pkg/address"
11 | "github.com/harmony-one/go-sdk/pkg/rpc"
12 | "github.com/harmony-one/harmony/crypto/bls"
13 | "github.com/spf13/cobra"
14 | )
15 |
16 | func init() {
17 | cmdUtilities := &cobra.Command{
18 | Use: "utility",
19 | Short: "common harmony blockchain utilities",
20 | RunE: func(cmd *cobra.Command, args []string) error {
21 | cmd.Help()
22 | return nil
23 | },
24 | }
25 |
26 | cmdUtilities.AddCommand(&cobra.Command{
27 | Use: "metadata",
28 | Short: "data includes network specific values",
29 | RunE: func(cmd *cobra.Command, args []string) error {
30 | noLatest = true
31 | return request(rpc.Method.GetNodeMetadata, []interface{}{})
32 | },
33 | })
34 |
35 | cmdMetrics := &cobra.Command{
36 | Use: "metrics",
37 | Short: "mostly in-memory fluctuating values",
38 | RunE: func(cmd *cobra.Command, args []string) error {
39 | cmd.Help()
40 | return nil
41 | },
42 | }
43 |
44 | cmdMetrics.AddCommand([]*cobra.Command{{
45 | Use: "pending-crosslinks",
46 | Short: "dump the pending crosslinks in memory of target node",
47 | RunE: func(cmd *cobra.Command, args []string) error {
48 | noLatest = true
49 | return request(rpc.Method.GetPendingCrosslinks, []interface{}{})
50 | },
51 | }, {
52 | Use: "pending-cx-receipts",
53 | Short: "dump the pending cross shard receipts in memory of target node",
54 | RunE: func(cmd *cobra.Command, args []string) error {
55 | noLatest = true
56 | return request(rpc.Method.GetPendingCXReceipts, []interface{}{})
57 | },
58 | },
59 | }...)
60 |
61 | cmdUtilities.AddCommand(cmdMetrics)
62 | cmdUtilities.AddCommand([]*cobra.Command{{
63 | Use: "bech32-to-addr",
64 | Args: cobra.ExactArgs(1),
65 | Short: "0x Address of a bech32 one-address",
66 | RunE: func(cmd *cobra.Command, args []string) error {
67 | addr, err := address.Bech32ToAddress(args[0])
68 | if err != nil {
69 | return err
70 | }
71 | fmt.Println(addr.Hex())
72 | return nil
73 | },
74 | }, {
75 | Use: "addr-to-bech32",
76 | Args: cobra.ExactArgs(1),
77 | Short: "bech32 one-address of an 0x address",
78 | RunE: func(cmd *cobra.Command, args []string) error {
79 | fmt.Println(address.ToBech32(address.Parse(args[0])))
80 | return nil
81 | },
82 | }, {
83 | Use: "committees",
84 | Short: "current and previous committees",
85 | RunE: func(cmd *cobra.Command, args []string) error {
86 | noLatest = true
87 | return request(rpc.Method.GetSuperCommmittees, []interface{}{})
88 | },
89 | }, {
90 | Use: "bad-blocks",
91 | Short: "bad blocks in memory of the target node",
92 | RunE: func(cmd *cobra.Command, args []string) error {
93 | noLatest = true
94 | return request(rpc.Method.GetCurrentBadBlocks, []interface{}{})
95 | },
96 | }, {
97 | Use: "shards",
98 | Short: "sharding structure and end points",
99 | RunE: func(cmd *cobra.Command, args []string) error {
100 | noLatest = true
101 | return request(rpc.Method.GetShardingStructure, []interface{}{})
102 | },
103 | }, {
104 | // Temp utility that should be removed once resharding implemented
105 | Use: "shard-for-bls",
106 | Args: cobra.ExactArgs(1),
107 | Short: "which shard this BLS key would be assigned to",
108 | RunE: func(cmd *cobra.Command, args []string) error {
109 | inputKey := strings.TrimPrefix(args[0], "0x")
110 | key := bls_core.PublicKey{}
111 | if err := key.DeserializeHexStr(inputKey); err != nil {
112 | return err
113 | }
114 | reply, err := rpc.Request(rpc.Method.GetShardingStructure, node, []interface{}{})
115 | if err != nil {
116 | return err
117 | }
118 | shardBig := len(reply["result"].([]interface{})) // assume the response is a JSON Array
119 | wrapper := bls.FromLibBLSPublicKeyUnsafe(&key)
120 | shardID := int(new(big.Int).Mod(wrapper.Big(), big.NewInt(int64(shardBig))).Int64())
121 | type t struct {
122 | ShardID int `json:"shard-id"`
123 | }
124 | result, err := json.Marshal(t{shardID})
125 | if err != nil {
126 | return err
127 | }
128 |
129 | fmt.Println(string(result))
130 | return nil
131 | },
132 | }, {
133 | Use: "last-cross-links",
134 | Short: "last crosslinks processed",
135 | RunE: func(cmd *cobra.Command, args []string) error {
136 | noLatest = true
137 | return request(rpc.Method.GetLastCrossLinks, []interface{}{})
138 | },
139 | }}...)
140 |
141 | RootCmd.AddCommand(cmdUtilities)
142 | }
143 |
--------------------------------------------------------------------------------
/pkg/store/local.go:
--------------------------------------------------------------------------------
1 | package store
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "os"
7 | "path"
8 | "time"
9 |
10 | "github.com/harmony-one/go-sdk/pkg/address"
11 | "github.com/harmony-one/go-sdk/pkg/common"
12 | c "github.com/harmony-one/go-sdk/pkg/common"
13 | "github.com/harmony-one/harmony/accounts"
14 | "github.com/harmony-one/harmony/accounts/keystore"
15 | "github.com/pkg/errors"
16 |
17 | homedir "github.com/mitchellh/go-homedir"
18 | )
19 |
20 | func init() {
21 | uDir, _ := homedir.Dir()
22 | hmyCLIDir := path.Join(uDir, common.DefaultConfigDirName, common.DefaultConfigAccountAliasesDirName)
23 | if _, err := os.Stat(hmyCLIDir); os.IsNotExist(err) {
24 | os.MkdirAll(hmyCLIDir, 0700)
25 | }
26 | }
27 |
28 | // LocalAccounts returns a slice of local account alias names
29 | func LocalAccounts() []string {
30 | uDir, _ := homedir.Dir()
31 | files, _ := ioutil.ReadDir(path.Join(
32 | uDir,
33 | common.DefaultConfigDirName,
34 | common.DefaultConfigAccountAliasesDirName,
35 | ))
36 | accounts := []string{}
37 | for _, node := range files {
38 | if node.IsDir() {
39 | accounts = append(accounts, path.Base(node.Name()))
40 | }
41 | }
42 | return accounts
43 | }
44 |
45 | var (
46 | describe = fmt.Sprintf("%-24s\t\t%23s\n", "NAME", "ADDRESS")
47 | NoUnlockBadPassphrase = errors.New("could not unlock wallet with given passphrase")
48 | )
49 |
50 | // DescribeLocalAccounts will display all the account alias name and their corresponding one address
51 | func DescribeLocalAccounts() {
52 | fmt.Println(describe)
53 | for _, name := range LocalAccounts() {
54 | ks := FromAccountName(name)
55 | allAccounts := ks.Accounts()
56 | for _, account := range allAccounts {
57 | fmt.Printf("%-48s\t%s\n", name, address.ToBech32(account.Address))
58 | }
59 | }
60 | }
61 |
62 | // DoesNamedAccountExist return true if the given string name is an alias account already define,
63 | // and return false otherwise
64 | func DoesNamedAccountExist(name string) bool {
65 | for _, account := range LocalAccounts() {
66 | if account == name {
67 | return true
68 | }
69 | }
70 | return false
71 | }
72 |
73 | // Returns one address for account name if exists
74 | func AddressFromAccountName(name string) (string, error) {
75 | ks := FromAccountName(name)
76 | // FIXME: Assume 1 account per keystore for now
77 | for _, account := range ks.Accounts() {
78 | return address.ToBech32(account.Address), nil
79 | }
80 | return "", fmt.Errorf("Keystore not found.")
81 | }
82 |
83 | // FromAddress will return nil if the bech32 string is not found in the imported accounts
84 | func FromAddress(bech32 string) *keystore.KeyStore {
85 | for _, name := range LocalAccounts() {
86 | ks := FromAccountName(name)
87 | allAccounts := ks.Accounts()
88 | for _, account := range allAccounts {
89 | if bech32 == address.ToBech32(account.Address) {
90 | return ks
91 | }
92 | }
93 | }
94 | return nil
95 | }
96 |
97 | func FromAccountName(name string) *keystore.KeyStore {
98 | uDir, _ := homedir.Dir()
99 | p := path.Join(uDir, c.DefaultConfigDirName, c.DefaultConfigAccountAliasesDirName, name)
100 | return common.KeyStoreForPath(p)
101 | }
102 |
103 | func DefaultLocation() string {
104 | uDir, _ := homedir.Dir()
105 | return path.Join(uDir, c.DefaultConfigDirName, c.DefaultConfigAccountAliasesDirName)
106 | }
107 |
108 | func UnlockedKeystore(from, passphrase string) (*keystore.KeyStore, *accounts.Account, error) {
109 | return UnlockedKeystoreTimeLimit(from, passphrase, 0)
110 | }
111 |
112 | func LockKeystore(from string) (*keystore.KeyStore, *accounts.Account, error) {
113 | sender := address.Parse(from)
114 | ks := FromAddress(address.ToBech32(sender))
115 | if ks == nil {
116 | return nil, nil, fmt.Errorf("could not open local keystore for %s", from)
117 | }
118 | account, lookupErr := ks.Find(accounts.Account{Address: sender})
119 | if lookupErr != nil {
120 | return nil, nil, fmt.Errorf("could not find %s in keystore", from)
121 | }
122 | if lockError := ks.Lock(account.Address); lockError != nil {
123 | return nil, nil, lockError
124 | }
125 | return ks, &account, nil
126 | }
127 |
128 | func UnlockedKeystoreTimeLimit(from, passphrase string, time time.Duration) (*keystore.KeyStore, *accounts.Account, error) {
129 | sender := address.Parse(from)
130 | ks := FromAddress(address.ToBech32(sender))
131 | if ks == nil {
132 | return nil, nil, fmt.Errorf("could not open local keystore for %s", from)
133 | }
134 | account, lookupErr := ks.Find(accounts.Account{Address: sender})
135 | if lookupErr != nil {
136 | return nil, nil, fmt.Errorf("could not find %s in keystore", from)
137 | }
138 | if unlockError := ks.TimedUnlock(account, passphrase, time); unlockError != nil {
139 | return nil, nil, errors.Wrap(NoUnlockBadPassphrase, unlockError.Error())
140 | }
141 | return ks, &account, nil
142 | }
143 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/harmony-one/go-sdk
2 |
3 | go 1.22.5
4 |
5 | require (
6 | github.com/btcsuite/btcd v0.22.1
7 | github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce
8 | github.com/cosmos/cosmos-sdk v0.37.0
9 | github.com/deckarep/golang-set v1.7.1
10 | github.com/dop251/goja v0.0.0-20210427212725-462d53687b0d
11 | github.com/ethereum/go-ethereum v1.9.23
12 | github.com/fatih/color v1.9.0
13 | github.com/harmony-one/bls v0.0.7-0.20191214005344-88c23f91a8a9
14 | github.com/harmony-one/harmony v1.10.2-0.20210123081216-6993b9ad0ca1
15 | github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356
16 | github.com/mattn/go-colorable v0.1.9
17 | github.com/mitchellh/go-homedir v1.1.0
18 | github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7
19 | github.com/pkg/errors v0.9.1
20 | github.com/spf13/cobra v0.0.5
21 | github.com/tyler-smith/go-bip39 v1.0.2
22 | github.com/valyala/fasthttp v1.2.0
23 | github.com/valyala/fastjson v1.6.3
24 | golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
25 | )
26 |
27 | require (
28 | github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
29 | github.com/VictoriaMetrics/fastcache v1.5.7 // indirect
30 | github.com/aristanetworks/goarista v0.0.0-20191023202215-f096da5361bb // indirect
31 | github.com/cespare/xxhash/v2 v2.1.1 // indirect
32 | github.com/cpuguy83/go-md2man v1.0.10 // indirect
33 | github.com/davecgh/go-spew v1.1.1 // indirect
34 | github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect
35 | github.com/edsrzf/mmap-go v1.0.0 // indirect
36 | github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa // indirect
37 | github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
38 | github.com/go-ole/go-ole v1.2.1 // indirect
39 | github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
40 | github.com/go-stack/stack v1.8.0 // indirect
41 | github.com/gogo/protobuf v1.3.1 // indirect
42 | github.com/golang/protobuf v1.4.3 // indirect
43 | github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26 // indirect
44 | github.com/google/uuid v1.1.2 // indirect
45 | github.com/gorilla/websocket v1.4.2 // indirect
46 | github.com/harmony-one/taggedrlp v0.1.4 // indirect
47 | github.com/hashicorp/golang-lru v0.5.4 // indirect
48 | github.com/huin/goupnp v1.0.0 // indirect
49 | github.com/inconshreveable/mousetrap v1.0.0 // indirect
50 | github.com/ipfs/go-cid v0.0.7 // indirect
51 | github.com/jackpal/go-nat-pmp v1.0.2 // indirect
52 | github.com/klauspost/compress v1.4.1 // indirect
53 | github.com/klauspost/cpuid v1.2.1 // indirect
54 | github.com/libp2p/go-buffer-pool v0.0.2 // indirect
55 | github.com/libp2p/go-libp2p-core v0.8.0 // indirect
56 | github.com/libp2p/go-openssl v0.0.7 // indirect
57 | github.com/mattn/go-isatty v0.0.14 // indirect
58 | github.com/mattn/go-runewidth v0.0.9 // indirect
59 | github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect
60 | github.com/minio/sha256-simd v0.1.1 // indirect
61 | github.com/mr-tron/base58 v1.2.0 // indirect
62 | github.com/multiformats/go-base32 v0.0.3 // indirect
63 | github.com/multiformats/go-base36 v0.1.0 // indirect
64 | github.com/multiformats/go-multiaddr v0.3.1 // indirect
65 | github.com/multiformats/go-multibase v0.0.3 // indirect
66 | github.com/multiformats/go-multihash v0.0.14 // indirect
67 | github.com/multiformats/go-varint v0.0.6 // indirect
68 | github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
69 | github.com/olekukonko/tablewriter v0.0.5 // indirect
70 | github.com/pborman/uuid v1.2.0 // indirect
71 | github.com/prometheus/tsdb v0.7.1 // indirect
72 | github.com/rjeczalik/notify v0.9.2 // indirect
73 | github.com/rs/cors v1.7.0 // indirect
74 | github.com/rs/zerolog v1.18.0 // indirect
75 | github.com/russross/blackfriday v1.5.2 // indirect
76 | github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
77 | github.com/spaolacci/murmur3 v1.1.0 // indirect
78 | github.com/spf13/pflag v1.0.5 // indirect
79 | github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 // indirect
80 | github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 // indirect
81 | github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect
82 | github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca // indirect
83 | github.com/valyala/bytebufferpool v1.0.0 // indirect
84 | github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 // indirect
85 | golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect
86 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
87 | golang.org/x/text v0.3.6 // indirect
88 | google.golang.org/protobuf v1.25.0 // indirect
89 | gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
90 | gopkg.in/yaml.v2 v2.4.0 // indirect
91 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
92 | )
93 |
94 | replace github.com/ethereum/go-ethereum => github.com/ethereum/go-ethereum v1.9.9
95 |
96 | replace github.com/fatih/color => github.com/fatih/color v1.13.0
97 |
--------------------------------------------------------------------------------
/cmd/subcommands/values.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/fatih/color"
7 | )
8 |
9 | const (
10 | hmyDocsDir = "hmy-docs"
11 | defaultNodeAddr = "http://localhost:9500"
12 | defaultRpcPrefix = "hmy"
13 | defaultMainnetEndpoint = "https://api.s0.t.hmny.io/"
14 | )
15 |
16 | var (
17 | g = color.New(color.FgGreen).SprintFunc()
18 | cookbookDoc = fmt.Sprintf(`
19 | Cookbook of Usage
20 |
21 | Note:
22 |
23 | 1) Every subcommand recognizes a '--help' flag
24 | 2) If a passphrase is used by a subcommand, one can enter their own passphrase interactively
25 | with the --passphrase option. Alternatively, one can pass their own passphrase via a file
26 | using the --passphrase-file option. If no passphrase option is selected, the default
27 | passphrase of '' is used.
28 | 3) These examples use Shard 0 of [NETWORK] as argument for --node
29 |
30 | Examples:
31 |
32 | %s
33 | ./hmy --node=[NODE] balances
34 |
35 | %s
36 | ./hmy --node=[NODE] blockchain transaction-by-hash
37 |
38 | %s
39 | ./hmy keys list
40 |
41 | %s
42 | ./hmy --node=[NODE] transfer \
43 | --from --to \
44 | --from-shard 0 --to-shard 1 --amount 200 --passphrase
45 |
46 | %s
47 | ./hmy --node=[NODE] transfer --file
48 | Check README for details on json file format.
49 |
50 | %s
51 | ./hmy --node=[NODE] blockchain transaction-receipt
52 |
53 | %s
54 | ./hmy keys recover-from-mnemonic --passphrase
55 |
56 | %s
57 | ./hmy keys import-ks
58 |
59 | %s
60 | ./hmy keys import-private-key
61 |
62 | %s
63 | ./hmy keys export-private-key --passphrase
64 |
65 | %s
66 | ./hmy keys generate-bls-key --bls-file-path
67 |
68 | %s
69 | ./hmy --node=[NODE] staking create-validator --amount 10 --validator-addr \
70 | --bls-pubkeys ,, \
71 | --identity foo --details bar --name baz --max-change-rate 0.1 --max-rate 0.1 --max-total-delegation 10 \
72 | --min-self-delegation 10 --rate 0.1 --security-contact Leo --website harmony.one --passphrase
73 |
74 | %s
75 | ./hmy --node=[NODE] staking edit-validator \
76 | --validator-addr --identity foo --details bar \
77 | --name baz --security-contact EK --website harmony.one \
78 | --min-self-delegation 0 --max-total-delegation 10 --rate 0.1\
79 | --add-bls-key --remove-bls-key --passphrase
80 |
81 | %s
82 | ./hmy --node=[NODE] staking delegate \
83 | --delegator-addr --validator-addr \
84 | --amount 10 --passphrase
85 |
86 | %s
87 | ./hmy --node=[NODE] staking undelegate \
88 | --delegator-addr --validator-addr \
89 | --amount 10 --passphrase
90 |
91 | %s
92 | ./hmy --node=[NODE] staking collect-rewards \
93 | --delegator-addr --passphrase
94 |
95 | %s
96 | ./hmy --node=[NODE] blockchain validator elected
97 |
98 | %s
99 | ./hmy --node=[NODE] blockchain utility-metrics
100 |
101 | %s
102 | ./hmy --node=[NODE] failures staking
103 |
104 | %s
105 | ./hmy --node=[NODE] utility shard-for-bls
106 |
107 | %s
108 | ./hmy governance vote-proposal --space=[harmony-mainnet.eth] \
109 | --proposal= --proposal-type=[single-choice] \
110 | --choice= --app=[APP] --key=
111 | PS: key must first use (hmy keys import-private-key) to import
112 |
113 | %s
114 | ./hmy command --net=testnet
115 | `,
116 | g("1. Check account balance on given chain"),
117 | g("2. Check sent transaction"),
118 | g("3. List local account keys"),
119 | g("4. Sending a transaction (waits 40 seconds for transaction confirmation)"),
120 | g("5. Sending a batch of transactions as dictated from a file (the `--dry-run` options still apply)"),
121 | g("6. Check a completed transaction receipt"),
122 | g("7. Import an account using the mnemonic. Prompts the user to give the mnemonic."),
123 | g("8. Import an existing keystore file"),
124 | g("9. Import a keystore file using a secp256k1 private key"),
125 | g("10. Export a keystore file's secp256k1 private key"),
126 | g("11. Generate a BLS key then encrypt and save the private key to the specified location."),
127 | g("12. Create a new validator with a list of BLS keys"),
128 | g("13. Edit an existing validator"),
129 | g("14. Delegate an amount to a validator"),
130 | g("15. Undelegate to a validator"),
131 | g("16. Collect block rewards as a delegator"),
132 | g("17. Check elected validators"),
133 | g("18. Get current staking utility metrics"),
134 | g("19. Check in-memory record of failed staking transactions"),
135 | g("20. Check which shard your BLS public key would be assigned to as a validator"),
136 | g("21. Vote on a governance proposal on https://snapshot.org"),
137 | g("22. Enter console"),
138 | )
139 | )
140 |
--------------------------------------------------------------------------------
/pkg/rpc/eth/methods.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "fmt"
5 |
6 | rpcCommon "github.com/harmony-one/go-sdk/pkg/rpc/common"
7 | )
8 |
9 | const (
10 | prefix = "eth"
11 | )
12 |
13 | // Method is a list of known RPC methods
14 | var Method = rpcCommon.RpcEnumList{
15 | GetShardingStructure: fmt.Sprintf("%s_getShardingStructure", prefix),
16 | GetNodeMetadata: fmt.Sprintf("%s_getNodeMetadata", prefix),
17 | GetLatestBlockHeader: fmt.Sprintf("%s_latestHeader", prefix),
18 | GetBlockByHash: fmt.Sprintf("%s_getBlockByHash", prefix),
19 | GetBlockByNumber: fmt.Sprintf("%s_getBlockByNumber", prefix),
20 | GetBlockTransactionCountByHash: fmt.Sprintf("%s_getBlockTransactionCountByHash", prefix),
21 | GetBlockTransactionCountByNumber: fmt.Sprintf("%s_getBlockTransactionCountByNumber", prefix),
22 | GetCode: fmt.Sprintf("%s_getCode", prefix),
23 | GetTransactionByBlockHashAndIndex: fmt.Sprintf("%s_getTransactionByBlockHashAndIndex", prefix),
24 | GetTransactionByBlockNumberAndIndex: fmt.Sprintf("%s_getTransactionByBlockNumberAndIndex", prefix),
25 | GetTransactionByHash: fmt.Sprintf("%s_getTransactionByHash", prefix),
26 | GetStakingTransactionByHash: fmt.Sprintf("%s_getStakingTransactionByHash", prefix),
27 | GetTransactionReceipt: fmt.Sprintf("%s_getTransactionReceipt", prefix),
28 | Syncing: fmt.Sprintf("%s_syncing", prefix),
29 | PeerCount: "net_peerCount",
30 | GetBalance: fmt.Sprintf("%s_getBalance", prefix),
31 | GetStorageAt: fmt.Sprintf("%s_getStorageAt", prefix),
32 | GetTransactionCount: fmt.Sprintf("%s_getTransactionCount", prefix),
33 | SendTransaction: fmt.Sprintf("%s_sendTransaction", prefix),
34 | SendRawTransaction: fmt.Sprintf("%s_sendRawTransaction", prefix),
35 | Subscribe: fmt.Sprintf("%s_subscribe", prefix),
36 | GetPastLogs: fmt.Sprintf("%s_getLogs", prefix),
37 | GetWork: fmt.Sprintf("%s_getWork", prefix),
38 | GetProof: fmt.Sprintf("%s_getProof", prefix),
39 | GetFilterChanges: fmt.Sprintf("%s_getFilterChanges", prefix),
40 | NewPendingTransactionFilter: fmt.Sprintf("%s_newPendingTransactionFilter", prefix),
41 | NewBlockFilter: fmt.Sprintf("%s_newBlockFilter", prefix),
42 | NewFilter: fmt.Sprintf("%s_newFilter", prefix),
43 | Call: fmt.Sprintf("%s_call", prefix),
44 | EstimateGas: fmt.Sprintf("%s_estimateGas", prefix),
45 | GasPrice: fmt.Sprintf("%s_gasPrice", prefix),
46 | BlockNumber: fmt.Sprintf("%s_blockNumber", prefix),
47 | UnSubscribe: fmt.Sprintf("%s_unsubscribe", prefix),
48 | NetVersion: "net_version",
49 | ProtocolVersion: fmt.Sprintf("%s_protocolVersion", prefix),
50 | SendRawStakingTransaction: fmt.Sprintf("%s_sendRawStakingTransaction", prefix),
51 | GetElectedValidatorAddresses: fmt.Sprintf("%s_getElectedValidatorAddresses", prefix),
52 | GetAllValidatorAddresses: fmt.Sprintf("%s_getAllValidatorAddresses", prefix),
53 | GetValidatorInformation: fmt.Sprintf("%s_getValidatorInformation", prefix),
54 | GetAllValidatorInformation: fmt.Sprintf("%s_getAllValidatorInformation", prefix),
55 | GetValidatorInformationByBlockNumber: fmt.Sprintf("%s_getValidatorInformationByBlockNumber", prefix),
56 | GetAllValidatorInformationByBlockNumber: fmt.Sprintf("%s_getAllValidatorInformationByBlockNumber", prefix),
57 | GetDelegationsByDelegator: fmt.Sprintf("%s_getDelegationsByDelegator", prefix),
58 | GetDelegationsByValidator: fmt.Sprintf("%s_getDelegationsByValidator", prefix),
59 | GetCurrentTransactionErrorSink: fmt.Sprintf("%s_getCurrentTransactionErrorSink", prefix),
60 | GetMedianRawStakeSnapshot: fmt.Sprintf("%s_getMedianRawStakeSnapshot", prefix),
61 | GetCurrentStakingErrorSink: fmt.Sprintf("%s_getCurrentStakingErrorSink", prefix),
62 | GetTransactionsHistory: fmt.Sprintf("%s_getTransactionsHistory", prefix),
63 | GetPendingTxnsInPool: fmt.Sprintf("%s_pendingTransactions", prefix),
64 | GetPendingCrosslinks: fmt.Sprintf("%s_getPendingCrossLinks", prefix),
65 | GetPendingCXReceipts: fmt.Sprintf("%s_getPendingCXReceipts", prefix),
66 | GetCurrentUtilityMetrics: fmt.Sprintf("%s_getCurrentUtilityMetrics", prefix),
67 | ResendCX: fmt.Sprintf("%s_resendCx", prefix),
68 | GetSuperCommmittees: fmt.Sprintf("%s_getSuperCommittees", prefix),
69 | GetCurrentBadBlocks: fmt.Sprintf("%s_getCurrentBadBlocks", prefix),
70 | GetShardID: fmt.Sprintf("%s_getShardID", prefix),
71 | GetLastCrossLinks: fmt.Sprintf("%s_getLastCrossLinks", prefix),
72 | GetLatestChainHeaders: fmt.Sprintf("%s_getLatestChainHeaders", prefix),
73 | }
74 |
--------------------------------------------------------------------------------
/pkg/rpc/v1/methods.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "fmt"
5 |
6 | rpcCommon "github.com/harmony-one/go-sdk/pkg/rpc/common"
7 | )
8 |
9 | const (
10 | prefix = "hmy"
11 | )
12 |
13 | // Method is a list of known RPC methods
14 | var Method = rpcCommon.RpcEnumList{
15 | GetShardingStructure: fmt.Sprintf("%s_getShardingStructure", prefix),
16 | GetNodeMetadata: fmt.Sprintf("%s_getNodeMetadata", prefix),
17 | GetLatestBlockHeader: fmt.Sprintf("%s_latestHeader", prefix),
18 | GetBlockByHash: fmt.Sprintf("%s_getBlockByHash", prefix),
19 | GetBlockByNumber: fmt.Sprintf("%s_getBlockByNumber", prefix),
20 | GetBlockTransactionCountByHash: fmt.Sprintf("%s_getBlockTransactionCountByHash", prefix),
21 | GetBlockTransactionCountByNumber: fmt.Sprintf("%s_getBlockTransactionCountByNumber", prefix),
22 | GetCode: fmt.Sprintf("%s_getCode", prefix),
23 | GetTransactionByBlockHashAndIndex: fmt.Sprintf("%s_getTransactionByBlockHashAndIndex", prefix),
24 | GetTransactionByBlockNumberAndIndex: fmt.Sprintf("%s_getTransactionByBlockNumberAndIndex", prefix),
25 | GetTransactionByHash: fmt.Sprintf("%s_getTransactionByHash", prefix),
26 | GetStakingTransactionByHash: fmt.Sprintf("%s_getStakingTransactionByHash", prefix),
27 | GetTransactionReceipt: fmt.Sprintf("%s_getTransactionReceipt", prefix),
28 | Syncing: fmt.Sprintf("%s_syncing", prefix),
29 | PeerCount: "net_peerCount",
30 | GetBalance: fmt.Sprintf("%s_getBalance", prefix),
31 | GetStorageAt: fmt.Sprintf("%s_getStorageAt", prefix),
32 | GetTransactionCount: fmt.Sprintf("%s_getTransactionCount", prefix),
33 | SendTransaction: fmt.Sprintf("%s_sendTransaction", prefix),
34 | SendRawTransaction: fmt.Sprintf("%s_sendRawTransaction", prefix),
35 | Subscribe: fmt.Sprintf("%s_subscribe", prefix),
36 | GetPastLogs: fmt.Sprintf("%s_getLogs", prefix),
37 | GetWork: fmt.Sprintf("%s_getWork", prefix),
38 | GetProof: fmt.Sprintf("%s_getProof", prefix),
39 | GetFilterChanges: fmt.Sprintf("%s_getFilterChanges", prefix),
40 | NewPendingTransactionFilter: fmt.Sprintf("%s_newPendingTransactionFilter", prefix),
41 | NewBlockFilter: fmt.Sprintf("%s_newBlockFilter", prefix),
42 | NewFilter: fmt.Sprintf("%s_newFilter", prefix),
43 | Call: fmt.Sprintf("%s_call", prefix),
44 | EstimateGas: fmt.Sprintf("%s_estimateGas", prefix),
45 | GasPrice: fmt.Sprintf("%s_gasPrice", prefix),
46 | BlockNumber: fmt.Sprintf("%s_blockNumber", prefix),
47 | UnSubscribe: fmt.Sprintf("%s_unsubscribe", prefix),
48 | NetVersion: "net_version",
49 | ProtocolVersion: fmt.Sprintf("%s_protocolVersion", prefix),
50 | SendRawStakingTransaction: fmt.Sprintf("%s_sendRawStakingTransaction", prefix),
51 | GetElectedValidatorAddresses: fmt.Sprintf("%s_getElectedValidatorAddresses", prefix),
52 | GetAllValidatorAddresses: fmt.Sprintf("%s_getAllValidatorAddresses", prefix),
53 | GetValidatorInformation: fmt.Sprintf("%s_getValidatorInformation", prefix),
54 | GetAllValidatorInformation: fmt.Sprintf("%s_getAllValidatorInformation", prefix),
55 | GetValidatorInformationByBlockNumber: fmt.Sprintf("%s_getValidatorInformationByBlockNumber", prefix),
56 | GetAllValidatorInformationByBlockNumber: fmt.Sprintf("%s_getAllValidatorInformationByBlockNumber", prefix),
57 | GetDelegationsByDelegator: fmt.Sprintf("%s_getDelegationsByDelegator", prefix),
58 | GetDelegationsByValidator: fmt.Sprintf("%s_getDelegationsByValidator", prefix),
59 | GetCurrentTransactionErrorSink: fmt.Sprintf("%s_getCurrentTransactionErrorSink", prefix),
60 | GetMedianRawStakeSnapshot: fmt.Sprintf("%s_getMedianRawStakeSnapshot", prefix),
61 | GetCurrentStakingErrorSink: fmt.Sprintf("%s_getCurrentStakingErrorSink", prefix),
62 | GetTransactionsHistory: fmt.Sprintf("%s_getTransactionsHistory", prefix),
63 | GetPendingTxnsInPool: fmt.Sprintf("%s_pendingTransactions", prefix),
64 | GetPendingCrosslinks: fmt.Sprintf("%s_getPendingCrossLinks", prefix),
65 | GetPendingCXReceipts: fmt.Sprintf("%s_getPendingCXReceipts", prefix),
66 | GetCurrentUtilityMetrics: fmt.Sprintf("%s_getCurrentUtilityMetrics", prefix),
67 | ResendCX: fmt.Sprintf("%s_resendCx", prefix),
68 | GetSuperCommmittees: fmt.Sprintf("%s_getSuperCommittees", prefix),
69 | GetCurrentBadBlocks: fmt.Sprintf("%s_getCurrentBadBlocks", prefix),
70 | GetShardID: fmt.Sprintf("%s_getShardID", prefix),
71 | GetLastCrossLinks: fmt.Sprintf("%s_getLastCrossLinks", prefix),
72 | GetLatestChainHeaders: fmt.Sprintf("%s_getLatestChainHeaders", prefix),
73 | }
74 |
--------------------------------------------------------------------------------
/pkg/ledger/hw_wallet.go:
--------------------------------------------------------------------------------
1 | package ledger
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "log"
7 | "math/big"
8 | "os"
9 | "sync"
10 |
11 | "github.com/pkg/errors"
12 | "golang.org/x/crypto/sha3"
13 |
14 | "github.com/ethereum/go-ethereum/crypto"
15 | "github.com/ethereum/go-ethereum/rlp"
16 | "github.com/harmony-one/go-sdk/pkg/address"
17 | "github.com/harmony-one/harmony/core/types"
18 | staking "github.com/harmony-one/harmony/staking/types"
19 | )
20 |
21 | var (
22 | nanos *NanoS //singleton
23 | once sync.Once
24 | )
25 |
26 | func getLedger() *NanoS {
27 | once.Do(func() {
28 | var err error
29 | nanos, err = OpenNanoS()
30 | if err != nil {
31 | log.Fatalln("Couldn't open device:", err)
32 | os.Exit(-1)
33 | }
34 | })
35 |
36 | return nanos
37 | }
38 |
39 | //ProcessAddressCommand list the address associated with Ledger Nano S
40 | func GetAddress() string {
41 | n := getLedger()
42 | oneAddr, err := n.GetAddress()
43 | if err != nil {
44 | log.Fatalln("Couldn't get one address:", err)
45 | os.Exit(-1)
46 | }
47 |
48 | return oneAddr
49 | }
50 |
51 | //ProcessAddressCommand list the address associated with Ledger Nano S
52 | func ProcessAddressCommand() {
53 | n := getLedger()
54 | oneAddr, err := n.GetAddress()
55 | if err != nil {
56 | log.Fatalln("Couldn't get one address:", err)
57 | os.Exit(-1)
58 | }
59 |
60 | fmt.Printf("%-24s\t\t%23s\n", "NAME", "ADDRESS")
61 | fmt.Printf("%-48s\t%s\n", "Ledger Nano S", oneAddr)
62 | }
63 |
64 | // SignTx signs the given transaction with the requested account.
65 | func SignTx(tx *types.Transaction, chainID *big.Int) ([]byte, string, error) {
66 | var rlpEncodedTx []byte
67 |
68 | // Depending on the presence of the chain ID, sign with EIP155 or frontier
69 | if chainID != nil {
70 | rlpEncodedTx, _ = rlp.EncodeToBytes(
71 | []interface{}{
72 | tx.Nonce(),
73 | tx.GasPrice(),
74 | tx.GasLimit(),
75 | tx.ShardID(),
76 | tx.ToShardID(),
77 | tx.To(),
78 | tx.Value(),
79 | tx.Data(),
80 | chainID, uint(0), uint(0),
81 | })
82 | } else {
83 | rlpEncodedTx, _ = rlp.EncodeToBytes(
84 | []interface{}{
85 | tx.Nonce(),
86 | tx.GasPrice(),
87 | tx.GasLimit(),
88 | tx.ShardID(),
89 | tx.ToShardID(),
90 | tx.To(),
91 | tx.Value(),
92 | tx.Data(),
93 | })
94 | }
95 |
96 | n := getLedger()
97 | sig, err := n.SignTxn(rlpEncodedTx)
98 | if err != nil {
99 | log.Println("Couldn't sign transaction, error:", err)
100 | return nil, "", err
101 | }
102 |
103 | var hashBytes [32]byte
104 | hw := sha3.NewLegacyKeccak256()
105 | hw.Write(rlpEncodedTx[:])
106 | hw.Sum(hashBytes[:0])
107 |
108 | pubkey, err := crypto.Ecrecover(hashBytes[:], sig[:])
109 | if err != nil {
110 | log.Println("Ecrecover failed :", err)
111 | return nil, "", err
112 | }
113 |
114 | if len(pubkey) == 0 || pubkey[0] != 4 {
115 | log.Println("invalid public key")
116 | return nil, "", err
117 | }
118 |
119 | pubBytes := crypto.Keccak256(pubkey[1:65])[12:]
120 | signerAddr, _ := address.ConvertAndEncode("one", pubBytes)
121 |
122 | var r, s, v *big.Int
123 | if chainID != nil {
124 | r, s, v, err = eip155SignerSignatureValues(chainID, sig[:])
125 | } else {
126 | r, s, v, err = frontierSignatureValues(sig[:])
127 | }
128 |
129 | if err != nil {
130 | log.Println(err)
131 | return nil, "", err
132 | }
133 |
134 | // Depending on the presence of the chain ID, sign with EIP155 or frontier
135 | rawTx, err := rlp.EncodeToBytes(
136 | []interface{}{
137 | tx.Nonce(),
138 | tx.GasPrice(),
139 | tx.GasLimit(),
140 | tx.ShardID(),
141 | tx.ToShardID(),
142 | tx.To(),
143 | tx.Value(),
144 | tx.Data(),
145 | v,
146 | r,
147 | s,
148 | })
149 |
150 | return rawTx, signerAddr, err
151 | }
152 |
153 | func frontierSignatureValues(sig []byte) (r, s, v *big.Int, err error) {
154 | if len(sig) != 65 {
155 | return nil, nil, nil, errors.New("get signature with wrong size from ledger nano")
156 | }
157 | r = new(big.Int).SetBytes(sig[:32])
158 | s = new(big.Int).SetBytes(sig[32:64])
159 | v = new(big.Int).SetBytes([]byte{sig[64] + 27})
160 | return r, s, v, nil
161 | }
162 |
163 | func eip155SignerSignatureValues(chainID *big.Int, sig []byte) (R, S, V *big.Int, err error) {
164 | R, S, V, err = frontierSignatureValues(sig)
165 | if err != nil {
166 | return nil, nil, nil, err
167 | }
168 |
169 | chainIDMul := new(big.Int).Mul(chainID, big.NewInt(2))
170 | if chainID.Sign() != 0 {
171 | V = big.NewInt(int64(sig[64] + 35))
172 | V.Add(V, chainIDMul)
173 | }
174 | return R, S, V, nil
175 | }
176 |
177 | // SignTx signs the given transaction with ledger.
178 | func SignStakingTx(tx *staking.StakingTransaction, chainID *big.Int) (*staking.StakingTransaction, string, error) {
179 | //get the RLP encoding of raw staking with R,S,V = 0
180 | w := &bytes.Buffer{}
181 | err := tx.EncodeRLP(w)
182 | if err != nil {
183 | return nil, "", err
184 | }
185 | rlpEncodedTx := w.Bytes()
186 |
187 | //get the RLP encoding of chain data
188 | chainData, _ := rlp.EncodeToBytes([]interface{}{
189 | chainID, uint(0), uint(0),
190 | })
191 |
192 | //replace R,S,V with RLP encoded (chainID, 0, 0)
193 | rlpEncodedTx = append(rlpEncodedTx[0:len(rlpEncodedTx)-3], chainData[1:]...)
194 |
195 | //send the RLP encoded staking tx to ledger
196 | n := getLedger()
197 | sig, err := n.SignStaking(rlpEncodedTx)
198 | if err != nil {
199 | log.Println("Couldn't sign staking transaction, error:", err)
200 | return nil, "", err
201 | }
202 |
203 | var hashBytes [32]byte
204 | hw := sha3.NewLegacyKeccak256()
205 | hw.Write(rlpEncodedTx[:])
206 | hw.Sum(hashBytes[:0])
207 |
208 | pubkey, err := crypto.Ecrecover(hashBytes[:], sig[:])
209 | if err != nil {
210 | log.Println("Ecrecover failed :", err)
211 | return nil, "", err
212 | }
213 |
214 | if len(pubkey) == 0 || pubkey[0] != 4 {
215 | log.Println("invalid public key")
216 | return nil, "", err
217 | }
218 |
219 | pubBytes := crypto.Keccak256(pubkey[1:65])[12:]
220 | signerAddr, _ := address.ConvertAndEncode("one", pubBytes)
221 |
222 | // WithSignature returns a new transaction with the given signature.
223 | rawTx, err := tx.WithSignature(staking.NewEIP155Signer(chainID), sig[:])
224 | return rawTx, signerAddr, err
225 | }
226 |
--------------------------------------------------------------------------------
/pkg/governance/eip712.go:
--------------------------------------------------------------------------------
1 | package governance
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "fmt"
7 | "math/big"
8 | "strings"
9 |
10 | "github.com/ethereum/go-ethereum/common/hexutil"
11 | "github.com/ethereum/go-ethereum/common/math"
12 | "github.com/ethereum/go-ethereum/crypto"
13 | "github.com/ethereum/go-ethereum/signer/core"
14 | "github.com/pkg/errors"
15 | )
16 |
17 | // This embedded type was created to override the EncodeData function
18 | // and remove the validation for a mandatory chain id
19 | type TypedData struct {
20 | core.TypedData
21 | }
22 |
23 | // dataMismatchError generates an error for a mismatch between
24 | // the provided type and data
25 | func dataMismatchError(encType string, encValue interface{}) error {
26 | return fmt.Errorf("provided data '%v' doesn't match type '%s'", encValue, encType)
27 | }
28 |
29 | // EncodeData generates the following encoding:
30 | // `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)`
31 | //
32 | // each encoded member is 32-byte long
33 | // This method overridden here to remove the validation for mandatory chain id
34 | func (typedData *TypedData) EncodeData(primaryType string, data map[string]interface{}, depth int) (hexutil.Bytes, error) {
35 | // if err := typedData.validate(); err != nil {
36 | // return nil, err
37 | // }
38 |
39 | buffer := bytes.Buffer{}
40 |
41 | // Verify extra data
42 | if len(typedData.Types[primaryType]) < len(data) {
43 | return nil, errors.New("there is extra data provided in the message")
44 | }
45 |
46 | // Add typehash
47 | buffer.Write(typedData.TypeHash(primaryType))
48 |
49 | // Add field contents. Structs and arrays have special handlers.
50 | for _, field := range typedData.Types[primaryType] {
51 | encType := field.Type
52 | encValue := data[field.Name]
53 | if encType[len(encType)-1:] == "]" {
54 | arrayValue, ok := encValue.([]interface{})
55 | if !ok {
56 | return nil, dataMismatchError(encType, encValue)
57 | }
58 |
59 | arrayBuffer := bytes.Buffer{}
60 | parsedType := strings.Split(encType, "[")[0]
61 | for _, item := range arrayValue {
62 | if typedData.Types[parsedType] != nil {
63 | mapValue, ok := item.(map[string]interface{})
64 | if !ok {
65 | return nil, dataMismatchError(parsedType, item)
66 | }
67 | encodedData, err := typedData.EncodeData(parsedType, mapValue, depth+1)
68 | if err != nil {
69 | return nil, err
70 | }
71 | arrayBuffer.Write(encodedData)
72 | } else {
73 | bytesValue, err := typedData.EncodePrimitiveValue(parsedType, item, depth)
74 | if err != nil {
75 | return nil, err
76 | }
77 | arrayBuffer.Write(bytesValue)
78 | }
79 | }
80 |
81 | buffer.Write(crypto.Keccak256(arrayBuffer.Bytes()))
82 | } else if typedData.Types[field.Type] != nil {
83 | mapValue, ok := encValue.(map[string]interface{})
84 | if !ok {
85 | return nil, dataMismatchError(encType, encValue)
86 | }
87 | encodedData, err := typedData.EncodeData(field.Type, mapValue, depth+1)
88 | if err != nil {
89 | return nil, err
90 | }
91 | buffer.Write(crypto.Keccak256(encodedData))
92 | } else {
93 | byteValue, err := typedData.EncodePrimitiveValue(encType, encValue, depth)
94 | if err != nil {
95 | return nil, err
96 | }
97 | buffer.Write(byteValue)
98 | }
99 | }
100 | return buffer.Bytes(), nil
101 | }
102 |
103 | type TypedDataMessage = map[string]interface{}
104 |
105 | // HashStruct generates a keccak256 hash of the encoding of the provided data
106 | // This method overridden here to allow calling the overriden EncodeData above
107 | func (typedData *TypedData) HashStruct(primaryType string, data TypedDataMessage) (hexutil.Bytes, error) {
108 | encodedData, err := typedData.EncodeData(primaryType, data, 1)
109 | if err != nil {
110 | return nil, err
111 | }
112 | return crypto.Keccak256(encodedData), nil
113 | }
114 |
115 | func (typedData *TypedData) String() (string, error) {
116 | type domain struct {
117 | Name string `json:"name"`
118 | Version string `json:"version"`
119 | }
120 | // this data structure created to remove unused fields
121 | // for example, domain type is not sent in post request
122 | // and neither are the blank fields in the domain
123 | type data struct {
124 | Domain domain `json:"domain"`
125 | Types core.Types `json:"types"`
126 | Message core.TypedDataMessage `json:"message"`
127 | }
128 | var ts uint64
129 | var err error
130 | if ts, err = toUint64(typedData.Message["timestamp"]); err != nil {
131 | // should not happen
132 | return "", errors.Wrapf(err, "timestamp")
133 | }
134 | formatted := data{
135 | Domain: domain{
136 | Name: typedData.Domain.Name,
137 | Version: typedData.Domain.Version,
138 | },
139 | Types: core.Types{
140 | typedData.PrimaryType: typedData.Types[typedData.PrimaryType],
141 | },
142 | Message: core.TypedDataMessage{
143 | "space": typedData.Message["space"],
144 | "proposal": typedData.Message["proposal"],
145 | "choice": typedData.Message["choice"],
146 | "app": typedData.Message["app"],
147 | "reason": typedData.Message["reason"],
148 | // this conversion is required to stop snapshot
149 | // from complaining about `wrong envelope format`
150 | "timestamp": ts,
151 | "from": typedData.Message["from"],
152 | },
153 | }
154 | // same comment as above
155 | if typedData.Types["Vote"][4].Type == "uint32" {
156 | if choice, err := toUint64(typedData.Message["choice"]); err != nil {
157 | return "", errors.Wrapf(err, "choice")
158 | } else {
159 | formatted.Message["choice"] = choice
160 | }
161 | // prevent hex choice interpretation
162 | } else if typedData.Types["Vote"][4].Type == "uint32[]" {
163 | arr := typedData.Message["choice"].([]interface{})
164 | res := make([]uint64, len(arr))
165 | for i, a := range arr {
166 | if c, err := toUint64(a); err != nil {
167 | return "", errors.Wrapf(err, "choice member %d", i)
168 | } else {
169 | res[i] = c
170 | }
171 | }
172 | formatted.Message["choice"] = res
173 | }
174 | message, err := json.Marshal(formatted)
175 | if err != nil {
176 | return "", err
177 | } else {
178 | return string(message), nil
179 | }
180 | }
181 |
182 | func toUint64(x interface{}) (uint64, error) {
183 | y, ok := x.(*math.HexOrDecimal256)
184 | if !ok {
185 | return 0, errors.New(
186 | fmt.Sprintf("%+v is not a *math.HexOrDecimal256", x),
187 | )
188 | }
189 | z := (*big.Int)(y)
190 | return z.Uint64(), nil
191 | }
--------------------------------------------------------------------------------
/cmd/subcommands/blockchain.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/harmony-one/go-sdk/pkg/common"
7 | "github.com/harmony-one/go-sdk/pkg/rpc"
8 | "github.com/spf13/cobra"
9 | )
10 |
11 | var (
12 | addr oneAddress
13 | size int64
14 | )
15 |
16 | func init() {
17 | cmdValidator := &cobra.Command{
18 | Use: "validator",
19 | Short: "information about validators",
20 | Long: `
21 | Look up information about validator information
22 | `,
23 | RunE: func(cmd *cobra.Command, args []string) error {
24 | cmd.Help()
25 | return nil
26 | },
27 | }
28 |
29 | cmdDelegation := &cobra.Command{
30 | Use: "delegation",
31 | Short: "information about delegations",
32 | Long: `
33 | Look up information about delegation
34 | `,
35 | RunE: func(cmd *cobra.Command, args []string) error {
36 | cmd.Help()
37 | return nil
38 | },
39 | }
40 |
41 | cmdBlockchain := &cobra.Command{
42 | Use: "blockchain",
43 | Short: "Interact with the Harmony.one Blockchain",
44 | Long: `
45 | Query Harmony's blockchain for completed transaction, historic records
46 | `,
47 | RunE: func(cmd *cobra.Command, args []string) error {
48 | cmd.Help()
49 | return nil
50 | },
51 | }
52 |
53 | accountHistorySubCmd := &cobra.Command{
54 | Use: "account-history",
55 | Short: "Get history of all transactions for given account",
56 | Long: `
57 | High level information about each transaction for given account
58 | `,
59 | Args: cobra.ExactArgs(1),
60 | PreRunE: validateAddress,
61 | RunE: func(cmd *cobra.Command, args []string) error {
62 | type historyParams struct {
63 | Address string `json:"address"`
64 | PageIndex int64 `json:"pageIndex"`
65 | PageSize int64 `json:"pageSize"`
66 | FullTx bool `json:"fullTx"`
67 | TxType string `json:"txType"`
68 | Order string `json:"order"`
69 | }
70 | noLatest = true
71 | params := historyParams{args[0], 0, size, true, "", ""}
72 | return request(rpc.Method.GetTransactionsHistory, []interface{}{params})
73 | },
74 | }
75 |
76 | accountHistorySubCmd.Flags().Int64Var(&size, "max-tx", 1000, "max number of transactions to list")
77 |
78 | subCommands := []*cobra.Command{{
79 | Use: "block-by-number",
80 | Short: "Get a harmony blockchain block by block number",
81 | Args: cobra.ExactArgs(1),
82 | RunE: func(cmd *cobra.Command, args []string) error {
83 | noLatest = true
84 | return request(rpc.Method.GetBlockByNumber, []interface{}{args[0], true})
85 | },
86 | }, {
87 | Use: "known-chains",
88 | Short: "Print out the known chain-ids",
89 | RunE: func(cmd *cobra.Command, args []string) error {
90 | fmt.Println(common.Chain.String())
91 | return nil
92 | },
93 | }, {
94 | Use: "protocol-version",
95 | Short: "The version of the Harmony Protocol",
96 | Long: `
97 | Query Harmony's blockchain for high level metrics, queries
98 | `,
99 | RunE: func(cmd *cobra.Command, args []string) error {
100 | return request(rpc.Method.ProtocolVersion, []interface{}{})
101 | },
102 | }, {
103 | Use: "transaction-by-hash",
104 | Short: "Get transaction by hash",
105 | Args: cobra.ExactArgs(1),
106 | Long: `
107 | Inputs of a transaction and r, s, v value of transaction
108 | `,
109 | RunE: func(cmd *cobra.Command, args []string) error {
110 | noLatest = true
111 | return request(rpc.Method.GetTransactionByHash, []interface{}{args[0]})
112 | },
113 | }, {
114 | Use: "staking-transaction-by-hash",
115 | Short: "Get staking transaction by hash",
116 | Args: cobra.ExactArgs(1),
117 | Long: `
118 | Inputs of a transaction and r, s, v value of transaction
119 | `,
120 | RunE: func(cmd *cobra.Command, args []string) error {
121 | noLatest = true
122 | return request(rpc.Method.GetStakingTransactionByHash, []interface{}{args[0]})
123 | },
124 | }, {
125 | Use: "transaction-receipt",
126 | Short: "Get information about a finalized transaction",
127 | Args: cobra.ExactArgs(1),
128 | Long: `
129 | High level information about transaction, like blockNumber, blockHash
130 | `,
131 | RunE: func(cmd *cobra.Command, args []string) error {
132 | noLatest = true
133 | return request(rpc.Method.GetTransactionReceipt, []interface{}{args[0]})
134 | },
135 | }, {
136 | Use: "median-stake",
137 | Short: "median stake of top 320 validators with delegations applied stake (pre-epos processing)",
138 | RunE: func(cmd *cobra.Command, args []string) error {
139 | noLatest = true
140 | return request(rpc.Method.GetMedianRawStakeSnapshot, []interface{}{})
141 | },
142 | }, {
143 | Use: "current-nonce",
144 | Short: "Current nonce of an account",
145 | Args: cobra.ExactArgs(1),
146 | Long: `Current nonce number of a one-address`,
147 | PreRunE: validateAddress,
148 | RunE: func(cmd *cobra.Command, args []string) error {
149 | return request(rpc.Method.GetTransactionCount, []interface{}{addr.address})
150 | },
151 | }, {
152 | Use: "pool",
153 | Short: "Dump a node's transaction pool",
154 | RunE: func(cmd *cobra.Command, args []string) error {
155 | noLatest = true
156 | return request(rpc.Method.GetPendingTxnsInPool, []interface{}{})
157 | },
158 | }, {
159 | Use: "latest-header",
160 | Short: "Get the latest header",
161 | RunE: func(cmd *cobra.Command, args []string) error {
162 | noLatest = true
163 | return request(rpc.Method.GetLatestBlockHeader, []interface{}{})
164 | },
165 | }, {
166 | Use: "latest-headers",
167 | Short: "Get the latest chain headers",
168 | RunE: func(cmd *cobra.Command, args []string) error {
169 | noLatest = true
170 | return request(rpc.Method.GetLatestChainHeaders, []interface{}{})
171 | },
172 | }, {
173 | Use: "resend-cx",
174 | Short: "Re-play a cross shard transaction",
175 | Args: cobra.ExactArgs(1),
176 | RunE: func(cmd *cobra.Command, args []string) error {
177 | noLatest = true
178 | return request(rpc.Method.ResendCX, []interface{}{args[0]})
179 | },
180 | }, {
181 | Use: "utility-metrics",
182 | Short: "Current utility metrics",
183 | Long: `Current staking utility metrics`,
184 | RunE: func(cmd *cobra.Command, args []string) error {
185 | noLatest = true
186 | return request(rpc.Method.GetCurrentUtilityMetrics, []interface{}{})
187 | },
188 | },
189 | accountHistorySubCmd,
190 | }
191 |
192 | cmdBlockchain.AddCommand(cmdValidator)
193 | cmdBlockchain.AddCommand(cmdDelegation)
194 | cmdValidator.AddCommand(validatorSubCmds[:]...)
195 | cmdDelegation.AddCommand(delegationSubCmds[:]...)
196 | cmdBlockchain.AddCommand(subCommands[:]...)
197 | RootCmd.AddCommand(cmdBlockchain)
198 | }
199 |
--------------------------------------------------------------------------------
/pkg/governance/types.go:
--------------------------------------------------------------------------------
1 | package governance
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "strconv"
7 | "strings"
8 | "time"
9 |
10 | "github.com/ethereum/go-ethereum/common/hexutil"
11 | "github.com/ethereum/go-ethereum/common/math"
12 | eip712 "github.com/ethereum/go-ethereum/signer/core"
13 | "github.com/pkg/errors"
14 | )
15 |
16 | var (
17 | voteToNumberMapping = map[string]int64{
18 | "for": 1,
19 | "against": 2,
20 | "abstain": 3,
21 | }
22 | )
23 |
24 | type Vote struct {
25 | From string // --key
26 | Space string // --space
27 | Proposal string // --proposal
28 | ProposalType string // --proposal-type
29 | Choice string // --choice
30 | // Privacy string // --privacy
31 | App string // --app
32 | Reason string // --reason
33 | Timestamp int64 // not exposed to the end user
34 | }
35 |
36 | func (v *Vote) ToEIP712() (*TypedData, error) {
37 | // common types regardless of parameters
38 | // key `app` appended later because order matters
39 | myType := []eip712.Type{
40 | {
41 | Name: "from",
42 | Type: "address",
43 | },
44 | {
45 | Name: "space",
46 | Type: "string",
47 | },
48 | {
49 | Name: "timestamp",
50 | Type: "uint64",
51 | },
52 | }
53 |
54 | var proposal interface{}
55 | isHex := strings.HasPrefix(v.Proposal, "0x")
56 | if isHex {
57 | myType = append(myType, eip712.Type{
58 | Name: "proposal",
59 | Type: "bytes32",
60 | })
61 | if proposalBytes, err := hexutil.Decode(v.Proposal); err != nil {
62 | return nil, errors.Wrapf(
63 | err, "invalid proposal hash %s", v.Proposal,
64 | )
65 | } else {
66 | // EncodePrimitiveValue accepts only hexutil.Bytes not []byte
67 | proposal = hexutil.Bytes(proposalBytes)
68 | }
69 | } else {
70 | myType = append(myType, eip712.Type{
71 | Name: "proposal",
72 | Type: "string",
73 | })
74 | proposal = v.Proposal
75 | }
76 |
77 | // vote type, vote choice and vote privacy (TODO)
78 | // choice needs to be converted into its native format for envelope
79 | var choice interface{}
80 | // The space between [1, 2, 3] does not matter since we parse it
81 | // hmy governance vote-proposal \
82 | // --space harmony-mainnet.eth \
83 | // --proposal 0xTruncated \
84 | // --proposal-type {"approval","ranked-choice"} \
85 | // --choice "[1, 2, 3]" \
86 | // --key
87 | if v.ProposalType == "approval" || v.ProposalType == "ranked-choice" {
88 | myType = append(myType, eip712.Type{
89 | Name: "choice",
90 | Type: "uint32[]",
91 | })
92 | var is []int64
93 | if err := json.Unmarshal([]byte(v.Choice), &is); err == nil {
94 | local := make([]interface{}, len(is))
95 | for i := range is {
96 | local[i] = math.NewHexOrDecimal256(is[i])
97 | }
98 | choice = local
99 | } else {
100 | return nil, errors.Wrapf(err,
101 | "unexpected value of choice %s (expected uint32[])", choice,
102 | )
103 | }
104 | // The space between --choice {value} does not matter to snapshot.org
105 | // But for comparing with the snapshot-js library, remove it
106 | // hmy governance vote-proposal \
107 | // --space harmony-mainnet.eth \
108 | // --proposal 0xTruncated \
109 | // # either quadratic or weighted
110 | // --proposal-type {"quadratic","weighted"} \
111 | // # 20, 20, 40 of my vote (total 80) goes to 1, 2, 3 - note the single / double quotes
112 | // --choice '{"1":20,"2":20,"3":40}' \
113 | // --key
114 | } else if v.ProposalType == "quadratic" || v.ProposalType == "weighted" {
115 | myType = append(myType, eip712.Type{
116 | Name: "choice",
117 | Type: "string",
118 | })
119 | choice = v.Choice
120 | // TODO Untested
121 | // hmy governance vote-proposal \
122 | // --space harmony-mainnet.eth \
123 | // --proposal 0xTruncated \
124 | // --proposal-type ANY \
125 | // --choice "unknown-format" \
126 | // --key
127 | // --privacy shutter
128 | // } else if v.Privacy == "shutter" {
129 | // myType = append(myType, eip712.Type{
130 | // Name: "choice",
131 | // Type: "string",
132 | // })
133 | // choice = v.Choice
134 | // hmy governance vote-proposal \
135 | // --space harmony-mainnet.eth \
136 | // --proposal 0xTruncated \
137 | // --proposal-type single-choice \
138 | // --choice 1 \
139 | // --key
140 | } else if v.ProposalType == "single-choice" {
141 | myType = append(myType, eip712.Type{
142 | Name: "choice",
143 | Type: "uint32",
144 | })
145 | if x, err := strconv.Atoi(v.Choice); err != nil {
146 | return nil, errors.Wrapf(err,
147 | "unexpected value of choice %s (expected uint32)", choice,
148 | )
149 | } else {
150 | choice = math.NewHexOrDecimal256(int64(x))
151 | }
152 | // hmy governance vote-proposal \
153 | // --space harmony-mainnet.eth \
154 | // --proposal 0xTruncated \
155 | // --proposal-type basic \
156 | // # any character case works
157 | // --choice {aBstAin/agAiNst/for} \
158 | // --key
159 | } else if v.ProposalType == "basic" {
160 | myType = append(myType, eip712.Type{
161 | Name: "choice",
162 | Type: "uint32",
163 | })
164 | if number, ok := voteToNumberMapping[strings.ToLower(v.Choice)]; ok {
165 | choice = math.NewHexOrDecimal256(number)
166 | } else {
167 | return nil, errors.New(
168 | fmt.Sprintf(
169 | "unknown basic choice %s",
170 | v.Choice,
171 | ),
172 | )
173 | }
174 | } else {
175 | return nil, errors.New(
176 | fmt.Sprintf(
177 | "unknown proposal type %s",
178 | v.ProposalType,
179 | ),
180 | )
181 | }
182 |
183 | // order matters so these are added last
184 | myType = append(myType, eip712.Type{
185 | Name: "reason",
186 | Type: "string",
187 | })
188 | myType = append(myType, eip712.Type{
189 | Name: "app",
190 | Type: "string",
191 | })
192 | // metadata is skipped in this code intentionally
193 |
194 | if v.Timestamp == 0 {
195 | v.Timestamp = time.Now().Unix()
196 | }
197 |
198 | return &TypedData{
199 | eip712.TypedData{
200 | Domain: eip712.TypedDataDomain{
201 | Name: name,
202 | Version: version,
203 | },
204 | Types: eip712.Types{
205 | "EIP712Domain": {
206 | {
207 | Name: "name",
208 | Type: "string",
209 | },
210 | {
211 | Name: "version",
212 | Type: "string",
213 | },
214 | },
215 | "Vote": myType,
216 | },
217 | Message: eip712.TypedDataMessage{
218 | "from": v.From,
219 | "space": v.Space,
220 | // EncodePrimitiveValue accepts string, float64, or this type
221 | "timestamp": math.NewHexOrDecimal256(v.Timestamp),
222 | "proposal": proposal,
223 | "choice": choice,
224 | "reason": v.Reason,
225 | "app": v.App,
226 | },
227 | PrimaryType: "Vote",
228 | },
229 | }, nil
230 | }
231 |
--------------------------------------------------------------------------------
/pkg/console/prompt/prompter.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package prompt
18 |
19 | import (
20 | "fmt"
21 | "strings"
22 |
23 | "github.com/peterh/liner"
24 | )
25 |
26 | // Stdin holds the stdin line reader (also using stdout for printing prompts).
27 | // Only this reader may be used for input because it keeps an internal buffer.
28 | var Stdin = newTerminalPrompter()
29 |
30 | // UserPrompter defines the methods needed by the console to prompt the user for
31 | // various types of inputs.
32 | type UserPrompter interface {
33 | // PromptInput displays the given prompt to the user and requests some textual
34 | // data to be entered, returning the input of the user.
35 | PromptInput(prompt string) (string, error)
36 |
37 | // PromptPassword displays the given prompt to the user and requests some textual
38 | // data to be entered, but one which must not be echoed out into the terminal.
39 | // The method returns the input provided by the user.
40 | PromptPassword(prompt string) (string, error)
41 |
42 | // PromptConfirm displays the given prompt to the user and requests a boolean
43 | // choice to be made, returning that choice.
44 | PromptConfirm(prompt string) (bool, error)
45 |
46 | // SetHistory sets the input scrollback history that the prompter will allow
47 | // the user to scroll back to.
48 | SetHistory(history []string)
49 |
50 | // AppendHistory appends an entry to the scrollback history. It should be called
51 | // if and only if the prompt to append was a valid command.
52 | AppendHistory(command string)
53 |
54 | // ClearHistory clears the entire history
55 | ClearHistory()
56 |
57 | // SetWordCompleter sets the completion function that the prompter will call to
58 | // fetch completion candidates when the user presses tab.
59 | SetWordCompleter(completer WordCompleter)
60 | }
61 |
62 | // WordCompleter takes the currently edited line with the cursor position and
63 | // returns the completion candidates for the partial word to be completed. If
64 | // the line is "Hello, wo!!!" and the cursor is before the first '!', ("Hello,
65 | // wo!!!", 9) is passed to the completer which may returns ("Hello, ", {"world",
66 | // "Word"}, "!!!") to have "Hello, world!!!".
67 | type WordCompleter func(line string, pos int) (string, []string, string)
68 |
69 | // terminalPrompter is a UserPrompter backed by the liner package. It supports
70 | // prompting the user for various input, among others for non-echoing password
71 | // input.
72 | type terminalPrompter struct {
73 | *liner.State
74 | warned bool
75 | supported bool
76 | normalMode liner.ModeApplier
77 | rawMode liner.ModeApplier
78 | }
79 |
80 | // newTerminalPrompter creates a liner based user input prompter working off the
81 | // standard input and output streams.
82 | func newTerminalPrompter() *terminalPrompter {
83 | p := new(terminalPrompter)
84 | // Get the original mode before calling NewLiner.
85 | // This is usually regular "cooked" mode where characters echo.
86 | normalMode, _ := liner.TerminalMode()
87 | // Turn on liner. It switches to raw mode.
88 | p.State = liner.NewLiner()
89 | rawMode, err := liner.TerminalMode()
90 | if err != nil || !liner.TerminalSupported() {
91 | p.supported = false
92 | } else {
93 | p.supported = true
94 | p.normalMode = normalMode
95 | p.rawMode = rawMode
96 | // Switch back to normal mode while we're not prompting.
97 | normalMode.ApplyMode()
98 | }
99 | p.SetCtrlCAborts(true)
100 | p.SetTabCompletionStyle(liner.TabPrints)
101 | p.SetMultiLineMode(true)
102 | return p
103 | }
104 |
105 | // PromptInput displays the given prompt to the user and requests some textual
106 | // data to be entered, returning the input of the user.
107 | func (p *terminalPrompter) PromptInput(prompt string) (string, error) {
108 | if p.supported {
109 | p.rawMode.ApplyMode()
110 | defer p.normalMode.ApplyMode()
111 | } else {
112 | // liner tries to be smart about printing the prompt
113 | // and doesn't print anything if input is redirected.
114 | // Un-smart it by printing the prompt always.
115 | fmt.Print(prompt)
116 | prompt = ""
117 | defer fmt.Println()
118 | }
119 | return p.State.Prompt(prompt)
120 | }
121 |
122 | // PromptPassword displays the given prompt to the user and requests some textual
123 | // data to be entered, but one which must not be echoed out into the terminal.
124 | // The method returns the input provided by the user.
125 | func (p *terminalPrompter) PromptPassword(prompt string) (passwd string, err error) {
126 | if p.supported {
127 | p.rawMode.ApplyMode()
128 | defer p.normalMode.ApplyMode()
129 | return p.State.PasswordPrompt(prompt)
130 | }
131 | if !p.warned {
132 | fmt.Println("!! Unsupported terminal, password will be echoed.")
133 | p.warned = true
134 | }
135 | // Just as in Prompt, handle printing the prompt here instead of relying on liner.
136 | fmt.Print(prompt)
137 | passwd, err = p.State.Prompt("")
138 | fmt.Println()
139 | return passwd, err
140 | }
141 |
142 | // PromptConfirm displays the given prompt to the user and requests a boolean
143 | // choice to be made, returning that choice.
144 | func (p *terminalPrompter) PromptConfirm(prompt string) (bool, error) {
145 | input, err := p.Prompt(prompt + " [y/n] ")
146 | if len(input) > 0 && strings.ToUpper(input[:1]) == "Y" {
147 | return true, nil
148 | }
149 | return false, err
150 | }
151 |
152 | // SetHistory sets the input scrollback history that the prompter will allow
153 | // the user to scroll back to.
154 | func (p *terminalPrompter) SetHistory(history []string) {
155 | p.State.ReadHistory(strings.NewReader(strings.Join(history, "\n")))
156 | }
157 |
158 | // AppendHistory appends an entry to the scrollback history.
159 | func (p *terminalPrompter) AppendHistory(command string) {
160 | p.State.AppendHistory(command)
161 | }
162 |
163 | // ClearHistory clears the entire history
164 | func (p *terminalPrompter) ClearHistory() {
165 | p.State.ClearHistory()
166 | }
167 |
168 | // SetWordCompleter sets the completion function that the prompter will call to
169 | // fetch completion candidates when the user presses tab.
170 | func (p *terminalPrompter) SetWordCompleter(completer WordCompleter) {
171 | p.State.SetWordCompleter(liner.WordCompleter(completer))
172 | }
173 |
--------------------------------------------------------------------------------
/pkg/ledger/nano_S_driver.go:
--------------------------------------------------------------------------------
1 | package ledger
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "encoding/hex"
7 | "errors"
8 | "fmt"
9 | "io"
10 |
11 | "github.com/karalabe/usb"
12 | )
13 |
14 | const (
15 | signatureSize int = 65
16 | packetSize int = 255
17 | )
18 |
19 | var DEBUG bool
20 |
21 | type hidFramer struct {
22 | rw io.ReadWriter
23 | seq uint16
24 | buf [64]byte
25 | pos int
26 | }
27 |
28 | type APDU struct {
29 | CLA byte
30 | INS byte
31 | P1, P2 byte
32 | Payload []byte
33 | }
34 |
35 | type apduFramer struct {
36 | hf *hidFramer
37 | buf [2]byte // to read APDU length prefix
38 | }
39 |
40 | type NanoS struct {
41 | device *apduFramer
42 | }
43 |
44 | type ErrCode uint16
45 |
46 | func (hf *hidFramer) Reset() {
47 | hf.seq = 0
48 | }
49 |
50 | func (hf *hidFramer) Write(p []byte) (int, error) {
51 | if DEBUG {
52 | fmt.Println("HID <=", hex.EncodeToString(p))
53 | }
54 | // split into 64-byte chunks
55 | chunk := make([]byte, 64)
56 | binary.BigEndian.PutUint16(chunk[:2], 0x0101)
57 | chunk[2] = 0x05
58 | var seq uint16
59 | buf := new(bytes.Buffer)
60 | binary.Write(buf, binary.BigEndian, uint16(len(p)))
61 | buf.Write(p)
62 | for buf.Len() > 0 {
63 | binary.BigEndian.PutUint16(chunk[3:5], seq)
64 | n, _ := buf.Read(chunk[5:])
65 | if n, err := hf.rw.Write(chunk[:5+n]); err != nil {
66 | return n, err
67 | }
68 | seq++
69 | }
70 | return len(p), nil
71 | }
72 |
73 | func (hf *hidFramer) Read(p []byte) (int, error) {
74 | if hf.seq > 0 && hf.pos != 64 {
75 | // drain buf
76 | n := copy(p, hf.buf[hf.pos:])
77 | hf.pos += n
78 | return n, nil
79 | }
80 | // read next 64-byte packet
81 | if n, err := hf.rw.Read(hf.buf[:]); err != nil {
82 | return 0, err
83 | } else if n != 64 {
84 | panic("read less than 64 bytes from HID")
85 | }
86 | // parse header
87 | channelID := binary.BigEndian.Uint16(hf.buf[:2])
88 | commandTag := hf.buf[2]
89 | seq := binary.BigEndian.Uint16(hf.buf[3:5])
90 | if channelID != 0x0101 {
91 | return 0, fmt.Errorf("bad channel ID 0x%x", channelID)
92 | } else if commandTag != 0x05 {
93 | return 0, fmt.Errorf("bad command tag 0x%x", commandTag)
94 | } else if seq != hf.seq {
95 | return 0, fmt.Errorf("bad sequence number %v (expected %v)", seq, hf.seq)
96 | }
97 | hf.seq++
98 | // start filling p
99 | n := copy(p, hf.buf[5:])
100 | hf.pos = 5 + n
101 | return n, nil
102 | }
103 |
104 | func (af *apduFramer) Exchange(apdu APDU) ([]byte, error) {
105 | if len(apdu.Payload) > packetSize {
106 | panic("APDU payload cannot exceed 255 bytes")
107 | }
108 | af.hf.Reset()
109 | data := append([]byte{
110 | apdu.CLA,
111 | apdu.INS,
112 | apdu.P1, apdu.P2,
113 | byte(len(apdu.Payload)),
114 | }, apdu.Payload...)
115 | if _, err := af.hf.Write(data); err != nil {
116 | return nil, err
117 | }
118 |
119 | // read APDU length
120 | if _, err := io.ReadFull(af.hf, af.buf[:]); err != nil {
121 | return nil, err
122 | }
123 | // read APDU payload
124 | respLen := binary.BigEndian.Uint16(af.buf[:2])
125 | resp := make([]byte, respLen)
126 | _, err := io.ReadFull(af.hf, resp)
127 | if DEBUG {
128 | fmt.Println("HID =>", hex.EncodeToString(resp))
129 | }
130 | return resp, err
131 | }
132 |
133 | func (c ErrCode) Error() string {
134 | return fmt.Sprintf("Error code 0x%x", uint16(c))
135 | }
136 |
137 | const codeSuccess = 0x9000
138 | const codeUserRejected = 0x6985
139 | const codeInvalidParam = 0x6b01
140 |
141 | var errUserRejected = errors.New("user denied request")
142 | var errInvalidParam = errors.New("invalid request parameters")
143 |
144 | func (n *NanoS) Exchange(cmd byte, p1, p2 byte, data []byte) (resp []byte, err error) {
145 | resp, err = n.device.Exchange(APDU{
146 | CLA: 0xe0,
147 | INS: cmd,
148 | P1: p1,
149 | P2: p2,
150 | Payload: data,
151 | })
152 | if err != nil {
153 | return nil, err
154 | } else if len(resp) < 2 {
155 | return nil, errors.New("APDU response missing status code")
156 | }
157 | code := binary.BigEndian.Uint16(resp[len(resp)-2:])
158 | resp = resp[:len(resp)-2]
159 | switch code {
160 | case codeSuccess:
161 | err = nil
162 | case codeUserRejected:
163 | err = errUserRejected
164 | case codeInvalidParam:
165 | err = errInvalidParam
166 | default:
167 | err = ErrCode(code)
168 | }
169 | return
170 | }
171 |
172 | const (
173 | cmdGetVersion = 0x01
174 | cmdGetPublicKey = 0x02
175 | cmdSignStaking = 0x04
176 | cmdSignTx = 0x08
177 |
178 | p1First = 0x0
179 | p1More = 0x80
180 |
181 | p2DisplayAddress = 0x00
182 | p2DisplayHash = 0x00
183 | p2SignHash = 0x01
184 | p2Finish = 0x02
185 | )
186 |
187 | func (n *NanoS) GetVersion() (version string, err error) {
188 | resp, err := n.Exchange(cmdGetVersion, 0, 0, nil)
189 | if err != nil {
190 | return "", err
191 | } else if len(resp) != 3 {
192 | return "", errors.New("version has wrong length")
193 | }
194 | return fmt.Sprintf("v%d.%d.%d", resp[0], resp[1], resp[2]), nil
195 | }
196 |
197 | func (n *NanoS) GetAddress() (oneAddr string, err error) {
198 | resp, err := n.Exchange(cmdGetPublicKey, 0, p2DisplayAddress, []byte{})
199 | if err != nil {
200 | return "", err
201 | }
202 |
203 | var pubkey [42]byte
204 | if copy(pubkey[:], resp) != len(pubkey) {
205 | return "", errors.New("pubkey has wrong length")
206 | }
207 | return string(pubkey[:]), nil
208 | }
209 |
210 | func (n *NanoS) SignTxn(txn []byte) (sig [signatureSize]byte, err error) {
211 | buf := bytes.NewBuffer(txn)
212 | var resp []byte
213 |
214 | for buf.Len() > 0 {
215 | var p1 byte = p1More
216 | var p2 byte = p2SignHash
217 | if resp == nil {
218 | p1 = p1First
219 | }
220 | if buf.Len() < packetSize {
221 | p2 = p2Finish
222 | }
223 | resp, err = n.Exchange(cmdSignTx, p1, p2, buf.Next(packetSize))
224 | if err != nil {
225 | return [signatureSize]byte{}, err
226 | }
227 | }
228 |
229 | copy(sig[:], resp)
230 |
231 | if copy(sig[:], resp) != len(sig) {
232 | return [signatureSize]byte{}, errors.New("signature has wrong length")
233 | }
234 | return
235 | }
236 |
237 | func (n *NanoS) SignStaking(stake []byte) (sig [signatureSize]byte, err error) {
238 | buf := bytes.NewBuffer(stake)
239 | var resp []byte
240 |
241 | for buf.Len() > 0 {
242 | var p1 byte = p1More
243 | var p2 byte = p2SignHash
244 | if resp == nil {
245 | p1 = p1First
246 | }
247 |
248 | if buf.Len() < packetSize {
249 | p2 = p2Finish
250 | }
251 |
252 | resp, err = n.Exchange(cmdSignStaking, p1, p2, buf.Next(packetSize))
253 | if err != nil {
254 | return [signatureSize]byte{}, err
255 | }
256 | }
257 |
258 | copy(sig[:], resp)
259 |
260 | if copy(sig[:], resp) != len(sig) {
261 | return [signatureSize]byte{}, errors.New("signature has wrong length")
262 | }
263 | return
264 | }
265 |
266 | func OpenNanoS() (*NanoS, error) {
267 | const (
268 | ledgerVendorID = 0x2c97
269 | // new device ID for firmware 1.6.0
270 | ledgerNanoSProductID = 0x1011
271 | // ledgerNanoSProductID = 0x0001
272 | //ledgerUsageID = 0xffa0
273 | )
274 |
275 | // search for Nano S
276 | devices, err := usb.EnumerateHid(ledgerVendorID, ledgerNanoSProductID)
277 | if err != nil {
278 | return nil, err
279 | }
280 | if len(devices) == 0 {
281 | return nil, errors.New("nano S not detected")
282 | } else if len(devices) > 1 {
283 | return nil, errors.New("detected multiple Nano S devices")
284 | }
285 |
286 | // open the device
287 | device, err := devices[0].Open()
288 | if err != nil {
289 | return nil, err
290 | }
291 |
292 | // wrap raw device I/O in HID+APDU protocols
293 | return &NanoS{
294 | device: &apduFramer{
295 | hf: &hidFramer{
296 | rw: device,
297 | },
298 | },
299 | }, nil
300 | }
301 |
--------------------------------------------------------------------------------
/cmd/subcommands/root.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "fmt"
7 | "github.com/harmony-one/go-sdk/pkg/address"
8 | "net/http"
9 | "os"
10 | "path"
11 | "regexp"
12 | "strings"
13 |
14 | color "github.com/fatih/color"
15 | "github.com/harmony-one/go-sdk/pkg/common"
16 | "github.com/harmony-one/go-sdk/pkg/rpc"
17 | rpcEth "github.com/harmony-one/go-sdk/pkg/rpc/eth"
18 | rpcV1 "github.com/harmony-one/go-sdk/pkg/rpc/v1"
19 | "github.com/harmony-one/go-sdk/pkg/sharding"
20 | "github.com/harmony-one/go-sdk/pkg/store"
21 | "github.com/pkg/errors"
22 | "github.com/spf13/cobra"
23 | "github.com/spf13/cobra/doc"
24 | )
25 |
26 | var (
27 | verbose bool
28 | useLedgerWallet bool
29 | noLatest bool
30 | noPrettyOutput bool
31 | node string
32 | rpcPrefix string
33 | keyStoreDir string
34 | givenFilePath string
35 | endpoint = regexp.MustCompile(`https://api\.s[0-9]\..*\.hmny\.io`)
36 | request = func(method string, params []interface{}) error {
37 | if !noLatest {
38 | params = append(params, "latest")
39 | }
40 | success, failure := rpc.Request(method, node, params)
41 | if failure != nil {
42 | return failure
43 | }
44 | asJSON, _ := json.Marshal(success)
45 | if noPrettyOutput {
46 | fmt.Println(string(asJSON))
47 | return nil
48 | }
49 | fmt.Println(common.JSONPrettyFormat(string(asJSON)))
50 | return nil
51 | }
52 | // RootCmd is single entry point of the CLI
53 | RootCmd = &cobra.Command{
54 | Use: "hmy",
55 | Short: "Harmony blockchain",
56 | SilenceUsage: true,
57 | PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
58 | if verbose {
59 | common.EnableAllVerbose()
60 | }
61 | switch rpcPrefix {
62 | case "hmy":
63 | rpc.Method = rpcV1.Method
64 | case "eth":
65 | rpc.Method = rpcEth.Method
66 | default:
67 | rpc.Method = rpcV1.Method
68 | }
69 | if strings.HasPrefix(node, "https://") || strings.HasPrefix(node, "http://") ||
70 | strings.HasPrefix(node, "ws://") {
71 | //No op, already has protocol, respect protocol default ports.
72 | } else if strings.HasPrefix(node, "api") || strings.HasPrefix(node, "ws") {
73 | node = "https://" + node
74 | } else {
75 | switch URLcomponents := strings.Split(node, ":"); len(URLcomponents) {
76 | case 1:
77 | node = "http://" + node + ":9500"
78 | case 2:
79 | node = "http://" + node
80 | default:
81 | node = node
82 | }
83 | }
84 |
85 | if targetChain == "" {
86 | if node == defaultNodeAddr {
87 | routes, err := sharding.Structure(node)
88 | if err != nil {
89 | chainName = chainIDWrapper{chainID: &common.Chain.TestNet}
90 | } else {
91 | if len(routes) == 0 {
92 | return errors.New("empty reply from sharding structure")
93 | }
94 | chainName = endpointToChainID(routes[0].HTTP)
95 | }
96 | } else if endpoint.Match([]byte(node)) {
97 | chainName = endpointToChainID(node)
98 | } else if strings.Contains(node, "api.harmony.one") {
99 | chainName = chainIDWrapper{chainID: &common.Chain.MainNet}
100 | } else {
101 | chainName = chainIDWrapper{chainID: &common.Chain.TestNet}
102 | }
103 | } else {
104 | chain, err := common.StringToChainID(targetChain)
105 | if err != nil {
106 | return err
107 | }
108 | chainName = chainIDWrapper{chainID: chain}
109 | }
110 |
111 | return nil
112 | },
113 | Long: fmt.Sprintf(`
114 | CLI interface to the Harmony blockchain
115 |
116 | %s`, g("Invoke 'hmy cookbook' for examples of the most common, important usages")),
117 | RunE: func(cmd *cobra.Command, args []string) error {
118 | cmd.Help()
119 | return nil
120 | },
121 | }
122 | )
123 |
124 | func init() {
125 | vS := "dump out debug information, same as env var HMY_ALL_DEBUG=true"
126 | RootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, vS)
127 | RootCmd.PersistentFlags().StringVarP(&node, "node", "n", defaultNodeAddr, "")
128 | RootCmd.PersistentFlags().StringVarP(&rpcPrefix, "rpc-prefix", "r", defaultRpcPrefix, "")
129 | RootCmd.PersistentFlags().BoolVar(
130 | &noLatest, "no-latest", false, "Do not add 'latest' to RPC params",
131 | )
132 | RootCmd.PersistentFlags().BoolVar(
133 | &noPrettyOutput, "no-pretty", false, "Disable pretty print JSON outputs",
134 | )
135 | RootCmd.AddCommand(&cobra.Command{
136 | Use: "cookbook",
137 | Short: "Example usages of the most important, frequently used commands",
138 | RunE: func(cmd *cobra.Command, args []string) error {
139 | var docNode, docNet string
140 | if node == defaultNodeAddr || chainName.chainID == &common.Chain.MainNet {
141 | docNode = `https://api.s0.t.hmny.io`
142 | docNet = `Mainnet`
143 | } else if chainName.chainID == &common.Chain.TestNet {
144 | docNode = `https://api.s0.b.hmny.io`
145 | docNet = `Long-Running Testnet`
146 | } else if chainName.chainID == &common.Chain.PangaeaNet {
147 | docNode = `https://api.s0.os.hmny.io`
148 | docNet = `Open Staking Network`
149 | } else if chainName.chainID == &common.Chain.PartnerNet {
150 | docNode = `https://api.s0.ps.hmny.io`
151 | docNet = `Partner Testnet`
152 | } else if chainName.chainID == &common.Chain.StressNet {
153 | docNode = `https://api.s0.stn.hmny.io`
154 | docNet = `Stress Testing Network`
155 | }
156 | fmt.Print(strings.ReplaceAll(strings.ReplaceAll(cookbookDoc, `[NODE]`, docNode), `[NETWORK]`, docNet))
157 | return nil
158 | },
159 | })
160 | RootCmd.PersistentFlags().BoolVarP(&useLedgerWallet, "ledger", "e", false, "Use ledger hardware wallet")
161 | RootCmd.PersistentFlags().StringVar(&givenFilePath, "file", "", "Path to file for given command when applicable")
162 | RootCmd.AddCommand(&cobra.Command{
163 | Use: "docs",
164 | Short: fmt.Sprintf("Generate docs to a local %s directory", hmyDocsDir),
165 | RunE: func(cmd *cobra.Command, args []string) error {
166 | cwd, _ := os.Getwd()
167 | docDir := path.Join(cwd, hmyDocsDir)
168 | os.Mkdir(docDir, 0700)
169 | doc.GenMarkdownTree(RootCmd, docDir)
170 | return nil
171 | },
172 | })
173 | }
174 |
175 | var (
176 | // VersionWrapDump meant to be set from main.go
177 | VersionWrapDump = ""
178 | cookbook = color.GreenString("hmy cookbook")
179 | versionLink = "https://harmony.one/hmycli_ver"
180 | versionFormat = regexp.MustCompile("v[0-9]+-[a-z0-9]{7}")
181 | )
182 |
183 | // Execute kicks off the hmy CLI
184 | func Execute() {
185 | RootCmd.SilenceErrors = true
186 | if err := RootCmd.Execute(); err != nil {
187 | resp, httpErr := http.Get(versionLink)
188 | if httpErr != nil {
189 | return
190 | }
191 | defer resp.Body.Close()
192 | // If error, no op
193 | if resp != nil && resp.StatusCode == 200 {
194 | buf := new(bytes.Buffer)
195 | buf.ReadFrom(resp.Body)
196 |
197 | currentVersion := versionFormat.FindAllString(buf.String(), 1)
198 | if currentVersion != nil && currentVersion[0] != VersionWrapDump {
199 | warnMsg := fmt.Sprintf("Warning: Using outdated version. Redownload to upgrade to %s\n", currentVersion[0])
200 | fmt.Fprintf(os.Stderr, color.RedString(warnMsg))
201 | }
202 | }
203 | errMsg := errors.Wrapf(err, "commit: %s, error", VersionWrapDump).Error()
204 | fmt.Fprintf(os.Stderr, errMsg+"\n")
205 | fmt.Fprintf(os.Stderr, "check "+cookbook+" for valid examples or try adding a `--help` flag\n")
206 | os.Exit(1)
207 | }
208 | }
209 |
210 | func endpointToChainID(nodeAddr string) chainIDWrapper {
211 | if strings.Contains(nodeAddr, ".t.") {
212 | return chainIDWrapper{chainID: &common.Chain.MainNet}
213 | } else if strings.Contains(nodeAddr, ".b.") {
214 | return chainIDWrapper{chainID: &common.Chain.TestNet}
215 | } else if strings.Contains(nodeAddr, ".os.") {
216 | return chainIDWrapper{chainID: &common.Chain.PangaeaNet}
217 | } else if strings.Contains(nodeAddr, ".ps.") {
218 | return chainIDWrapper{chainID: &common.Chain.PartnerNet}
219 | } else if strings.Contains(nodeAddr, ".stn.") {
220 | return chainIDWrapper{chainID: &common.Chain.StressNet}
221 | } else if strings.Contains(nodeAddr, ".dry.") {
222 | return chainIDWrapper{chainID: &common.Chain.MainNet}
223 | }
224 | return chainIDWrapper{chainID: &common.Chain.TestNet}
225 | }
226 |
227 | func validateAddress(cmd *cobra.Command, args []string) error {
228 | // Check if input valid one address
229 | tmpAddr := oneAddress{}
230 | if err := tmpAddr.Set(args[0]); err != nil {
231 | // Check if input is valid account name
232 | if acc, err := store.AddressFromAccountName(args[0]); err == nil {
233 | addr = oneAddress{acc}
234 | return nil
235 | }
236 |
237 | bech32Addr := address.ToBech32(address.Parse(args[0]))
238 | if bech32Addr == "" {
239 | return fmt.Errorf("Invalid one address/Invalid account name: %s", args[0])
240 | }
241 |
242 | tmpAddr = oneAddress{bech32Addr}
243 | }
244 | addr = tmpAddr
245 | return nil
246 | }
247 |
--------------------------------------------------------------------------------
/pkg/rpc/methods.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "fmt"
5 |
6 | rpcCommon "github.com/harmony-one/go-sdk/pkg/rpc/common"
7 | rpcV1 "github.com/harmony-one/go-sdk/pkg/rpc/v1"
8 | "github.com/pkg/errors"
9 | )
10 |
11 | var (
12 | RPCPrefix = "hmy"
13 | Method rpcCommon.RpcEnumList = rpcV1.Method
14 | )
15 |
16 | type RpcEnumList struct {
17 | GetShardingStructure rpcCommon.RpcMethod
18 | GetBlockByHash rpcCommon.RpcMethod
19 | GetBlockByNumber rpcCommon.RpcMethod
20 | GetBlockTransactionCountByHash rpcCommon.RpcMethod
21 | GetBlockTransactionCountByNumber rpcCommon.RpcMethod
22 | GetCode rpcCommon.RpcMethod
23 | GetTransactionByBlockHashAndIndex rpcCommon.RpcMethod
24 | GetTransactionByBlockNumberAndIndex rpcCommon.RpcMethod
25 | GetTransactionByHash rpcCommon.RpcMethod
26 | GetStakingTransactionByHash rpcCommon.RpcMethod
27 | GetTransactionReceipt rpcCommon.RpcMethod
28 | Syncing rpcCommon.RpcMethod
29 | PeerCount rpcCommon.RpcMethod
30 | GetBalance rpcCommon.RpcMethod
31 | GetStorageAt rpcCommon.RpcMethod
32 | GetTransactionCount rpcCommon.RpcMethod
33 | SendTransaction rpcCommon.RpcMethod
34 | SendRawTransaction rpcCommon.RpcMethod
35 | Subscribe rpcCommon.RpcMethod
36 | GetPastLogs rpcCommon.RpcMethod
37 | GetWork rpcCommon.RpcMethod
38 | GetProof rpcCommon.RpcMethod
39 | GetFilterChanges rpcCommon.RpcMethod
40 | NewPendingTransactionFilter rpcCommon.RpcMethod
41 | NewBlockFilter rpcCommon.RpcMethod
42 | NewFilter rpcCommon.RpcMethod
43 | Call rpcCommon.RpcMethod
44 | EstimateGas rpcCommon.RpcMethod
45 | GasPrice rpcCommon.RpcMethod
46 | BlockNumber rpcCommon.RpcMethod
47 | UnSubscribe rpcCommon.RpcMethod
48 | NetVersion rpcCommon.RpcMethod
49 | ProtocolVersion rpcCommon.RpcMethod
50 | GetNodeMetadata rpcCommon.RpcMethod
51 | GetLatestBlockHeader rpcCommon.RpcMethod
52 | SendRawStakingTransaction rpcCommon.RpcMethod
53 | GetElectedValidatorAddresses rpcCommon.RpcMethod
54 | GetAllValidatorAddresses rpcCommon.RpcMethod
55 | GetValidatorInformation rpcCommon.RpcMethod
56 | GetAllValidatorInformation rpcCommon.RpcMethod
57 | GetValidatorInformationByBlockNumber rpcCommon.RpcMethod
58 | GetAllValidatorInformationByBlockNumber rpcCommon.RpcMethod
59 | GetDelegationsByDelegator rpcCommon.RpcMethod
60 | GetDelegationsByValidator rpcCommon.RpcMethod
61 | GetCurrentTransactionErrorSink rpcCommon.RpcMethod
62 | GetMedianRawStakeSnapshot rpcCommon.RpcMethod
63 | GetCurrentStakingErrorSink rpcCommon.RpcMethod
64 | GetTransactionsHistory rpcCommon.RpcMethod
65 | GetPendingTxnsInPool rpcCommon.RpcMethod
66 | GetPendingCrosslinks rpcCommon.RpcMethod
67 | GetPendingCXReceipts rpcCommon.RpcMethod
68 | GetCurrentUtilityMetrics rpcCommon.RpcMethod
69 | ResendCX rpcCommon.RpcMethod
70 | GetSuperCommmittees rpcCommon.RpcMethod
71 | GetCurrentBadBlocks rpcCommon.RpcMethod
72 | GetShardID rpcCommon.RpcMethod
73 | GetLastCrossLinks rpcCommon.RpcMethod
74 | GetLatestChainHeaders rpcCommon.RpcMethod
75 | }
76 |
77 | type errorCode int
78 | type rpcErrorCodeList struct {
79 | rpcInvalidRequest errorCode
80 | rpcMethodNotFound errorCode
81 | rpcInvalidParams errorCode
82 | rpcInternalError errorCode
83 | rpcParseError errorCode
84 | rpcMiscError errorCode
85 | rpcTypeError errorCode
86 | rpcInvalidAddressOrKey errorCode
87 | rpcInvalidParameter errorCode
88 | rpcDatabaseError errorCode
89 | rpcDeserializationError errorCode
90 | rpcVerifyError errorCode
91 | rpcVerifyRejected errorCode
92 | rpcInWarmup errorCode
93 | rpcMethodDeprecated errorCode
94 | rpcGenericError errorCode
95 | }
96 |
97 | // TODO Do not punt on the field names
98 | var errorCodeEnumeration = rpcErrorCodeList{
99 | // Standard JSON-RPC 2.0 errors
100 | // RPC_INVALID_REQUEST is internally mapped to HTTP_BAD_REQUEST (400).
101 | // It should not be used for application-layer errors.
102 | rpcInvalidRequest: -32600,
103 | // RPC_METHOD_NOT_FOUND is internally mapped to HTTP_NOT_FOUND (404).
104 | // It should not be used for application-layer errors.
105 | rpcMethodNotFound: -32601,
106 | rpcInvalidParams: -32602,
107 | // RPC_INTERNAL_ERROR should only be used for genuine errors in bitcoind
108 | // (for example datadir corruption).
109 | rpcInternalError: -32603,
110 | rpcParseError: -32700,
111 | // General application defined errors
112 | rpcMiscError: -1, // std::exception thrown in command handling
113 | rpcTypeError: -3, // Unexpected type was passed as parameter
114 | rpcInvalidAddressOrKey: -5, // Invalid address or key
115 | rpcInvalidParameter: -8, // Invalid, missing or duplicate parameter
116 | rpcDatabaseError: -20, // Database error
117 | rpcDeserializationError: -22, // Error parsing or validating structure in raw format
118 | rpcVerifyError: -25, // General error during transaction or block submission
119 | rpcVerifyRejected: -26, // Transaction or block was rejected by network rules
120 | rpcInWarmup: -28, // Client still warming up
121 | rpcMethodDeprecated: -32, // RPC method is deprecated
122 | rpcGenericError: -32000, // Generic catchall error code
123 | }
124 |
125 | const (
126 | invalidRequestError = "Invalid Request object"
127 | methodNotFoundError = "Method not found"
128 | invalidParamsError = "Invalid method parameter(s)"
129 | internalError = "Internal JSON-RPC error"
130 | parseError = "Error while parsing the JSON text"
131 | miscError = "std::exception thrown in command handling"
132 | typeError = "Unexpected type was passed as parameter"
133 | invalidAddressOrKeyError = "Invalid address or key"
134 | invalidParameterError = "Invalid, missing or duplicate parameter"
135 | databaseError = "Database error"
136 | deserializationError = "Error parsing or validating structure in raw format"
137 | verifyError = "General error during transaction or block submission"
138 | verifyRejectedError = "Transaction or block was rejected by network rules"
139 | rpcInWarmupError = "Client still warming up"
140 | methodDeprecatedError = "RPC method deprecated"
141 | catchAllError = "Catch all RPC error"
142 | )
143 |
144 | // ErrorCodeToError lifts an untyped error code from RPC to Error value
145 | func ErrorCodeToError(message string, code float64) error {
146 | return errors.Wrap(errors.New(message), codeToMessage(code))
147 | }
148 |
149 | // TODO Use reflection here instead of typing out the cases or at least a map
150 | func codeToMessage(err float64) string {
151 | switch e := errorCode(err); e {
152 | case errorCodeEnumeration.rpcInvalidRequest:
153 | return invalidRequestError
154 | case errorCodeEnumeration.rpcMethodNotFound:
155 | return methodNotFoundError
156 | case errorCodeEnumeration.rpcInvalidParams:
157 | return invalidParamsError
158 | case errorCodeEnumeration.rpcInternalError:
159 | return internalError
160 | case errorCodeEnumeration.rpcParseError:
161 | return parseError
162 | case errorCodeEnumeration.rpcMiscError:
163 | return miscError
164 | case errorCodeEnumeration.rpcTypeError:
165 | return typeError
166 | case errorCodeEnumeration.rpcInvalidAddressOrKey:
167 | return invalidAddressOrKeyError
168 | case errorCodeEnumeration.rpcInvalidParameter:
169 | return invalidParameterError
170 | case errorCodeEnumeration.rpcDatabaseError:
171 | return databaseError
172 | case errorCodeEnumeration.rpcDeserializationError:
173 | return deserializationError
174 | case errorCodeEnumeration.rpcVerifyError:
175 | return verifyError
176 | case errorCodeEnumeration.rpcVerifyRejected:
177 | return verifyRejectedError
178 | case errorCodeEnumeration.rpcInWarmup:
179 | return rpcInWarmupError
180 | case errorCodeEnumeration.rpcMethodDeprecated:
181 | return methodDeprecatedError
182 | case errorCodeEnumeration.rpcGenericError:
183 | return catchAllError
184 | default:
185 | return fmt.Sprintf("Error number %v not found", err)
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/pkg/console/jsre/pretty.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package jsre
18 |
19 | import (
20 | "fmt"
21 | "io"
22 | "reflect"
23 | "sort"
24 | "strconv"
25 | "strings"
26 |
27 | "github.com/dop251/goja"
28 | "github.com/fatih/color"
29 | )
30 |
31 | const (
32 | maxPrettyPrintLevel = 3
33 | indentString = " "
34 | )
35 |
36 | var (
37 | FunctionColor = color.New(color.FgMagenta).SprintfFunc()
38 | SpecialColor = color.New(color.Bold).SprintfFunc()
39 | NumberColor = color.New(color.FgRed).SprintfFunc()
40 | StringColor = color.New(color.FgGreen).SprintfFunc()
41 | ErrorColor = color.New(color.FgHiRed).SprintfFunc()
42 | )
43 |
44 | // these fields are hidden when printing objects.
45 | var boringKeys = map[string]bool{
46 | "valueOf": true,
47 | "toString": true,
48 | "toLocaleString": true,
49 | "hasOwnProperty": true,
50 | "isPrototypeOf": true,
51 | "propertyIsEnumerable": true,
52 | "constructor": true,
53 | }
54 |
55 | // prettyPrint writes value to standard output.
56 | func prettyPrint(vm *goja.Runtime, value goja.Value, w io.Writer) {
57 | ppctx{vm: vm, w: w}.printValue(value, 0, false)
58 | }
59 |
60 | // prettyError writes err to standard output.
61 | func prettyError(vm *goja.Runtime, err error, w io.Writer) {
62 | failure := err.Error()
63 | if gojaErr, ok := err.(*goja.Exception); ok {
64 | failure = gojaErr.String()
65 | }
66 | fmt.Fprint(w, ErrorColor("%s", failure))
67 | }
68 |
69 | func (re *JSRE) prettyPrintJS(call goja.FunctionCall) goja.Value {
70 | for _, v := range call.Arguments {
71 | prettyPrint(re.vm, v, re.output)
72 | fmt.Fprintln(re.output)
73 | }
74 | return goja.Undefined()
75 | }
76 |
77 | type ppctx struct {
78 | vm *goja.Runtime
79 | w io.Writer
80 | }
81 |
82 | func (ctx ppctx) indent(level int) string {
83 | return strings.Repeat(indentString, level)
84 | }
85 |
86 | func (ctx ppctx) printValue(v goja.Value, level int, inArray bool) {
87 | if goja.IsNull(v) || goja.IsUndefined(v) {
88 | fmt.Fprint(ctx.w, SpecialColor(v.String()))
89 | return
90 | }
91 | kind := v.ExportType().Kind()
92 | switch {
93 | case kind == reflect.Bool:
94 | fmt.Fprint(ctx.w, SpecialColor("%t", v.ToBoolean()))
95 | case kind == reflect.String:
96 | fmt.Fprint(ctx.w, StringColor("%q", v.String()))
97 | case kind >= reflect.Int && kind <= reflect.Complex128:
98 | fmt.Fprint(ctx.w, NumberColor("%s", v.String()))
99 | default:
100 | if obj, ok := v.(*goja.Object); ok {
101 | ctx.printObject(obj, level, inArray)
102 | } else {
103 | fmt.Fprintf(ctx.w, "", v)
104 | }
105 | }
106 | }
107 |
108 | // SafeGet attempt to get the value associated to `key`, and
109 | // catches the panic that goja creates if an error occurs in
110 | // key.
111 | func SafeGet(obj *goja.Object, key string) (ret goja.Value) {
112 | defer func() {
113 | if r := recover(); r != nil {
114 | ret = goja.Undefined()
115 | }
116 | }()
117 | ret = obj.Get(key)
118 |
119 | return ret
120 | }
121 |
122 | func (ctx ppctx) printObject(obj *goja.Object, level int, inArray bool) {
123 | switch obj.ClassName() {
124 | case "Array", "GoArray":
125 | lv := obj.Get("length")
126 | len := lv.ToInteger()
127 | if len == 0 {
128 | fmt.Fprintf(ctx.w, "[]")
129 | return
130 | }
131 | if level > maxPrettyPrintLevel {
132 | fmt.Fprint(ctx.w, "[...]")
133 | return
134 | }
135 | fmt.Fprint(ctx.w, "[")
136 | for i := int64(0); i < len; i++ {
137 | el := obj.Get(strconv.FormatInt(i, 10))
138 | if el != nil {
139 | ctx.printValue(el, level+1, true)
140 | }
141 | if i < len-1 {
142 | fmt.Fprintf(ctx.w, ", ")
143 | }
144 | }
145 | fmt.Fprint(ctx.w, "]")
146 |
147 | case "Object":
148 | // Print values from bignumber.js as regular numbers.
149 | if ctx.isBigNumber(obj) {
150 | fmt.Fprint(ctx.w, NumberColor("%s", toString(obj)))
151 | return
152 | }
153 | // Otherwise, print all fields indented, but stop if we're too deep.
154 | keys := ctx.fields(obj)
155 | if len(keys) == 0 {
156 | fmt.Fprint(ctx.w, "{}")
157 | return
158 | }
159 | if level > maxPrettyPrintLevel {
160 | fmt.Fprint(ctx.w, "{...}")
161 | return
162 | }
163 | fmt.Fprintln(ctx.w, "{")
164 | for i, k := range keys {
165 | v := SafeGet(obj, k)
166 | fmt.Fprintf(ctx.w, "%s%s: ", ctx.indent(level+1), k)
167 | ctx.printValue(v, level+1, false)
168 | if i < len(keys)-1 {
169 | fmt.Fprintf(ctx.w, ",")
170 | }
171 | fmt.Fprintln(ctx.w)
172 | }
173 | if inArray {
174 | level--
175 | }
176 | fmt.Fprintf(ctx.w, "%s}", ctx.indent(level))
177 |
178 | case "Function":
179 | robj := obj.ToString()
180 | desc := strings.Trim(strings.Split(robj.String(), "{")[0], " \t\n")
181 | desc = strings.Replace(desc, " (", "(", 1)
182 | fmt.Fprint(ctx.w, FunctionColor("%s", desc))
183 |
184 | case "RegExp":
185 | fmt.Fprint(ctx.w, StringColor("%s", toString(obj)))
186 |
187 | default:
188 | if level <= maxPrettyPrintLevel {
189 | s := obj.ToString().String()
190 | fmt.Fprintf(ctx.w, "<%s %s>", obj.ClassName(), s)
191 | } else {
192 | fmt.Fprintf(ctx.w, "<%s>", obj.ClassName())
193 | }
194 | }
195 | }
196 |
197 | func (ctx ppctx) fields(obj *goja.Object) []string {
198 | var (
199 | vals, methods []string
200 | seen = make(map[string]bool)
201 | )
202 | add := func(k string) {
203 | if seen[k] || boringKeys[k] || strings.HasPrefix(k, "_") {
204 | return
205 | }
206 | seen[k] = true
207 |
208 | key := SafeGet(obj, k)
209 | if key == nil {
210 | // The value corresponding to that key could not be found
211 | // (typically because it is backed by an RPC call that is
212 | // not supported by this instance. Add it to the list of
213 | // values so that it appears as `undefined` to the user.
214 | vals = append(vals, k)
215 | } else {
216 | if _, callable := goja.AssertFunction(key); callable {
217 | methods = append(methods, k)
218 | } else {
219 | vals = append(vals, k)
220 | }
221 | }
222 |
223 | }
224 | iterOwnAndConstructorKeys(ctx.vm, obj, add)
225 | sort.Strings(vals)
226 | sort.Strings(methods)
227 | return append(vals, methods...)
228 | }
229 |
230 | func iterOwnAndConstructorKeys(vm *goja.Runtime, obj *goja.Object, f func(string)) {
231 | seen := make(map[string]bool)
232 | iterOwnKeys(vm, obj, func(prop string) {
233 | seen[prop] = true
234 | f(prop)
235 | })
236 | if cp := constructorPrototype(vm, obj); cp != nil {
237 | iterOwnKeys(vm, cp, func(prop string) {
238 | if !seen[prop] {
239 | f(prop)
240 | }
241 | })
242 | }
243 | }
244 |
245 | func iterOwnKeys(vm *goja.Runtime, obj *goja.Object, f func(string)) {
246 | Object := vm.Get("Object").ToObject(vm)
247 | getOwnPropertyNames, isFunc := goja.AssertFunction(Object.Get("getOwnPropertyNames"))
248 | if !isFunc {
249 | panic(vm.ToValue("Object.getOwnPropertyNames isn't a function"))
250 | }
251 | rv, err := getOwnPropertyNames(goja.Null(), obj)
252 | if err != nil {
253 | panic(vm.ToValue(fmt.Sprintf("Error getting object properties: %v", err)))
254 | }
255 | gv := rv.Export()
256 | switch gv := gv.(type) {
257 | case []interface{}:
258 | for _, v := range gv {
259 | f(v.(string))
260 | }
261 | case []string:
262 | for _, v := range gv {
263 | f(v)
264 | }
265 | default:
266 | panic(fmt.Errorf("Object.getOwnPropertyNames returned unexpected type %T", gv))
267 | }
268 | }
269 |
270 | func (ctx ppctx) isBigNumber(v *goja.Object) bool {
271 | // Handle numbers with custom constructor.
272 | if obj := v.Get("constructor").ToObject(ctx.vm); obj != nil {
273 | if strings.HasPrefix(toString(obj), "function BigNumber") {
274 | return true
275 | }
276 | }
277 | // Handle default constructor.
278 | BigNumber := ctx.vm.Get("BigNumber").ToObject(ctx.vm)
279 | if BigNumber == nil {
280 | return false
281 | }
282 | prototype := BigNumber.Get("prototype").ToObject(ctx.vm)
283 | isPrototypeOf, callable := goja.AssertFunction(prototype.Get("isPrototypeOf"))
284 | if !callable {
285 | return false
286 | }
287 | bv, _ := isPrototypeOf(prototype, v)
288 | return bv.ToBoolean()
289 | }
290 |
291 | func toString(obj *goja.Object) string {
292 | return obj.ToString().String()
293 | }
294 |
295 | func constructorPrototype(vm *goja.Runtime, obj *goja.Object) *goja.Object {
296 | if v := obj.Get("constructor"); v != nil {
297 | if v := v.ToObject(vm).Get("prototype"); v != nil {
298 | return v.ToObject(vm)
299 | }
300 | }
301 | return nil
302 | }
303 |
--------------------------------------------------------------------------------
/pkg/transaction/ethcontroller.go:
--------------------------------------------------------------------------------
1 | package transaction
2 |
3 | import (
4 | "fmt"
5 | "math/big"
6 | "time"
7 |
8 | "github.com/ethereum/go-ethereum/common/hexutil"
9 | "github.com/ethereum/go-ethereum/rlp"
10 | "github.com/harmony-one/go-sdk/pkg/address"
11 | "github.com/harmony-one/go-sdk/pkg/common"
12 | "github.com/harmony-one/go-sdk/pkg/rpc"
13 | "github.com/harmony-one/harmony/accounts"
14 | "github.com/harmony-one/harmony/accounts/keystore"
15 | "github.com/harmony-one/harmony/core/types"
16 | "github.com/harmony-one/harmony/numeric"
17 | )
18 |
19 | type ethTransactionForRPC struct {
20 | params map[string]interface{}
21 | transaction *types.EthTransaction
22 | // Hex encoded
23 | signature *string
24 | transactionHash *string
25 | receipt rpc.Reply
26 | }
27 |
28 | // EthController drives the eth transaction signing process
29 | type EthController struct {
30 | executionError error
31 | transactionErrors Errors
32 | messenger rpc.T
33 | sender sender
34 | transactionForRPC ethTransactionForRPC
35 | chain common.ChainID
36 | Behavior behavior
37 | }
38 |
39 | // NewEthController initializes a EthController, caller can control behavior via options
40 | func NewEthController(
41 | handler rpc.T, senderKs *keystore.KeyStore,
42 | senderAcct *accounts.Account, chain common.ChainID,
43 | options ...func(*EthController),
44 | ) *EthController {
45 | txParams := make(map[string]interface{})
46 | ctrlr := &EthController{
47 | executionError: nil,
48 | messenger: handler,
49 | sender: sender{
50 | ks: senderKs,
51 | account: senderAcct,
52 | },
53 | transactionForRPC: ethTransactionForRPC{
54 | params: txParams,
55 | signature: nil,
56 | transactionHash: nil,
57 | receipt: nil,
58 | },
59 | chain: chain,
60 | Behavior: behavior{false, false, Software, 0},
61 | }
62 | for _, option := range options {
63 | option(ctrlr)
64 | }
65 | return ctrlr
66 | }
67 |
68 | // EthTransactionToJSON dumps JSON representation
69 | func (C *EthController) EthTransactionToJSON(pretty bool) string {
70 | r, _ := C.transactionForRPC.transaction.MarshalJSON()
71 | if pretty {
72 | return common.JSONPrettyFormat(string(r))
73 | }
74 | return string(r)
75 | }
76 |
77 | // RawTransaction dumps the signature as string
78 | func (C *EthController) RawTransaction() string {
79 | return *C.transactionForRPC.signature
80 | }
81 |
82 | // TransactionHash - the tx hash
83 | func (C *EthController) TransactionHash() *string {
84 | return C.transactionForRPC.transactionHash
85 | }
86 |
87 | // Receipt - the tx receipt
88 | func (C *EthController) Receipt() rpc.Reply {
89 | return C.transactionForRPC.receipt
90 | }
91 |
92 | // TransactionErrors - tx errors
93 | func (C *EthController) TransactionErrors() Errors {
94 | return C.transactionErrors
95 | }
96 |
97 | func (C *EthController) setIntrinsicGas(gasLimit uint64) {
98 | if C.executionError != nil {
99 | return
100 | }
101 | C.transactionForRPC.params["gas-limit"] = gasLimit
102 | }
103 |
104 | func (C *EthController) setGasPrice(gasPrice numeric.Dec) {
105 | if C.executionError != nil {
106 | return
107 | }
108 | if gasPrice.Sign() == -1 {
109 | C.executionError = ErrBadTransactionParam
110 | errorMsg := fmt.Sprintf(
111 | "can't set negative gas price: %d", gasPrice,
112 | )
113 | C.transactionErrors = append(C.transactionErrors, &Error{
114 | ErrMessage: &errorMsg,
115 | TimestampOfRejection: time.Now().Unix(),
116 | })
117 | return
118 | }
119 | C.transactionForRPC.params["gas-price"] = gasPrice.Mul(nanoAsDec)
120 | }
121 |
122 | func (C *EthController) setAmount(amount numeric.Dec) {
123 | if C.executionError != nil {
124 | return
125 | }
126 | if amount.Sign() == -1 {
127 | C.executionError = ErrBadTransactionParam
128 | errorMsg := fmt.Sprintf(
129 | "can't set negative amount: %d", amount,
130 | )
131 | C.transactionErrors = append(C.transactionErrors, &Error{
132 | ErrMessage: &errorMsg,
133 | TimestampOfRejection: time.Now().Unix(),
134 | })
135 | return
136 | }
137 |
138 | gasAsDec := C.transactionForRPC.params["gas-price"].(numeric.Dec)
139 | gasAsDec = gasAsDec.Mul(numeric.NewDec(int64(C.transactionForRPC.params["gas-limit"].(uint64))))
140 | amountInAtto := amount.Mul(oneAsDec)
141 | total := amountInAtto.Add(gasAsDec)
142 |
143 | if !C.Behavior.OfflineSign {
144 | balanceRPCReply, err := C.messenger.SendRPC(
145 | rpc.Method.GetBalance,
146 | p{address.ToBech32(C.sender.account.Address), "latest"},
147 | )
148 | if err != nil {
149 | C.executionError = err
150 | return
151 | }
152 | currentBalance, _ := balanceRPCReply["result"].(string)
153 | bal, _ := new(big.Int).SetString(currentBalance[2:], 16)
154 | balance := numeric.NewDecFromBigInt(bal)
155 | if total.GT(balance) {
156 | balanceInOne := balance.Quo(oneAsDec)
157 | C.executionError = ErrBadTransactionParam
158 | errorMsg := fmt.Sprintf(
159 | "insufficient balance of %s in shard %d for the requested transfer of %s",
160 | balanceInOne.String(), C.transactionForRPC.params["from-shard"].(uint32), amount.String(),
161 | )
162 | C.transactionErrors = append(C.transactionErrors, &Error{
163 | ErrMessage: &errorMsg,
164 | TimestampOfRejection: time.Now().Unix(),
165 | })
166 | return
167 | }
168 | }
169 | C.transactionForRPC.params["transfer-amount"] = amountInAtto
170 | }
171 |
172 | func (C *EthController) setReceiver(receiver string) {
173 | C.transactionForRPC.params["receiver"] = address.Parse(receiver)
174 | }
175 |
176 | func (C *EthController) setNewTransactionWithDataAndGas(data []byte) {
177 | if C.executionError != nil {
178 | return
179 | }
180 | C.transactionForRPC.transaction = NewEthTransaction(
181 | C.transactionForRPC.params["nonce"].(uint64),
182 | C.transactionForRPC.params["gas-limit"].(uint64),
183 | C.transactionForRPC.params["receiver"].(address.T),
184 | C.transactionForRPC.params["transfer-amount"].(numeric.Dec),
185 | C.transactionForRPC.params["gas-price"].(numeric.Dec),
186 | data,
187 | )
188 | }
189 |
190 | func (C *EthController) signAndPrepareTxEncodedForSending() {
191 | if C.executionError != nil {
192 | return
193 | }
194 | signedTransaction, err :=
195 | C.sender.ks.SignEthTx(*C.sender.account, C.transactionForRPC.transaction, C.chain.Value)
196 | if err != nil {
197 | C.executionError = err
198 | return
199 | }
200 | C.transactionForRPC.transaction = signedTransaction
201 | enc, _ := rlp.EncodeToBytes(signedTransaction)
202 | hexSignature := hexutil.Encode(enc)
203 | C.transactionForRPC.signature = &hexSignature
204 | if common.DebugTransaction {
205 | r, _ := signedTransaction.MarshalJSON()
206 | fmt.Println("Signed with ChainID:", C.transactionForRPC.transaction.ChainID())
207 | fmt.Println(common.JSONPrettyFormat(string(r)))
208 | }
209 | }
210 |
211 | /*func (C *EthController) hardwareSignAndPrepareTxEncodedForSending() {
212 | if C.executionError != nil {
213 | return
214 | }
215 | enc, signerAddr, err := ledger.SignEthTx(C.transactionForRPC.transaction, C.chain.Value)
216 | if err != nil {
217 | C.executionError = err
218 | return
219 | }
220 | if strings.Compare(signerAddr, address.ToBech32(C.sender.account.Address)) != 0 {
221 | C.executionError = ErrBadTransactionParam
222 | errorMsg := "signature verification failed : sender address doesn't match with ledger hardware addresss"
223 | C.transactionErrors = append(C.transactionErrors, &Error{
224 | ErrMessage: &errorMsg,
225 | TimestampOfRejection: time.Now().Unix(),
226 | })
227 | return
228 | }
229 | hexSignature := hexutil.Encode(enc)
230 | C.transactionForRPC.signature = &hexSignature
231 | }*/
232 |
233 | func (C *EthController) sendSignedTx() {
234 | if C.executionError != nil || C.Behavior.DryRun {
235 | return
236 | }
237 | reply, err := C.messenger.SendRPC(rpc.Method.SendRawTransaction, p{C.transactionForRPC.signature})
238 | if err != nil {
239 | C.executionError = err
240 | return
241 | }
242 | r, _ := reply["result"].(string)
243 | C.transactionForRPC.transactionHash = &r
244 | }
245 |
246 | func (C *EthController) txConfirmation() {
247 | if C.executionError != nil || C.Behavior.DryRun {
248 | return
249 | }
250 | if C.Behavior.ConfirmationWaitTime > 0 {
251 | txHash := *C.TransactionHash()
252 | start := int(C.Behavior.ConfirmationWaitTime)
253 | for {
254 | r, _ := C.messenger.SendRPC(rpc.Method.GetTransactionReceipt, p{txHash})
255 | if r["result"] != nil {
256 | C.transactionForRPC.receipt = r
257 | return
258 | }
259 | transactionErrors, err := GetError(txHash, C.messenger)
260 | if err != nil {
261 | errMsg := fmt.Sprintf(err.Error())
262 | C.transactionErrors = append(C.transactionErrors, &Error{
263 | TxHashID: &txHash,
264 | ErrMessage: &errMsg,
265 | TimestampOfRejection: time.Now().Unix(),
266 | })
267 | }
268 | C.transactionErrors = append(C.transactionErrors, transactionErrors...)
269 | if len(transactionErrors) > 0 {
270 | C.executionError = fmt.Errorf("error found for transaction hash: %s", txHash)
271 | return
272 | }
273 | if start < 0 {
274 | C.executionError = fmt.Errorf("could not confirm transaction after %d seconds", C.Behavior.ConfirmationWaitTime)
275 | return
276 | }
277 | time.Sleep(time.Second)
278 | start--
279 | }
280 | }
281 | }
282 |
283 | // ExecuteEthTransaction is the single entrypoint to execute an eth transaction.
284 | // Each step in transaction creation, execution probably includes a mutation
285 | // Each becomes a no-op if executionError occurred in any previous step
286 | func (C *EthController) ExecuteEthTransaction(
287 | nonce, gasLimit uint64,
288 | to string,
289 | amount, gasPrice numeric.Dec,
290 | inputData []byte,
291 | ) error {
292 | // WARNING Order of execution matters
293 | C.setIntrinsicGas(gasLimit)
294 | C.setGasPrice(gasPrice)
295 | C.setAmount(amount)
296 | C.setReceiver(to)
297 | C.transactionForRPC.params["nonce"] = nonce
298 | C.setNewTransactionWithDataAndGas(inputData)
299 | switch C.Behavior.SigningImpl {
300 | case Software:
301 | C.signAndPrepareTxEncodedForSending()
302 | /*case Ledger:
303 | C.hardwareSignAndPrepareTxEncodedForSending()*/
304 | }
305 | C.sendSignedTx()
306 | C.txConfirmation()
307 | return C.executionError
308 | }
309 |
310 | func (C *EthController) ExecuteRawTransaction(txn string) error {
311 | C.transactionForRPC.signature = &txn
312 |
313 | C.sendSignedTx()
314 | C.txConfirmation()
315 | return C.executionError
316 | }
317 |
--------------------------------------------------------------------------------
/pkg/console/jsre/jsre.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package jsre
18 |
19 | import (
20 | crand "crypto/rand"
21 | "encoding/binary"
22 | "fmt"
23 | "io"
24 | "io/ioutil"
25 | "math/rand"
26 | "path/filepath"
27 | "time"
28 |
29 | "github.com/dop251/goja"
30 | )
31 |
32 | // JSRE is a JS runtime environment embedding the goja interpreter.
33 | // It provides helper functions to load code from files, run code snippets
34 | // and bind native go objects to JS.
35 | //
36 | // The runtime runs all code on a dedicated event loop and does not expose the underlying
37 | // goja runtime directly. To use the runtime, call JSRE.Do. When binding a Go function,
38 | // use the Call type to gain access to the runtime.
39 | type JSRE struct {
40 | assetPath string
41 | output io.Writer
42 | evalQueue chan *evalReq
43 | stopEventLoop chan bool
44 | closed chan struct{}
45 | vm *goja.Runtime
46 | }
47 |
48 | // Call is the argument type of Go functions which are callable from JS.
49 | type Call struct {
50 | goja.FunctionCall
51 | VM *goja.Runtime
52 | }
53 |
54 | // jsTimer is a single timer instance with a callback function
55 | type jsTimer struct {
56 | timer *time.Timer
57 | duration time.Duration
58 | interval bool
59 | call goja.FunctionCall
60 | }
61 |
62 | // evalReq is a serialized vm execution request processed by runEventLoop.
63 | type evalReq struct {
64 | fn func(vm *goja.Runtime)
65 | done chan bool
66 | }
67 |
68 | // runtime must be stopped with Stop() after use and cannot be used after stopping
69 | func New(assetPath string, output io.Writer) *JSRE {
70 | re := &JSRE{
71 | assetPath: assetPath,
72 | output: output,
73 | closed: make(chan struct{}),
74 | evalQueue: make(chan *evalReq),
75 | stopEventLoop: make(chan bool),
76 | vm: goja.New(),
77 | }
78 | go re.runEventLoop()
79 | re.Set("loadScript", MakeCallback(re.vm, re.loadScript))
80 | re.Set("inspect", re.prettyPrintJS)
81 | return re
82 | }
83 |
84 | // randomSource returns a pseudo random value generator.
85 | func randomSource() *rand.Rand {
86 | bytes := make([]byte, 8)
87 | seed := time.Now().UnixNano()
88 | if _, err := crand.Read(bytes); err == nil {
89 | seed = int64(binary.LittleEndian.Uint64(bytes))
90 | }
91 |
92 | src := rand.NewSource(seed)
93 | return rand.New(src)
94 | }
95 |
96 | // This function runs the main event loop from a goroutine that is started
97 | // when JSRE is created. Use Stop() before exiting to properly stop it.
98 | // The event loop processes vm access requests from the evalQueue in a
99 | // serialized way and calls timer callback functions at the appropriate time.
100 |
101 | // Exported functions always access the vm through the event queue. You can
102 | // call the functions of the goja vm directly to circumvent the queue. These
103 | // functions should be used if and only if running a routine that was already
104 | // called from JS through an RPC call.
105 | func (re *JSRE) runEventLoop() {
106 | defer close(re.closed)
107 |
108 | r := randomSource()
109 | re.vm.SetRandSource(r.Float64)
110 |
111 | registry := map[*jsTimer]*jsTimer{}
112 | ready := make(chan *jsTimer)
113 |
114 | newTimer := func(call goja.FunctionCall, interval bool) (*jsTimer, goja.Value) {
115 | delay := call.Argument(1).ToInteger()
116 | if 0 >= delay {
117 | delay = 1
118 | }
119 | timer := &jsTimer{
120 | duration: time.Duration(delay) * time.Millisecond,
121 | call: call,
122 | interval: interval,
123 | }
124 | registry[timer] = timer
125 |
126 | timer.timer = time.AfterFunc(timer.duration, func() {
127 | ready <- timer
128 | })
129 |
130 | return timer, re.vm.ToValue(timer)
131 | }
132 |
133 | setTimeout := func(call goja.FunctionCall) goja.Value {
134 | _, value := newTimer(call, false)
135 | return value
136 | }
137 |
138 | setInterval := func(call goja.FunctionCall) goja.Value {
139 | _, value := newTimer(call, true)
140 | return value
141 | }
142 |
143 | clearTimeout := func(call goja.FunctionCall) goja.Value {
144 | timer := call.Argument(0).Export()
145 | if timer, ok := timer.(*jsTimer); ok {
146 | timer.timer.Stop()
147 | delete(registry, timer)
148 | }
149 | return goja.Undefined()
150 | }
151 | re.vm.Set("_setTimeout", setTimeout)
152 | re.vm.Set("_setInterval", setInterval)
153 | re.vm.RunString(`var setTimeout = function(args) {
154 | if (arguments.length < 1) {
155 | throw TypeError("Failed to execute 'setTimeout': 1 argument required, but only 0 present.");
156 | }
157 | return _setTimeout.apply(this, arguments);
158 | }`)
159 | re.vm.RunString(`var setInterval = function(args) {
160 | if (arguments.length < 1) {
161 | throw TypeError("Failed to execute 'setInterval': 1 argument required, but only 0 present.");
162 | }
163 | return _setInterval.apply(this, arguments);
164 | }`)
165 | re.vm.Set("clearTimeout", clearTimeout)
166 | re.vm.Set("clearInterval", clearTimeout)
167 |
168 | var waitForCallbacks bool
169 |
170 | loop:
171 | for {
172 | select {
173 | case timer := <-ready:
174 | // execute callback, remove/reschedule the timer
175 | var arguments []interface{}
176 | if len(timer.call.Arguments) > 2 {
177 | tmp := timer.call.Arguments[2:]
178 | arguments = make([]interface{}, 2+len(tmp))
179 | for i, value := range tmp {
180 | arguments[i+2] = value
181 | }
182 | } else {
183 | arguments = make([]interface{}, 1)
184 | }
185 | arguments[0] = timer.call.Arguments[0]
186 | call, isFunc := goja.AssertFunction(timer.call.Arguments[0])
187 | if !isFunc {
188 | panic(re.vm.ToValue("js error: timer/timeout callback is not a function"))
189 | }
190 | call(goja.Null(), timer.call.Arguments...)
191 |
192 | _, inreg := registry[timer] // when clearInterval is called from within the callback don't reset it
193 | if timer.interval && inreg {
194 | timer.timer.Reset(timer.duration)
195 | } else {
196 | delete(registry, timer)
197 | if waitForCallbacks && (len(registry) == 0) {
198 | break loop
199 | }
200 | }
201 | case req := <-re.evalQueue:
202 | // run the code, send the result back
203 | req.fn(re.vm)
204 | close(req.done)
205 | if waitForCallbacks && (len(registry) == 0) {
206 | break loop
207 | }
208 | case waitForCallbacks = <-re.stopEventLoop:
209 | if !waitForCallbacks || (len(registry) == 0) {
210 | break loop
211 | }
212 | }
213 | }
214 |
215 | for _, timer := range registry {
216 | timer.timer.Stop()
217 | delete(registry, timer)
218 | }
219 | }
220 |
221 | // Do executes the given function on the JS event loop.
222 | func (re *JSRE) Do(fn func(*goja.Runtime)) {
223 | done := make(chan bool)
224 | req := &evalReq{fn, done}
225 | re.evalQueue <- req
226 | <-done
227 | }
228 |
229 | // stops the event loop before exit, optionally waits for all timers to expire
230 | func (re *JSRE) Stop(waitForCallbacks bool) {
231 | select {
232 | case <-re.closed:
233 | case re.stopEventLoop <- waitForCallbacks:
234 | <-re.closed
235 | }
236 | }
237 |
238 | // Exec(file) loads and runs the contents of a file
239 | // if a relative path is given, the jsre's assetPath is used
240 | func (re *JSRE) Exec(file string) error {
241 | code, err := ioutil.ReadFile(AbsolutePath(re.assetPath, file))
242 | if err != nil {
243 | return err
244 | }
245 | return re.Compile(file, string(code))
246 | }
247 |
248 | // Run runs a piece of JS code.
249 | func (re *JSRE) Run(code string) (v goja.Value, err error) {
250 | re.Do(func(vm *goja.Runtime) { v, err = vm.RunString(code) })
251 | return v, err
252 | }
253 |
254 | // Set assigns value v to a variable in the JS environment.
255 | func (re *JSRE) Set(ns string, v interface{}) (err error) {
256 | re.Do(func(vm *goja.Runtime) { vm.Set(ns, v) })
257 | return err
258 | }
259 |
260 | // MakeCallback turns the given function into a function that's callable by JS.
261 | func MakeCallback(vm *goja.Runtime, fn func(Call) (goja.Value, error)) goja.Value {
262 | return vm.ToValue(func(call goja.FunctionCall) goja.Value {
263 | result, err := fn(Call{call, vm})
264 | if err != nil {
265 | panic(vm.NewGoError(err))
266 | }
267 | return result
268 | })
269 | }
270 |
271 | // Evaluate executes code and pretty prints the result to the specified output stream.
272 | func (re *JSRE) Evaluate(code string, w io.Writer) {
273 | re.Do(func(vm *goja.Runtime) {
274 | val, err := vm.RunString(code)
275 | if err != nil {
276 | prettyError(vm, err, w)
277 | } else {
278 | prettyPrint(vm, val, w)
279 | }
280 | fmt.Fprintln(w)
281 | })
282 | }
283 |
284 | // Compile compiles and then runs a piece of JS code.
285 | func (re *JSRE) Compile(filename string, src string) (err error) {
286 | re.Do(func(vm *goja.Runtime) { _, err = compileAndRun(vm, filename, src) })
287 | return err
288 | }
289 |
290 | // loadScript loads and executes a JS file.
291 | func (re *JSRE) loadScript(call Call) (goja.Value, error) {
292 | file := call.Argument(0).ToString().String()
293 | file = AbsolutePath(re.assetPath, file)
294 | source, err := ioutil.ReadFile(file)
295 | if err != nil {
296 | return nil, fmt.Errorf("Could not read file %s: %v", file, err)
297 | }
298 | value, err := compileAndRun(re.vm, file, string(source))
299 | if err != nil {
300 | return nil, fmt.Errorf("Error while compiling or running script: %v", err)
301 | }
302 | return value, nil
303 | }
304 |
305 | func compileAndRun(vm *goja.Runtime, filename string, src string) (goja.Value, error) {
306 | script, err := goja.Compile(filename, src, false)
307 | if err != nil {
308 | return goja.Null(), err
309 | }
310 | return vm.RunProgram(script)
311 | }
312 |
313 | // AbsolutePath returns datadir + filename, or filename if it is absolute.
314 | func AbsolutePath(datadir string, filename string) string {
315 | if filepath.IsAbs(filename) {
316 | return filename
317 | }
318 | return filepath.Join(datadir, filename)
319 | }
320 |
--------------------------------------------------------------------------------
/.github/workflows/hmybuild.yml:
--------------------------------------------------------------------------------
1 | name: release hmy
2 |
3 | on:
4 | push:
5 | tags:
6 | - v*
7 |
8 | env:
9 | GOPATH: ${{ github.workspace }}
10 | GOBIN: ${{ github.workspace }}/bin
11 |
12 | jobs:
13 | build-x86_64:
14 | name: Build hmy binary for x86_64
15 | runs-on: ${{ matrix.os }}
16 | strategy:
17 | matrix:
18 | os: [ ubuntu-22.04, macos-latest ]
19 |
20 | steps:
21 | - name: Checkout hmy code
22 | uses: actions/checkout@v4
23 | with:
24 | path: go-sdk
25 |
26 | - name: Set up Go
27 | uses: actions/setup-go@v5
28 | with:
29 | go-version-file: go-sdk/go.mod
30 |
31 | - name: Checkout dependence repo
32 | uses: actions/checkout@v4
33 | with:
34 | repository: harmony-one/mcl
35 | path: ${{ github.workspace }}/src/github.com/harmony-one/mcl
36 | ref: master
37 | fetch-depth: 0
38 |
39 | - name: Checkout dependence repo
40 | uses: actions/checkout@v4
41 | with:
42 | repository: harmony-one/bls
43 | path: ${{ github.workspace }}/src/github.com/harmony-one/bls
44 | ref: master
45 | fetch-depth: 0
46 |
47 | - name: Checkout dependence code
48 | uses: actions/checkout@v4
49 | with:
50 | repository: harmony-one/harmony
51 | path: ${{ github.workspace }}/src/github.com/harmony-one/harmony
52 | ref: main
53 | fetch-depth: 0
54 |
55 | - name: Get latest version and release
56 | run: |
57 | VERSION=$(git tag -l --sort=-v:refname | head -n 1 | tr -d v)
58 | RELEASE=$(git describe --long | cut -f2 -d-)
59 | echo "build_version=$VERSION" >> $GITHUB_ENV
60 | echo "build_release=$RELEASE" >> $GITHUB_ENV
61 | working-directory: go-sdk
62 |
63 | - name: Debug
64 | run: |
65 | pwd
66 | echo ${HOME}
67 | echo ${GITHUB_WORKSPACE}
68 | echo ${GOPATH}
69 | echo ${GOROOT}
70 |
71 | - name: Build hmy binary for Linux
72 | if: matrix.os == 'ubuntu-22.04'
73 | run: |
74 | make static
75 | working-directory: go-sdk
76 |
77 | - name: Build libs for macos-latest
78 | if: matrix.os == 'macos-latest'
79 | run: |
80 | brew install gmp
81 | brew install openssl
82 | sudo mkdir -p /opt/homebrew/opt/
83 | sudo ln -sf /usr/local/opt/openssl@1.1 /opt/homebrew/opt/openssl@1.1
84 | echo "ls -l /opt/homebrew/opt/openssl@1.1"; ls -l /opt/homebrew/opt/openssl@1.1
85 | make libs
86 | working-directory: ${{ github.workspace }}/src/github.com/harmony-one/harmony
87 |
88 | - name: Build hmy binary for macos-latest x86_64
89 | if: matrix.os == 'macos-latest'
90 | run: |
91 | make all
92 | mv dist/hmy dist/hmy-darwin-x86_64
93 | working-directory: go-sdk
94 |
95 | - name: Upload artifact
96 | if: matrix.os != 'macos-latest'
97 | uses: actions/upload-artifact@v4
98 | with:
99 | name: hmy
100 | path: ${{ github.workspace }}/go-sdk/dist/*
101 | retention-days: 1
102 |
103 | - name: Upload artifact darwin
104 | if: matrix.os == 'macos-latest'
105 | uses: actions/upload-artifact@v4
106 | with:
107 | name: hmy-darwin-x86_64
108 | path: ${{ github.workspace }}/go-sdk/dist/hmy-darwin-x86_64
109 | retention-days: 1
110 |
111 | # build-arm64:
112 | # name: Build hmy binary
113 | # runs-on: ${{ matrix.os }}
114 | # strategy:
115 | # matrix:
116 | # os: [ [ self-hosted,linux,ARM64 ] ]
117 |
118 | # steps:
119 |
120 | # - name: Set up Go 1.16.5
121 | # uses: actions/setup-go@v2
122 | # with:
123 | # go-version: 1.16.5
124 |
125 |
126 | # - name: Checkout hmy code
127 | # uses: actions/checkout@v2
128 | # with:
129 | # path: go/src/github.com/harmony-one/go-sdk
130 |
131 | # - name: Debug
132 | # run: |
133 | # pwd
134 | # echo ${HOME}
135 | # echo ${GITHUB_WORKSPACE}
136 | # echo ${GOPATH}
137 | # echo ${GOROOT}
138 | # env:
139 | # GOPATH: /home/ubuntu/actions-runner/_work/go-sdk/go-sdk/go
140 |
141 | # - name: Checkout dependence repo
142 | # uses: actions/checkout@v2
143 | # with:
144 | # repository: harmony-one/mcl
145 | # path: go/src/github.com/harmony-one/mcl
146 | # env:
147 | # GOPATH: /home/ubuntu/actions-runner/_work/go-sdk/go-sdk/go
148 |
149 | # - name: Checkout dependence repo
150 | # uses: actions/checkout@v2
151 | # with:
152 | # repository: harmony-one/bls
153 | # path: go/src/github.com/harmony-one/bls
154 | # env:
155 | # GOPATH: /home/ubuntu/actions-runner/_work/go-sdk/go-sdk/go
156 |
157 | # - name: Checkout dependence code
158 | # uses: actions/checkout@v2
159 | # with:
160 | # repository: harmony-one/harmony
161 | # path: go/src/github.com/harmony-one/harmony
162 | # ref: main
163 | # fetch-depth: 0
164 | # env:
165 | # GOPATH: /home/ubuntu/actions-runner/_work/go-sdk/go-sdk/go
166 |
167 | # - name: Build hmy binary for Arm
168 | # run: |
169 | # make static
170 | # mv dist/hmy dist/hmy-arm64
171 | # chmod +x dist/hmy-arm64
172 | # working-directory: /home/ubuntu/actions-runner/_work/go-sdk/go-sdk/go/src/github.com/harmony-one/go-sdk
173 | # env:
174 | # GOPATH: /home/ubuntu/actions-runner/_work/go-sdk/go-sdk/go
175 |
176 | # - name: Upload artifact
177 | # uses: actions/upload-artifact@v4
178 | # with:
179 | # name: hmy-arm64
180 | # path: /home/ubuntu/actions-runner/_work/go-sdk/go-sdk/go/src/github.com/harmony-one/go-sdk/dist/*
181 | # retention-days: 1
182 | # env:
183 | # GOPATH: /home/ubuntu/actions-runner/_work/go-sdk/go-sdk/go
184 |
185 | release-page:
186 | name: Sign binary and create and publish release page
187 | needs: [ build-x86_64 ]
188 | runs-on: ubuntu-22.04
189 | steps:
190 | - name: Import GPG key
191 | uses: crazy-max/ghaction-import-gpg@v6
192 | with:
193 | gpg_private_key: ${{ secrets.HMY_GPG_PRIVATE_KEY }}
194 | passphrase: ${{ secrets.HMY_GPG_PRIVATE_KEY_PASS }}
195 |
196 | - name: Checkout hmy core code
197 | uses: actions/checkout@v4
198 | with:
199 | path: go-sdk
200 |
201 | - name: Get latest version
202 | run: |
203 | VERSION=$(git tag -l --sort=-v:refname | head -n 1 | tr -d v)
204 | VERSION_LONG=$(git describe --always --long --dirty)
205 | RELEASE=$(git describe --long | cut -f2 -d-)
206 | echo "build_version=$VERSION" >> $GITHUB_ENV
207 | echo "build_version_long=$VERSION_LONG" >> $GITHUB_ENV
208 | echo "build_release=$RELEASE" >> $GITHUB_ENV
209 | working-directory: go-sdk
210 |
211 | - name: Download artifact
212 | uses: actions/download-artifact@v4
213 | with:
214 | name: hmy
215 |
216 | - name: Download artifact
217 | uses: actions/download-artifact@v4
218 | with:
219 | name: hmy-darwin-x86_64
220 |
221 | - name: Display structure of downloaded files
222 | run: ls -R
223 |
224 | - name: Signed darwin x86_64 hmy binary
225 | run: |
226 | gpg --detach-sign hmy-darwin-x86_64
227 | sha256sum hmy-darwin-x86_64 >> hmy-darwin-x86_64.sha256
228 |
229 | - name: Get tag message
230 | env:
231 | TAG_SHA: ${{ github.event.after }}
232 | run: |
233 | touch ./tag_message.md
234 | echo -e "$TAG_SHA\n\nThe released version: $build_version_long" >> ./tag_message.md
235 | working-directory: go-sdk
236 |
237 | - name: Create Release
238 | id: create_release
239 | uses: actions/create-release@v1
240 | env:
241 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
242 | with:
243 | tag_name: ${{ github.ref }}
244 | release_name: Mainnet Release ${{ env.build_version }}
245 | draft: true
246 | prerelease: false
247 | body_path: ${{ github.workspace }}/go-sdk/tag_message.md
248 |
249 | - name: Upload hmy binary for Linux (x86_64)
250 | uses: actions/upload-release-asset@v1
251 | env:
252 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
253 | with:
254 | upload_url: ${{ steps.create_release.outputs.upload_url }}
255 | asset_path: ./hmy
256 | asset_name: hmy
257 | asset_content_type: application/octet-stream
258 |
259 | - name: Upload hmy binary darwin-x86_64
260 | uses: actions/upload-release-asset@v1
261 | env:
262 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
263 | with:
264 | upload_url: ${{ steps.create_release.outputs.upload_url }}
265 | asset_path: ./hmy-darwin-x86_64
266 | asset_name: hmy-darwin-x86_64
267 | asset_content_type: application/octet-stream
268 |
269 |
270 | # - name: Upload hmy binary for ARM64
271 | # uses: actions/upload-release-asset@v1
272 | # env:
273 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
274 | # GOPATH: /home/runner/work/go-sdk/go-sdk/go
275 | # with:
276 | # upload_url: ${{ steps.create_release.outputs.upload_url }}
277 | # asset_path: ./hmy-arm64
278 | # asset_name: hmy-arm64
279 | # asset_content_type: application/octet-stream
280 |
281 | # - name: Upload sha256 signature of hmy arm64 binary
282 | # uses: actions/upload-release-asset@v1
283 | # env:
284 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
285 | # with:
286 | # upload_url: ${{ steps.create_release.outputs.upload_url }}
287 | # asset_path: ./hmy-arm64.sha256
288 | # asset_name: hmy-arm64.sha256
289 | # asset_content_type: text/plain
290 |
291 | # - name: Upload gpg signature of hmy arm64 binary
292 | # uses: actions/upload-release-asset@v1
293 | # env:
294 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
295 | # with:
296 | # upload_url: ${{ steps.create_release.outputs.upload_url }}
297 | # asset_path: ./hmy-arm64.sig
298 | # asset_name: hmy-arm64.sig
299 | # asset_content_type: application/octet-stream
300 |
--------------------------------------------------------------------------------