pull/254/head
Lutty 4 years ago
parent 04d6def83c
commit fe647eaba0
  1. 48
      cmd/subcommands/command.go
  2. 1
      pkg/common/values.go
  3. 2
      pkg/console/bridge.go
  4. 303
      pkg/console/console.go
  5. 278
      pkg/console/hmy.go
  6. 16
      pkg/console/jsre/completion.go
  7. 16
      pkg/console/jsre/completion_test.go
  8. 16
      pkg/console/jsre/jsre.go
  9. 16
      pkg/console/jsre/jsre_test.go
  10. 16
      pkg/console/jsre/pretty.go
  11. 16
      pkg/console/prompt/prompter.go

@ -1,9 +1,10 @@
package cmd
import (
"fmt"
"github.com/ethereum/go-ethereum/rpc"
ethereum_rpc "github.com/ethereum/go-ethereum/rpc"
"github.com/harmony-one/go-sdk/pkg/common"
"github.com/harmony-one/go-sdk/pkg/console"
"github.com/harmony-one/go-sdk/pkg/rpc"
"github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"log"
@ -12,26 +13,24 @@ import (
)
func init() {
shardNum := 0
net := "mainnet"
cmdCommand := &cobra.Command{
Use: "command",
Short: "Start an interactive JavaScript environment (connect to node)",
RunE: func(cmd *cobra.Command, args []string) error {
return openConsole(net, shardNum)
return openConsole(net)
},
}
cmdCommand.Flags().IntVar(&shardNum, "shard", 0, "used shard(default: 0)")
cmdCommand.Flags().StringVar(&net, "net", "mainnet", "used net(default: mainnet, choose: mainnet or testnet)")
cmdCommand.Flags().StringVar(&net, "net", "mainnet", "used net(default: mainnet, eg: mainnet, testnet ...)")
RootCmd.AddCommand(cmdCommand)
}
func checkAndMakeDirIfNeeded() string {
userDir, _ := homedir.Dir()
hmyCLIDir := path.Join(userDir, ".hmy_cli", "command")
hmyCLIDir := path.Join(userDir, common.DefaultConfigDirName, common.DefaultCommandAliasesDirName)
if _, err := os.Stat(hmyCLIDir); os.IsNotExist(err) {
// Double check with Leo what is right file persmission
os.Mkdir(hmyCLIDir, 0700)
@ -40,37 +39,34 @@ func checkAndMakeDirIfNeeded() string {
return hmyCLIDir
}
// remoteConsole will connect to a remote geth instance, attaching a JavaScript
// remoteConsole will connect to a remote node instance, attaching a JavaScript
// console to it.
func openConsole(net string, shard int) error {
var netMap = map[string]string{
"mainnet-0": "https://api.harmony.one",
"mainnet-1": "https://s1.api.harmony.one",
"mainnet-2": "https://s2.api.harmony.one",
"mainnet-3": "https://s3.api.harmony.one",
"testnet-0": "https://api.s0.b.hmny.io",
"testnet-1": "https://api.s1.b.hmny.io",
"testnet-2": "https://api.s2.b.hmny.io",
"testnet-3": "https://api.s3.b.hmny.io",
func openConsole(net string) error {
client, err := ethereum_rpc.Dial(node)
if err != nil {
log.Fatalf("Unable to attach to remote node: %v", err)
}
endpoint := ""
if findedEndpoint, ok := netMap[fmt.Sprintf("%s-%d", net, shard)]; ok {
endpoint = findedEndpoint
} else {
log.Fatalf("Unknown network `%s` or shardId `%d`", net, shard)
// check net type
_, err = common.StringToChainID(net)
if err != nil {
return err
}
client, err := rpc.Dial(endpoint)
// get shard id
nodeRPCReply, err := rpc.Request(rpc.Method.GetShardID, node, []interface{}{})
if err != nil {
log.Fatalf("Unable to attach to remote geth: %v", err)
return err
}
shard := int(nodeRPCReply["result"].(float64))
config := console.Config{
DataDir: checkAndMakeDirIfNeeded(),
DocRoot: ".",
Client: client,
Preload: nil,
NodeUrl: endpoint,
NodeUrl: node,
ShardId: shard,
Net: net,
}

@ -10,6 +10,7 @@ import (
const (
DefaultConfigDirName = ".hmy_cli"
DefaultConfigAccountAliasesDirName = "account-keys"
DefaultCommandAliasesDirName = "command"
DefaultPassphrase = ""
JSONRPCVersion = "2.0"
Secp256k1PrivateKeyBytesLength = 32

@ -15,7 +15,7 @@ import (
"github.com/ethereum/go-ethereum/rpc"
)
// bridge is a collection of JavaScript utility methods to bride the .js runtime
// bridge is a collection of JavaScript utility methods to bridge the .js runtime
// environment and the Go RPC connection backing the remote method calls.
type bridge struct {
client *rpc.Client // RPC client to execute Ethereum requests through

@ -1,21 +1,33 @@
package console
import (
"encoding/hex"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/rpc"
ethereum_rpc "github.com/ethereum/go-ethereum/rpc"
"github.com/harmony-one/go-sdk/pkg/account"
"github.com/harmony-one/go-sdk/pkg/address"
"github.com/harmony-one/go-sdk/pkg/common"
"github.com/harmony-one/go-sdk/pkg/console/jsre"
"github.com/harmony-one/go-sdk/pkg/console/jsre/deps"
"github.com/harmony-one/go-sdk/pkg/console/prompt"
"github.com/harmony-one/go-sdk/pkg/console/web3ext"
"github.com/harmony-one/go-sdk/pkg/rpc"
"github.com/harmony-one/go-sdk/pkg/store"
"github.com/harmony-one/go-sdk/pkg/transaction"
"github.com/harmony-one/harmony/accounts"
"io"
"io/ioutil"
"math/big"
"os"
"os/signal"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"syscall"
"time"
"github.com/dop251/goja"
"github.com/mattn/go-colorable"
@ -38,32 +50,32 @@ const DefaultPrompt = "> "
// Config is the collection of configurations to fine tune the behavior of the
// JavaScript console.
type Config struct {
DataDir string // Data directory to store the console history at
DocRoot string // Filesystem path from where to load JavaScript files from
Client *rpc.Client // RPC client to execute Ethereum requests through
Prompt string // Input prompt prefix string (defaults to DefaultPrompt)
Prompter prompt.UserPrompter // Input prompter to allow interactive user feedback (defaults to TerminalPrompter)
Printer io.Writer // Output writer to serialize any display strings to (defaults to os.Stdout)
Preload []string // Absolute paths to JavaScript files to preload
NodeUrl string // Hmy Node url
ShardId int // Hmy Shard ID
Net string // Hmy Network
DataDir string // Data directory to store the console history at
DocRoot string // Filesystem path from where to load JavaScript files from
Client *ethereum_rpc.Client // RPC client to execute Ethereum requests through
Prompt string // Input prompt prefix string (defaults to DefaultPrompt)
Prompter prompt.UserPrompter // Input prompter to allow interactive user feedback (defaults to TerminalPrompter)
Printer io.Writer // Output writer to serialize any display strings to (defaults to os.Stdout)
Preload []string // Absolute paths to JavaScript files to preload
NodeUrl string // Hmy Node url
ShardId int // Hmy Shard ID
Net string // Hmy Network
}
// Console is a JavaScript interpreted runtime environment. It is a fully fledged
// JavaScript console attached to a running node via an external or in-process RPC
// client.
type Console struct {
client *rpc.Client // RPC client to execute Ethereum requests through
jsre *jsre.JSRE // JavaScript runtime environment running the interpreter
prompt string // Input prompt prefix string
prompter prompt.UserPrompter // Input prompter to allow interactive user feedback
histPath string // Absolute path to the console scrollback history
history []string // Scroll history maintained by the console
printer io.Writer // Output writer to serialize any display strings to
nodeUrl string // Hmy Node url
shardId int // Hmy Shard ID
net string // Hmy Network
client *ethereum_rpc.Client // RPC client to execute Ethereum requests through
jsre *jsre.JSRE // JavaScript runtime environment running the interpreter
prompt string // Input prompt prefix string
prompter prompt.UserPrompter // Input prompter to allow interactive user feedback
histPath string // Absolute path to the console scrollback history
history []string // Scroll history maintained by the console
printer io.Writer // Output writer to serialize any display strings to
nodeUrl string // Hmy Node url
shardId int // Hmy Shard ID
net string // Hmy Network
}
// New initializes a JavaScript interpreted runtime environment and sets defaults
@ -493,3 +505,252 @@ func (c *Console) Stop(graceful bool) error {
c.jsre.Stop(graceful)
return nil
}
func (b *bridge) callbackProtected(protectedFunc func(call jsre.Call) (goja.Value, error)) func(call jsre.Call) (goja.Value, error) {
return func(call jsre.Call) (goja.Value, error) {
var availableCB goja.Callable = nil
for i, args := range call.Arguments {
if cb, ok := goja.AssertFunction(args); ok {
availableCB = cb
call.Arguments = call.Arguments[:i] // callback must be last
break
}
}
value, err := protectedFunc(call)
jsErr := goja.Undefined()
if err != nil {
jsErr = call.VM.NewGoError(err)
}
if availableCB != nil {
_, _ = availableCB(nil, jsErr, value)
}
return value, err
}
}
func (b *bridge) HmyGetListAccounts(call jsre.Call) (goja.Value, error) {
var accounts = []string{}
for _, name := range store.LocalAccounts() {
ks := store.FromAccountName(name)
allAccounts := ks.Accounts()
for _, account := range allAccounts {
accounts = append(accounts, account.Address.String())
}
}
return call.VM.ToValue(accounts), nil
}
func (b *bridge) HmySignTransaction(call jsre.Call) (goja.Value, error) {
txObj := call.Arguments[0].ToObject(call.VM)
password := call.Arguments[1].String()
from := getStringFromJsObjWithDefault(txObj, "from", "")
to := getStringFromJsObjWithDefault(txObj, "to", "")
gasLimit := getStringFromJsObjWithDefault(txObj, "gas", "1000000")
amount := getStringFromJsObjWithDefault(txObj, "value", "0")
gasPrice := getStringFromJsObjWithDefault(txObj, "gasPrice", "1")
networkHandler := rpc.NewHTTPHandler(b.console.nodeUrl)
chanId, err := common.StringToChainID(b.console.net)
if err != nil {
return nil, err
}
ks, acct, err := store.UnlockedKeystore(from, password)
if err != nil {
return nil, err
}
ctrlr := transaction.NewController(networkHandler, ks, acct, *chanId, func(controller *transaction.Controller) {
// nop
})
tempLimit, err := strconv.ParseInt(gasLimit, 10, 64)
if err != nil {
return nil, err
}
if tempLimit < 0 {
return nil, errors.New(fmt.Sprintf("gas-limit can not be negative: %s", gasLimit))
}
gLimit := uint64(tempLimit)
amt, err := common.NewDecFromString(amount)
if err != nil {
return nil, fmt.Errorf("amount %w", err)
}
gPrice, err := common.NewDecFromString(gasPrice)
if err != nil {
return nil, fmt.Errorf("gas-price %w", err)
}
toP := &to
if to == "" {
toP = nil
}
nonce := transaction.GetNextPendingNonce(from, networkHandler)
err = ctrlr.SignTransaction(
nonce, gLimit,
toP,
uint32(b.console.shardId), uint32(b.console.shardId),
amt, gPrice,
[]byte{},
)
if err != nil {
return nil, err
}
info := ctrlr.TransactionInfo()
return call.VM.ToValue(map[string]interface{}{
"raw": ctrlr.RawTransaction(),
"tx": map[string]string{
"nonce": "0x" + big.NewInt(int64(info.Nonce())).Text(16),
"gasPrice": "0x" + info.GasPrice().Text(16),
"gas": "0x" + big.NewInt(int64(info.GasLimit())).Text(16),
"to": info.To().Hex(),
"value": "0x" + info.Value().Text(16),
"input": "0x" + hex.EncodeToString(info.Data()),
"v": "0x" + info.V().Text(16),
"r": "0x" + info.R().Text(16),
"s": "0x" + info.S().Text(16),
"hash": info.Hash().Hex(),
},
}), nil
}
func (b *bridge) HmySendTransaction(call jsre.Call) (goja.Value, error) {
txObj := call.Arguments[0].ToObject(call.VM)
password := ""
if len(call.Arguments) > 1 {
password = call.Arguments[1].String()
}
from := getStringFromJsObjWithDefault(txObj, "from", "")
to := getStringFromJsObjWithDefault(txObj, "to", "")
gasLimit := getStringFromJsObjWithDefault(txObj, "gas", "1000000")
amount := getStringFromJsObjWithDefault(txObj, "value", "0")
gasPrice := getStringFromJsObjWithDefault(txObj, "gasPrice", "1")
networkHandler := rpc.NewHTTPHandler(b.console.nodeUrl)
chanId, err := common.StringToChainID(b.console.net)
if err != nil {
return nil, err
}
ks, acct, err := store.UnlockedKeystore(from, password)
if err != nil {
return nil, err
}
ctrlr := transaction.NewController(networkHandler, ks, acct, *chanId, func(controller *transaction.Controller) {
// nop
})
tempLimit, err := strconv.ParseInt(gasLimit, 10, 64)
if err != nil {
return nil, err
}
if tempLimit < 0 {
return nil, errors.New(fmt.Sprintf("gas-limit can not be negative: %s", gasLimit))
}
gLimit := uint64(tempLimit)
amt, err := common.NewDecFromString(amount)
if err != nil {
return nil, fmt.Errorf("amount %w", err)
}
gPrice, err := common.NewDecFromString(gasPrice)
if err != nil {
return nil, fmt.Errorf("gas-price %w", err)
}
toP := &to
if to == "" {
toP = nil
}
nonce := transaction.GetNextPendingNonce(from, networkHandler)
err = ctrlr.ExecuteTransaction(
nonce, gLimit,
toP,
uint32(b.console.shardId), uint32(b.console.shardId),
amt, gPrice,
[]byte{},
)
if err != nil {
return nil, err
}
return call.VM.ToValue(*ctrlr.TransactionHash()), nil
}
func (b *bridge) HmyLockAccount(call jsre.Call) (goja.Value, error) {
address := call.Arguments[0].String()
_, _, err := store.LockKeystore(address)
if err != nil {
return nil, err
}
return goja.Null(), nil
}
func (b *bridge) HmyImportRawKey(call jsre.Call) (goja.Value, error) {
privateKey := call.Arguments[0].String()
password := call.Arguments[1].String()
name, err := account.ImportFromPrivateKey(privateKey, "", password)
if err != nil {
return nil, err
}
return call.VM.ToValue(name), nil
}
func (b *bridge) HmyUnlockAccount(call jsre.Call) (goja.Value, error) {
if len(call.Arguments) < 3 {
return nil, errors.New("arguments < 3")
}
address := call.Arguments[0].String()
password := call.Arguments[1].String()
unlockDuration := call.Arguments[2].ToInteger()
_, _, err := store.UnlockedKeystoreTimeLimit(address, password, time.Duration(unlockDuration)*time.Second)
if err != nil {
return nil, err
}
return goja.Null(), nil
}
func (b *bridge) HmyNewAccount(call jsre.Call) (goja.Value, error) {
return goja.Null(), nil
}
func (b *bridge) HmySign(call jsre.Call) (goja.Value, error) {
dataToSign := call.Arguments[0].String()
addressStr := call.Arguments[1].String()
password := call.Arguments[2].String()
ks := store.FromAddress(addressStr)
if ks == nil {
return nil, fmt.Errorf("could not open local keystore for %s", addressStr)
}
acc, err := ks.Find(accounts.Account{Address: address.Parse(addressStr)})
if err != nil {
return nil, err
}
message, err := signMessageWithPassword(ks, acc, password, []byte(dataToSign))
if err != nil {
return nil, err
}
return call.VM.ToValue(hex.EncodeToString(message)), nil
}

@ -1,283 +1,14 @@
package console
import (
"encoding/hex"
"errors"
"fmt"
"github.com/dop251/goja"
"github.com/harmony-one/go-sdk/pkg/account"
"github.com/harmony-one/go-sdk/pkg/address"
"github.com/harmony-one/go-sdk/pkg/common"
"github.com/harmony-one/go-sdk/pkg/console/jsre"
"github.com/harmony-one/go-sdk/pkg/rpc"
"github.com/harmony-one/go-sdk/pkg/store"
"github.com/harmony-one/go-sdk/pkg/transaction"
"github.com/harmony-one/harmony/accounts"
"github.com/harmony-one/harmony/accounts/keystore"
"github.com/harmony-one/harmony/crypto/hash"
"math/big"
"strconv"
"time"
)
func getStringFromJsObjWithDefault(o *goja.Object, key string, def string) string {
get := o.Get(key)
if get == nil {
return def
} else {
return get.String()
}
}
func (b *bridge) callbackProtected(protectedFunc func(call jsre.Call) (goja.Value, error)) func(call jsre.Call) (goja.Value, error) {
return func(call jsre.Call) (goja.Value, error) {
var availableCB goja.Callable = nil
for i, args := range call.Arguments {
if cb, ok := goja.AssertFunction(args); ok {
availableCB = cb
call.Arguments = call.Arguments[:i] // callback must be last
break
}
}
value, err := protectedFunc(call)
jsErr := goja.Undefined()
if err != nil {
jsErr = call.VM.NewGoError(err)
}
if availableCB != nil {
_, _ = availableCB(nil, jsErr, value)
}
return value, err
}
}
func (b *bridge) HmyGetListAccounts(call jsre.Call) (goja.Value, error) {
var accounts = []string{}
for _, name := range store.LocalAccounts() {
ks := store.FromAccountName(name)
allAccounts := ks.Accounts()
for _, account := range allAccounts {
accounts = append(accounts, account.Address.String())
}
}
return call.VM.ToValue(accounts), nil
}
func (b *bridge) HmySignTransaction(call jsre.Call) (goja.Value, error) {
txObj := call.Arguments[0].ToObject(call.VM)
password := call.Arguments[1].String()
from := getStringFromJsObjWithDefault(txObj, "from", "")
to := getStringFromJsObjWithDefault(txObj, "to", "")
gasLimit := getStringFromJsObjWithDefault(txObj, "gas", "1000000")
amount := getStringFromJsObjWithDefault(txObj, "value", "0")
gasPrice := getStringFromJsObjWithDefault(txObj, "gasPrice", "1")
networkHandler := rpc.NewHTTPHandler(b.console.nodeUrl)
chanId, err := common.StringToChainID(b.console.net)
if err != nil {
return nil, err
}
ks, acct, err := store.UnlockedKeystore(from, password)
if err != nil {
return nil, err
}
ctrlr := transaction.NewController(networkHandler, ks, acct, *chanId, func(controller *transaction.Controller) {
// nop
})
tempLimit, err := strconv.ParseInt(gasLimit, 10, 64)
if err != nil {
return nil, err
}
if tempLimit < 0 {
return nil, errors.New(fmt.Sprintf("gas-limit can not be negative: %s", gasLimit))
}
gLimit := uint64(tempLimit)
amt, err := common.NewDecFromString(amount)
if err != nil {
return nil, fmt.Errorf("amount %w", err)
}
gPrice, err := common.NewDecFromString(gasPrice)
if err != nil {
return nil, fmt.Errorf("gas-price %w", err)
}
toP := &to
if to == "" {
toP = nil
}
nonce := transaction.GetNextPendingNonce(from, networkHandler)
err = ctrlr.SignTransaction(
nonce, gLimit,
toP,
uint32(b.console.shardId), uint32(b.console.shardId),
amt, gPrice,
[]byte{},
)
if err != nil {
return nil, err
}
info := ctrlr.TransactionInfo()
return call.VM.ToValue(map[string]interface{}{
"raw": ctrlr.RawTransaction(),
"tx": map[string]string{
"nonce": "0x" + big.NewInt(int64(info.Nonce())).Text(16),
"gasPrice": "0x" + info.GasPrice().Text(16),
"gas": "0x" + big.NewInt(int64(info.GasLimit())).Text(16),
"to": info.To().Hex(),
"value": "0x" + info.Value().Text(16),
"input": "0x" + hex.EncodeToString(info.Data()),
"v": "0x" + info.V().Text(16),
"r": "0x" + info.R().Text(16),
"s": "0x" + info.S().Text(16),
"hash": info.Hash().Hex(),
},
}), nil
}
func (b *bridge) HmySendTransaction(call jsre.Call) (goja.Value, error) {
txObj := call.Arguments[0].ToObject(call.VM)
password := ""
if len(call.Arguments) > 1 {
password = call.Arguments[1].String()
}
from := getStringFromJsObjWithDefault(txObj, "from", "")
to := getStringFromJsObjWithDefault(txObj, "to", "")
gasLimit := getStringFromJsObjWithDefault(txObj, "gas", "1000000")
amount := getStringFromJsObjWithDefault(txObj, "value", "0")
gasPrice := getStringFromJsObjWithDefault(txObj, "gasPrice", "1")
networkHandler := rpc.NewHTTPHandler(b.console.nodeUrl)
chanId, err := common.StringToChainID(b.console.net)
if err != nil {
return nil, err
}
ks, acct, err := store.UnlockedKeystore(from, password)
if err != nil {
return nil, err
}
ctrlr := transaction.NewController(networkHandler, ks, acct, *chanId, func(controller *transaction.Controller) {
// nop
})
tempLimit, err := strconv.ParseInt(gasLimit, 10, 64)
if err != nil {
return nil, err
}
if tempLimit < 0 {
return nil, errors.New(fmt.Sprintf("gas-limit can not be negative: %s", gasLimit))
}
gLimit := uint64(tempLimit)
amt, err := common.NewDecFromString(amount)
if err != nil {
return nil, fmt.Errorf("amount %w", err)
}
gPrice, err := common.NewDecFromString(gasPrice)
if err != nil {
return nil, fmt.Errorf("gas-price %w", err)
}
toP := &to
if to == "" {
toP = nil
}
nonce := transaction.GetNextPendingNonce(from, networkHandler)
err = ctrlr.ExecuteTransaction(
nonce, gLimit,
toP,
uint32(b.console.shardId), uint32(b.console.shardId),
amt, gPrice,
[]byte{},
)
if err != nil {
return nil, err
}
return call.VM.ToValue(*ctrlr.TransactionHash()), nil
}
func (b *bridge) HmyLockAccount(call jsre.Call) (goja.Value, error) {
address := call.Arguments[0].String()
_, _, err := store.LockKeystore(address)
if err != nil {
return nil, err
}
return goja.Null(), nil
}
func (b *bridge) HmyImportRawKey(call jsre.Call) (goja.Value, error) {
privateKey := call.Arguments[0].String()
password := call.Arguments[1].String()
name, err := account.ImportFromPrivateKey(privateKey, "", password)
if err != nil {
return nil, err
}
return call.VM.ToValue(name), nil
}
func (b *bridge) HmyUnlockAccount(call jsre.Call) (goja.Value, error) {
if len(call.Arguments) < 3 {
return nil, errors.New("arguments < 3")
}
address := call.Arguments[0].String()
password := call.Arguments[1].String()
unlockDuration := call.Arguments[2].ToInteger()
_, _, err := store.UnlockedKeystoreTimeLimit(address, password, time.Duration(unlockDuration)*time.Second)
if err != nil {
return nil, err
}
return goja.Null(), nil
}
func (b *bridge) HmyNewAccount(call jsre.Call) (goja.Value, error) {
return goja.Null(), nil
}
func (b *bridge) HmySign(call jsre.Call) (goja.Value, error) {
dataToSign := call.Arguments[0].String()
addressStr := call.Arguments[1].String()
password := call.Arguments[2].String()
ks := store.FromAddress(addressStr)
if ks == nil {
return nil, fmt.Errorf("could not open local keystore for %s", addressStr)
}
acc, err := ks.Find(accounts.Account{Address: address.Parse(addressStr)})
if err != nil {
return nil, err
}
message, err := signMessageWithPassword(ks, acc, password, []byte(dataToSign))
if err != nil {
return nil, err
}
return call.VM.ToValue(hex.EncodeToString(message)), nil
}
func signMessageWithPassword(keyStore *keystore.KeyStore, account accounts.Account, password string, data []byte) (sign []byte, err error) {
signData := append([]byte("\x19Ethereum Signed Message:\n" + strconv.Itoa(len(data))))
msgHash := hash.Keccak256(append(signData, data...))
@ -294,3 +25,12 @@ func signMessageWithPassword(keyStore *keystore.KeyStore, account accounts.Accou
sign[64] += 0x1b
return sign, nil
}
func getStringFromJsObjWithDefault(o *goja.Object, key string, def string) string {
get := o.Get(key)
if get == nil {
return def
} else {
return get.String()
}
}

@ -1,3 +1,19 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package jsre
import (

@ -1,3 +1,19 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package jsre
import (

@ -1,3 +1,19 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package jsre
import (

@ -1,3 +1,19 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package jsre
import (

@ -1,3 +1,19 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package jsre
import (

@ -1,3 +1,19 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package prompt
import (

Loading…
Cancel
Save