Compare commits
63 Commits
Author | SHA1 | Date | |
---|---|---|---|
021c3c2816 | |||
ff2c966e7f | |||
14cc40a31a | |||
881df0e629 | |||
1c2f6f5597 | |||
d51a9fd6b7 | |||
464f30d301 | |||
8a28408616 | |||
e1dc7ece62 | |||
99127ff2e7 | |||
81d6ec908a | |||
38b4fc8069 | |||
11ab73f6d8 | |||
18e9cb1187 | |||
502a2bd69f | |||
8b517d7f00 | |||
cad071009d | |||
181a3309df | |||
02fa3e3179 | |||
a8eafcdc0e | |||
bcf2465b0b | |||
c3dc01caf1 | |||
cf4faa491a | |||
59966255ad | |||
f8acc0af7e | |||
02a29060d2 | |||
0255ed6335 | |||
96c2ab22e0 | |||
5494e6e7ab | |||
32db571681 | |||
a6af56fa4d | |||
5884606ec3 | |||
f6c0f76cc5 | |||
f9be9a2302 | |||
95f0bd0acf | |||
8dce4c283d | |||
fff16169c6 | |||
5f7eb78918 | |||
e61035c5a3 | |||
37e3f561f1 | |||
ba3bcd16a6 | |||
207bd7d2cd | |||
4047ccad2f | |||
a13e920af0 | |||
f958d7d482 | |||
7cc6abeef6 | |||
54253aae4c | |||
09aabaea9f | |||
ecec454e92 | |||
7b2fc0643f | |||
d2fda73ad7 | |||
5aa21d8b32 | |||
9fc90b6747 | |||
edef84da2b | |||
6430e672c9 | |||
a31d268b76 | |||
e353f9c088 | |||
af48a331bf | |||
80e74fc1e0 | |||
03dffe3efd | |||
cb3f5f8b93 | |||
c7a4d9cf8a | |||
7d0ac94809 |
@ -161,6 +161,12 @@ and agree upon. This consists of a small JSON file (e.g. call it `genesis.json`)
|
||||
|
||||
```json
|
||||
{
|
||||
"config": {
|
||||
"chainId": 0,
|
||||
"homesteadBlock": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0
|
||||
},
|
||||
"alloc" : {},
|
||||
"coinbase" : "0x0000000000000000000000000000000000000000",
|
||||
"difficulty" : "0x20000",
|
||||
|
@ -77,6 +77,7 @@ var (
|
||||
executablePath("puppeth"),
|
||||
executablePath("rlpdump"),
|
||||
executablePath("swarm"),
|
||||
executablePath("wnode"),
|
||||
}
|
||||
|
||||
// A debian package is created for all executables listed here.
|
||||
@ -109,6 +110,10 @@ var (
|
||||
Name: "swarm",
|
||||
Description: "Ethereum Swarm daemon and tools",
|
||||
},
|
||||
{
|
||||
Name: "wnode",
|
||||
Description: "Ethereum Whisper diagnostic tool",
|
||||
},
|
||||
}
|
||||
|
||||
// Distros for which packages are created.
|
||||
|
@ -68,6 +68,7 @@ func main() {
|
||||
if err = crypto.SaveECDSA(*genKey, nodeKey); err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
return
|
||||
case *nodeKeyFile == "" && *nodeKeyHex == "":
|
||||
utils.Fatalf("Use -nodekey or -nodekeyhex to specify a private key")
|
||||
case *nodeKeyFile != "" && *nodeKeyHex != "":
|
||||
|
@ -27,10 +27,13 @@ import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@ -60,12 +63,13 @@ var (
|
||||
apiPortFlag = flag.Int("apiport", 8080, "Listener port for the HTTP API connection")
|
||||
ethPortFlag = flag.Int("ethport", 30303, "Listener port for the devp2p connection")
|
||||
bootFlag = flag.String("bootnodes", "", "Comma separated bootnode enode URLs to seed with")
|
||||
netFlag = flag.Int("network", 0, "Network ID to use for the Ethereum protocol")
|
||||
netFlag = flag.Uint64("network", 0, "Network ID to use for the Ethereum protocol")
|
||||
statsFlag = flag.String("ethstats", "", "Ethstats network monitoring auth string")
|
||||
|
||||
netnameFlag = flag.String("faucet.name", "", "Network name to assign to the faucet")
|
||||
payoutFlag = flag.Int("faucet.amount", 1, "Number of Ethers to pay out per user request")
|
||||
minutesFlag = flag.Int("faucet.minutes", 1440, "Number of minutes to wait between funding rounds")
|
||||
tiersFlag = flag.Int("faucet.tiers", 3, "Number of funding tiers to enable (x3 time, x2.5 funds)")
|
||||
|
||||
accJSONFlag = flag.String("account.json", "", "Key json file to fund user requests with")
|
||||
accPassFlag = flag.String("account.pass", "", "Decryption password to access faucet funds")
|
||||
@ -73,6 +77,9 @@ var (
|
||||
githubUser = flag.String("github.user", "", "GitHub user to authenticate with for Gist access")
|
||||
githubToken = flag.String("github.token", "", "GitHub personal token to access Gists with")
|
||||
|
||||
captchaToken = flag.String("captcha.token", "", "Recaptcha site key to authenticate client side")
|
||||
captchaSecret = flag.String("captcha.secret", "", "Recaptcha secret key to authenticate server side")
|
||||
|
||||
logFlag = flag.Int("loglevel", 3, "Log level to use for Ethereum and the faucet")
|
||||
)
|
||||
|
||||
@ -85,21 +92,47 @@ func main() {
|
||||
flag.Parse()
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*logFlag), log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
||||
|
||||
// Construct the payout tiers
|
||||
amounts := make([]string, *tiersFlag)
|
||||
periods := make([]string, *tiersFlag)
|
||||
for i := 0; i < *tiersFlag; i++ {
|
||||
// Calculate the amount for the next tier and format it
|
||||
amount := float64(*payoutFlag) * math.Pow(2.5, float64(i))
|
||||
amounts[i] = fmt.Sprintf("%s Ethers", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
if amount == 1 {
|
||||
amounts[i] = strings.TrimSuffix(amounts[i], "s")
|
||||
}
|
||||
// Calcualte the period for th enext tier and format it
|
||||
period := *minutesFlag * int(math.Pow(3, float64(i)))
|
||||
periods[i] = fmt.Sprintf("%d mins", period)
|
||||
if period%60 == 0 {
|
||||
period /= 60
|
||||
periods[i] = fmt.Sprintf("%d hours", period)
|
||||
|
||||
if period%24 == 0 {
|
||||
period /= 24
|
||||
periods[i] = fmt.Sprintf("%d days", period)
|
||||
}
|
||||
}
|
||||
if period == 1 {
|
||||
periods[i] = strings.TrimSuffix(periods[i], "s")
|
||||
}
|
||||
}
|
||||
// Load up and render the faucet website
|
||||
tmpl, err := Asset("faucet.html")
|
||||
if err != nil {
|
||||
log.Crit("Failed to load the faucet template", "err", err)
|
||||
}
|
||||
period := fmt.Sprintf("%d minute(s)", *minutesFlag)
|
||||
if *minutesFlag%60 == 0 {
|
||||
period = fmt.Sprintf("%d hour(s)", *minutesFlag/60)
|
||||
}
|
||||
website := new(bytes.Buffer)
|
||||
template.Must(template.New("").Parse(string(tmpl))).Execute(website, map[string]interface{}{
|
||||
"Network": *netnameFlag,
|
||||
"Amount": *payoutFlag,
|
||||
"Period": period,
|
||||
err = template.Must(template.New("").Parse(string(tmpl))).Execute(website, map[string]interface{}{
|
||||
"Network": *netnameFlag,
|
||||
"Amounts": amounts,
|
||||
"Periods": periods,
|
||||
"Recaptcha": *captchaToken,
|
||||
})
|
||||
if err != nil {
|
||||
log.Crit("Failed to render the faucet template", "err", err)
|
||||
}
|
||||
// Load and parse the genesis block requested by the user
|
||||
blob, err := ioutil.ReadFile(*genesisFlag)
|
||||
if err != nil {
|
||||
@ -166,15 +199,15 @@ type faucet struct {
|
||||
nonce uint64 // Current pending nonce of the faucet
|
||||
price *big.Int // Current gas price to issue funds with
|
||||
|
||||
conns []*websocket.Conn // Currently live websocket connections
|
||||
history map[string]time.Time // History of users and their funding requests
|
||||
reqs []*request // Currently pending funding requests
|
||||
update chan struct{} // Channel to signal request updates
|
||||
conns []*websocket.Conn // Currently live websocket connections
|
||||
timeouts map[string]time.Time // History of users and their funding timeouts
|
||||
reqs []*request // Currently pending funding requests
|
||||
update chan struct{} // Channel to signal request updates
|
||||
|
||||
lock sync.RWMutex // Lock protecting the faucet's internals
|
||||
}
|
||||
|
||||
func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network int, stats string, ks *keystore.KeyStore, index []byte) (*faucet, error) {
|
||||
func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network uint64, stats string, ks *keystore.KeyStore, index []byte) (*faucet, error) {
|
||||
// Assemble the raw devp2p protocol stack
|
||||
stack, err := node.New(&node.Config{
|
||||
Name: "geth",
|
||||
@ -236,7 +269,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network i
|
||||
index: index,
|
||||
keystore: ks,
|
||||
account: ks.Accounts()[0],
|
||||
history: make(map[string]time.Time),
|
||||
timeouts: make(map[string]time.Time),
|
||||
update: make(chan struct{}, 1),
|
||||
}, nil
|
||||
}
|
||||
@ -290,14 +323,23 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
|
||||
"peers": f.stack.Server().PeerCount(),
|
||||
"requests": f.reqs,
|
||||
})
|
||||
header, _ := f.client.HeaderByNumber(context.Background(), nil)
|
||||
websocket.JSON.Send(conn, header)
|
||||
// Send the initial block to the client
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
header, err := f.client.HeaderByNumber(ctx, nil)
|
||||
cancel()
|
||||
|
||||
if err != nil {
|
||||
log.Error("Failed to retrieve latest header", "err", err)
|
||||
} else {
|
||||
websocket.JSON.Send(conn, header)
|
||||
}
|
||||
// Keep reading requests from the websocket until the connection breaks
|
||||
for {
|
||||
// Fetch the next funding request and validate against github
|
||||
var msg struct {
|
||||
URL string `json:"url"`
|
||||
URL string `json:"url"`
|
||||
Tier uint `json:"tier"`
|
||||
Captcha string `json:"captcha"`
|
||||
}
|
||||
if err := websocket.JSON.Receive(conn, &msg); err != nil {
|
||||
return
|
||||
@ -306,8 +348,39 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
|
||||
websocket.JSON.Send(conn, map[string]string{"error": "URL doesn't link to GitHub Gists"})
|
||||
continue
|
||||
}
|
||||
log.Info("Faucet funds requested", "gist", msg.URL)
|
||||
if msg.Tier >= uint(*tiersFlag) {
|
||||
websocket.JSON.Send(conn, map[string]string{"error": "Invalid funding tier requested"})
|
||||
continue
|
||||
}
|
||||
log.Info("Faucet funds requested", "gist", msg.URL, "tier", msg.Tier)
|
||||
|
||||
// If captcha verifications are enabled, make sure we're not dealing with a robot
|
||||
if *captchaToken != "" {
|
||||
form := url.Values{}
|
||||
form.Add("secret", *captchaSecret)
|
||||
form.Add("response", msg.Captcha)
|
||||
|
||||
res, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", form)
|
||||
if err != nil {
|
||||
websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
|
||||
continue
|
||||
}
|
||||
var result struct {
|
||||
Success bool `json:"success"`
|
||||
Errors json.RawMessage `json:"error-codes"`
|
||||
}
|
||||
err = json.NewDecoder(res.Body).Decode(&result)
|
||||
res.Body.Close()
|
||||
if err != nil {
|
||||
websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
|
||||
continue
|
||||
}
|
||||
if !result.Success {
|
||||
log.Warn("Captcha verification failed", "err", string(result.Errors))
|
||||
websocket.JSON.Send(conn, map[string]string{"error": "Beep-bop, you're a robot!"})
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Retrieve the gist from the GitHub Gist APIs
|
||||
parts := strings.Split(msg.URL, "/")
|
||||
req, _ := http.NewRequest("GET", "https://api.github.com/gists/"+parts[len(parts)-1], nil)
|
||||
@ -334,7 +407,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
|
||||
continue
|
||||
}
|
||||
if gist.Owner.Login == "" {
|
||||
websocket.JSON.Send(conn, map[string]string{"error": "Nice try ;)"})
|
||||
websocket.JSON.Send(conn, map[string]string{"error": "Anonymous Gists not allowed"})
|
||||
continue
|
||||
}
|
||||
// Iterate over all the files and look for Ethereum addresses
|
||||
@ -348,15 +421,30 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
|
||||
websocket.JSON.Send(conn, map[string]string{"error": "No Ethereum address found to fund"})
|
||||
continue
|
||||
}
|
||||
// Validate the user's existence since the API is unhelpful here
|
||||
if res, err = http.Head("https://github.com/" + gist.Owner.Login); err != nil {
|
||||
websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
|
||||
continue
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
websocket.JSON.Send(conn, map[string]string{"error": "Invalid user... boom!"})
|
||||
continue
|
||||
}
|
||||
// Ensure the user didn't request funds too recently
|
||||
f.lock.Lock()
|
||||
var (
|
||||
fund bool
|
||||
elapsed time.Duration
|
||||
timeout time.Time
|
||||
)
|
||||
if elapsed = time.Since(f.history[gist.Owner.Login]); elapsed > time.Duration(*minutesFlag)*time.Minute {
|
||||
if timeout = f.timeouts[gist.Owner.Login]; time.Now().After(timeout) {
|
||||
// User wasn't funded recently, create the funding transaction
|
||||
tx := types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, new(big.Int).Mul(big.NewInt(int64(*payoutFlag)), ether), big.NewInt(21000), f.price, nil)
|
||||
amount := new(big.Int).Mul(big.NewInt(int64(*payoutFlag)), ether)
|
||||
amount = new(big.Int).Mul(amount, new(big.Int).Exp(big.NewInt(5), big.NewInt(int64(msg.Tier)), nil))
|
||||
amount = new(big.Int).Div(amount, new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(msg.Tier)), nil))
|
||||
|
||||
tx := types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, amount, big.NewInt(21000), f.price, nil)
|
||||
signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainId)
|
||||
if err != nil {
|
||||
websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
|
||||
@ -375,14 +463,14 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
|
||||
Time: time.Now(),
|
||||
Tx: signed,
|
||||
})
|
||||
f.history[gist.Owner.Login] = time.Now()
|
||||
f.timeouts[gist.Owner.Login] = time.Now().Add(time.Duration(*minutesFlag*int(math.Pow(3, float64(msg.Tier)))) * time.Minute)
|
||||
fund = true
|
||||
}
|
||||
f.lock.Unlock()
|
||||
|
||||
// Send an error if too frequent funding, othewise a success
|
||||
if !fund {
|
||||
websocket.JSON.Send(conn, map[string]string{"error": fmt.Sprintf("User already funded %s ago", common.PrettyDuration(elapsed))})
|
||||
websocket.JSON.Send(conn, map[string]string{"error": fmt.Sprintf("%s left until next allowance", common.PrettyDuration(timeout.Sub(time.Now())))})
|
||||
continue
|
||||
}
|
||||
websocket.JSON.Send(conn, map[string]string{"success": fmt.Sprintf("Funding request accepted for %s into %s", gist.Owner.Login, address.Hex())})
|
||||
|
@ -51,9 +51,13 @@
|
||||
<div class="input-group">
|
||||
<input id="gist" type="text" class="form-control" placeholder="GitHub Gist URL containing your Ethereum address...">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-default" type="button" onclick="submit()">Give me Ether!</button>
|
||||
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Give me Ether <i class="fa fa-caret-down" aria-hidden="true"></i></button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">{{range $idx, $amount := .Amounts}}
|
||||
<li><a style="text-align: center;" onclick="tier={{$idx}}; {{if $.Recaptcha}}grecaptcha.execute(){{else}}submit({{$idx}}){{end}}">{{$amount}} / {{index $.Periods $idx}}</a></li>{{end}}
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
</div>{{if .Recaptcha}}
|
||||
<div class="g-recaptcha" data-sitekey="{{.Recaptcha}}" data-callback="submit" data-size="invisible"></div>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="margin-top: 32px;">
|
||||
@ -76,8 +80,9 @@
|
||||
<div class="row" style="margin-top: 32px;">
|
||||
<div class="col-lg-12">
|
||||
<h3>How does this work?</h3>
|
||||
<p>This Ether faucet is running on the {{.Network}} network. To prevent malicious actors from exhausting all available funds or accumulating enough Ether to mount long running spam attacks, requests are tied to GitHub accounts. Anyone having a GitHub account may request funds within the permitted limit of <strong>{{.Amount}} Ether(s) / {{.Period}}</strong>.</p>
|
||||
<p>This Ether faucet is running on the {{.Network}} network. To prevent malicious actors from exhausting all available funds or accumulating enough Ether to mount long running spam attacks, requests are tied to GitHub accounts. Anyone having a GitHub account may request funds within the permitted limits.</p>
|
||||
<p>To request funds, simply create a <a href="https://gist.github.com/" target="_about:blank">GitHub Gist</a> with your Ethereum address pasted into the contents (the file name doesn't matter), copy paste the gists URL into the above input box and fire away! You can track the current pending requests below the input field to see how much you have to wait until your turn comes.</p>
|
||||
{{if .Recaptcha}}<em>The faucet is running invisible reCaptcha protection against bots.</em>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -87,10 +92,12 @@
|
||||
// Global variables to hold the current status of the faucet
|
||||
var attempt = 0;
|
||||
var server;
|
||||
var tier = 0;
|
||||
|
||||
// Define the function that submits a gist url to the server
|
||||
var submit = function() {
|
||||
server.send(JSON.stringify({url: $("#gist")[0].value}));
|
||||
var submit = function({{if .Recaptcha}}captcha{{end}}) {
|
||||
server.send(JSON.stringify({url: $("#gist")[0].value, tier: tier{{if .Recaptcha}}, captcha: captcha{{end}}}));{{if .Recaptcha}}
|
||||
grecaptcha.reset();{{end}}
|
||||
};
|
||||
// Define a method to reconnect upon server loss
|
||||
var reconnect = function() {
|
||||
@ -134,10 +141,10 @@
|
||||
}
|
||||
}
|
||||
server.onclose = function() { setTimeout(reconnect, 3000); };
|
||||
server.onerror = function() { setTimeout(reconnect, 3000); };
|
||||
}
|
||||
// Establish a websocket connection to the API server
|
||||
reconnect();
|
||||
</script>
|
||||
</script>{{if .Recaptcha}}
|
||||
<script src="https://www.google.com/recaptcha/api.js" async defer></script>{{end}}
|
||||
</body>
|
||||
</html>
|
||||
|
File diff suppressed because one or more lines are too long
@ -31,41 +31,33 @@ import (
|
||||
|
||||
var (
|
||||
walletCommand = cli.Command{
|
||||
Name: "wallet",
|
||||
Usage: "Manage Ethereum presale wallets",
|
||||
ArgsUsage: "",
|
||||
Category: "ACCOUNT COMMANDS",
|
||||
Description: `
|
||||
geth wallet import /path/to/my/presale.wallet
|
||||
|
||||
will prompt for your password and imports your ether presale account.
|
||||
It can be used non-interactively with the --password option taking a
|
||||
passwordfile as argument containing the wallet password in plaintext.
|
||||
|
||||
`,
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Action: importWallet,
|
||||
Name: "import",
|
||||
Usage: "Import Ethereum presale wallet",
|
||||
ArgsUsage: "<keyFile>",
|
||||
Description: `
|
||||
TODO: Please write this
|
||||
`,
|
||||
},
|
||||
Name: "wallet",
|
||||
Usage: "Import Ethereum presale wallets",
|
||||
Action: utils.MigrateFlags(importWallet),
|
||||
Category: "ACCOUNT COMMANDS",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.KeyStoreDirFlag,
|
||||
utils.PasswordFileFlag,
|
||||
utils.LightKDFFlag,
|
||||
},
|
||||
Description: `
|
||||
geth wallet [options] /path/to/my/presale.wallet
|
||||
|
||||
will prompt for your password and imports your ether presale account.
|
||||
It can be used non-interactively with the --password option taking a
|
||||
passwordfile as argument containing the wallet password in plaintext.
|
||||
|
||||
`,
|
||||
}
|
||||
accountCommand = cli.Command{
|
||||
Action: accountList,
|
||||
Name: "account",
|
||||
Usage: "Manage accounts",
|
||||
ArgsUsage: "",
|
||||
Category: "ACCOUNT COMMANDS",
|
||||
Name: "account",
|
||||
Usage: "Manage accounts",
|
||||
Category: "ACCOUNT COMMANDS",
|
||||
Description: `
|
||||
Manage accounts lets you create new accounts, list all existing accounts,
|
||||
import a private key into a new account.
|
||||
|
||||
' help' shows a list of subcommands or help for one subcommand.
|
||||
Manage accounts, list all existing accounts, import a private key into a new
|
||||
account, create a new account or update an existing account.
|
||||
|
||||
It supports interactive mode, when you are prompted for password as well as
|
||||
non-interactive mode where passwords are supplied via a given password file.
|
||||
@ -80,36 +72,34 @@ Note that exporting your key in unencrypted format is NOT supported.
|
||||
Keys are stored under <DATADIR>/keystore.
|
||||
It is safe to transfer the entire directory or the individual keys therein
|
||||
between ethereum nodes by simply copying.
|
||||
Make sure you backup your keys regularly.
|
||||
|
||||
In order to use your account to send transactions, you need to unlock them using
|
||||
the '--unlock' option. The argument is a space separated list of addresses or
|
||||
indexes. If used non-interactively with a passwordfile, the file should contain
|
||||
the respective passwords one per line. If you unlock n accounts and the password
|
||||
file contains less than n entries, then the last password is meant to apply to
|
||||
all remaining accounts.
|
||||
|
||||
And finally. DO NOT FORGET YOUR PASSWORD.
|
||||
`,
|
||||
Make sure you backup your keys regularly.`,
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Action: accountList,
|
||||
Name: "list",
|
||||
Usage: "Print account addresses",
|
||||
ArgsUsage: " ",
|
||||
Name: "list",
|
||||
Usage: "Print summary of existing accounts",
|
||||
Action: utils.MigrateFlags(accountList),
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.KeyStoreDirFlag,
|
||||
},
|
||||
Description: `
|
||||
TODO: Please write this
|
||||
`,
|
||||
Print a short summary of all accounts`,
|
||||
},
|
||||
{
|
||||
Action: accountCreate,
|
||||
Name: "new",
|
||||
Usage: "Create a new account",
|
||||
ArgsUsage: " ",
|
||||
Name: "new",
|
||||
Usage: "Create a new account",
|
||||
Action: utils.MigrateFlags(accountCreate),
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.KeyStoreDirFlag,
|
||||
utils.PasswordFileFlag,
|
||||
utils.LightKDFFlag,
|
||||
},
|
||||
Description: `
|
||||
geth account new
|
||||
|
||||
Creates a new account. Prints the address.
|
||||
Creates a new account and prints the address.
|
||||
|
||||
The account is saved in encrypted format, you are prompted for a passphrase.
|
||||
|
||||
@ -117,17 +107,20 @@ You must remember this passphrase to unlock your account in the future.
|
||||
|
||||
For non-interactive use the passphrase can be specified with the --password flag:
|
||||
|
||||
geth --password <passwordfile> account new
|
||||
|
||||
Note, this is meant to be used for testing only, it is a bad idea to save your
|
||||
password to file or expose in any other way.
|
||||
`,
|
||||
},
|
||||
{
|
||||
Action: accountUpdate,
|
||||
Name: "update",
|
||||
Usage: "Update an existing account",
|
||||
Action: utils.MigrateFlags(accountUpdate),
|
||||
ArgsUsage: "<address>",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.KeyStoreDirFlag,
|
||||
utils.LightKDFFlag,
|
||||
},
|
||||
Description: `
|
||||
geth account update <address>
|
||||
|
||||
@ -141,16 +134,22 @@ format to the newest format or change the password for an account.
|
||||
|
||||
For non-interactive use the passphrase can be specified with the --password flag:
|
||||
|
||||
geth --password <passwordfile> account update <address>
|
||||
geth account update [options] <address>
|
||||
|
||||
Since only one password can be given, only format update can be performed,
|
||||
changing your password is only possible interactively.
|
||||
`,
|
||||
},
|
||||
{
|
||||
Action: accountImport,
|
||||
Name: "import",
|
||||
Usage: "Import a private key into a new account",
|
||||
Name: "import",
|
||||
Usage: "Import a private key into a new account",
|
||||
Action: utils.MigrateFlags(accountImport),
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.KeyStoreDirFlag,
|
||||
utils.PasswordFileFlag,
|
||||
utils.LightKDFFlag,
|
||||
},
|
||||
ArgsUsage: "<keyFile>",
|
||||
Description: `
|
||||
geth account import <keyfile>
|
||||
@ -166,7 +165,7 @@ You must remember this passphrase to unlock your account in the future.
|
||||
|
||||
For non-interactive use the passphrase can be specified with the -password flag:
|
||||
|
||||
geth --password <passwordfile> account import <keyfile>
|
||||
geth account import [options] <keyfile>
|
||||
|
||||
Note:
|
||||
As you can directly copy your encrypted accounts to another ethereum instance,
|
||||
@ -298,10 +297,12 @@ func accountUpdate(ctx *cli.Context) error {
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
||||
|
||||
account, oldPassword := unlockAccount(ctx, ks, ctx.Args().First(), 0, nil)
|
||||
newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
|
||||
if err := ks.Update(account, oldPassword, newPassword); err != nil {
|
||||
utils.Fatalf("Could not update the account: %v", err)
|
||||
for _, addr := range ctx.Args() {
|
||||
account, oldPassword := unlockAccount(ctx, ks, addr, 0, nil)
|
||||
newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
|
||||
if err := ks.Update(account, oldPassword, newPassword); err != nil {
|
||||
utils.Fatalf("Could not update the account: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -43,13 +43,13 @@ func tmpDatadirWithKeystore(t *testing.T) string {
|
||||
}
|
||||
|
||||
func TestAccountListEmpty(t *testing.T) {
|
||||
geth := runGeth(t, "account")
|
||||
geth := runGeth(t, "account", "list")
|
||||
geth.expectExit()
|
||||
}
|
||||
|
||||
func TestAccountList(t *testing.T) {
|
||||
datadir := tmpDatadirWithKeystore(t)
|
||||
geth := runGeth(t, "--datadir", datadir, "account")
|
||||
geth := runGeth(t, "account", "list", "--datadir", datadir)
|
||||
defer geth.expectExit()
|
||||
if runtime.GOOS == "windows" {
|
||||
geth.expect(`
|
||||
@ -67,7 +67,7 @@ Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}/k
|
||||
}
|
||||
|
||||
func TestAccountNew(t *testing.T) {
|
||||
geth := runGeth(t, "--lightkdf", "account", "new")
|
||||
geth := runGeth(t, "account", "new", "--lightkdf")
|
||||
defer geth.expectExit()
|
||||
geth.expect(`
|
||||
Your new account is locked with a password. Please give a password. Do not forget this password.
|
||||
@ -79,7 +79,7 @@ Repeat passphrase: {{.InputLine "foobar"}}
|
||||
}
|
||||
|
||||
func TestAccountNewBadRepeat(t *testing.T) {
|
||||
geth := runGeth(t, "--lightkdf", "account", "new")
|
||||
geth := runGeth(t, "account", "new", "--lightkdf")
|
||||
defer geth.expectExit()
|
||||
geth.expect(`
|
||||
Your new account is locked with a password. Please give a password. Do not forget this password.
|
||||
@ -92,9 +92,9 @@ Fatal: Passphrases do not match
|
||||
|
||||
func TestAccountUpdate(t *testing.T) {
|
||||
datadir := tmpDatadirWithKeystore(t)
|
||||
geth := runGeth(t,
|
||||
geth := runGeth(t, "account", "update",
|
||||
"--datadir", datadir, "--lightkdf",
|
||||
"account", "update", "f466859ead1932d743d622cb74fc058882e8648a")
|
||||
"f466859ead1932d743d622cb74fc058882e8648a")
|
||||
defer geth.expectExit()
|
||||
geth.expect(`
|
||||
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
||||
@ -107,7 +107,7 @@ Repeat passphrase: {{.InputLine "foobar2"}}
|
||||
}
|
||||
|
||||
func TestWalletImport(t *testing.T) {
|
||||
geth := runGeth(t, "--lightkdf", "wallet", "import", "testdata/guswallet.json")
|
||||
geth := runGeth(t, "wallet", "import", "--lightkdf", "testdata/guswallet.json")
|
||||
defer geth.expectExit()
|
||||
geth.expect(`
|
||||
!! Unsupported terminal, password will be echoed.
|
||||
@ -122,7 +122,7 @@ Address: {d4584b5f6229b7be90727b0fc8c6b91bb427821f}
|
||||
}
|
||||
|
||||
func TestWalletImportBadPassword(t *testing.T) {
|
||||
geth := runGeth(t, "--lightkdf", "wallet", "import", "testdata/guswallet.json")
|
||||
geth := runGeth(t, "wallet", "import", "--lightkdf", "testdata/guswallet.json")
|
||||
defer geth.expectExit()
|
||||
geth.expect(`
|
||||
!! Unsupported terminal, password will be echoed.
|
||||
|
@ -29,11 +29,12 @@ import (
|
||||
"github.com/ethereum/go-ethereum/cmd/internal/browser"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
var bugCommand = cli.Command{
|
||||
Action: reportBug,
|
||||
Action: utils.MigrateFlags(reportBug),
|
||||
Name: "bug",
|
||||
Usage: "opens a window to report a bug on the geth repo",
|
||||
ArgsUsage: " ",
|
||||
|
@ -40,80 +40,98 @@ import (
|
||||
|
||||
var (
|
||||
initCommand = cli.Command{
|
||||
Action: initGenesis,
|
||||
Action: utils.MigrateFlags(initGenesis),
|
||||
Name: "init",
|
||||
Usage: "Bootstrap and initialize a new genesis block",
|
||||
ArgsUsage: "<genesisPath>",
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.LightModeFlag,
|
||||
},
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Description: `
|
||||
The init command initializes a new genesis block and definition for the network.
|
||||
This is a destructive action and changes the network in which you will be
|
||||
participating.
|
||||
`,
|
||||
|
||||
It expects the genesis file as argument.`,
|
||||
}
|
||||
importCommand = cli.Command{
|
||||
Action: importChain,
|
||||
Action: utils.MigrateFlags(importChain),
|
||||
Name: "import",
|
||||
Usage: "Import a blockchain file",
|
||||
ArgsUsage: "<filename> (<filename 2> ... <filename N>) ",
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.CacheFlag,
|
||||
utils.LightModeFlag,
|
||||
},
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Description: `
|
||||
The import command imports blocks from an RLP-encoded form. The form can be one file
|
||||
with several RLP-encoded blocks, or several files can be used.
|
||||
The import command imports blocks from an RLP-encoded form. The form can be one file
|
||||
with several RLP-encoded blocks, or several files can be used.
|
||||
|
||||
If only one file is used, import error will result in failure. If several files are used,
|
||||
processing will proceed even if an individual RLP-file import failure occurs.
|
||||
`,
|
||||
processing will proceed even if an individual RLP-file import failure occurs.`,
|
||||
}
|
||||
exportCommand = cli.Command{
|
||||
Action: exportChain,
|
||||
Action: utils.MigrateFlags(exportChain),
|
||||
Name: "export",
|
||||
Usage: "Export blockchain into file",
|
||||
ArgsUsage: "<filename> [<blockNumFirst> <blockNumLast>]",
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.CacheFlag,
|
||||
utils.LightModeFlag,
|
||||
},
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Description: `
|
||||
Requires a first argument of the file to write to.
|
||||
Optional second and third arguments control the first and
|
||||
last block to write. In this mode, the file will be appended
|
||||
if already existing.
|
||||
`,
|
||||
if already existing.`,
|
||||
}
|
||||
removedbCommand = cli.Command{
|
||||
Action: removeDB,
|
||||
Action: utils.MigrateFlags(removeDB),
|
||||
Name: "removedb",
|
||||
Usage: "Remove blockchain and state databases",
|
||||
ArgsUsage: " ",
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.LightModeFlag,
|
||||
},
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Description: `
|
||||
TODO: Please write this
|
||||
`,
|
||||
Remove blockchain and state databases`,
|
||||
}
|
||||
dumpCommand = cli.Command{
|
||||
Action: dump,
|
||||
Action: utils.MigrateFlags(dump),
|
||||
Name: "dump",
|
||||
Usage: "Dump a specific block from storage",
|
||||
ArgsUsage: "[<blockHash> | <blockNum>]...",
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.CacheFlag,
|
||||
utils.LightModeFlag,
|
||||
},
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Description: `
|
||||
The arguments are interpreted as block numbers or hashes.
|
||||
Use "ethereum dump 0" to dump the genesis block.
|
||||
`,
|
||||
Use "ethereum dump 0" to dump the genesis block.`,
|
||||
}
|
||||
)
|
||||
|
||||
// initGenesis will initialise the given JSON format genesis file and writes it as
|
||||
// the zero'd block (i.e. genesis) or will fail hard if it can't succeed.
|
||||
func initGenesis(ctx *cli.Context) error {
|
||||
// Make sure we have a valid genesis JSON
|
||||
genesisPath := ctx.Args().First()
|
||||
if len(genesisPath) == 0 {
|
||||
utils.Fatalf("must supply path to genesis JSON file")
|
||||
utils.Fatalf("Must supply path to genesis JSON file")
|
||||
}
|
||||
|
||||
stack := makeFullNode(ctx)
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack)
|
||||
|
||||
file, err := os.Open(genesisPath)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to read genesis file: %v", err)
|
||||
utils.Fatalf("Failed to read genesis file: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
@ -121,12 +139,19 @@ func initGenesis(ctx *cli.Context) error {
|
||||
if err := json.NewDecoder(file).Decode(genesis); err != nil {
|
||||
utils.Fatalf("invalid genesis file: %v", err)
|
||||
}
|
||||
|
||||
_, hash, err := core.SetupGenesisBlock(chaindb, genesis)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to write genesis block: %v", err)
|
||||
// Open an initialise both full and light databases
|
||||
stack := makeFullNode(ctx)
|
||||
for _, name := range []string{"chaindata", "lightchaindata"} {
|
||||
chaindb, err := stack.OpenDatabase(name, 0, 0)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
_, hash, err := core.SetupGenesisBlock(chaindb, genesis)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to write genesis block: %v", err)
|
||||
}
|
||||
log.Info("Successfully wrote genesis state", "database", name, "hash", hash)
|
||||
}
|
||||
log.Info("Successfully wrote genesis state", "hash", hash)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -245,24 +270,29 @@ func exportChain(ctx *cli.Context) error {
|
||||
|
||||
func removeDB(ctx *cli.Context) error {
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
dbdir := stack.ResolvePath(utils.ChainDbName(ctx))
|
||||
if !common.FileExist(dbdir) {
|
||||
fmt.Println(dbdir, "does not exist")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println(dbdir)
|
||||
confirm, err := console.Stdin.PromptConfirm("Remove this database?")
|
||||
switch {
|
||||
case err != nil:
|
||||
utils.Fatalf("%v", err)
|
||||
case !confirm:
|
||||
fmt.Println("Operation aborted")
|
||||
default:
|
||||
fmt.Println("Removing...")
|
||||
start := time.Now()
|
||||
os.RemoveAll(dbdir)
|
||||
fmt.Printf("Removed in %v\n", time.Since(start))
|
||||
for _, name := range []string{"chaindata", "lightchaindata"} {
|
||||
// Ensure the database exists in the first place
|
||||
logger := log.New("database", name)
|
||||
|
||||
dbdir := stack.ResolvePath(name)
|
||||
if !common.FileExist(dbdir) {
|
||||
logger.Info("Database doesn't exist, skipping", "path", dbdir)
|
||||
continue
|
||||
}
|
||||
// Confirm removal and execute
|
||||
fmt.Println(dbdir)
|
||||
confirm, err := console.Stdin.PromptConfirm("Remove this database?")
|
||||
switch {
|
||||
case err != nil:
|
||||
utils.Fatalf("%v", err)
|
||||
case !confirm:
|
||||
logger.Warn("Database deletion aborted")
|
||||
default:
|
||||
start := time.Now()
|
||||
os.RemoveAll(dbdir)
|
||||
logger.Info("Database successfully deleted", "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -38,10 +38,11 @@ import (
|
||||
|
||||
var (
|
||||
dumpConfigCommand = cli.Command{
|
||||
Action: dumpConfig,
|
||||
Action: utils.MigrateFlags(dumpConfig),
|
||||
Name: "dumpconfig",
|
||||
Usage: "Show configuration values",
|
||||
ArgsUsage: "",
|
||||
Flags: append(nodeFlags, rpcFlags...),
|
||||
Category: "MISCELLANEOUS COMMANDS",
|
||||
Description: `The dumpconfig command shows configuration values.`,
|
||||
}
|
||||
|
@ -28,42 +28,47 @@ import (
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
consoleFlags = []cli.Flag{utils.JSpathFlag, utils.ExecFlag, utils.PreloadJSFlag}
|
||||
)
|
||||
|
||||
var (
|
||||
consoleCommand = cli.Command{
|
||||
Action: localConsole,
|
||||
Name: "console",
|
||||
Usage: "Start an interactive JavaScript environment",
|
||||
ArgsUsage: "", // TODO: Write this!
|
||||
Category: "CONSOLE COMMANDS",
|
||||
Action: utils.MigrateFlags(localConsole),
|
||||
Name: "console",
|
||||
Usage: "Start an interactive JavaScript environment",
|
||||
Flags: append(append(nodeFlags, rpcFlags...), consoleFlags...),
|
||||
Category: "CONSOLE COMMANDS",
|
||||
Description: `
|
||||
The Geth console is an interactive shell for the JavaScript runtime environment
|
||||
which exposes a node admin interface as well as the Ðapp JavaScript API.
|
||||
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
|
||||
`,
|
||||
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console.`,
|
||||
}
|
||||
|
||||
attachCommand = cli.Command{
|
||||
Action: remoteConsole,
|
||||
Action: utils.MigrateFlags(remoteConsole),
|
||||
Name: "attach",
|
||||
Usage: "Start an interactive JavaScript environment (connect to node)",
|
||||
ArgsUsage: "", // TODO: Write this!
|
||||
ArgsUsage: "[endpoint]",
|
||||
Flags: append(consoleFlags, utils.DataDirFlag),
|
||||
Category: "CONSOLE COMMANDS",
|
||||
Description: `
|
||||
The Geth console is an interactive shell for the JavaScript runtime environment
|
||||
which exposes a node admin interface as well as the Ðapp JavaScript API.
|
||||
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console.
|
||||
This command allows to open a console on a running geth node.
|
||||
`,
|
||||
This command allows to open a console on a running geth node.`,
|
||||
}
|
||||
|
||||
javascriptCommand = cli.Command{
|
||||
Action: ephemeralConsole,
|
||||
Action: utils.MigrateFlags(ephemeralConsole),
|
||||
Name: "js",
|
||||
Usage: "Execute the specified JavaScript files",
|
||||
ArgsUsage: "", // TODO: Write this!
|
||||
ArgsUsage: "<jsfile> [jsfile...]",
|
||||
Flags: append(nodeFlags, consoleFlags...),
|
||||
Category: "CONSOLE COMMANDS",
|
||||
Description: `
|
||||
The JavaScript VM exposes a node admin interface as well as the Ðapp
|
||||
JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
|
||||
`,
|
||||
JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console`,
|
||||
}
|
||||
)
|
||||
|
||||
@ -81,11 +86,12 @@ func localConsole(ctx *cli.Context) error {
|
||||
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
|
||||
}
|
||||
config := console.Config{
|
||||
DataDir: node.DataDir(),
|
||||
DataDir: utils.MakeDataDir(ctx),
|
||||
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
|
||||
Client: client,
|
||||
Preload: utils.MakeConsolePreloads(ctx),
|
||||
}
|
||||
|
||||
console, err := console.New(config)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to start the JavaScript console: %v", err)
|
||||
@ -118,17 +124,18 @@ func remoteConsole(ctx *cli.Context) error {
|
||||
Client: client,
|
||||
Preload: utils.MakeConsolePreloads(ctx),
|
||||
}
|
||||
|
||||
console, err := console.New(config)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to start the JavaScript console: %v", err)
|
||||
}
|
||||
defer console.Stop(false)
|
||||
|
||||
// If only a short execution was requested, evaluate and return
|
||||
if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" {
|
||||
console.Evaluate(script)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Otherwise print the welcome screen and enter interactive mode
|
||||
console.Welcome()
|
||||
console.Interactive()
|
||||
@ -151,7 +158,7 @@ func dialRPC(endpoint string) (*rpc.Client, error) {
|
||||
}
|
||||
|
||||
// ephemeralConsole starts a new geth node, attaches an ephemeral JavaScript
|
||||
// console to it, and each of the files specified as arguments and tears the
|
||||
// console to it, executes each of the files specified as arguments and tears
|
||||
// everything down.
|
||||
func ephemeralConsole(ctx *cli.Context) error {
|
||||
// Create and start the node based on the CLI flags
|
||||
@ -165,11 +172,12 @@ func ephemeralConsole(ctx *cli.Context) error {
|
||||
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
|
||||
}
|
||||
config := console.Config{
|
||||
DataDir: node.DataDir(),
|
||||
DataDir: utils.MakeDataDir(ctx),
|
||||
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
|
||||
Client: client,
|
||||
Preload: utils.MakeConsolePreloads(ctx),
|
||||
}
|
||||
|
||||
console, err := console.New(config)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to start the JavaScript console: %v", err)
|
||||
|
134
cmd/geth/main.go
134
cmd/geth/main.go
@ -49,6 +49,73 @@ var (
|
||||
relOracle = common.HexToAddress("0xfa7b9770ca4cb04296cac84f37736d4041251cdf")
|
||||
// The app that holds all commands and flags.
|
||||
app = utils.NewApp(gitCommit, "the go-ethereum command line interface")
|
||||
// flags that configure the node
|
||||
nodeFlags = []cli.Flag{
|
||||
utils.IdentityFlag,
|
||||
utils.UnlockedAccountFlag,
|
||||
utils.PasswordFileFlag,
|
||||
utils.BootnodesFlag,
|
||||
utils.DataDirFlag,
|
||||
utils.KeyStoreDirFlag,
|
||||
utils.NoUSBFlag,
|
||||
utils.EthashCacheDirFlag,
|
||||
utils.EthashCachesInMemoryFlag,
|
||||
utils.EthashCachesOnDiskFlag,
|
||||
utils.EthashDatasetDirFlag,
|
||||
utils.EthashDatasetsInMemoryFlag,
|
||||
utils.EthashDatasetsOnDiskFlag,
|
||||
utils.FastSyncFlag,
|
||||
utils.LightModeFlag,
|
||||
utils.SyncModeFlag,
|
||||
utils.LightServFlag,
|
||||
utils.LightPeersFlag,
|
||||
utils.LightKDFFlag,
|
||||
utils.CacheFlag,
|
||||
utils.TrieCacheGenFlag,
|
||||
utils.ListenPortFlag,
|
||||
utils.MaxPeersFlag,
|
||||
utils.MaxPendingPeersFlag,
|
||||
utils.EtherbaseFlag,
|
||||
utils.GasPriceFlag,
|
||||
utils.MinerThreadsFlag,
|
||||
utils.MiningEnabledFlag,
|
||||
utils.TargetGasLimitFlag,
|
||||
utils.NATFlag,
|
||||
utils.NoDiscoverFlag,
|
||||
utils.DiscoveryV5Flag,
|
||||
utils.NetrestrictFlag,
|
||||
utils.NodeKeyFileFlag,
|
||||
utils.NodeKeyHexFlag,
|
||||
utils.WhisperEnabledFlag,
|
||||
utils.DevModeFlag,
|
||||
utils.TestnetFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.VMEnableDebugFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.RPCCORSDomainFlag,
|
||||
utils.EthStatsURLFlag,
|
||||
utils.MetricsEnabledFlag,
|
||||
utils.FakePoWFlag,
|
||||
utils.NoCompactionFlag,
|
||||
utils.GpoBlocksFlag,
|
||||
utils.GpoPercentileFlag,
|
||||
utils.ExtraDataFlag,
|
||||
configFileFlag,
|
||||
}
|
||||
|
||||
rpcFlags = []cli.Flag{
|
||||
utils.RPCEnabledFlag,
|
||||
utils.RPCListenAddrFlag,
|
||||
utils.RPCPortFlag,
|
||||
utils.RPCApiFlag,
|
||||
utils.WSEnabledFlag,
|
||||
utils.WSListenAddrFlag,
|
||||
utils.WSPortFlag,
|
||||
utils.WSApiFlag,
|
||||
utils.WSAllowedOriginsFlag,
|
||||
utils.IPCDisabledFlag,
|
||||
utils.IPCPathFlag,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -81,70 +148,9 @@ func init() {
|
||||
dumpConfigCommand,
|
||||
}
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
utils.IdentityFlag,
|
||||
utils.UnlockedAccountFlag,
|
||||
utils.PasswordFileFlag,
|
||||
utils.BootnodesFlag,
|
||||
utils.DataDirFlag,
|
||||
utils.KeyStoreDirFlag,
|
||||
utils.EthashCacheDirFlag,
|
||||
utils.EthashCachesInMemoryFlag,
|
||||
utils.EthashCachesOnDiskFlag,
|
||||
utils.EthashDatasetDirFlag,
|
||||
utils.EthashDatasetsInMemoryFlag,
|
||||
utils.EthashDatasetsOnDiskFlag,
|
||||
utils.FastSyncFlag,
|
||||
utils.LightModeFlag,
|
||||
utils.SyncModeFlag,
|
||||
utils.LightServFlag,
|
||||
utils.LightPeersFlag,
|
||||
utils.LightKDFFlag,
|
||||
utils.CacheFlag,
|
||||
utils.TrieCacheGenFlag,
|
||||
utils.JSpathFlag,
|
||||
utils.ListenPortFlag,
|
||||
utils.MaxPeersFlag,
|
||||
utils.MaxPendingPeersFlag,
|
||||
utils.EtherbaseFlag,
|
||||
utils.GasPriceFlag,
|
||||
utils.MinerThreadsFlag,
|
||||
utils.MiningEnabledFlag,
|
||||
utils.TargetGasLimitFlag,
|
||||
utils.NATFlag,
|
||||
utils.NoDiscoverFlag,
|
||||
utils.DiscoveryV5Flag,
|
||||
utils.NetrestrictFlag,
|
||||
utils.NodeKeyFileFlag,
|
||||
utils.NodeKeyHexFlag,
|
||||
utils.RPCEnabledFlag,
|
||||
utils.RPCListenAddrFlag,
|
||||
utils.RPCPortFlag,
|
||||
utils.RPCApiFlag,
|
||||
utils.WSEnabledFlag,
|
||||
utils.WSListenAddrFlag,
|
||||
utils.WSPortFlag,
|
||||
utils.WSApiFlag,
|
||||
utils.WSAllowedOriginsFlag,
|
||||
utils.IPCDisabledFlag,
|
||||
utils.IPCPathFlag,
|
||||
utils.ExecFlag,
|
||||
utils.PreloadJSFlag,
|
||||
utils.WhisperEnabledFlag,
|
||||
utils.DevModeFlag,
|
||||
utils.TestNetFlag,
|
||||
utils.VMEnableDebugFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.RPCCORSDomainFlag,
|
||||
utils.EthStatsURLFlag,
|
||||
utils.MetricsEnabledFlag,
|
||||
utils.FakePoWFlag,
|
||||
utils.NoCompactionFlag,
|
||||
utils.GpoBlocksFlag,
|
||||
utils.GpoPercentileFlag,
|
||||
utils.ExtraDataFlag,
|
||||
configFileFlag,
|
||||
}
|
||||
app.Flags = append(app.Flags, nodeFlags...)
|
||||
app.Flags = append(app.Flags, rpcFlags...)
|
||||
app.Flags = append(app.Flags, consoleFlags...)
|
||||
app.Flags = append(app.Flags, debug.Flags...)
|
||||
|
||||
app.Before = func(ctx *cli.Context) error {
|
||||
|
@ -34,7 +34,7 @@ import (
|
||||
|
||||
var (
|
||||
makedagCommand = cli.Command{
|
||||
Action: makedag,
|
||||
Action: utils.MigrateFlags(makedag),
|
||||
Name: "makedag",
|
||||
Usage: "Generate ethash DAG (for testing)",
|
||||
ArgsUsage: "<blockNum> <outputDir>",
|
||||
@ -47,7 +47,7 @@ Regular users do not need to execute it.
|
||||
`,
|
||||
}
|
||||
versionCommand = cli.Command{
|
||||
Action: version,
|
||||
Action: utils.MigrateFlags(version),
|
||||
Name: "version",
|
||||
Usage: "Print version numbers",
|
||||
ArgsUsage: " ",
|
||||
@ -57,7 +57,7 @@ The output of this command is supposed to be machine-readable.
|
||||
`,
|
||||
}
|
||||
licenseCommand = cli.Command{
|
||||
Action: license,
|
||||
Action: utils.MigrateFlags(license),
|
||||
Name: "license",
|
||||
Usage: "Display license information",
|
||||
ArgsUsage: " ",
|
||||
@ -103,7 +103,7 @@ func version(ctx *cli.Context) error {
|
||||
}
|
||||
fmt.Println("Architecture:", runtime.GOARCH)
|
||||
fmt.Println("Protocol Versions:", eth.ProtocolVersions)
|
||||
fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name))
|
||||
fmt.Println("Network Id:", eth.DefaultConfig.NetworkId)
|
||||
fmt.Println("Go Version:", runtime.Version())
|
||||
fmt.Println("Operating System:", runtime.GOOS)
|
||||
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
|
||||
|
@ -49,7 +49,7 @@ var (
|
||||
Usage: "Refresh interval in seconds",
|
||||
}
|
||||
monitorCommand = cli.Command{
|
||||
Action: monitor,
|
||||
Action: utils.MigrateFlags(monitor), // keep track of migration progress
|
||||
Name: "monitor",
|
||||
Usage: "Monitor and visualize node metrics",
|
||||
ArgsUsage: " ",
|
||||
|
@ -67,8 +67,10 @@ var AppHelpFlagGroups = []flagGroup{
|
||||
configFileFlag,
|
||||
utils.DataDirFlag,
|
||||
utils.KeyStoreDirFlag,
|
||||
utils.NoUSBFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.TestNetFlag,
|
||||
utils.TestnetFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.DevModeFlag,
|
||||
utils.SyncModeFlag,
|
||||
utils.EthStatsURLFlag,
|
||||
|
@ -51,9 +51,10 @@ ADD account.pass /account.pass
|
||||
EXPOSE 8080
|
||||
|
||||
CMD [ \
|
||||
"/faucet", "--genesis", "/genesis.json", "--network", "{{.NetworkID}}", "--bootnodes", "{{.Bootnodes}}", "--ethstats", "{{.Ethstats}}", \
|
||||
"--ethport", "{{.EthPort}}", "--faucet.name", "{{.FaucetName}}", "--faucet.amount", "{{.FaucetAmount}}", "--faucet.minutes", "{{.FaucetMinutes}}", \
|
||||
"--github.user", "{{.GitHubUser}}", "--github.token", "{{.GitHubToken}}", "--account.json", "/account.json", "--account.pass", "/account.pass" \
|
||||
"/faucet", "--genesis", "/genesis.json", "--network", "{{.NetworkID}}", "--bootnodes", "{{.Bootnodes}}", "--ethstats", "{{.Ethstats}}", "--ethport", "{{.EthPort}}", \
|
||||
"--faucet.name", "{{.FaucetName}}", "--faucet.amount", "{{.FaucetAmount}}", "--faucet.minutes", "{{.FaucetMinutes}}", "--faucet.tiers", "{{.FaucetTiers}}", \
|
||||
"--github.user", "{{.GitHubUser}}", "--github.token", "{{.GitHubToken}}", "--account.json", "/account.json", "--account.pass", "/account.pass" \
|
||||
{{if .CaptchaToken}}, "--captcha.token", "{{.CaptchaToken}}", "--captcha.secret", "{{.CaptchaSecret}}"{{end}} \
|
||||
]`
|
||||
|
||||
// faucetComposefile is the docker-compose.yml file required to deploy and maintain
|
||||
@ -74,8 +75,11 @@ services:
|
||||
- ETH_NAME={{.EthName}}
|
||||
- FAUCET_AMOUNT={{.FaucetAmount}}
|
||||
- FAUCET_MINUTES={{.FaucetMinutes}}
|
||||
- FAUCET_TIERS={{.FaucetTiers}}
|
||||
- GITHUB_USER={{.GitHubUser}}
|
||||
- GITHUB_TOKEN={{.GitHubToken}}{{if .VHost}}
|
||||
- GITHUB_TOKEN={{.GitHubToken}}
|
||||
- CAPTCHA_TOKEN={{.CaptchaToken}}
|
||||
- CAPTCHA_SECRET={{.CaptchaSecret}}{{if .VHost}}
|
||||
- VIRTUAL_HOST={{.VHost}}
|
||||
- VIRTUAL_PORT=8080{{end}}
|
||||
restart: always
|
||||
@ -97,9 +101,12 @@ func deployFaucet(client *sshClient, network string, bootnodes []string, config
|
||||
"EthPort": config.node.portFull,
|
||||
"GitHubUser": config.githubUser,
|
||||
"GitHubToken": config.githubToken,
|
||||
"CaptchaToken": config.captchaToken,
|
||||
"CaptchaSecret": config.captchaSecret,
|
||||
"FaucetName": strings.Title(network),
|
||||
"FaucetAmount": config.amount,
|
||||
"FaucetMinutes": config.minutes,
|
||||
"FaucetTiers": config.tiers,
|
||||
})
|
||||
files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes()
|
||||
|
||||
@ -113,8 +120,11 @@ func deployFaucet(client *sshClient, network string, bootnodes []string, config
|
||||
"EthName": config.node.ethstats[:strings.Index(config.node.ethstats, ":")],
|
||||
"GitHubUser": config.githubUser,
|
||||
"GitHubToken": config.githubToken,
|
||||
"CaptchaToken": config.captchaToken,
|
||||
"CaptchaSecret": config.captchaSecret,
|
||||
"FaucetAmount": config.amount,
|
||||
"FaucetMinutes": config.minutes,
|
||||
"FaucetTiers": config.tiers,
|
||||
})
|
||||
files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes()
|
||||
|
||||
@ -135,18 +145,21 @@ func deployFaucet(client *sshClient, network string, bootnodes []string, config
|
||||
// faucetInfos is returned from an faucet status check to allow reporting various
|
||||
// configuration parameters.
|
||||
type faucetInfos struct {
|
||||
node *nodeInfos
|
||||
host string
|
||||
port int
|
||||
amount int
|
||||
minutes int
|
||||
githubUser string
|
||||
githubToken string
|
||||
node *nodeInfos
|
||||
host string
|
||||
port int
|
||||
amount int
|
||||
minutes int
|
||||
tiers int
|
||||
githubUser string
|
||||
githubToken string
|
||||
captchaToken string
|
||||
captchaSecret string
|
||||
}
|
||||
|
||||
// String implements the stringer interface.
|
||||
func (info *faucetInfos) String() string {
|
||||
return fmt.Sprintf("host=%s, api=%d, eth=%d, amount=%d, minutes=%d, github=%s, ethstats=%s", info.host, info.port, info.node.portFull, info.amount, info.minutes, info.githubUser, info.node.ethstats)
|
||||
return fmt.Sprintf("host=%s, api=%d, eth=%d, amount=%d, minutes=%d, tiers=%d, github=%s, captcha=%v, ethstats=%s", info.host, info.port, info.node.portFull, info.amount, info.minutes, info.tiers, info.githubUser, info.captchaToken != "", info.node.ethstats)
|
||||
}
|
||||
|
||||
// checkFaucet does a health-check against an faucet server to verify whether
|
||||
@ -177,6 +190,7 @@ func checkFaucet(client *sshClient, network string) (*faucetInfos, error) {
|
||||
}
|
||||
amount, _ := strconv.Atoi(infos.envvars["FAUCET_AMOUNT"])
|
||||
minutes, _ := strconv.Atoi(infos.envvars["FAUCET_MINUTES"])
|
||||
tiers, _ := strconv.Atoi(infos.envvars["FAUCET_TIERS"])
|
||||
|
||||
// Retrieve the funding account informations
|
||||
var out []byte
|
||||
@ -200,11 +214,14 @@ func checkFaucet(client *sshClient, network string) (*faucetInfos, error) {
|
||||
keyJSON: keyJSON,
|
||||
keyPass: keyPass,
|
||||
},
|
||||
host: host,
|
||||
port: port,
|
||||
amount: amount,
|
||||
minutes: minutes,
|
||||
githubUser: infos.envvars["GITHUB_USER"],
|
||||
githubToken: infos.envvars["GITHUB_TOKEN"],
|
||||
host: host,
|
||||
port: port,
|
||||
amount: amount,
|
||||
minutes: minutes,
|
||||
tiers: tiers,
|
||||
githubUser: infos.envvars["GITHUB_USER"],
|
||||
githubToken: infos.envvars["GITHUB_TOKEN"],
|
||||
captchaToken: infos.envvars["CAPTCHA_TOKEN"],
|
||||
captchaSecret: infos.envvars["CAPTCHA_SECRET"],
|
||||
}, nil
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@ -37,18 +39,26 @@ import (
|
||||
type sshClient struct {
|
||||
server string // Server name or IP without port number
|
||||
address string // IP address of the remote server
|
||||
pubkey []byte // RSA public key to authenticate the server
|
||||
client *ssh.Client
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
// dial establishes an SSH connection to a remote node using the current user and
|
||||
// the user's configured private RSA key.
|
||||
func dial(server string) (*sshClient, error) {
|
||||
// the user's configured private RSA key. If that fails, password authentication
|
||||
// is fallen back to. The caller may override the login user via user@server:port.
|
||||
func dial(server string, pubkey []byte) (*sshClient, error) {
|
||||
// Figure out a label for the server and a logger
|
||||
label := server
|
||||
if strings.Contains(label, ":") {
|
||||
label = label[:strings.Index(label, ":")]
|
||||
}
|
||||
login := ""
|
||||
if strings.Contains(server, "@") {
|
||||
login = label[:strings.Index(label, "@")]
|
||||
label = label[strings.Index(label, "@")+1:]
|
||||
server = server[strings.Index(server, "@")+1:]
|
||||
}
|
||||
logger := log.New("server", label)
|
||||
logger.Debug("Attempting to establish SSH connection")
|
||||
|
||||
@ -56,6 +66,9 @@ func dial(server string) (*sshClient, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if login == "" {
|
||||
login = user.Username
|
||||
}
|
||||
// Configure the supported authentication methods (private key and password)
|
||||
var auths []ssh.AuthMethod
|
||||
|
||||
@ -71,7 +84,7 @@ func dial(server string) (*sshClient, error) {
|
||||
}
|
||||
}
|
||||
auths = append(auths, ssh.PasswordCallback(func() (string, error) {
|
||||
fmt.Printf("What's the login password for %s at %s? (won't be echoed)\n> ", user.Username, server)
|
||||
fmt.Printf("What's the login password for %s at %s? (won't be echoed)\n> ", login, server)
|
||||
blob, err := terminal.ReadPassword(int(syscall.Stdin))
|
||||
|
||||
fmt.Println()
|
||||
@ -86,11 +99,36 @@ func dial(server string) (*sshClient, error) {
|
||||
return nil, errors.New("no IPs associated with domain")
|
||||
}
|
||||
// Try to dial in to the remote server
|
||||
logger.Trace("Dialing remote SSH server", "user", user.Username, "key", path)
|
||||
logger.Trace("Dialing remote SSH server", "user", login)
|
||||
if !strings.Contains(server, ":") {
|
||||
server += ":22"
|
||||
}
|
||||
client, err := ssh.Dial("tcp", server, &ssh.ClientConfig{User: user.Username, Auth: auths})
|
||||
keycheck := func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
// If no public key is known for SSH, ask the user to confirm
|
||||
if pubkey == nil {
|
||||
fmt.Printf("The authenticity of host '%s (%s)' can't be established.\n", hostname, remote)
|
||||
fmt.Printf("SSH key fingerprint is %s [MD5]\n", ssh.FingerprintLegacyMD5(key))
|
||||
fmt.Printf("Are you sure you want to continue connecting (yes/no)? ")
|
||||
|
||||
text, err := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case strings.TrimSpace(text) == "yes":
|
||||
pubkey = key.Marshal()
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unknown auth choice: %v", text)
|
||||
}
|
||||
}
|
||||
// If a public key exists for this SSH server, check that it matches
|
||||
if bytes.Compare(pubkey, key.Marshal()) == 0 {
|
||||
return nil
|
||||
}
|
||||
// We have a mismatch, forbid connecting
|
||||
return errors.New("ssh key mismatch, readd the machine to update")
|
||||
}
|
||||
client, err := ssh.Dial("tcp", server, &ssh.ClientConfig{User: login, Auth: auths, HostKeyCallback: keycheck})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -98,6 +136,7 @@ func dial(server string) (*sshClient, error) {
|
||||
c := &sshClient{
|
||||
server: label,
|
||||
address: addr[0],
|
||||
pubkey: pubkey,
|
||||
client: client,
|
||||
logger: logger,
|
||||
}
|
||||
|
@ -44,14 +44,24 @@ type config struct {
|
||||
bootLight []string // Bootnodes to always connect to by light nodes
|
||||
ethstats string // Ethstats settings to cache for node deploys
|
||||
|
||||
Servers []string `json:"servers,omitempty"`
|
||||
Servers map[string][]byte `json:"servers,omitempty"`
|
||||
}
|
||||
|
||||
// servers retrieves an alphabetically sorted list of servers.
|
||||
func (c config) servers() []string {
|
||||
servers := make([]string, 0, len(c.Servers))
|
||||
for server := range c.Servers {
|
||||
servers = append(servers, server)
|
||||
}
|
||||
sort.Strings(servers)
|
||||
|
||||
return servers
|
||||
}
|
||||
|
||||
// flush dumps the contents of config to disk.
|
||||
func (c config) flush() {
|
||||
os.MkdirAll(filepath.Dir(c.path), 0755)
|
||||
|
||||
sort.Strings(c.Servers)
|
||||
out, _ := json.MarshalIndent(c, "", " ")
|
||||
if err := ioutil.WriteFile(c.path, out, 0644); err != nil {
|
||||
log.Warn("Failed to save puppeth configs", "file", c.path, "err", err)
|
||||
|
@ -44,6 +44,7 @@ func (w *wizard) deployFaucet() {
|
||||
host: client.server,
|
||||
amount: 1,
|
||||
minutes: 1440,
|
||||
tiers: 3,
|
||||
}
|
||||
}
|
||||
infos.node.genesis, _ = json.MarshalIndent(w.conf.genesis, "", " ")
|
||||
@ -68,10 +69,17 @@ func (w *wizard) deployFaucet() {
|
||||
fmt.Printf("How many minutes to enforce between requests? (default = %d)\n", infos.minutes)
|
||||
infos.minutes = w.readDefaultInt(infos.minutes)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("How many funding tiers to feature (x2.5 amounts, x3 timeout)? (default = %d)\n", infos.tiers)
|
||||
infos.tiers = w.readDefaultInt(infos.tiers)
|
||||
if infos.tiers == 0 {
|
||||
log.Error("At least one funding tier must be set")
|
||||
return
|
||||
}
|
||||
// Accessing GitHub gists requires API authorization, retrieve it
|
||||
if infos.githubUser != "" {
|
||||
fmt.Println()
|
||||
fmt.Printf("Reused previous (%s) GitHub API authorization (y/n)? (default = yes)\n", infos.githubUser)
|
||||
fmt.Printf("Reuse previous (%s) GitHub API authorization (y/n)? (default = yes)\n", infos.githubUser)
|
||||
if w.readDefaultString("y") != "y" {
|
||||
infos.githubUser, infos.githubToken = "", ""
|
||||
}
|
||||
@ -109,6 +117,29 @@ func (w *wizard) deployFaucet() {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Accessing the reCaptcha service requires API authorizations, request it
|
||||
if infos.captchaToken != "" {
|
||||
fmt.Println()
|
||||
fmt.Println("Reuse previous reCaptcha API authorization (y/n)? (default = yes)")
|
||||
if w.readDefaultString("y") != "y" {
|
||||
infos.captchaToken, infos.captchaSecret = "", ""
|
||||
}
|
||||
}
|
||||
if infos.captchaToken == "" {
|
||||
// No previous authorization (or old one discarded)
|
||||
fmt.Println()
|
||||
fmt.Println("Enable reCaptcha protection against robots (y/n)? (default = no)")
|
||||
if w.readDefaultString("n") == "y" {
|
||||
// Captcha protection explicitly requested, read the site and secret keys
|
||||
fmt.Println()
|
||||
fmt.Printf("What is the reCaptcha site key to authenticate human users?\n")
|
||||
infos.captchaToken = w.readString()
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("What is the reCaptcha secret key to verify authentications? (won't be echoed)\n")
|
||||
infos.captchaSecret = w.readPassword()
|
||||
}
|
||||
}
|
||||
// Figure out where the user wants to store the persistent data
|
||||
fmt.Println()
|
||||
if infos.node.datadir == "" {
|
||||
|
@ -120,7 +120,7 @@ func (w *wizard) makeGenesis() {
|
||||
// Query the user for some custom extras
|
||||
fmt.Println()
|
||||
fmt.Println("Specify your chain/network ID if you want an explicit one (default = random)")
|
||||
genesis.Config.ChainId = big.NewInt(int64(w.readDefaultInt(rand.Intn(65536))))
|
||||
genesis.Config.ChainId = new(big.Int).SetUint64(uint64(w.readDefaultInt(rand.Intn(65536))))
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println("Anything fun to embed into the genesis block? (max 32 bytes)")
|
||||
|
@ -31,7 +31,10 @@ import (
|
||||
// makeWizard creates and returns a new puppeth wizard.
|
||||
func makeWizard(network string) *wizard {
|
||||
return &wizard{
|
||||
network: network,
|
||||
network: network,
|
||||
conf: config{
|
||||
Servers: make(map[string][]byte),
|
||||
},
|
||||
servers: make(map[string]*sshClient),
|
||||
services: make(map[string][]string),
|
||||
in: bufio.NewReader(os.Stdin),
|
||||
@ -77,9 +80,9 @@ func (w *wizard) run() {
|
||||
} else if err := json.Unmarshal(blob, &w.conf); err != nil {
|
||||
log.Crit("Previous configuration corrupted", "path", w.conf.path, "err", err)
|
||||
} else {
|
||||
for _, server := range w.conf.Servers {
|
||||
for server, pubkey := range w.conf.Servers {
|
||||
log.Info("Dialing previously configured server", "server", server)
|
||||
client, err := dial(server)
|
||||
client, err := dial(server, pubkey)
|
||||
if err != nil {
|
||||
log.Error("Previous server unreachable", "server", server, "err", err)
|
||||
}
|
||||
|
@ -41,14 +41,14 @@ func (w *wizard) networkStats(tips bool) {
|
||||
stats.SetHeader([]string{"Server", "IP", "Status", "Service", "Details"})
|
||||
stats.SetColWidth(128)
|
||||
|
||||
for _, server := range w.conf.Servers {
|
||||
for server, pubkey := range w.conf.Servers {
|
||||
client := w.servers[server]
|
||||
logger := log.New("server", server)
|
||||
logger.Info("Starting remote server health-check")
|
||||
|
||||
// If the server is not connected, try to connect again
|
||||
if client == nil {
|
||||
conn, err := dial(server)
|
||||
conn, err := dial(server, pubkey)
|
||||
if err != nil {
|
||||
logger.Error("Failed to establish remote connection", "err", err)
|
||||
stats.Append([]string{server, "", err.Error(), "", ""})
|
||||
|
@ -28,7 +28,9 @@ import (
|
||||
func (w *wizard) manageServers() {
|
||||
// List all the servers we can disconnect, along with an entry to connect a new one
|
||||
fmt.Println()
|
||||
for i, server := range w.conf.Servers {
|
||||
|
||||
servers := w.conf.servers()
|
||||
for i, server := range servers {
|
||||
fmt.Printf(" %d. Disconnect %s\n", i+1, server)
|
||||
}
|
||||
fmt.Printf(" %d. Connect another server\n", len(w.conf.Servers)+1)
|
||||
@ -40,14 +42,14 @@ func (w *wizard) manageServers() {
|
||||
}
|
||||
// If the user selected an existing server, drop it
|
||||
if choice <= len(w.conf.Servers) {
|
||||
server := w.conf.Servers[choice-1]
|
||||
server := servers[choice-1]
|
||||
client := w.servers[server]
|
||||
|
||||
delete(w.servers, server)
|
||||
if client != nil {
|
||||
client.Close()
|
||||
}
|
||||
w.conf.Servers = append(w.conf.Servers[:choice-1], w.conf.Servers[choice:]...)
|
||||
delete(w.conf.Servers, server)
|
||||
w.conf.flush()
|
||||
|
||||
log.Info("Disconnected existing server", "server", server)
|
||||
@ -73,14 +75,14 @@ func (w *wizard) makeServer() string {
|
||||
// Read and fial the server to ensure docker is present
|
||||
input := w.readString()
|
||||
|
||||
client, err := dial(input)
|
||||
client, err := dial(input, nil)
|
||||
if err != nil {
|
||||
log.Error("Server not ready for puppeth", "err", err)
|
||||
return ""
|
||||
}
|
||||
// All checks passed, start tracking the server
|
||||
w.servers[input] = client
|
||||
w.conf.Servers = append(w.conf.Servers, input)
|
||||
w.conf.Servers[input] = client.pubkey
|
||||
w.conf.flush()
|
||||
|
||||
return input
|
||||
@ -93,7 +95,9 @@ func (w *wizard) selectServer() string {
|
||||
// List the available server to the user and wait for a choice
|
||||
fmt.Println()
|
||||
fmt.Println("Which server do you want to interact with?")
|
||||
for i, server := range w.conf.Servers {
|
||||
|
||||
servers := w.conf.servers()
|
||||
for i, server := range servers {
|
||||
fmt.Printf(" %d. %s\n", i+1, server)
|
||||
}
|
||||
fmt.Printf(" %d. Connect another server\n", len(w.conf.Servers)+1)
|
||||
@ -105,7 +109,7 @@ func (w *wizard) selectServer() string {
|
||||
}
|
||||
// If the user requested connecting to a new server, go for it
|
||||
if choice <= len(w.conf.Servers) {
|
||||
return w.conf.Servers[choice-1]
|
||||
return servers[choice-1]
|
||||
}
|
||||
return w.makeServer()
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv2"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
@ -115,6 +115,10 @@ var (
|
||||
Name: "keystore",
|
||||
Usage: "Directory for the keystore (default = inside the datadir)",
|
||||
}
|
||||
NoUSBFlag = cli.BoolFlag{
|
||||
Name: "nousb",
|
||||
Usage: "Disables monitoring for and managine USB hardware wallets",
|
||||
}
|
||||
EthashCacheDirFlag = DirectoryFlag{
|
||||
Name: "ethash.cachedir",
|
||||
Usage: "Directory to store the ethash verification caches (default = inside the datadir)",
|
||||
@ -144,15 +148,19 @@ var (
|
||||
Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)",
|
||||
Value: eth.DefaultConfig.EthashDatasetsOnDisk,
|
||||
}
|
||||
NetworkIdFlag = cli.IntFlag{
|
||||
NetworkIdFlag = cli.Uint64Flag{
|
||||
Name: "networkid",
|
||||
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten)",
|
||||
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby)",
|
||||
Value: eth.DefaultConfig.NetworkId,
|
||||
}
|
||||
TestNetFlag = cli.BoolFlag{
|
||||
TestnetFlag = cli.BoolFlag{
|
||||
Name: "testnet",
|
||||
Usage: "Ropsten network: pre-configured proof-of-work test network",
|
||||
}
|
||||
RinkebyFlag = cli.BoolFlag{
|
||||
Name: "rinkeby",
|
||||
Usage: "Rinkeby network: pre-configured proof-of-authority test network",
|
||||
}
|
||||
DevModeFlag = cli.BoolFlag{
|
||||
Name: "dev",
|
||||
Usage: "Developer mode: pre-configured private network with several debugging flags",
|
||||
@ -327,7 +335,7 @@ var (
|
||||
}
|
||||
ExecFlag = cli.StringFlag{
|
||||
Name: "exec",
|
||||
Usage: "Execute JavaScript statement (only in combination with console/attach)",
|
||||
Usage: "Execute JavaScript statement",
|
||||
}
|
||||
PreloadJSFlag = cli.StringFlag{
|
||||
Name: "preload",
|
||||
@ -411,10 +419,12 @@ var (
|
||||
// the a subdirectory of the specified datadir will be used.
|
||||
func MakeDataDir(ctx *cli.Context) string {
|
||||
if path := ctx.GlobalString(DataDirFlag.Name); path != "" {
|
||||
// TODO: choose a different location outside of the regular datadir.
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
if ctx.GlobalBool(TestnetFlag.Name) {
|
||||
return filepath.Join(path, "testnet")
|
||||
}
|
||||
if ctx.GlobalBool(RinkebyFlag.Name) {
|
||||
return filepath.Join(path, "rinkeby")
|
||||
}
|
||||
return path
|
||||
}
|
||||
Fatalf("Cannot determine default data directory, please set manually (--datadir)")
|
||||
@ -458,10 +468,13 @@ func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) {
|
||||
// flags, reverting to pre-configured ones if none have been specified.
|
||||
func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||
urls := params.MainnetBootnodes
|
||||
if ctx.GlobalIsSet(BootnodesFlag.Name) {
|
||||
switch {
|
||||
case ctx.GlobalIsSet(BootnodesFlag.Name):
|
||||
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
|
||||
} else if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
case ctx.GlobalBool(TestnetFlag.Name):
|
||||
urls = params.TestnetBootnodes
|
||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||
urls = params.RinkebyBootnodes
|
||||
}
|
||||
|
||||
cfg.BootstrapNodes = make([]*discover.Node, 0, len(urls))
|
||||
@ -479,9 +492,12 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||
// flags, reverting to pre-configured ones if none have been specified.
|
||||
func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
|
||||
urls := params.DiscoveryV5Bootnodes
|
||||
if ctx.GlobalIsSet(BootnodesFlag.Name) {
|
||||
switch {
|
||||
case ctx.GlobalIsSet(BootnodesFlag.Name):
|
||||
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
|
||||
} else if cfg.BootstrapNodesV5 == nil {
|
||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||
urls = params.RinkebyV5Bootnodes
|
||||
case cfg.BootstrapNodesV5 != nil:
|
||||
return // already set, don't apply defaults.
|
||||
}
|
||||
|
||||
@ -643,7 +659,7 @@ func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) {
|
||||
}
|
||||
}
|
||||
|
||||
// MakePasswordList reads password lines from the file specified by --password.
|
||||
// MakePasswordList reads password lines from the file specified by the global --password flag.
|
||||
func MakePasswordList(ctx *cli.Context) []string {
|
||||
path := ctx.GlobalString(PasswordFileFlag.Name)
|
||||
if path == "" {
|
||||
@ -719,8 +735,10 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
|
||||
cfg.DataDir = ctx.GlobalString(DataDirFlag.Name)
|
||||
case ctx.GlobalBool(DevModeFlag.Name):
|
||||
cfg.DataDir = filepath.Join(os.TempDir(), "ethereum_dev_mode")
|
||||
case ctx.GlobalBool(TestNetFlag.Name):
|
||||
case ctx.GlobalBool(TestnetFlag.Name):
|
||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet")
|
||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet(KeyStoreDirFlag.Name) {
|
||||
@ -729,6 +747,9 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
|
||||
if ctx.GlobalIsSet(LightKDFFlag.Name) {
|
||||
cfg.UseLightweightKDF = ctx.GlobalBool(LightKDFFlag.Name)
|
||||
}
|
||||
if ctx.GlobalIsSet(NoUSBFlag.Name) {
|
||||
cfg.NoUSB = ctx.GlobalBool(NoUSBFlag.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func setGPO(ctx *cli.Context, cfg *gasprice.Config) {
|
||||
@ -776,7 +797,7 @@ func checkExclusive(ctx *cli.Context, flags ...cli.Flag) {
|
||||
// SetEthConfig applies eth-related command line flags to the config.
|
||||
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||
// Avoid conflicting network flags
|
||||
checkExclusive(ctx, DevModeFlag, TestNetFlag)
|
||||
checkExclusive(ctx, DevModeFlag, TestnetFlag, RinkebyFlag)
|
||||
checkExclusive(ctx, FastSyncFlag, LightModeFlag, SyncModeFlag)
|
||||
|
||||
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
||||
@ -799,7 +820,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||
cfg.LightPeers = ctx.GlobalInt(LightPeersFlag.Name)
|
||||
}
|
||||
if ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = ctx.GlobalInt(NetworkIdFlag.Name)
|
||||
cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name)
|
||||
}
|
||||
|
||||
// Ethereum needs to know maxPeers to calculate the light server peer ratio.
|
||||
@ -828,13 +849,18 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||
cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name)
|
||||
}
|
||||
|
||||
// Override any default configs for --dev and --testnet.
|
||||
// Override any default configs for hard coded networks.
|
||||
switch {
|
||||
case ctx.GlobalBool(TestNetFlag.Name):
|
||||
case ctx.GlobalBool(TestnetFlag.Name):
|
||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 3
|
||||
}
|
||||
cfg.Genesis = core.DefaultTestnetGenesisBlock()
|
||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 4
|
||||
}
|
||||
cfg.Genesis = core.DefaultRinkebyGenesisBlock()
|
||||
case ctx.GlobalBool(DevModeFlag.Name):
|
||||
cfg.Genesis = core.DevGenesisBlock()
|
||||
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
|
||||
@ -901,22 +927,16 @@ func SetupNetwork(ctx *cli.Context) {
|
||||
params.TargetGasLimit = new(big.Int).SetUint64(ctx.GlobalUint64(TargetGasLimitFlag.Name))
|
||||
}
|
||||
|
||||
func ChainDbName(ctx *cli.Context) string {
|
||||
if ctx.GlobalBool(LightModeFlag.Name) {
|
||||
return "lightchaindata"
|
||||
} else {
|
||||
return "chaindata"
|
||||
}
|
||||
}
|
||||
|
||||
// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.
|
||||
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
||||
var (
|
||||
cache = ctx.GlobalInt(CacheFlag.Name)
|
||||
handles = makeDatabaseHandles()
|
||||
name = ChainDbName(ctx)
|
||||
)
|
||||
|
||||
name := "chaindata"
|
||||
if ctx.GlobalBool(LightModeFlag.Name) {
|
||||
name = "lightchaindata"
|
||||
}
|
||||
chainDb, err := stack.OpenDatabase(name, cache, handles)
|
||||
if err != nil {
|
||||
Fatalf("Could not open database: %v", err)
|
||||
@ -927,8 +947,10 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
||||
func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
||||
var genesis *core.Genesis
|
||||
switch {
|
||||
case ctx.GlobalBool(TestNetFlag.Name):
|
||||
case ctx.GlobalBool(TestnetFlag.Name):
|
||||
genesis = core.DefaultTestnetGenesisBlock()
|
||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||
genesis = core.DefaultRinkebyGenesisBlock()
|
||||
case ctx.GlobalBool(DevModeFlag.Name):
|
||||
genesis = core.DevGenesisBlock()
|
||||
}
|
||||
@ -972,3 +994,27 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
|
||||
}
|
||||
return preloads
|
||||
}
|
||||
|
||||
// MigrateFlags sets the global flag from a local flag when it's set.
|
||||
// This is a temporary function used for migrating old command/flags to the
|
||||
// new format.
|
||||
//
|
||||
// e.g. geth account new --keystore /tmp/mykeystore --lightkdf
|
||||
//
|
||||
// is equivalent after calling this method with:
|
||||
//
|
||||
// geth --keystore /tmp/mykeystore --lightkdf account new
|
||||
//
|
||||
// This allows the use of the existing configuration functionality.
|
||||
// When all flags are migrated this function can be removed and the existing
|
||||
// configuration functionality must be changed that is uses local flags
|
||||
func MigrateFlags(action func(ctx *cli.Context) error) func(*cli.Context) error {
|
||||
return func(ctx *cli.Context) error {
|
||||
for _, name := range ctx.FlagNames() {
|
||||
if ctx.IsSet(name) {
|
||||
ctx.GlobalSet(name, ctx.String(name))
|
||||
}
|
||||
}
|
||||
return action(ctx)
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ var (
|
||||
pub *ecdsa.PublicKey
|
||||
asymKey *ecdsa.PrivateKey
|
||||
nodeid *ecdsa.PrivateKey
|
||||
topic []byte
|
||||
topic whisper.TopicType
|
||||
asymKeyID string
|
||||
filterID string
|
||||
symPass string
|
||||
@ -84,7 +84,7 @@ var (
|
||||
testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics")
|
||||
echoMode = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics")
|
||||
|
||||
argVerbosity = flag.Int("verbosity", int(log.LvlWarn), "log verbosity level")
|
||||
argVerbosity = flag.Int("verbosity", int(log.LvlError), "log verbosity level")
|
||||
argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds")
|
||||
argWorkTime = flag.Uint("work", 5, "work time in seconds")
|
||||
argMaxSize = flag.Int("maxsize", whisper.DefaultMaxMessageLength, "max size of message")
|
||||
@ -129,7 +129,7 @@ func processArgs() {
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to parse the topic: %s", err)
|
||||
}
|
||||
topic = x
|
||||
topic = whisper.BytesToTopic(x)
|
||||
}
|
||||
|
||||
if *asymmetricMode && len(*argPub) > 0 {
|
||||
@ -183,7 +183,7 @@ func initialize() {
|
||||
|
||||
if *testMode {
|
||||
symPass = "wwww" // ascii code: 0x77777777
|
||||
msPassword = "mail server test password"
|
||||
msPassword = "wwww"
|
||||
}
|
||||
|
||||
if *bootstrapMode {
|
||||
@ -307,7 +307,11 @@ func configureNode() {
|
||||
if *asymmetricMode {
|
||||
if len(*argPub) == 0 {
|
||||
s := scanLine("Please enter the peer's public key: ")
|
||||
pub = crypto.ToECDSAPub(common.FromHex(s))
|
||||
b := common.FromHex(s)
|
||||
if b == nil {
|
||||
utils.Fatalf("Error: can not convert hexadecimal string")
|
||||
}
|
||||
pub = crypto.ToECDSAPub(b)
|
||||
if !isKeyValid(pub) {
|
||||
utils.Fatalf("Error: invalid public key")
|
||||
}
|
||||
@ -326,7 +330,7 @@ func configureNode() {
|
||||
|
||||
if !*asymmetricMode && !*forwarderMode {
|
||||
if len(symPass) == 0 {
|
||||
symPass, err = console.Stdin.PromptPassword("Please enter the password: ")
|
||||
symPass, err = console.Stdin.PromptPassword("Please enter the password for symmetric encryption: ")
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to read passphrase: %v", err)
|
||||
}
|
||||
@ -343,6 +347,8 @@ func configureNode() {
|
||||
if len(*argTopic) == 0 {
|
||||
generateTopic([]byte(symPass))
|
||||
}
|
||||
|
||||
fmt.Printf("Filter is configured for the topic: %x \n", topic)
|
||||
}
|
||||
|
||||
if *mailServerMode {
|
||||
@ -354,18 +360,17 @@ func configureNode() {
|
||||
filter := whisper.Filter{
|
||||
KeySym: symKey,
|
||||
KeyAsym: asymKey,
|
||||
Topics: [][]byte{topic},
|
||||
Topics: [][]byte{topic[:]},
|
||||
AllowP2P: p2pAccept,
|
||||
}
|
||||
filterID, err = shh.Subscribe(&filter)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to install filter: %s", err)
|
||||
}
|
||||
fmt.Printf("Filter is configured for the topic: %x \n", topic)
|
||||
}
|
||||
|
||||
func generateTopic(password []byte) {
|
||||
x := pbkdf2.Key(password, password, 8196, 128, sha512.New)
|
||||
x := pbkdf2.Key(password, password, 4096, 128, sha512.New)
|
||||
for i := 0; i < len(x); i++ {
|
||||
topic[i%whisper.TopicLength] ^= x[i]
|
||||
}
|
||||
@ -485,16 +490,15 @@ func sendMsg(payload []byte) common.Hash {
|
||||
Dst: pub,
|
||||
KeySym: symKey,
|
||||
Payload: payload,
|
||||
Topic: whisper.BytesToTopic(topic),
|
||||
Topic: topic,
|
||||
TTL: uint32(*argTTL),
|
||||
PoW: *argPoW,
|
||||
WorkTime: uint32(*argWorkTime),
|
||||
}
|
||||
|
||||
msg := whisper.NewSentMessage(¶ms)
|
||||
if msg == nil {
|
||||
fmt.Printf("failed to create new message (OS level error)")
|
||||
os.Exit(0)
|
||||
msg, err := whisper.NewSentMessage(¶ms)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to create new message: %s", err)
|
||||
}
|
||||
envelope, err := msg.Wrap(¶ms)
|
||||
if err != nil {
|
||||
@ -624,9 +628,9 @@ func requestExpiredMessagesLoop() {
|
||||
params.Src = nodeid
|
||||
params.WorkTime = 5
|
||||
|
||||
msg := whisper.NewSentMessage(¶ms)
|
||||
if msg == nil {
|
||||
utils.Fatalf("failed to create new message (OS level error)")
|
||||
msg, err := whisper.NewSentMessage(¶ms)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to create new message: %s", err)
|
||||
}
|
||||
env, err := msg.Wrap(¶ms)
|
||||
if err != nil {
|
||||
|
@ -27,6 +27,8 @@ var (
|
||||
tt256 = BigPow(2, 256)
|
||||
tt256m1 = new(big.Int).Sub(tt256, big.NewInt(1))
|
||||
MaxBig256 = new(big.Int).Set(tt256m1)
|
||||
tt63 = BigPow(2, 63)
|
||||
MaxBig63 = new(big.Int).Sub(tt63, big.NewInt(1))
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -599,7 +599,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, stop <-ch
|
||||
for seen, recent := range snap.Recents {
|
||||
if recent == signer {
|
||||
// Signer is among recents, only wait if the current block doens't shift it out
|
||||
if limit := uint64(len(snap.Signers)/2 + 1); seen > number-limit {
|
||||
if limit := uint64(len(snap.Signers)/2 + 1); number < limit || seen > number-limit {
|
||||
log.Info("Signed recently, must wait for others")
|
||||
<-stop
|
||||
return nil, nil
|
||||
|
@ -243,6 +243,15 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
|
||||
if expected.Cmp(header.Difficulty) != 0 {
|
||||
return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected)
|
||||
}
|
||||
// Verify that the gas limit is <= 2^63-1
|
||||
if header.GasLimit.Cmp(math.MaxBig63) > 0 {
|
||||
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, math.MaxBig63)
|
||||
}
|
||||
// Verify that the gasUsed is <= gasLimit
|
||||
if header.GasUsed.Cmp(header.GasLimit) > 0 {
|
||||
return fmt.Errorf("invalid gasUsed: have %v, gasLimit %v", header.GasUsed, header.GasLimit)
|
||||
}
|
||||
|
||||
// Verify that the gas limit remains within allowed bounds
|
||||
diff := new(big.Int).Set(parent.GasLimit)
|
||||
diff = diff.Sub(diff, header.GasLimit)
|
||||
|
@ -467,8 +467,9 @@ func (ethash *Ethash) cache(block uint64) []uint32 {
|
||||
future = &cache{epoch: epoch + 1}
|
||||
ethash.fcache = future
|
||||
}
|
||||
// New current cache, set its initial timestamp
|
||||
current.used = time.Now()
|
||||
}
|
||||
current.used = time.Now()
|
||||
ethash.lock.Unlock()
|
||||
|
||||
// Wait for generation finish, bump the timestamp and finalize the cache
|
||||
@ -529,8 +530,9 @@ func (ethash *Ethash) dataset(block uint64) []uint32 {
|
||||
future = &dataset{epoch: epoch + 1}
|
||||
ethash.fdataset = future
|
||||
}
|
||||
// New current dataset, set its initial timestamp
|
||||
current.used = time.Now()
|
||||
}
|
||||
current.used = time.Now()
|
||||
ethash.lock.Unlock()
|
||||
|
||||
// Wait for generation finish, bump the timestamp and finalize the cache
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
@ -240,17 +241,19 @@ func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
|
||||
throwJSException(err.Error())
|
||||
}
|
||||
var (
|
||||
rawReq = []byte(reqVal.String())
|
||||
rawReq = reqVal.String()
|
||||
dec = json.NewDecoder(strings.NewReader(rawReq))
|
||||
reqs []jsonrpcCall
|
||||
batch bool
|
||||
)
|
||||
dec.UseNumber() // avoid float64s
|
||||
if rawReq[0] == '[' {
|
||||
batch = true
|
||||
json.Unmarshal(rawReq, &reqs)
|
||||
dec.Decode(&reqs)
|
||||
} else {
|
||||
batch = false
|
||||
reqs = make([]jsonrpcCall, 1)
|
||||
json.Unmarshal(rawReq, &reqs[0])
|
||||
dec.Decode(&reqs[0])
|
||||
}
|
||||
|
||||
// Execute the requests.
|
||||
|
@ -628,7 +628,6 @@ type WriteStatus byte
|
||||
const (
|
||||
NonStatTy WriteStatus = iota
|
||||
CanonStatTy
|
||||
SplitStatTy
|
||||
SideStatTy
|
||||
)
|
||||
|
||||
@ -1029,9 +1028,6 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
|
||||
blockInsertTimer.UpdateSince(bstart)
|
||||
events = append(events, ChainSideEvent{block})
|
||||
|
||||
case SplitStatTy:
|
||||
events = append(events, ChainSplitEvent{block, logs})
|
||||
}
|
||||
stats.processed++
|
||||
stats.usedGas += usedGas.Uint64()
|
||||
@ -1226,8 +1222,9 @@ func (self *BlockChain) postChainEvents(events []interface{}, logs []*types.Log)
|
||||
self.eventMux.Post(logs)
|
||||
for _, event := range events {
|
||||
if event, ok := event.(ChainEvent); ok {
|
||||
// We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long
|
||||
// and in most cases isn't even necessary.
|
||||
// We need some control over the mining operation. Acquiring locks and waiting
|
||||
// for the miner to create new block takes too long and in most cases isn't
|
||||
// even necessary.
|
||||
if self.LastBlockHash() == event.Hash {
|
||||
self.eventMux.Post(ChainHeadEvent{event.Block})
|
||||
}
|
||||
|
@ -46,12 +46,6 @@ type RemovedTransactionEvent struct{ Txs types.Transactions }
|
||||
// RemovedLogEvent is posted when a reorg happens
|
||||
type RemovedLogsEvent struct{ Logs []*types.Log }
|
||||
|
||||
// ChainSplit is posted when a new head is detected
|
||||
type ChainSplitEvent struct {
|
||||
Block *types.Block
|
||||
Logs []*types.Log
|
||||
}
|
||||
|
||||
type ChainEvent struct {
|
||||
Block *types.Block
|
||||
Hash common.Hash
|
||||
|
@ -86,7 +86,7 @@ type GenesisMismatchError struct {
|
||||
}
|
||||
|
||||
func (e *GenesisMismatchError) Error() string {
|
||||
return fmt.Sprintf("wrong genesis block in database (have %x, new %x)", e.Stored[:8], e.New[:8])
|
||||
return fmt.Sprintf("database already contains an incompatible genesis block (have %x, new %x)", e.Stored[:8], e.New[:8])
|
||||
}
|
||||
|
||||
// SetupGenesisBlock writes or updates the genesis block in db.
|
||||
@ -278,6 +278,18 @@ func DefaultTestnetGenesisBlock() *Genesis {
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultRinkebyGenesisBlock returns the Rinkeby network genesis block.
|
||||
func DefaultRinkebyGenesisBlock() *Genesis {
|
||||
return &Genesis{
|
||||
Config: params.RinkebyChainConfig,
|
||||
Timestamp: 1492009146,
|
||||
ExtraData: hexutil.MustDecode("0x52657370656374206d7920617574686f7269746168207e452e436172746d616e42eb768f2244c8811c63729a21a3569731535f067ffc57839b00206d1ad20c69a1981b489f772031b279182d99e65703f0076e4812653aab85fca0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||
GasLimit: 4700000,
|
||||
Difficulty: big.NewInt(1),
|
||||
Alloc: decodePrealloc(rinkebyAllocData),
|
||||
}
|
||||
}
|
||||
|
||||
// DevGenesisBlock returns the 'geth --dev' genesis block.
|
||||
func DevGenesisBlock() *Genesis {
|
||||
return &Genesis{
|
||||
|
File diff suppressed because one or more lines are too long
@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
type DumpAccount struct {
|
||||
@ -44,7 +45,7 @@ func (self *StateDB) RawDump() Dump {
|
||||
Accounts: make(map[string]DumpAccount),
|
||||
}
|
||||
|
||||
it := self.trie.Iterator()
|
||||
it := trie.NewIterator(self.trie.NodeIterator(nil))
|
||||
for it.Next() {
|
||||
addr := self.trie.GetKey(it.Key)
|
||||
var data Account
|
||||
@ -61,7 +62,7 @@ func (self *StateDB) RawDump() Dump {
|
||||
Code: common.Bytes2Hex(obj.Code(self.db)),
|
||||
Storage: make(map[string]string),
|
||||
}
|
||||
storageIt := obj.getTrie(self.db).Iterator()
|
||||
storageIt := trie.NewIterator(obj.getTrie(self.db).NodeIterator(nil))
|
||||
for storageIt.Next() {
|
||||
account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ func (it *NodeIterator) step() error {
|
||||
}
|
||||
// Initialize the iterator if we've just started
|
||||
if it.stateIt == nil {
|
||||
it.stateIt = it.state.trie.NodeIterator()
|
||||
it.stateIt = it.state.trie.NodeIterator(nil)
|
||||
}
|
||||
// If we had data nodes previously, we surely have at least state nodes
|
||||
if it.dataIt != nil {
|
||||
@ -118,7 +118,7 @@ func (it *NodeIterator) step() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
it.dataIt = trie.NewNodeIterator(dataTrie)
|
||||
it.dataIt = dataTrie.NodeIterator(nil)
|
||||
if !it.dataIt.Next(true) {
|
||||
it.dataIt = nil
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ func (self *stateObject) setState(key, value common.Hash) {
|
||||
}
|
||||
|
||||
// updateTrie writes cached storage modifications into the object's storage trie.
|
||||
func (self *stateObject) updateTrie(db trie.Database) {
|
||||
func (self *stateObject) updateTrie(db trie.Database) *trie.SecureTrie {
|
||||
tr := self.getTrie(db)
|
||||
for key, value := range self.dirtyStorage {
|
||||
delete(self.dirtyStorage, key)
|
||||
@ -213,6 +213,7 @@ func (self *stateObject) updateTrie(db trie.Database) {
|
||||
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
|
||||
tr.Update(key[:], v)
|
||||
}
|
||||
return tr
|
||||
}
|
||||
|
||||
// UpdateRoot sets the trie root to the current root hash of
|
||||
@ -280,7 +281,11 @@ func (c *stateObject) ReturnGas(gas *big.Int) {}
|
||||
|
||||
func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject {
|
||||
stateObject := newObject(db, self.address, self.data, onDirty)
|
||||
stateObject.trie = self.trie
|
||||
if self.trie != nil {
|
||||
// A shallow copy makes the two tries independent.
|
||||
cpy := *self.trie
|
||||
stateObject.trie = &cpy
|
||||
}
|
||||
stateObject.code = self.code
|
||||
stateObject.dirtyStorage = self.dirtyStorage.Copy()
|
||||
stateObject.cachedStorage = self.dirtyStorage.Copy()
|
||||
|
@ -296,6 +296,17 @@ func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
// StorageTrie returns the storage trie of an account.
|
||||
// The return value is a copy and is nil for non-existent accounts.
|
||||
func (self *StateDB) StorageTrie(a common.Address) *trie.SecureTrie {
|
||||
stateObject := self.getStateObject(a)
|
||||
if stateObject == nil {
|
||||
return nil
|
||||
}
|
||||
cpy := stateObject.deepCopy(self, nil)
|
||||
return cpy.updateTrie(self.db)
|
||||
}
|
||||
|
||||
func (self *StateDB) HasSuicided(addr common.Address) bool {
|
||||
stateObject := self.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
@ -481,7 +492,7 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common
|
||||
cb(h, value)
|
||||
}
|
||||
|
||||
it := so.getTrie(db.db).Iterator()
|
||||
it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil))
|
||||
for it.Next() {
|
||||
// ignore cached values
|
||||
key := common.BytesToHash(db.trie.GetKey(it.Key))
|
||||
|
151
eth/api.go
151
eth/api.go
@ -20,7 +20,6 @@ import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@ -41,6 +40,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
const defaultTraceTimeout = 5 * time.Second
|
||||
@ -526,59 +526,67 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
|
||||
if tx == nil {
|
||||
return nil, fmt.Errorf("transaction %x not found", txHash)
|
||||
}
|
||||
block := api.eth.BlockChain().GetBlockByHash(blockHash)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("block %x not found", blockHash)
|
||||
}
|
||||
// Create the state database to mutate and eventually trace
|
||||
parent := api.eth.BlockChain().GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
if parent == nil {
|
||||
return nil, fmt.Errorf("block parent %x not found", block.ParentHash())
|
||||
}
|
||||
stateDb, err := api.eth.BlockChain().StateAt(parent.Root())
|
||||
msg, context, statedb, err := api.computeTxEnv(blockHash, int(txIndex))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signer := types.MakeSigner(api.config, block.Number())
|
||||
// Mutate the state and trace the selected transaction
|
||||
for idx, tx := range block.Transactions() {
|
||||
// Assemble the transaction call message
|
||||
msg, err := tx.AsMessage(signer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sender retrieval failed: %v", err)
|
||||
}
|
||||
context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain(), nil)
|
||||
|
||||
// Mutate the state if we haven't reached the tracing transaction yet
|
||||
if uint64(idx) < txIndex {
|
||||
vmenv := vm.NewEVM(context, stateDb, api.config, vm.Config{})
|
||||
_, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("mutation failed: %v", err)
|
||||
}
|
||||
stateDb.DeleteSuicides()
|
||||
continue
|
||||
}
|
||||
|
||||
vmenv := vm.NewEVM(context, stateDb, api.config, vm.Config{Debug: true, Tracer: tracer})
|
||||
ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tracing failed: %v", err)
|
||||
}
|
||||
|
||||
switch tracer := tracer.(type) {
|
||||
case *vm.StructLogger:
|
||||
return ðapi.ExecutionResult{
|
||||
Gas: gas,
|
||||
ReturnValue: fmt.Sprintf("%x", ret),
|
||||
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
|
||||
}, nil
|
||||
case *ethapi.JavascriptTracer:
|
||||
return tracer.GetResult()
|
||||
}
|
||||
// Run the transaction with tracing enabled.
|
||||
vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{Debug: true, Tracer: tracer})
|
||||
ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tracing failed: %v", err)
|
||||
}
|
||||
return nil, errors.New("database inconsistency")
|
||||
switch tracer := tracer.(type) {
|
||||
case *vm.StructLogger:
|
||||
return ðapi.ExecutionResult{
|
||||
Gas: gas,
|
||||
ReturnValue: fmt.Sprintf("%x", ret),
|
||||
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
|
||||
}, nil
|
||||
case *ethapi.JavascriptTracer:
|
||||
return tracer.GetResult()
|
||||
default:
|
||||
panic(fmt.Sprintf("bad tracer type %T", tracer))
|
||||
}
|
||||
}
|
||||
|
||||
// computeTxEnv returns the execution environment of a certain transaction.
|
||||
func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int) (core.Message, vm.Context, *state.StateDB, error) {
|
||||
// Create the parent state.
|
||||
block := api.eth.BlockChain().GetBlockByHash(blockHash)
|
||||
if block == nil {
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("block %x not found", blockHash)
|
||||
}
|
||||
parent := api.eth.BlockChain().GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
if parent == nil {
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("block parent %x not found", block.ParentHash())
|
||||
}
|
||||
statedb, err := api.eth.BlockChain().StateAt(parent.Root())
|
||||
if err != nil {
|
||||
return nil, vm.Context{}, nil, err
|
||||
}
|
||||
txs := block.Transactions()
|
||||
|
||||
// Recompute transactions up to the target index.
|
||||
signer := types.MakeSigner(api.config, block.Number())
|
||||
for idx, tx := range txs {
|
||||
// Assemble the transaction call message
|
||||
msg, _ := tx.AsMessage(signer)
|
||||
context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain(), nil)
|
||||
if idx == txIndex {
|
||||
return msg, context, statedb, nil
|
||||
}
|
||||
|
||||
vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{})
|
||||
gp := new(core.GasPool).AddGas(tx.Gas())
|
||||
_, _, err := core.ApplyMessage(vmenv, msg, gp)
|
||||
if err != nil {
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
|
||||
}
|
||||
statedb.DeleteSuicides()
|
||||
}
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash)
|
||||
}
|
||||
|
||||
// Preimage is a debug API function that returns the preimage for a sha3 hash, if known.
|
||||
@ -592,3 +600,48 @@ func (api *PrivateDebugAPI) Preimage(ctx context.Context, hash common.Hash) (hex
|
||||
func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]core.BadBlockArgs, error) {
|
||||
return api.eth.BlockChain().BadBlocks()
|
||||
}
|
||||
|
||||
// StorageRangeResult is the result of a debug_storageRangeAt API call.
|
||||
type StorageRangeResult struct {
|
||||
Storage storageMap `json:"storage"`
|
||||
NextKey *common.Hash `json:"nextKey"` // nil if Storage includes the last key in the trie.
|
||||
}
|
||||
|
||||
type storageMap map[common.Hash]storageEntry
|
||||
|
||||
type storageEntry struct {
|
||||
Key *common.Hash `json:"key"`
|
||||
Value common.Hash `json:"value"`
|
||||
}
|
||||
|
||||
// StorageRangeAt returns the storage at the given block height and transaction index.
|
||||
func (api *PrivateDebugAPI) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex int, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
|
||||
_, _, statedb, err := api.computeTxEnv(blockHash, txIndex)
|
||||
if err != nil {
|
||||
return StorageRangeResult{}, err
|
||||
}
|
||||
st := statedb.StorageTrie(contractAddress)
|
||||
if st == nil {
|
||||
return StorageRangeResult{}, fmt.Errorf("account %x doesn't exist", contractAddress)
|
||||
}
|
||||
return storageRangeAt(st, keyStart, maxResult), nil
|
||||
}
|
||||
|
||||
func storageRangeAt(st *trie.SecureTrie, start []byte, maxResult int) StorageRangeResult {
|
||||
it := trie.NewIterator(st.NodeIterator(start))
|
||||
result := StorageRangeResult{Storage: storageMap{}}
|
||||
for i := 0; i < maxResult && it.Next(); i++ {
|
||||
e := storageEntry{Value: common.BytesToHash(it.Value)}
|
||||
if preimage := st.GetKey(it.Key); preimage != nil {
|
||||
preimage := common.BytesToHash(preimage)
|
||||
e.Key = &preimage
|
||||
}
|
||||
result.Storage[common.BytesToHash(it.Key)] = e
|
||||
}
|
||||
// Add the 'next key' so clients can continue downloading.
|
||||
if it.Next() {
|
||||
next := common.BytesToHash(it.Key)
|
||||
result.NextKey = &next
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
88
eth/api_test.go
Normal file
88
eth/api_test.go
Normal file
@ -0,0 +1,88 @@
|
||||
// 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 eth
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
var dumper = spew.ConfigState{Indent: " "}
|
||||
|
||||
func TestStorageRangeAt(t *testing.T) {
|
||||
// Create a state where account 0x010000... has a few storage entries.
|
||||
var (
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
state, _ = state.New(common.Hash{}, db)
|
||||
addr = common.Address{0x01}
|
||||
keys = []common.Hash{ // hashes of Keys of storage
|
||||
common.HexToHash("340dd630ad21bf010b4e676dbfa9ba9a02175262d1fa356232cfde6cb5b47ef2"),
|
||||
common.HexToHash("426fcb404ab2d5d8e61a3d918108006bbb0a9be65e92235bb10eefbdb6dcd053"),
|
||||
common.HexToHash("48078cfed56339ea54962e72c37c7f588fc4f8e5bc173827ba75cb10a63a96a5"),
|
||||
common.HexToHash("5723d2c3a83af9b735e3b7f21531e5623d183a9095a56604ead41f3582fdfb75"),
|
||||
}
|
||||
storage = storageMap{
|
||||
keys[0]: {Key: &common.Hash{0x02}, Value: common.Hash{0x01}},
|
||||
keys[1]: {Key: &common.Hash{0x04}, Value: common.Hash{0x02}},
|
||||
keys[2]: {Key: &common.Hash{0x01}, Value: common.Hash{0x03}},
|
||||
keys[3]: {Key: &common.Hash{0x03}, Value: common.Hash{0x04}},
|
||||
}
|
||||
)
|
||||
for _, entry := range storage {
|
||||
state.SetState(addr, *entry.Key, entry.Value)
|
||||
}
|
||||
|
||||
// Check a few combinations of limit and start/end.
|
||||
tests := []struct {
|
||||
start []byte
|
||||
limit int
|
||||
want StorageRangeResult
|
||||
}{
|
||||
{
|
||||
start: []byte{}, limit: 0,
|
||||
want: StorageRangeResult{storageMap{}, &keys[0]},
|
||||
},
|
||||
{
|
||||
start: []byte{}, limit: 100,
|
||||
want: StorageRangeResult{storage, nil},
|
||||
},
|
||||
{
|
||||
start: []byte{}, limit: 2,
|
||||
want: StorageRangeResult{storageMap{keys[0]: storage[keys[0]], keys[1]: storage[keys[1]]}, &keys[2]},
|
||||
},
|
||||
{
|
||||
start: []byte{0x00}, limit: 4,
|
||||
want: StorageRangeResult{storage, nil},
|
||||
},
|
||||
{
|
||||
start: []byte{0x40}, limit: 2,
|
||||
want: StorageRangeResult{storageMap{keys[1]: storage[keys[1]], keys[2]: storage[keys[2]]}, &keys[3]},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
result := storageRangeAt(state.StorageTrie(addr), test.start, test.limit)
|
||||
if !reflect.DeepEqual(result, test.want) {
|
||||
t.Fatalf("wrong result for range 0x%x.., limit %d:\ngot %s\nwant %s",
|
||||
test.start, test.limit, dumper.Sdump(result), dumper.Sdump(&test.want))
|
||||
}
|
||||
}
|
||||
}
|
@ -80,7 +80,7 @@ type Ethereum struct {
|
||||
MinerThreads int
|
||||
etherbase common.Address
|
||||
|
||||
netVersionId int
|
||||
networkId uint64
|
||||
netRPCService *ethapi.PublicNetAPI
|
||||
}
|
||||
|
||||
@ -118,7 +118,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
engine: CreateConsensusEngine(ctx, config, chainConfig, chainDb),
|
||||
shutdownChan: make(chan bool),
|
||||
stopDbUpgrade: stopDbUpgrade,
|
||||
netVersionId: config.NetworkId,
|
||||
networkId: config.NetworkId,
|
||||
etherbase: config.Etherbase,
|
||||
MinerThreads: config.MinerThreads,
|
||||
}
|
||||
@ -347,7 +347,7 @@ func (s *Ethereum) Engine() consensus.Engine { return s.engine }
|
||||
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
||||
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
||||
func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
|
||||
func (s *Ethereum) NetVersion() int { return s.netVersionId }
|
||||
func (s *Ethereum) NetVersion() uint64 { return s.networkId }
|
||||
func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
|
||||
|
||||
// Protocols implements node.Service, returning all the currently configured
|
||||
|
@ -72,7 +72,7 @@ type Config struct {
|
||||
Genesis *core.Genesis `toml:",omitempty"`
|
||||
|
||||
// Protocol options
|
||||
NetworkId int // Network ID to use for selecting peers to connect to
|
||||
NetworkId uint64 // Network ID to use for selecting peers to connect to
|
||||
SyncMode downloader.SyncMode
|
||||
|
||||
// Light client options
|
||||
|
@ -1491,6 +1491,10 @@ func (d *Downloader) qosTuner() {
|
||||
func (d *Downloader) qosReduceConfidence() {
|
||||
// If we have a single peer, confidence is always 1
|
||||
peers := uint64(d.peers.Len())
|
||||
if peers == 0 {
|
||||
// Ensure peer connectivity races don't catch us off guard
|
||||
return
|
||||
}
|
||||
if peers == 1 {
|
||||
atomic.StoreUint64(&d.rttConfidence, 1000000)
|
||||
return
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
func (c Config) MarshalTOML() (interface{}, error) {
|
||||
type Config struct {
|
||||
Genesis *core.Genesis `toml:",omitempty"`
|
||||
NetworkId int
|
||||
NetworkId uint64
|
||||
SyncMode downloader.SyncMode
|
||||
LightServ int `toml:",omitempty"`
|
||||
LightPeers int `toml:",omitempty"`
|
||||
@ -72,7 +72,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||
func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||
type Config struct {
|
||||
Genesis *core.Genesis `toml:",omitempty"`
|
||||
NetworkId *int
|
||||
NetworkId *uint64
|
||||
SyncMode *downloader.SyncMode
|
||||
LightServ *int `toml:",omitempty"`
|
||||
LightPeers *int `toml:",omitempty"`
|
||||
|
@ -60,7 +60,7 @@ func errResp(code errCode, format string, v ...interface{}) error {
|
||||
}
|
||||
|
||||
type ProtocolManager struct {
|
||||
networkId int
|
||||
networkId uint64
|
||||
|
||||
fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks)
|
||||
acceptTxs uint32 // Flag whether we're considered synchronised (enables transaction processing)
|
||||
@ -96,7 +96,7 @@ type ProtocolManager struct {
|
||||
|
||||
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
|
||||
// with the ethereum network.
|
||||
func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkId int, maxPeers int, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) {
|
||||
func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkId uint64, maxPeers int, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) {
|
||||
// Create the protocol manager with the base fields
|
||||
manager := &ProtocolManager{
|
||||
networkId: networkId,
|
||||
@ -733,7 +733,7 @@ func (self *ProtocolManager) txBroadcastLoop() {
|
||||
// EthNodeInfo represents a short summary of the Ethereum sub-protocol metadata known
|
||||
// about the host peer.
|
||||
type EthNodeInfo struct {
|
||||
Network int `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3)
|
||||
Network uint64 `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3)
|
||||
Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
|
||||
Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block
|
||||
Head common.Hash `json:"head"` // SHA3 hash of the host's best owned block
|
||||
|
@ -173,7 +173,7 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te
|
||||
func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash) {
|
||||
msg := &statusData{
|
||||
ProtocolVersion: uint32(p.version),
|
||||
NetworkId: uint32(DefaultConfig.NetworkId),
|
||||
NetworkId: DefaultConfig.NetworkId,
|
||||
TD: td,
|
||||
CurrentBlock: head,
|
||||
GenesisBlock: genesis,
|
||||
|
@ -230,7 +230,7 @@ func (p *peer) RequestReceipts(hashes []common.Hash) error {
|
||||
|
||||
// Handshake executes the eth protocol handshake, negotiating version number,
|
||||
// network IDs, difficulties, head and genesis blocks.
|
||||
func (p *peer) Handshake(network int, td *big.Int, head common.Hash, genesis common.Hash) error {
|
||||
func (p *peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis common.Hash) error {
|
||||
// Send out own handshake in a new thread
|
||||
errc := make(chan error, 2)
|
||||
var status statusData // safe to read after two values have been received from errc
|
||||
@ -238,7 +238,7 @@ func (p *peer) Handshake(network int, td *big.Int, head common.Hash, genesis com
|
||||
go func() {
|
||||
errc <- p2p.Send(p.rw, StatusMsg, &statusData{
|
||||
ProtocolVersion: uint32(p.version),
|
||||
NetworkId: uint32(network),
|
||||
NetworkId: network,
|
||||
TD: td,
|
||||
CurrentBlock: head,
|
||||
GenesisBlock: genesis,
|
||||
@ -263,7 +263,7 @@ func (p *peer) Handshake(network int, td *big.Int, head common.Hash, genesis com
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *peer) readStatus(network int, status *statusData, genesis common.Hash) (err error) {
|
||||
func (p *peer) readStatus(network uint64, status *statusData, genesis common.Hash) (err error) {
|
||||
msg, err := p.rw.ReadMsg()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -281,7 +281,7 @@ func (p *peer) readStatus(network int, status *statusData, genesis common.Hash)
|
||||
if status.GenesisBlock != genesis {
|
||||
return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock[:8], genesis[:8])
|
||||
}
|
||||
if int(status.NetworkId) != network {
|
||||
if status.NetworkId != network {
|
||||
return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, network)
|
||||
}
|
||||
if int(status.ProtocolVersion) != p.version {
|
||||
|
@ -105,7 +105,7 @@ type txPool interface {
|
||||
// statusData is the network packet for the status message.
|
||||
type statusData struct {
|
||||
ProtocolVersion uint32
|
||||
NetworkId uint32
|
||||
NetworkId uint64
|
||||
TD *big.Int
|
||||
CurrentBlock common.Hash
|
||||
GenesisBlock common.Hash
|
||||
|
@ -55,7 +55,7 @@ func testStatusMsgErrors(t *testing.T, protocol int) {
|
||||
wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"),
|
||||
},
|
||||
{
|
||||
code: StatusMsg, data: statusData{10, uint32(DefaultConfig.NetworkId), td, currentBlock, genesis},
|
||||
code: StatusMsg, data: statusData{10, DefaultConfig.NetworkId, td, currentBlock, genesis},
|
||||
wantError: errResp(ErrProtocolVersionMismatch, "10 (!= %d)", protocol),
|
||||
},
|
||||
{
|
||||
@ -63,7 +63,7 @@ func testStatusMsgErrors(t *testing.T, protocol int) {
|
||||
wantError: errResp(ErrNetworkIdMismatch, "999 (!= 1)"),
|
||||
},
|
||||
{
|
||||
code: StatusMsg, data: statusData{uint32(protocol), uint32(DefaultConfig.NetworkId), td, currentBlock, common.Hash{3}},
|
||||
code: StatusMsg, data: statusData{uint32(protocol), DefaultConfig.NetworkId, td, currentBlock, common.Hash{3}},
|
||||
wantError: errResp(ErrGenesisBlockMismatch, "0300000000000000 (!= %x)", genesis[:8]),
|
||||
},
|
||||
}
|
||||
|
@ -323,10 +323,10 @@ func (s *Service) login(conn *websocket.Conn) error {
|
||||
|
||||
var network, protocol string
|
||||
if info := infos.Protocols["eth"]; info != nil {
|
||||
network = strconv.Itoa(info.(*eth.EthNodeInfo).Network)
|
||||
network = fmt.Sprintf("%d", info.(*eth.EthNodeInfo).Network)
|
||||
protocol = fmt.Sprintf("eth/%d", eth.ProtocolVersions[0])
|
||||
} else {
|
||||
network = strconv.Itoa(infos.Protocols["les"].(*eth.EthNodeInfo).Network)
|
||||
network = fmt.Sprintf("%d", infos.Protocols["les"].(*eth.EthNodeInfo).Network)
|
||||
protocol = fmt.Sprintf("les/%d", les.ProtocolVersions[0])
|
||||
}
|
||||
auth := &authMsg{
|
||||
|
@ -191,7 +191,7 @@ func NewPublicAccountAPI(am *accounts.Manager) *PublicAccountAPI {
|
||||
|
||||
// Accounts returns the collection of accounts this node manages
|
||||
func (s *PublicAccountAPI) Accounts() []common.Address {
|
||||
var addresses []common.Address
|
||||
addresses := make([]common.Address, 0) // return [] instead of nil if empty
|
||||
for _, wallet := range s.am.Wallets() {
|
||||
for _, account := range wallet.Accounts() {
|
||||
addresses = append(addresses, account.Address)
|
||||
@ -218,7 +218,7 @@ func NewPrivateAccountAPI(b Backend) *PrivateAccountAPI {
|
||||
|
||||
// ListAccounts will return a list of addresses for accounts this node manages.
|
||||
func (s *PrivateAccountAPI) ListAccounts() []common.Address {
|
||||
var addresses []common.Address
|
||||
addresses := make([]common.Address, 0) // return [] instead of nil if empty
|
||||
for _, wallet := range s.am.Wallets() {
|
||||
for _, account := range wallet.Accounts() {
|
||||
addresses = append(addresses, account.Address)
|
||||
@ -237,7 +237,7 @@ type rawWallet struct {
|
||||
|
||||
// ListWallets will return a list of wallets this node manages.
|
||||
func (s *PrivateAccountAPI) ListWallets() []rawWallet {
|
||||
var wallets []rawWallet
|
||||
wallets := make([]rawWallet, 0) // return [] instead of nil if empty
|
||||
for _, wallet := range s.am.Wallets() {
|
||||
wallets = append(wallets, rawWallet{
|
||||
URL: wallet.URL().String(),
|
||||
@ -1435,11 +1435,11 @@ func (api *PrivateDebugAPI) SetHead(number hexutil.Uint64) {
|
||||
// PublicNetAPI offers network related RPC methods
|
||||
type PublicNetAPI struct {
|
||||
net *p2p.Server
|
||||
networkVersion int
|
||||
networkVersion uint64
|
||||
}
|
||||
|
||||
// NewPublicNetAPI creates a new net API instance.
|
||||
func NewPublicNetAPI(net *p2p.Server, networkVersion int) *PublicNetAPI {
|
||||
func NewPublicNetAPI(net *p2p.Server, networkVersion uint64) *PublicNetAPI {
|
||||
return &PublicNetAPI{net, networkVersion}
|
||||
}
|
||||
|
||||
|
@ -345,6 +345,11 @@ web3._extend({
|
||||
call: 'debug_getBadBlocks',
|
||||
params: 0,
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'storageRangeAt',
|
||||
call: 'debug_storageRangeAt',
|
||||
params: 5,
|
||||
}),
|
||||
],
|
||||
properties: []
|
||||
});
|
||||
@ -520,7 +525,105 @@ web3._extend({
|
||||
const Shh_JS = `
|
||||
web3._extend({
|
||||
property: 'shh',
|
||||
methods: [],
|
||||
methods: [
|
||||
new web3._extend.Method({
|
||||
name: 'info',
|
||||
call: 'shh_info'
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setMaxMessageLength',
|
||||
call: 'shh_setMaxMessageLength',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setMinimumPoW',
|
||||
call: 'shh_setMinimumPoW',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'allowP2PMessagesFromPeer',
|
||||
call: 'shh_allowP2PMessagesFromPeer',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'hasKeyPair',
|
||||
call: 'shh_hasKeyPair',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'deleteKeyPair',
|
||||
call: 'shh_deleteKeyPair',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'newKeyPair',
|
||||
call: 'shh_newKeyPair'
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getPublicKey',
|
||||
call: 'shh_getPublicKey',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getPrivateKey',
|
||||
call: 'shh_getPrivateKey',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'generateSymmetricKey',
|
||||
call: 'shh_generateSymmetricKey',
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'addSymmetricKeyDirect',
|
||||
call: 'shh_addSymmetricKeyDirect',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'addSymmetricKeyFromPassword',
|
||||
call: 'shh_addSymmetricKeyFromPassword',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'hasSymmetricKey',
|
||||
call: 'shh_hasSymmetricKey',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getSymmetricKey',
|
||||
call: 'shh_getSymmetricKey',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'deleteSymmetricKey',
|
||||
call: 'shh_deleteSymmetricKey',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'subscribe',
|
||||
call: 'shh_subscribe',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'unsubscribe',
|
||||
call: 'shh_unsubscribe',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getNewSubscriptionMessages',
|
||||
call: 'shh_getNewSubscriptionMessages',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getFloatingMessages',
|
||||
call: 'shh_getFloatingMessages',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'post',
|
||||
call: 'shh_post',
|
||||
params: 1
|
||||
})
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
@ -531,6 +634,7 @@ web3._extend({
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const SWARMFS_JS = `
|
||||
web3._extend({
|
||||
property: 'swarmfs',
|
||||
|
@ -61,7 +61,7 @@ type LightEthereum struct {
|
||||
engine consensus.Engine
|
||||
accountManager *accounts.Manager
|
||||
|
||||
netVersionId int
|
||||
networkId uint64
|
||||
netRPCService *ethapi.PublicNetAPI
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
|
||||
accountManager: ctx.AccountManager,
|
||||
engine: eth.CreateConsensusEngine(ctx, config, chainConfig, chainDb),
|
||||
shutdownChan: make(chan bool),
|
||||
netVersionId: config.NetworkId,
|
||||
networkId: config.NetworkId,
|
||||
}
|
||||
if eth.blockchain, err = light.NewLightChain(odr, eth.chainConfig, eth.engine, eth.eventMux); err != nil {
|
||||
return nil, err
|
||||
@ -187,7 +187,7 @@ func (s *LightEthereum) Protocols() []p2p.Protocol {
|
||||
// Ethereum protocol implementation.
|
||||
func (s *LightEthereum) Start(srvr *p2p.Server) error {
|
||||
log.Warn("Light client mode is an experimental feature")
|
||||
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.netVersionId)
|
||||
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.networkId)
|
||||
s.protocolManager.Start(srvr)
|
||||
return nil
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ type ProtocolManager struct {
|
||||
lightSync bool
|
||||
txpool txPool
|
||||
txrelay *LesTxRelay
|
||||
networkId int
|
||||
networkId uint64
|
||||
chainConfig *params.ChainConfig
|
||||
blockchain BlockChain
|
||||
chainDb ethdb.Database
|
||||
@ -128,7 +128,7 @@ type ProtocolManager struct {
|
||||
|
||||
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
|
||||
// with the ethereum network.
|
||||
func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, networkId int, mux *event.TypeMux, engine consensus.Engine, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay) (*ProtocolManager, error) {
|
||||
func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, networkId uint64, mux *event.TypeMux, engine consensus.Engine, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay) (*ProtocolManager, error) {
|
||||
// Create the protocol manager with the base fields
|
||||
manager := &ProtocolManager{
|
||||
lightSync: lightSync,
|
||||
@ -310,7 +310,7 @@ func (pm *ProtocolManager) Stop() {
|
||||
log.Info("Light Ethereum protocol stopped")
|
||||
}
|
||||
|
||||
func (pm *ProtocolManager) newPeer(pv, nv int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
||||
func (pm *ProtocolManager) newPeer(pv int, nv uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
||||
return newPeer(pv, nv, p, newMeteredMsgWriter(rw))
|
||||
}
|
||||
|
||||
|
@ -48,8 +48,8 @@ type peer struct {
|
||||
|
||||
rw p2p.MsgReadWriter
|
||||
|
||||
version int // Protocol version negotiated
|
||||
network int // Network ID being on
|
||||
version int // Protocol version negotiated
|
||||
network uint64 // Network ID being on
|
||||
|
||||
id string
|
||||
|
||||
@ -69,7 +69,7 @@ type peer struct {
|
||||
fcCosts requestCostTable
|
||||
}
|
||||
|
||||
func newPeer(version, network int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
||||
func newPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
||||
id := p.ID()
|
||||
|
||||
return &peer{
|
||||
@ -384,7 +384,7 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
|
||||
if rGenesis != genesis {
|
||||
return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", rGenesis[:8], genesis[:8])
|
||||
}
|
||||
if int(rNetwork) != p.network {
|
||||
if rNetwork != p.network {
|
||||
return errResp(ErrNetworkIdMismatch, "%d (!= %d)", rNetwork, p.network)
|
||||
}
|
||||
if int(rVersion) != p.version {
|
||||
|
@ -377,9 +377,6 @@ func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int)
|
||||
case core.SideStatTy:
|
||||
log.Debug("Inserted forked header", "number", header.Number, "hash", header.Hash())
|
||||
events = append(events, core.ChainSideEvent{Block: types.NewBlockWithHeader(header)})
|
||||
|
||||
case core.SplitStatTy:
|
||||
events = append(events, core.ChainSplitEvent{Block: types.NewBlockWithHeader(header)})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ package light
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
@ -46,26 +47,18 @@ func NewLightTrie(id *TrieID, odr OdrBackend, useFakeMap bool) *LightTrie {
|
||||
// retrieveKey retrieves a single key, returns true and stores nodes in local
|
||||
// database if successful
|
||||
func (t *LightTrie) retrieveKey(ctx context.Context, key []byte) bool {
|
||||
r := &TrieRequest{Id: t.id, Key: key}
|
||||
r := &TrieRequest{Id: t.id, Key: crypto.Keccak256(key)}
|
||||
return t.odr.Retrieve(ctx, r) == nil
|
||||
}
|
||||
|
||||
// do tries and retries to execute a function until it returns with no error or
|
||||
// an error type other than MissingNodeError
|
||||
func (t *LightTrie) do(ctx context.Context, fallbackKey []byte, fn func() error) error {
|
||||
func (t *LightTrie) do(ctx context.Context, key []byte, fn func() error) error {
|
||||
err := fn()
|
||||
for err != nil {
|
||||
mn, ok := err.(*trie.MissingNodeError)
|
||||
if !ok {
|
||||
if _, ok := err.(*trie.MissingNodeError); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
var key []byte
|
||||
if mn.PrefixLen+mn.SuffixLen > 0 {
|
||||
key = mn.Key
|
||||
} else {
|
||||
key = fallbackKey
|
||||
}
|
||||
if !t.retrieveKey(ctx, key) {
|
||||
break
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv2"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
)
|
||||
|
||||
// NodeConfig represents the collection of configuration values to fine tune the Geth
|
||||
@ -54,7 +54,7 @@ type NodeConfig struct {
|
||||
|
||||
// EthereumNetworkID is the network identifier used by the Ethereum protocol to
|
||||
// decide if remote peers should be accepted or not.
|
||||
EthereumNetworkID int
|
||||
EthereumNetworkID int64 // uint64 in truth, but Java can't handle that...
|
||||
|
||||
// EthereumGenesis is the genesis JSON to use to seed the blockchain with. An
|
||||
// empty genesis state is equivalent to using the mainnet's state.
|
||||
@ -148,7 +148,7 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) {
|
||||
ethConf := eth.DefaultConfig
|
||||
ethConf.Genesis = genesis
|
||||
ethConf.SyncMode = downloader.LightSync
|
||||
ethConf.NetworkId = config.EthereumNetworkID
|
||||
ethConf.NetworkId = uint64(config.EthereumNetworkID)
|
||||
ethConf.DatabaseCache = config.EthereumDatabaseCache
|
||||
if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
return les.New(ctx, ðConf)
|
||||
|
@ -82,6 +82,9 @@ type Config struct {
|
||||
// scrypt KDF at the expense of security.
|
||||
UseLightweightKDF bool `toml:",omitempty"`
|
||||
|
||||
// NoUSB disables hardware wallet monitoring and connectivity.
|
||||
NoUSB bool `toml:",omitempty"`
|
||||
|
||||
// IPCPath is the requested location to place the IPC endpoint. If the path is
|
||||
// a simple file name, it is placed inside the data directory (or on the root
|
||||
// pipe path on Windows), whereas if it's a resolvable path name (absolute or
|
||||
@ -389,10 +392,12 @@ func makeAccountManager(conf *Config) (*accounts.Manager, string, error) {
|
||||
backends := []accounts.Backend{
|
||||
keystore.NewKeyStore(keydir, scryptN, scryptP),
|
||||
}
|
||||
if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil {
|
||||
log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err))
|
||||
} else {
|
||||
backends = append(backends, ledgerhub)
|
||||
if !conf.NoUSB {
|
||||
if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil {
|
||||
log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err))
|
||||
} else {
|
||||
backends = append(backends, ledgerhub)
|
||||
}
|
||||
}
|
||||
return accounts.NewManager(backends...), ephemeral, nil
|
||||
}
|
||||
|
@ -536,6 +536,7 @@ func (n *Node) Stop() error {
|
||||
func (n *Node) Wait() {
|
||||
n.lock.RLock()
|
||||
if n.server == nil {
|
||||
n.lock.RUnlock()
|
||||
return
|
||||
}
|
||||
stop := n.stop
|
||||
|
@ -39,6 +39,18 @@ var TestnetBootnodes = []string{
|
||||
"enode://20c9ad97c081d63397d7b685a412227a40e23c8bdc6688c6f37e97cfbc22d2b4d1db1510d8f61e6a8866ad7f0e17c02b14182d37ea7c3c8b9c2683aeb6b733a1@52.169.14.227:30303", // IE
|
||||
}
|
||||
|
||||
// RinkebyBootnodes are the enode URLs of the P2P bootstrap nodes running on the
|
||||
// Rinkeby test network.
|
||||
var RinkebyBootnodes = []string{
|
||||
"enode://a24ac7c5484ef4ed0c5eb2d36620ba4e4aa13b8c84684e1b4aab0cebea2ae45cb4d375b77eab56516d34bfbd3c1a833fc51296ff084b770b94fb9028c4d25ccf@52.169.42.101:30303", // IE
|
||||
}
|
||||
|
||||
// RinkebyV5Bootnodes are the enode URLs of the P2P bootstrap nodes running on the
|
||||
// Rinkeby test network for the experimental RLPx v5 topic-discovery network.
|
||||
var RinkebyV5Bootnodes = []string{
|
||||
"enode://a24ac7c5484ef4ed0c5eb2d36620ba4e4aa13b8c84684e1b4aab0cebea2ae45cb4d375b77eab56516d34bfbd3c1a833fc51296ff084b770b94fb9028c4d25ccf@52.169.42.101:30303?discport=30304", // IE
|
||||
}
|
||||
|
||||
// DiscoveryV5Bootnodes are the enode URLs of the P2P bootstrap nodes for the
|
||||
// experimental RLPx v5 topic-discovery network.
|
||||
var DiscoveryV5Bootnodes = []string{
|
||||
|
@ -37,7 +37,7 @@ var (
|
||||
Ethash: new(EthashConfig),
|
||||
}
|
||||
|
||||
// TestnetChainConfig contains the chain parameters to run a node on the ropsten test network.
|
||||
// TestnetChainConfig contains the chain parameters to run a node on the Ropsten test network.
|
||||
TestnetChainConfig = &ChainConfig{
|
||||
ChainId: big.NewInt(3),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
@ -50,6 +50,22 @@ var (
|
||||
Ethash: new(EthashConfig),
|
||||
}
|
||||
|
||||
// RinkebyChainConfig contains the chain parameters to run a node on the Rinkeby test network.
|
||||
RinkebyChainConfig = &ChainConfig{
|
||||
ChainId: big.NewInt(4),
|
||||
HomesteadBlock: big.NewInt(1),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: true,
|
||||
EIP150Block: big.NewInt(2),
|
||||
EIP150Hash: common.HexToHash("0x9b095b36c15eaf13044373aef8ee0bd3a382a5abb92e402afa44b8249c3a90e9"),
|
||||
EIP155Block: big.NewInt(3),
|
||||
EIP158Block: big.NewInt(3),
|
||||
Clique: &CliqueConfig{
|
||||
Period: 15,
|
||||
Epoch: 30000,
|
||||
},
|
||||
}
|
||||
|
||||
// AllProtocolChanges contains every protocol change (EIPs)
|
||||
// introduced and accepted by the Ethereum core developers.
|
||||
// TestChainConfig is like AllProtocolChanges but has chain ID 1.
|
||||
|
@ -23,7 +23,7 @@ import (
|
||||
const (
|
||||
VersionMajor = 1 // Major version component of the current release
|
||||
VersionMinor = 6 // Minor version component of the current release
|
||||
VersionPatch = 0 // Patch version component of the current release
|
||||
VersionPatch = 1 // Patch version component of the current release
|
||||
VersionMeta = "stable" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@ -373,14 +374,14 @@ func (c *Client) EthSubscribe(ctx context.Context, channel interface{}, args ...
|
||||
return nil, ErrNotificationsUnsupported
|
||||
}
|
||||
|
||||
msg, err := c.newMessage(subscribeMethod, args...)
|
||||
msg, err := c.newMessage("eth"+subscribeMethodSuffix, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
op := &requestOp{
|
||||
ids: []json.RawMessage{msg.ID},
|
||||
resp: make(chan *jsonrpcMessage),
|
||||
sub: newClientSubscription(c, chanVal),
|
||||
sub: newClientSubscription(c, "eth", chanVal),
|
||||
}
|
||||
|
||||
// Send the subscription request.
|
||||
@ -575,7 +576,7 @@ func (c *Client) closeRequestOps(err error) {
|
||||
}
|
||||
|
||||
func (c *Client) handleNotification(msg *jsonrpcMessage) {
|
||||
if msg.Method != notificationMethod {
|
||||
if !strings.HasSuffix(msg.Method, notificationMethodSuffix) {
|
||||
log.Debug(fmt.Sprint("dropping non-subscription message: ", msg))
|
||||
return
|
||||
}
|
||||
@ -653,11 +654,12 @@ func (c *Client) read(conn net.Conn) error {
|
||||
|
||||
// A ClientSubscription represents a subscription established through EthSubscribe.
|
||||
type ClientSubscription struct {
|
||||
client *Client
|
||||
etype reflect.Type
|
||||
channel reflect.Value
|
||||
subid string
|
||||
in chan json.RawMessage
|
||||
client *Client
|
||||
etype reflect.Type
|
||||
channel reflect.Value
|
||||
namespace string
|
||||
subid string
|
||||
in chan json.RawMessage
|
||||
|
||||
quitOnce sync.Once // ensures quit is closed once
|
||||
quit chan struct{} // quit is closed when the subscription exits
|
||||
@ -665,14 +667,15 @@ type ClientSubscription struct {
|
||||
err chan error
|
||||
}
|
||||
|
||||
func newClientSubscription(c *Client, channel reflect.Value) *ClientSubscription {
|
||||
func newClientSubscription(c *Client, namespace string, channel reflect.Value) *ClientSubscription {
|
||||
sub := &ClientSubscription{
|
||||
client: c,
|
||||
etype: channel.Type().Elem(),
|
||||
channel: channel,
|
||||
quit: make(chan struct{}),
|
||||
err: make(chan error, 1),
|
||||
in: make(chan json.RawMessage),
|
||||
client: c,
|
||||
namespace: namespace,
|
||||
etype: channel.Type().Elem(),
|
||||
channel: channel,
|
||||
quit: make(chan struct{}),
|
||||
err: make(chan error, 1),
|
||||
in: make(chan json.RawMessage),
|
||||
}
|
||||
return sub
|
||||
}
|
||||
@ -774,5 +777,5 @@ func (sub *ClientSubscription) unmarshal(result json.RawMessage) (interface{}, e
|
||||
|
||||
func (sub *ClientSubscription) requestUnsubscribe() error {
|
||||
var result interface{}
|
||||
return sub.client.Call(&result, unsubscribeMethod, sub.subid)
|
||||
return sub.client.Call(&result, sub.namespace+unsubscribeMethodSuffix, sub.subid)
|
||||
}
|
||||
|
@ -162,6 +162,11 @@ func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler {
|
||||
// disable CORS support if user has not specified a custom CORS configuration
|
||||
if len(allowedOrigins) == 0 {
|
||||
return srv
|
||||
}
|
||||
|
||||
c := cors.New(cors.Options{
|
||||
AllowedOrigins: allowedOrigins,
|
||||
AllowedMethods: []string{"POST", "GET"},
|
||||
|
36
rpc/json.go
36
rpc/json.go
@ -30,11 +30,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
jsonrpcVersion = "2.0"
|
||||
serviceMethodSeparator = "_"
|
||||
subscribeMethod = "eth_subscribe"
|
||||
unsubscribeMethod = "eth_unsubscribe"
|
||||
notificationMethod = "eth_subscription"
|
||||
jsonrpcVersion = "2.0"
|
||||
serviceMethodSeparator = "_"
|
||||
subscribeMethodSuffix = "_subscribe"
|
||||
unsubscribeMethodSuffix = "_unsubscribe"
|
||||
notificationMethodSuffix = "_subscription"
|
||||
)
|
||||
|
||||
type jsonRequest struct {
|
||||
@ -164,7 +164,7 @@ func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, Error) {
|
||||
}
|
||||
|
||||
// subscribe are special, they will always use `subscribeMethod` as first param in the payload
|
||||
if in.Method == subscribeMethod {
|
||||
if strings.HasSuffix(in.Method, subscribeMethodSuffix) {
|
||||
reqs := []rpcRequest{{id: &in.Id, isPubSub: true}}
|
||||
if len(in.Payload) > 0 {
|
||||
// first param must be subscription name
|
||||
@ -174,17 +174,16 @@ func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, Error) {
|
||||
return nil, false, &invalidRequestError{"Unable to parse subscription request"}
|
||||
}
|
||||
|
||||
// all subscriptions are made on the eth service
|
||||
reqs[0].service, reqs[0].method = "eth", subscribeMethod[0]
|
||||
reqs[0].service, reqs[0].method = strings.TrimSuffix(in.Method, subscribeMethodSuffix), subscribeMethod[0]
|
||||
reqs[0].params = in.Payload
|
||||
return reqs, false, nil
|
||||
}
|
||||
return nil, false, &invalidRequestError{"Unable to parse subscription request"}
|
||||
}
|
||||
|
||||
if in.Method == unsubscribeMethod {
|
||||
if strings.HasSuffix(in.Method, unsubscribeMethodSuffix) {
|
||||
return []rpcRequest{{id: &in.Id, isPubSub: true,
|
||||
method: unsubscribeMethod, params: in.Payload}}, false, nil
|
||||
method: in.Method, params: in.Payload}}, false, nil
|
||||
}
|
||||
|
||||
elems := strings.Split(in.Method, serviceMethodSeparator)
|
||||
@ -216,8 +215,8 @@ func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, Error)
|
||||
|
||||
id := &in[i].Id
|
||||
|
||||
// subscribe are special, they will always use `subscribeMethod` as first param in the payload
|
||||
if r.Method == subscribeMethod {
|
||||
// subscribe are special, they will always use `subscriptionMethod` as first param in the payload
|
||||
if strings.HasSuffix(r.Method, subscribeMethodSuffix) {
|
||||
requests[i] = rpcRequest{id: id, isPubSub: true}
|
||||
if len(r.Payload) > 0 {
|
||||
// first param must be subscription name
|
||||
@ -227,8 +226,7 @@ func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, Error)
|
||||
return nil, false, &invalidRequestError{"Unable to parse subscription request"}
|
||||
}
|
||||
|
||||
// all subscriptions are made on the eth service
|
||||
requests[i].service, requests[i].method = "eth", subscribeMethod[0]
|
||||
requests[i].service, requests[i].method = strings.TrimSuffix(r.Method, subscribeMethodSuffix), subscribeMethod[0]
|
||||
requests[i].params = r.Payload
|
||||
continue
|
||||
}
|
||||
@ -236,8 +234,8 @@ func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, Error)
|
||||
return nil, true, &invalidRequestError{"Unable to parse (un)subscribe request arguments"}
|
||||
}
|
||||
|
||||
if r.Method == unsubscribeMethod {
|
||||
requests[i] = rpcRequest{id: id, isPubSub: true, method: unsubscribeMethod, params: r.Payload}
|
||||
if strings.HasSuffix(r.Method, unsubscribeMethodSuffix) {
|
||||
requests[i] = rpcRequest{id: id, isPubSub: true, method: r.Method, params: r.Payload}
|
||||
continue
|
||||
}
|
||||
|
||||
@ -325,13 +323,13 @@ func (c *jsonCodec) CreateErrorResponseWithInfo(id interface{}, err Error, info
|
||||
}
|
||||
|
||||
// CreateNotification will create a JSON-RPC notification with the given subscription id and event as params.
|
||||
func (c *jsonCodec) CreateNotification(subid string, event interface{}) interface{} {
|
||||
func (c *jsonCodec) CreateNotification(subid, namespace string, event interface{}) interface{} {
|
||||
if isHexNum(reflect.TypeOf(event)) {
|
||||
return &jsonNotification{Version: jsonrpcVersion, Method: notificationMethod,
|
||||
return &jsonNotification{Version: jsonrpcVersion, Method: namespace + notificationMethodSuffix,
|
||||
Params: jsonSubscription{Subscription: subid, Result: fmt.Sprintf(`%#x`, event)}}
|
||||
}
|
||||
|
||||
return &jsonNotification{Version: jsonrpcVersion, Method: notificationMethod,
|
||||
return &jsonNotification{Version: jsonrpcVersion, Method: namespace + notificationMethodSuffix,
|
||||
Params: jsonSubscription{Subscription: subid, Result: event}}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
@ -96,32 +97,30 @@ func (s *Server) RegisterName(name string, rcvr interface{}) error {
|
||||
return fmt.Errorf("%s is not exported", reflect.Indirect(rcvrVal).Type().Name())
|
||||
}
|
||||
|
||||
methods, subscriptions := suitableCallbacks(rcvrVal, svc.typ)
|
||||
|
||||
// already a previous service register under given sname, merge methods/subscriptions
|
||||
if regsvc, present := s.services[name]; present {
|
||||
methods, subscriptions := suitableCallbacks(rcvrVal, svc.typ)
|
||||
if len(methods) == 0 && len(subscriptions) == 0 {
|
||||
return fmt.Errorf("Service %T doesn't have any suitable methods/subscriptions to expose", rcvr)
|
||||
}
|
||||
|
||||
for _, m := range methods {
|
||||
regsvc.callbacks[formatName(m.method.Name)] = m
|
||||
}
|
||||
for _, s := range subscriptions {
|
||||
regsvc.subscriptions[formatName(s.method.Name)] = s
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
svc.name = name
|
||||
svc.callbacks, svc.subscriptions = suitableCallbacks(rcvrVal, svc.typ)
|
||||
svc.callbacks, svc.subscriptions = methods, subscriptions
|
||||
|
||||
if len(svc.callbacks) == 0 && len(svc.subscriptions) == 0 {
|
||||
return fmt.Errorf("Service %T doesn't have any suitable methods/subscriptions to expose", rcvr)
|
||||
}
|
||||
|
||||
s.services[svc.name] = svc
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -303,7 +302,7 @@ func (s *Server) handle(ctx context.Context, codec ServerCodec, req *serverReque
|
||||
// active the subscription after the sub id was successfully sent to the client
|
||||
activateSub := func() {
|
||||
notifier, _ := NotifierFromContext(ctx)
|
||||
notifier.activate(subid)
|
||||
notifier.activate(subid, req.svcname)
|
||||
}
|
||||
|
||||
return codec.CreateResponse(req.id, subid), activateSub
|
||||
@ -383,7 +382,7 @@ func (s *Server) execBatch(ctx context.Context, codec ServerCodec, requests []*s
|
||||
codec.Close()
|
||||
}
|
||||
|
||||
// when request holds one of more subscribe requests this allows these subscriptions to be actived
|
||||
// when request holds one of more subscribe requests this allows these subscriptions to be activated
|
||||
for _, c := range callbacks {
|
||||
c()
|
||||
}
|
||||
@ -410,7 +409,7 @@ func (s *Server) readRequest(codec ServerCodec) ([]*serverRequest, bool, Error)
|
||||
continue
|
||||
}
|
||||
|
||||
if r.isPubSub && r.method == unsubscribeMethod {
|
||||
if r.isPubSub && strings.HasSuffix(r.method, unsubscribeMethodSuffix) {
|
||||
requests[i] = &serverRequest{id: r.id, isUnsubscribe: true}
|
||||
argTypes := []reflect.Type{reflect.TypeOf("")} // expect subscription id as first arg
|
||||
if args, err := codec.ParseRequestArguments(argTypes, r.params); err == nil {
|
||||
@ -439,7 +438,7 @@ func (s *Server) readRequest(codec ServerCodec) ([]*serverRequest, bool, Error)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requests[i] = &serverRequest{id: r.id, err: &methodNotFoundError{subscribeMethod, r.method}}
|
||||
requests[i] = &serverRequest{id: r.id, err: &methodNotFoundError{r.method, r.method}}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
@ -35,8 +35,9 @@ type ID string
|
||||
// a Subscription is created by a notifier and tight to that notifier. The client can use
|
||||
// this subscription to wait for an unsubscribe request for the client, see Err().
|
||||
type Subscription struct {
|
||||
ID ID
|
||||
err chan error // closed on unsubscribe
|
||||
ID ID
|
||||
namespace string
|
||||
err chan error // closed on unsubscribe
|
||||
}
|
||||
|
||||
// Err returns a channel that is closed when the client send an unsubscribe request.
|
||||
@ -78,7 +79,7 @@ func NotifierFromContext(ctx context.Context) (*Notifier, bool) {
|
||||
// are dropped until the subscription is marked as active. This is done
|
||||
// by the RPC server after the subscription ID is send to the client.
|
||||
func (n *Notifier) CreateSubscription() *Subscription {
|
||||
s := &Subscription{NewID(), make(chan error)}
|
||||
s := &Subscription{ID: NewID(), err: make(chan error)}
|
||||
n.subMu.Lock()
|
||||
n.inactive[s.ID] = s
|
||||
n.subMu.Unlock()
|
||||
@ -91,9 +92,9 @@ func (n *Notifier) Notify(id ID, data interface{}) error {
|
||||
n.subMu.RLock()
|
||||
defer n.subMu.RUnlock()
|
||||
|
||||
_, active := n.active[id]
|
||||
sub, active := n.active[id]
|
||||
if active {
|
||||
notification := n.codec.CreateNotification(string(id), data)
|
||||
notification := n.codec.CreateNotification(string(id), sub.namespace, data)
|
||||
if err := n.codec.Write(notification); err != nil {
|
||||
n.codec.Close()
|
||||
return err
|
||||
@ -124,10 +125,11 @@ func (n *Notifier) unsubscribe(id ID) error {
|
||||
// notifications are dropped. This method is called by the RPC server after
|
||||
// the subscription ID was sent to client. This prevents notifications being
|
||||
// send to the client before the subscription ID is send to the client.
|
||||
func (n *Notifier) activate(id ID) {
|
||||
func (n *Notifier) activate(id ID, namespace string) {
|
||||
n.subMu.Lock()
|
||||
defer n.subMu.Unlock()
|
||||
if sub, found := n.inactive[id]; found {
|
||||
sub.namespace = namespace
|
||||
n.active[id] = sub
|
||||
delete(n.inactive, id)
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ package rpc
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
@ -162,3 +163,162 @@ func TestNotifications(t *testing.T) {
|
||||
t.Error("unsubscribe callback not called after closing connection")
|
||||
}
|
||||
}
|
||||
|
||||
func waitForMessages(t *testing.T, in *json.Decoder, successes chan<- jsonSuccessResponse,
|
||||
failures chan<- jsonErrResponse, notifications chan<- jsonNotification) {
|
||||
|
||||
// read and parse server messages
|
||||
for {
|
||||
var rmsg json.RawMessage
|
||||
if err := in.Decode(&rmsg); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var responses []map[string]interface{}
|
||||
if rmsg[0] == '[' {
|
||||
if err := json.Unmarshal(rmsg, &responses); err != nil {
|
||||
t.Fatalf("Received invalid message: %s", rmsg)
|
||||
}
|
||||
} else {
|
||||
var msg map[string]interface{}
|
||||
if err := json.Unmarshal(rmsg, &msg); err != nil {
|
||||
t.Fatalf("Received invalid message: %s", rmsg)
|
||||
}
|
||||
responses = append(responses, msg)
|
||||
}
|
||||
|
||||
for _, msg := range responses {
|
||||
// determine what kind of msg was received and broadcast
|
||||
// it to over the corresponding channel
|
||||
if _, found := msg["result"]; found {
|
||||
successes <- jsonSuccessResponse{
|
||||
Version: msg["jsonrpc"].(string),
|
||||
Id: msg["id"],
|
||||
Result: msg["result"],
|
||||
}
|
||||
continue
|
||||
}
|
||||
if _, found := msg["error"]; found {
|
||||
params := msg["params"].(map[string]interface{})
|
||||
failures <- jsonErrResponse{
|
||||
Version: msg["jsonrpc"].(string),
|
||||
Id: msg["id"],
|
||||
Error: jsonError{int(params["subscription"].(float64)), params["message"].(string), params["data"]},
|
||||
}
|
||||
continue
|
||||
}
|
||||
if _, found := msg["params"]; found {
|
||||
params := msg["params"].(map[string]interface{})
|
||||
notifications <- jsonNotification{
|
||||
Version: msg["jsonrpc"].(string),
|
||||
Method: msg["method"].(string),
|
||||
Params: jsonSubscription{params["subscription"].(string), params["result"]},
|
||||
}
|
||||
continue
|
||||
}
|
||||
t.Fatalf("Received invalid message: %s", msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestSubscriptionMultipleNamespaces ensures that subscriptions can exists
|
||||
// for multiple different namespaces.
|
||||
func TestSubscriptionMultipleNamespaces(t *testing.T) {
|
||||
var (
|
||||
namespaces = []string{"eth", "shh", "bzz"}
|
||||
server = NewServer()
|
||||
service = NotificationTestService{}
|
||||
clientConn, serverConn = net.Pipe()
|
||||
|
||||
out = json.NewEncoder(clientConn)
|
||||
in = json.NewDecoder(clientConn)
|
||||
successes = make(chan jsonSuccessResponse)
|
||||
failures = make(chan jsonErrResponse)
|
||||
notifications = make(chan jsonNotification)
|
||||
)
|
||||
|
||||
// setup and start server
|
||||
for _, namespace := range namespaces {
|
||||
if err := server.RegisterName(namespace, &service); err != nil {
|
||||
t.Fatalf("unable to register test service %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
go server.ServeCodec(NewJSONCodec(serverConn), OptionMethodInvocation|OptionSubscriptions)
|
||||
defer server.Stop()
|
||||
|
||||
// wait for message and write them to the given channels
|
||||
go waitForMessages(t, in, successes, failures, notifications)
|
||||
|
||||
// create subscriptions one by one
|
||||
n := 3
|
||||
for i, namespace := range namespaces {
|
||||
request := map[string]interface{}{
|
||||
"id": i,
|
||||
"method": fmt.Sprintf("%s_subscribe", namespace),
|
||||
"version": "2.0",
|
||||
"params": []interface{}{"someSubscription", n, i},
|
||||
}
|
||||
|
||||
if err := out.Encode(&request); err != nil {
|
||||
t.Fatalf("Could not create subscription: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// create all subscriptions in 1 batch
|
||||
var requests []interface{}
|
||||
for i, namespace := range namespaces {
|
||||
requests = append(requests, map[string]interface{}{
|
||||
"id": i,
|
||||
"method": fmt.Sprintf("%s_subscribe", namespace),
|
||||
"version": "2.0",
|
||||
"params": []interface{}{"someSubscription", n, i},
|
||||
})
|
||||
}
|
||||
|
||||
if err := out.Encode(&requests); err != nil {
|
||||
t.Fatalf("Could not create subscription in batch form: %v", err)
|
||||
}
|
||||
|
||||
timeout := time.After(30 * time.Second)
|
||||
subids := make(map[string]string, 2*len(namespaces))
|
||||
count := make(map[string]int, 2*len(namespaces))
|
||||
|
||||
for {
|
||||
done := true
|
||||
for id, _ := range count {
|
||||
if count, found := count[id]; !found || count < (2*n) {
|
||||
done = false
|
||||
}
|
||||
}
|
||||
|
||||
if done && len(count) == len(namespaces) {
|
||||
break
|
||||
}
|
||||
|
||||
select {
|
||||
case suc := <-successes: // subscription created
|
||||
subids[namespaces[int(suc.Id.(float64))]] = suc.Result.(string)
|
||||
case failure := <-failures:
|
||||
t.Errorf("received error: %v", failure.Error)
|
||||
case notification := <-notifications:
|
||||
if cnt, found := count[notification.Params.Subscription]; found {
|
||||
count[notification.Params.Subscription] = cnt + 1
|
||||
} else {
|
||||
count[notification.Params.Subscription] = 1
|
||||
}
|
||||
case <-timeout:
|
||||
for _, namespace := range namespaces {
|
||||
subid, found := subids[namespace]
|
||||
if !found {
|
||||
t.Errorf("Subscription for '%s' not created", namespace)
|
||||
continue
|
||||
}
|
||||
if count, found := count[subid]; !found || count < n {
|
||||
t.Errorf("Didn't receive all notifications (%d<%d) in time for namespace '%s'", count, n, namespace)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
71
rpc/types.go
71
rpc/types.go
@ -19,11 +19,11 @@ package rpc
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
@ -104,35 +104,29 @@ type ServerCodec interface {
|
||||
// Read next request
|
||||
ReadRequestHeaders() ([]rpcRequest, bool, Error)
|
||||
// Parse request argument to the given types
|
||||
ParseRequestArguments([]reflect.Type, interface{}) ([]reflect.Value, Error)
|
||||
ParseRequestArguments(argTypes []reflect.Type, params interface{}) ([]reflect.Value, Error)
|
||||
// Assemble success response, expects response id and payload
|
||||
CreateResponse(interface{}, interface{}) interface{}
|
||||
CreateResponse(id interface{}, reply interface{}) interface{}
|
||||
// Assemble error response, expects response id and error
|
||||
CreateErrorResponse(interface{}, Error) interface{}
|
||||
CreateErrorResponse(id interface{}, err Error) interface{}
|
||||
// Assemble error response with extra information about the error through info
|
||||
CreateErrorResponseWithInfo(id interface{}, err Error, info interface{}) interface{}
|
||||
// Create notification response
|
||||
CreateNotification(string, interface{}) interface{}
|
||||
CreateNotification(id, namespace string, event interface{}) interface{}
|
||||
// Write msg to client.
|
||||
Write(interface{}) error
|
||||
Write(msg interface{}) error
|
||||
// Close underlying data stream
|
||||
Close()
|
||||
// Closed when underlying connection is closed
|
||||
Closed() <-chan interface{}
|
||||
}
|
||||
|
||||
var (
|
||||
pendingBlockNumber = big.NewInt(-2)
|
||||
latestBlockNumber = big.NewInt(-1)
|
||||
earliestBlockNumber = big.NewInt(0)
|
||||
maxBlockNumber = big.NewInt(math.MaxInt64)
|
||||
)
|
||||
|
||||
type BlockNumber int64
|
||||
|
||||
const (
|
||||
PendingBlockNumber = BlockNumber(-2)
|
||||
LatestBlockNumber = BlockNumber(-1)
|
||||
PendingBlockNumber = BlockNumber(-2)
|
||||
LatestBlockNumber = BlockNumber(-1)
|
||||
EarliestBlockNumber = BlockNumber(0)
|
||||
)
|
||||
|
||||
// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports:
|
||||
@ -143,45 +137,32 @@ const (
|
||||
// - an out of range error when the given block number is either too little or too large
|
||||
func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
|
||||
input := strings.TrimSpace(string(data))
|
||||
|
||||
if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
|
||||
input = input[1 : len(input)-1]
|
||||
}
|
||||
|
||||
if len(input) == 0 {
|
||||
*bn = BlockNumber(latestBlockNumber.Int64())
|
||||
switch input {
|
||||
case "earliest":
|
||||
*bn = EarliestBlockNumber
|
||||
return nil
|
||||
case "latest":
|
||||
*bn = LatestBlockNumber
|
||||
return nil
|
||||
case "pending":
|
||||
*bn = PendingBlockNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
in := new(big.Int)
|
||||
_, ok := in.SetString(input, 0)
|
||||
|
||||
if !ok { // test if user supplied string tag
|
||||
strBlockNumber := input
|
||||
if strBlockNumber == "latest" {
|
||||
*bn = BlockNumber(latestBlockNumber.Int64())
|
||||
return nil
|
||||
}
|
||||
|
||||
if strBlockNumber == "earliest" {
|
||||
*bn = BlockNumber(earliestBlockNumber.Int64())
|
||||
return nil
|
||||
}
|
||||
|
||||
if strBlockNumber == "pending" {
|
||||
*bn = BlockNumber(pendingBlockNumber.Int64())
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf(`invalid blocknumber %s`, data)
|
||||
blckNum, err := hexutil.DecodeUint64(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if blckNum > math.MaxInt64 {
|
||||
return fmt.Errorf("Blocknumber too high")
|
||||
}
|
||||
|
||||
if in.Cmp(earliestBlockNumber) >= 0 && in.Cmp(maxBlockNumber) <= 0 {
|
||||
*bn = BlockNumber(in.Int64())
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("blocknumber not in range [%d, %d]", earliestBlockNumber, maxBlockNumber)
|
||||
*bn = BlockNumber(blckNum)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bn BlockNumber) Int64() int64 {
|
||||
|
66
rpc/types_test.go
Normal file
66
rpc/types_test.go
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2017 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 rpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
)
|
||||
|
||||
func TestBlockNumberJSONUnmarshal(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
mustFail bool
|
||||
expected BlockNumber
|
||||
}{
|
||||
0: {`"0x"`, true, BlockNumber(0)},
|
||||
1: {`"0x0"`, false, BlockNumber(0)},
|
||||
2: {`"0X1"`, false, BlockNumber(1)},
|
||||
3: {`"0x00"`, true, BlockNumber(0)},
|
||||
4: {`"0x01"`, true, BlockNumber(0)},
|
||||
5: {`"0x1"`, false, BlockNumber(1)},
|
||||
6: {`"0x12"`, false, BlockNumber(18)},
|
||||
7: {`"0x7fffffffffffffff"`, false, BlockNumber(math.MaxInt64)},
|
||||
8: {`"0x8000000000000000"`, true, BlockNumber(0)},
|
||||
9: {"0", true, BlockNumber(0)},
|
||||
10: {`"ff"`, true, BlockNumber(0)},
|
||||
11: {`"pending"`, false, PendingBlockNumber},
|
||||
12: {`"latest"`, false, LatestBlockNumber},
|
||||
13: {`"earliest"`, false, EarliestBlockNumber},
|
||||
14: {`someString`, true, BlockNumber(0)},
|
||||
15: {`""`, true, BlockNumber(0)},
|
||||
16: {``, true, BlockNumber(0)},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
var num BlockNumber
|
||||
err := json.Unmarshal([]byte(test.input), &num)
|
||||
if test.mustFail && err == nil {
|
||||
t.Errorf("Test %d should fail", i)
|
||||
continue
|
||||
}
|
||||
if !test.mustFail && err != nil {
|
||||
t.Errorf("Test %d should pass but got err: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if num != test.expected {
|
||||
t.Errorf("Test %d got unexpected value, want %d, got %d", i, test.expected, num)
|
||||
}
|
||||
}
|
||||
}
|
114
trie/encoding.go
114
trie/encoding.go
@ -16,49 +16,54 @@
|
||||
|
||||
package trie
|
||||
|
||||
func compactEncode(hexSlice []byte) []byte {
|
||||
// Trie keys are dealt with in three distinct encodings:
|
||||
//
|
||||
// KEYBYTES encoding contains the actual key and nothing else. This encoding is the
|
||||
// input to most API functions.
|
||||
//
|
||||
// HEX encoding contains one byte for each nibble of the key and an optional trailing
|
||||
// 'terminator' byte of value 0x10 which indicates whether or not the node at the key
|
||||
// contains a value. Hex key encoding is used for nodes loaded in memory because it's
|
||||
// convenient to access.
|
||||
//
|
||||
// COMPACT encoding is defined by the Ethereum Yellow Paper (it's called "hex prefix
|
||||
// encoding" there) and contains the bytes of the key and a flag. The high nibble of the
|
||||
// first byte contains the flag; the lowest bit encoding the oddness of the length and
|
||||
// the second-lowest encoding whether the node at the key is a value node. The low nibble
|
||||
// of the first byte is zero in the case of an even number of nibbles and the first nibble
|
||||
// in the case of an odd number. All remaining nibbles (now an even number) fit properly
|
||||
// into the remaining bytes. Compact encoding is used for nodes stored on disk.
|
||||
|
||||
func hexToCompact(hex []byte) []byte {
|
||||
terminator := byte(0)
|
||||
if hexSlice[len(hexSlice)-1] == 16 {
|
||||
if hasTerm(hex) {
|
||||
terminator = 1
|
||||
hexSlice = hexSlice[:len(hexSlice)-1]
|
||||
hex = hex[:len(hex)-1]
|
||||
}
|
||||
var (
|
||||
odd = byte(len(hexSlice) % 2)
|
||||
buflen = len(hexSlice)/2 + 1
|
||||
bi, hi = 0, 0 // indices
|
||||
hs = byte(0) // shift: flips between 0 and 4
|
||||
)
|
||||
if odd == 0 {
|
||||
bi = 1
|
||||
hs = 4
|
||||
}
|
||||
buf := make([]byte, buflen)
|
||||
buf[0] = terminator<<5 | byte(odd)<<4
|
||||
for bi < len(buf) && hi < len(hexSlice) {
|
||||
buf[bi] |= hexSlice[hi] << hs
|
||||
if hs == 0 {
|
||||
bi++
|
||||
}
|
||||
hi, hs = hi+1, hs^(1<<2)
|
||||
buf := make([]byte, len(hex)/2+1)
|
||||
buf[0] = terminator << 5 // the flag byte
|
||||
if len(hex)&1 == 1 {
|
||||
buf[0] |= 1 << 4 // odd flag
|
||||
buf[0] |= hex[0] // first nibble is contained in the first byte
|
||||
hex = hex[1:]
|
||||
}
|
||||
decodeNibbles(hex, buf[1:])
|
||||
return buf
|
||||
}
|
||||
|
||||
func compactDecode(str []byte) []byte {
|
||||
base := compactHexDecode(str)
|
||||
func compactToHex(compact []byte) []byte {
|
||||
base := keybytesToHex(compact)
|
||||
base = base[:len(base)-1]
|
||||
// apply terminator flag
|
||||
if base[0] >= 2 {
|
||||
base = append(base, 16)
|
||||
}
|
||||
if base[0]%2 == 1 {
|
||||
base = base[1:]
|
||||
} else {
|
||||
base = base[2:]
|
||||
}
|
||||
return base
|
||||
// apply odd flag
|
||||
chop := 2 - base[0]&1
|
||||
return base[chop:]
|
||||
}
|
||||
|
||||
func compactHexDecode(str []byte) []byte {
|
||||
func keybytesToHex(str []byte) []byte {
|
||||
l := len(str)*2 + 1
|
||||
var nibbles = make([]byte, l)
|
||||
for i, b := range str {
|
||||
@ -69,35 +74,24 @@ func compactHexDecode(str []byte) []byte {
|
||||
return nibbles
|
||||
}
|
||||
|
||||
// compactHexEncode encodes a series of nibbles into a byte array
|
||||
func compactHexEncode(nibbles []byte) []byte {
|
||||
nl := len(nibbles)
|
||||
if nl == 0 {
|
||||
return nil
|
||||
// hexToKeybytes turns hex nibbles into key bytes.
|
||||
// This can only be used for keys of even length.
|
||||
func hexToKeybytes(hex []byte) []byte {
|
||||
if hasTerm(hex) {
|
||||
hex = hex[:len(hex)-1]
|
||||
}
|
||||
if nibbles[nl-1] == 16 {
|
||||
nl--
|
||||
if len(hex)&1 != 0 {
|
||||
panic("can't convert hex key of odd length")
|
||||
}
|
||||
l := (nl + 1) / 2
|
||||
var str = make([]byte, l)
|
||||
for i := range str {
|
||||
b := nibbles[i*2] * 16
|
||||
if nl > i*2 {
|
||||
b += nibbles[i*2+1]
|
||||
}
|
||||
str[i] = b
|
||||
}
|
||||
return str
|
||||
key := make([]byte, (len(hex)+1)/2)
|
||||
decodeNibbles(hex, key)
|
||||
return key
|
||||
}
|
||||
|
||||
func decodeCompact(key []byte) []byte {
|
||||
l := len(key) / 2
|
||||
var res = make([]byte, l)
|
||||
for i := 0; i < l; i++ {
|
||||
v1, v0 := key[2*i], key[2*i+1]
|
||||
res[i] = v1*16 + v0
|
||||
func decodeNibbles(nibbles []byte, bytes []byte) {
|
||||
for bi, ni := 0, 0; ni < len(nibbles); bi, ni = bi+1, ni+2 {
|
||||
bytes[bi] = nibbles[ni]<<4 | nibbles[ni+1]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// prefixLen returns the length of the common prefix of a and b.
|
||||
@ -114,15 +108,7 @@ func prefixLen(a, b []byte) int {
|
||||
return i
|
||||
}
|
||||
|
||||
// hasTerm returns whether a hex key has the terminator flag.
|
||||
func hasTerm(s []byte) bool {
|
||||
return s[len(s)-1] == 16
|
||||
}
|
||||
|
||||
func remTerm(s []byte) []byte {
|
||||
if hasTerm(s) {
|
||||
b := make([]byte, len(s)-1)
|
||||
copy(b, s)
|
||||
return b
|
||||
}
|
||||
return s
|
||||
return len(s) > 0 && s[len(s)-1] == 16
|
||||
}
|
||||
|
@ -17,113 +17,88 @@
|
||||
package trie
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
checker "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func TestEncoding(t *testing.T) { checker.TestingT(t) }
|
||||
|
||||
type TrieEncodingSuite struct{}
|
||||
|
||||
var _ = checker.Suite(&TrieEncodingSuite{})
|
||||
|
||||
func (s *TrieEncodingSuite) TestCompactEncode(c *checker.C) {
|
||||
// even compact encode
|
||||
test1 := []byte{1, 2, 3, 4, 5}
|
||||
res1 := compactEncode(test1)
|
||||
c.Assert(res1, checker.DeepEquals, []byte("\x11\x23\x45"))
|
||||
|
||||
// odd compact encode
|
||||
test2 := []byte{0, 1, 2, 3, 4, 5}
|
||||
res2 := compactEncode(test2)
|
||||
c.Assert(res2, checker.DeepEquals, []byte("\x00\x01\x23\x45"))
|
||||
|
||||
//odd terminated compact encode
|
||||
test3 := []byte{0, 15, 1, 12, 11, 8 /*term*/, 16}
|
||||
res3 := compactEncode(test3)
|
||||
c.Assert(res3, checker.DeepEquals, []byte("\x20\x0f\x1c\xb8"))
|
||||
|
||||
// even terminated compact encode
|
||||
test4 := []byte{15, 1, 12, 11, 8 /*term*/, 16}
|
||||
res4 := compactEncode(test4)
|
||||
c.Assert(res4, checker.DeepEquals, []byte("\x3f\x1c\xb8"))
|
||||
}
|
||||
|
||||
func (s *TrieEncodingSuite) TestCompactHexDecode(c *checker.C) {
|
||||
exp := []byte{7, 6, 6, 5, 7, 2, 6, 2, 16}
|
||||
res := compactHexDecode([]byte("verb"))
|
||||
c.Assert(res, checker.DeepEquals, exp)
|
||||
}
|
||||
|
||||
func (s *TrieEncodingSuite) TestCompactHexEncode(c *checker.C) {
|
||||
exp := []byte("verb")
|
||||
res := compactHexEncode([]byte{7, 6, 6, 5, 7, 2, 6, 2, 16})
|
||||
c.Assert(res, checker.DeepEquals, exp)
|
||||
}
|
||||
|
||||
func (s *TrieEncodingSuite) TestCompactDecode(c *checker.C) {
|
||||
// odd compact decode
|
||||
exp := []byte{1, 2, 3, 4, 5}
|
||||
res := compactDecode([]byte("\x11\x23\x45"))
|
||||
c.Assert(res, checker.DeepEquals, exp)
|
||||
|
||||
// even compact decode
|
||||
exp = []byte{0, 1, 2, 3, 4, 5}
|
||||
res = compactDecode([]byte("\x00\x01\x23\x45"))
|
||||
c.Assert(res, checker.DeepEquals, exp)
|
||||
|
||||
// even terminated compact decode
|
||||
exp = []byte{0, 15, 1, 12, 11, 8 /*term*/, 16}
|
||||
res = compactDecode([]byte("\x20\x0f\x1c\xb8"))
|
||||
c.Assert(res, checker.DeepEquals, exp)
|
||||
|
||||
// even terminated compact decode
|
||||
exp = []byte{15, 1, 12, 11, 8 /*term*/, 16}
|
||||
res = compactDecode([]byte("\x3f\x1c\xb8"))
|
||||
c.Assert(res, checker.DeepEquals, exp)
|
||||
}
|
||||
|
||||
func (s *TrieEncodingSuite) TestDecodeCompact(c *checker.C) {
|
||||
exp, _ := hex.DecodeString("012345")
|
||||
res := decodeCompact([]byte{0, 1, 2, 3, 4, 5})
|
||||
c.Assert(res, checker.DeepEquals, exp)
|
||||
|
||||
exp, _ = hex.DecodeString("012345")
|
||||
res = decodeCompact([]byte{0, 1, 2, 3, 4, 5, 16})
|
||||
c.Assert(res, checker.DeepEquals, exp)
|
||||
|
||||
exp, _ = hex.DecodeString("abcdef")
|
||||
res = decodeCompact([]byte{10, 11, 12, 13, 14, 15})
|
||||
c.Assert(res, checker.DeepEquals, exp)
|
||||
}
|
||||
|
||||
func BenchmarkCompactEncode(b *testing.B) {
|
||||
|
||||
testBytes := []byte{0, 15, 1, 12, 11, 8 /*term*/, 16}
|
||||
for i := 0; i < b.N; i++ {
|
||||
compactEncode(testBytes)
|
||||
func TestHexCompact(t *testing.T) {
|
||||
tests := []struct{ hex, compact []byte }{
|
||||
// empty keys, with and without terminator.
|
||||
{hex: []byte{}, compact: []byte{0x00}},
|
||||
{hex: []byte{16}, compact: []byte{0x20}},
|
||||
// odd length, no terminator
|
||||
{hex: []byte{1, 2, 3, 4, 5}, compact: []byte{0x11, 0x23, 0x45}},
|
||||
// even length, no terminator
|
||||
{hex: []byte{0, 1, 2, 3, 4, 5}, compact: []byte{0x00, 0x01, 0x23, 0x45}},
|
||||
// odd length, terminator
|
||||
{hex: []byte{15, 1, 12, 11, 8, 16 /*term*/}, compact: []byte{0x3f, 0x1c, 0xb8}},
|
||||
// even length, terminator
|
||||
{hex: []byte{0, 15, 1, 12, 11, 8, 16 /*term*/}, compact: []byte{0x20, 0x0f, 0x1c, 0xb8}},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if c := hexToCompact(test.hex); !bytes.Equal(c, test.compact) {
|
||||
t.Errorf("hexToCompact(%x) -> %x, want %x", test.hex, c, test.compact)
|
||||
}
|
||||
if h := compactToHex(test.compact); !bytes.Equal(h, test.hex) {
|
||||
t.Errorf("compactToHex(%x) -> %x, want %x", test.compact, h, test.hex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompactDecode(b *testing.B) {
|
||||
testBytes := []byte{0, 15, 1, 12, 11, 8 /*term*/, 16}
|
||||
for i := 0; i < b.N; i++ {
|
||||
compactDecode(testBytes)
|
||||
func TestHexKeybytes(t *testing.T) {
|
||||
tests := []struct{ key, hexIn, hexOut []byte }{
|
||||
{key: []byte{}, hexIn: []byte{16}, hexOut: []byte{16}},
|
||||
{key: []byte{}, hexIn: []byte{}, hexOut: []byte{16}},
|
||||
{
|
||||
key: []byte{0x12, 0x34, 0x56},
|
||||
hexIn: []byte{1, 2, 3, 4, 5, 6, 16},
|
||||
hexOut: []byte{1, 2, 3, 4, 5, 6, 16},
|
||||
},
|
||||
{
|
||||
key: []byte{0x12, 0x34, 0x5},
|
||||
hexIn: []byte{1, 2, 3, 4, 0, 5, 16},
|
||||
hexOut: []byte{1, 2, 3, 4, 0, 5, 16},
|
||||
},
|
||||
{
|
||||
key: []byte{0x12, 0x34, 0x56},
|
||||
hexIn: []byte{1, 2, 3, 4, 5, 6},
|
||||
hexOut: []byte{1, 2, 3, 4, 5, 6, 16},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if h := keybytesToHex(test.key); !bytes.Equal(h, test.hexOut) {
|
||||
t.Errorf("keybytesToHex(%x) -> %x, want %x", test.key, h, test.hexOut)
|
||||
}
|
||||
if k := hexToKeybytes(test.hexIn); !bytes.Equal(k, test.key) {
|
||||
t.Errorf("hexToKeybytes(%x) -> %x, want %x", test.hexIn, k, test.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompactHexDecode(b *testing.B) {
|
||||
func BenchmarkHexToCompact(b *testing.B) {
|
||||
testBytes := []byte{0, 15, 1, 12, 11, 8, 16 /*term*/}
|
||||
for i := 0; i < b.N; i++ {
|
||||
hexToCompact(testBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompactToHex(b *testing.B) {
|
||||
testBytes := []byte{0, 15, 1, 12, 11, 8, 16 /*term*/}
|
||||
for i := 0; i < b.N; i++ {
|
||||
compactToHex(testBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkKeybytesToHex(b *testing.B) {
|
||||
testBytes := []byte{7, 6, 6, 5, 7, 2, 6, 2, 16}
|
||||
for i := 0; i < b.N; i++ {
|
||||
compactHexDecode(testBytes)
|
||||
keybytesToHex(testBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodeCompact(b *testing.B) {
|
||||
func BenchmarkHexToKeybytes(b *testing.B) {
|
||||
testBytes := []byte{7, 6, 6, 5, 7, 2, 6, 2, 16}
|
||||
for i := 0; i < b.N; i++ {
|
||||
decodeCompact(testBytes)
|
||||
hexToKeybytes(testBytes)
|
||||
}
|
||||
}
|
||||
|
@ -30,10 +30,6 @@ import (
|
||||
//
|
||||
// RootHash is the original root of the trie that contains the node
|
||||
//
|
||||
// Key is a binary-encoded key that contains the prefix that leads to the first
|
||||
// missing node and optionally a suffix that hints on which further nodes should
|
||||
// also be retrieved
|
||||
//
|
||||
// PrefixLen is the nibble length of the key prefix that leads from the root to
|
||||
// the missing node
|
||||
//
|
||||
@ -42,7 +38,6 @@ import (
|
||||
// such hints in the error message)
|
||||
type MissingNodeError struct {
|
||||
RootHash, NodeHash common.Hash
|
||||
Key []byte
|
||||
PrefixLen, SuffixLen int
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, err
|
||||
case *shortNode:
|
||||
// Hash the short node's child, caching the newly hashed subtree
|
||||
collapsed, cached := n.copy(), n.copy()
|
||||
collapsed.Key = compactEncode(n.Key)
|
||||
collapsed.Key = hexToCompact(n.Key)
|
||||
cached.Key = common.CopyBytes(n.Key)
|
||||
|
||||
if _, ok := n.Val.(valueNode); !ok {
|
||||
|
145
trie/iterator.go
145
trie/iterator.go
@ -19,9 +19,13 @@ package trie
|
||||
import (
|
||||
"bytes"
|
||||
"container/heap"
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var iteratorEnd = errors.New("end of iteration")
|
||||
|
||||
// Iterator is a key-value trie iterator that traverses a Trie.
|
||||
type Iterator struct {
|
||||
nodeIt NodeIterator
|
||||
@ -30,15 +34,8 @@ type Iterator struct {
|
||||
Value []byte // Current data value on which the iterator is positioned on
|
||||
}
|
||||
|
||||
// NewIterator creates a new key-value iterator.
|
||||
func NewIterator(trie *Trie) *Iterator {
|
||||
return &Iterator{
|
||||
nodeIt: NewNodeIterator(trie),
|
||||
}
|
||||
}
|
||||
|
||||
// FromNodeIterator creates a new key-value iterator from a node iterator
|
||||
func NewIteratorFromNodeIterator(it NodeIterator) *Iterator {
|
||||
// NewIterator creates a new key-value iterator from a node iterator
|
||||
func NewIterator(it NodeIterator) *Iterator {
|
||||
return &Iterator{
|
||||
nodeIt: it,
|
||||
}
|
||||
@ -48,7 +45,7 @@ func NewIteratorFromNodeIterator(it NodeIterator) *Iterator {
|
||||
func (it *Iterator) Next() bool {
|
||||
for it.nodeIt.Next(true) {
|
||||
if it.nodeIt.Leaf() {
|
||||
it.Key = decodeCompact(it.nodeIt.Path())
|
||||
it.Key = hexToKeybytes(it.nodeIt.Path())
|
||||
it.Value = it.nodeIt.LeafBlob()
|
||||
return true
|
||||
}
|
||||
@ -85,25 +82,24 @@ type nodeIteratorState struct {
|
||||
hash common.Hash // Hash of the node being iterated (nil if not standalone)
|
||||
node node // Trie node being iterated
|
||||
parent common.Hash // Hash of the first full ancestor node (nil if current is the root)
|
||||
child int // Child to be processed next
|
||||
index int // Child to be processed next
|
||||
pathlen int // Length of the path to this node
|
||||
}
|
||||
|
||||
type nodeIterator struct {
|
||||
trie *Trie // Trie being iterated
|
||||
stack []*nodeIteratorState // Hierarchy of trie nodes persisting the iteration state
|
||||
|
||||
err error // Failure set in case of an internal error in the iterator
|
||||
|
||||
path []byte // Path to the current node
|
||||
err error // Failure set in case of an internal error in the iterator
|
||||
path []byte // Path to the current node
|
||||
}
|
||||
|
||||
// NewNodeIterator creates an post-order trie iterator.
|
||||
func NewNodeIterator(trie *Trie) NodeIterator {
|
||||
func newNodeIterator(trie *Trie, start []byte) NodeIterator {
|
||||
if trie.Hash() == emptyState {
|
||||
return new(nodeIterator)
|
||||
}
|
||||
return &nodeIterator{trie: trie}
|
||||
it := &nodeIterator{trie: trie}
|
||||
it.seek(start)
|
||||
return it
|
||||
}
|
||||
|
||||
// Hash returns the hash of the current node
|
||||
@ -153,6 +149,9 @@ func (it *nodeIterator) Path() []byte {
|
||||
|
||||
// Error returns the error set in case of an internal error in the iterator
|
||||
func (it *nodeIterator) Error() error {
|
||||
if it.err == iteratorEnd {
|
||||
return nil
|
||||
}
|
||||
return it.err
|
||||
}
|
||||
|
||||
@ -161,47 +160,54 @@ func (it *nodeIterator) Error() error {
|
||||
// sets the Error field to the encountered failure. If `descend` is false,
|
||||
// skips iterating over any subnodes of the current node.
|
||||
func (it *nodeIterator) Next(descend bool) bool {
|
||||
// If the iterator failed previously, don't do anything
|
||||
if it.err != nil {
|
||||
return false
|
||||
}
|
||||
// Otherwise step forward with the iterator and report any errors
|
||||
if err := it.step(descend); err != nil {
|
||||
state, parentIndex, path, err := it.peek(descend)
|
||||
if err != nil {
|
||||
it.err = err
|
||||
return false
|
||||
}
|
||||
return it.trie != nil
|
||||
it.push(state, parentIndex, path)
|
||||
return true
|
||||
}
|
||||
|
||||
// step moves the iterator to the next node of the trie.
|
||||
func (it *nodeIterator) step(descend bool) error {
|
||||
if it.trie == nil {
|
||||
// Abort if we reached the end of the iteration
|
||||
return nil
|
||||
func (it *nodeIterator) seek(prefix []byte) {
|
||||
// The path we're looking for is the hex encoded key without terminator.
|
||||
key := keybytesToHex(prefix)
|
||||
key = key[:len(key)-1]
|
||||
// Move forward until we're just before the closest match to key.
|
||||
for {
|
||||
state, parentIndex, path, err := it.peek(bytes.HasPrefix(key, it.path))
|
||||
if err != nil || bytes.Compare(path, key) >= 0 {
|
||||
it.err = err
|
||||
return
|
||||
}
|
||||
it.push(state, parentIndex, path)
|
||||
}
|
||||
}
|
||||
|
||||
// peek creates the next state of the iterator.
|
||||
func (it *nodeIterator) peek(descend bool) (*nodeIteratorState, *int, []byte, error) {
|
||||
if len(it.stack) == 0 {
|
||||
// Initialize the iterator if we've just started.
|
||||
root := it.trie.Hash()
|
||||
state := &nodeIteratorState{node: it.trie.root, child: -1}
|
||||
state := &nodeIteratorState{node: it.trie.root, index: -1}
|
||||
if root != emptyRoot {
|
||||
state.hash = root
|
||||
}
|
||||
it.stack = append(it.stack, state)
|
||||
return nil
|
||||
return state, nil, nil, nil
|
||||
}
|
||||
|
||||
if !descend {
|
||||
// If we're skipping children, pop the current node first
|
||||
it.path = it.path[:it.stack[len(it.stack)-1].pathlen]
|
||||
it.stack = it.stack[:len(it.stack)-1]
|
||||
it.pop()
|
||||
}
|
||||
|
||||
// Continue iteration to the next child
|
||||
outer:
|
||||
for {
|
||||
if len(it.stack) == 0 {
|
||||
it.trie = nil
|
||||
return nil
|
||||
return nil, nil, nil, iteratorEnd
|
||||
}
|
||||
parent := it.stack[len(it.stack)-1]
|
||||
ancestor := parent.hash
|
||||
@ -209,63 +215,76 @@ outer:
|
||||
ancestor = parent.parent
|
||||
}
|
||||
if node, ok := parent.node.(*fullNode); ok {
|
||||
// Full node, iterate over children
|
||||
for parent.child++; parent.child < len(node.Children); parent.child++ {
|
||||
child := node.Children[parent.child]
|
||||
// Full node, move to the first non-nil child.
|
||||
for i := parent.index + 1; i < len(node.Children); i++ {
|
||||
child := node.Children[i]
|
||||
if child != nil {
|
||||
hash, _ := child.cache()
|
||||
it.stack = append(it.stack, &nodeIteratorState{
|
||||
state := &nodeIteratorState{
|
||||
hash: common.BytesToHash(hash),
|
||||
node: child,
|
||||
parent: ancestor,
|
||||
child: -1,
|
||||
index: -1,
|
||||
pathlen: len(it.path),
|
||||
})
|
||||
it.path = append(it.path, byte(parent.child))
|
||||
break outer
|
||||
}
|
||||
path := append(it.path, byte(i))
|
||||
parent.index = i - 1
|
||||
return state, &parent.index, path, nil
|
||||
}
|
||||
}
|
||||
} else if node, ok := parent.node.(*shortNode); ok {
|
||||
// Short node, return the pointer singleton child
|
||||
if parent.child < 0 {
|
||||
parent.child++
|
||||
if parent.index < 0 {
|
||||
hash, _ := node.Val.cache()
|
||||
it.stack = append(it.stack, &nodeIteratorState{
|
||||
state := &nodeIteratorState{
|
||||
hash: common.BytesToHash(hash),
|
||||
node: node.Val,
|
||||
parent: ancestor,
|
||||
child: -1,
|
||||
index: -1,
|
||||
pathlen: len(it.path),
|
||||
})
|
||||
if hasTerm(node.Key) {
|
||||
it.path = append(it.path, node.Key[:len(node.Key)-1]...)
|
||||
} else {
|
||||
it.path = append(it.path, node.Key...)
|
||||
}
|
||||
break
|
||||
var path []byte
|
||||
if hasTerm(node.Key) {
|
||||
path = append(it.path, node.Key[:len(node.Key)-1]...)
|
||||
} else {
|
||||
path = append(it.path, node.Key...)
|
||||
}
|
||||
return state, &parent.index, path, nil
|
||||
}
|
||||
} else if hash, ok := parent.node.(hashNode); ok {
|
||||
// Hash node, resolve the hash child from the database
|
||||
if parent.child < 0 {
|
||||
parent.child++
|
||||
if parent.index < 0 {
|
||||
node, err := it.trie.resolveHash(hash, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return it.stack[len(it.stack)-1], &parent.index, it.path, err
|
||||
}
|
||||
it.stack = append(it.stack, &nodeIteratorState{
|
||||
state := &nodeIteratorState{
|
||||
hash: common.BytesToHash(hash),
|
||||
node: node,
|
||||
parent: ancestor,
|
||||
child: -1,
|
||||
index: -1,
|
||||
pathlen: len(it.path),
|
||||
})
|
||||
break
|
||||
}
|
||||
return state, &parent.index, it.path, nil
|
||||
}
|
||||
}
|
||||
it.path = it.path[:parent.pathlen]
|
||||
it.stack = it.stack[:len(it.stack)-1]
|
||||
// No more child nodes, move back up.
|
||||
it.pop()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (it *nodeIterator) push(state *nodeIteratorState, parentIndex *int, path []byte) {
|
||||
it.path = path
|
||||
it.stack = append(it.stack, state)
|
||||
if parentIndex != nil {
|
||||
*parentIndex += 1
|
||||
}
|
||||
}
|
||||
|
||||
func (it *nodeIterator) pop() {
|
||||
parent := it.stack[len(it.stack)-1]
|
||||
it.path = it.path[:parent.pathlen]
|
||||
it.stack = it.stack[:len(it.stack)-1]
|
||||
}
|
||||
|
||||
func compareNodes(a, b NodeIterator) int {
|
||||
|
@ -17,6 +17,8 @@
|
||||
package trie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@ -42,7 +44,7 @@ func TestIterator(t *testing.T) {
|
||||
trie.Commit()
|
||||
|
||||
found := make(map[string]string)
|
||||
it := NewIterator(trie)
|
||||
it := NewIterator(trie.NodeIterator(nil))
|
||||
for it.Next() {
|
||||
found[string(it.Key)] = string(it.Value)
|
||||
}
|
||||
@ -72,7 +74,7 @@ func TestIteratorLargeData(t *testing.T) {
|
||||
vals[string(value2.k)] = value2
|
||||
}
|
||||
|
||||
it := NewIterator(trie)
|
||||
it := NewIterator(trie.NodeIterator(nil))
|
||||
for it.Next() {
|
||||
vals[string(it.Key)].t = true
|
||||
}
|
||||
@ -99,7 +101,7 @@ func TestNodeIteratorCoverage(t *testing.T) {
|
||||
|
||||
// Gather all the node hashes found by the iterator
|
||||
hashes := make(map[common.Hash]struct{})
|
||||
for it := NewNodeIterator(trie); it.Next(true); {
|
||||
for it := trie.NodeIterator(nil); it.Next(true); {
|
||||
if it.Hash() != (common.Hash{}) {
|
||||
hashes[it.Hash()] = struct{}{}
|
||||
}
|
||||
@ -117,18 +119,20 @@ func TestNodeIteratorCoverage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var testdata1 = []struct{ k, v string }{
|
||||
{"bar", "b"},
|
||||
type kvs struct{ k, v string }
|
||||
|
||||
var testdata1 = []kvs{
|
||||
{"barb", "ba"},
|
||||
{"bars", "bb"},
|
||||
{"bard", "bc"},
|
||||
{"bars", "bb"},
|
||||
{"bar", "b"},
|
||||
{"fab", "z"},
|
||||
{"foo", "a"},
|
||||
{"food", "ab"},
|
||||
{"foos", "aa"},
|
||||
{"foo", "a"},
|
||||
}
|
||||
|
||||
var testdata2 = []struct{ k, v string }{
|
||||
var testdata2 = []kvs{
|
||||
{"aardvark", "c"},
|
||||
{"bar", "b"},
|
||||
{"barb", "bd"},
|
||||
@ -140,6 +144,47 @@ var testdata2 = []struct{ k, v string }{
|
||||
{"jars", "d"},
|
||||
}
|
||||
|
||||
func TestIteratorSeek(t *testing.T) {
|
||||
trie := newEmpty()
|
||||
for _, val := range testdata1 {
|
||||
trie.Update([]byte(val.k), []byte(val.v))
|
||||
}
|
||||
|
||||
// Seek to the middle.
|
||||
it := NewIterator(trie.NodeIterator([]byte("fab")))
|
||||
if err := checkIteratorOrder(testdata1[4:], it); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Seek to a non-existent key.
|
||||
it = NewIterator(trie.NodeIterator([]byte("barc")))
|
||||
if err := checkIteratorOrder(testdata1[1:], it); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Seek beyond the end.
|
||||
it = NewIterator(trie.NodeIterator([]byte("z")))
|
||||
if err := checkIteratorOrder(nil, it); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func checkIteratorOrder(want []kvs, it *Iterator) error {
|
||||
for it.Next() {
|
||||
if len(want) == 0 {
|
||||
return fmt.Errorf("didn't expect any more values, got key %q", it.Key)
|
||||
}
|
||||
if !bytes.Equal(it.Key, []byte(want[0].k)) {
|
||||
return fmt.Errorf("wrong key: got %q, want %q", it.Key, want[0].k)
|
||||
}
|
||||
want = want[1:]
|
||||
}
|
||||
if len(want) > 0 {
|
||||
return fmt.Errorf("iterator ended early, want key %q", want[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestDifferenceIterator(t *testing.T) {
|
||||
triea := newEmpty()
|
||||
for _, val := range testdata1 {
|
||||
@ -154,8 +199,8 @@ func TestDifferenceIterator(t *testing.T) {
|
||||
trieb.Commit()
|
||||
|
||||
found := make(map[string]string)
|
||||
di, _ := NewDifferenceIterator(NewNodeIterator(triea), NewNodeIterator(trieb))
|
||||
it := NewIteratorFromNodeIterator(di)
|
||||
di, _ := NewDifferenceIterator(triea.NodeIterator(nil), trieb.NodeIterator(nil))
|
||||
it := NewIterator(di)
|
||||
for it.Next() {
|
||||
found[string(it.Key)] = string(it.Value)
|
||||
}
|
||||
@ -189,8 +234,8 @@ func TestUnionIterator(t *testing.T) {
|
||||
}
|
||||
trieb.Commit()
|
||||
|
||||
di, _ := NewUnionIterator([]NodeIterator{NewNodeIterator(triea), NewNodeIterator(trieb)})
|
||||
it := NewIteratorFromNodeIterator(di)
|
||||
di, _ := NewUnionIterator([]NodeIterator{triea.NodeIterator(nil), trieb.NodeIterator(nil)})
|
||||
it := NewIterator(di)
|
||||
|
||||
all := []struct{ k, v string }{
|
||||
{"aardvark", "c"},
|
||||
|
@ -139,8 +139,8 @@ func decodeShort(hash, buf, elems []byte, cachegen uint16) (node, error) {
|
||||
return nil, err
|
||||
}
|
||||
flag := nodeFlag{hash: hash, gen: cachegen}
|
||||
key := compactDecode(kbuf)
|
||||
if key[len(key)-1] == 16 {
|
||||
key := compactToHex(kbuf)
|
||||
if hasTerm(key) {
|
||||
// value node
|
||||
val, _, err := rlp.SplitString(rest)
|
||||
if err != nil {
|
||||
|
@ -38,7 +38,7 @@ import (
|
||||
// absence of the key.
|
||||
func (t *Trie) Prove(key []byte) []rlp.RawValue {
|
||||
// Collect all nodes on the path to key.
|
||||
key = compactHexDecode(key)
|
||||
key = keybytesToHex(key)
|
||||
nodes := []node{}
|
||||
tn := t.root
|
||||
for len(key) > 0 && tn != nil {
|
||||
@ -89,7 +89,7 @@ func (t *Trie) Prove(key []byte) []rlp.RawValue {
|
||||
// returns an error if the proof contains invalid trie nodes or the
|
||||
// wrong value.
|
||||
func VerifyProof(rootHash common.Hash, key []byte, proof []rlp.RawValue) (value []byte, err error) {
|
||||
key = compactHexDecode(key)
|
||||
key = keybytesToHex(key)
|
||||
sha := sha3.NewKeccak256()
|
||||
wantHash := rootHash.Bytes()
|
||||
for i, buf := range proof {
|
||||
|
@ -156,12 +156,10 @@ func (t *SecureTrie) Root() []byte {
|
||||
return t.trie.Root()
|
||||
}
|
||||
|
||||
func (t *SecureTrie) Iterator() *Iterator {
|
||||
return t.trie.Iterator()
|
||||
}
|
||||
|
||||
func (t *SecureTrie) NodeIterator() NodeIterator {
|
||||
return NewNodeIterator(&t.trie)
|
||||
// NodeIterator returns an iterator that returns nodes of the underlying trie. Iteration
|
||||
// starts at the key after the given start key.
|
||||
func (t *SecureTrie) NodeIterator(start []byte) NodeIterator {
|
||||
return t.trie.NodeIterator(start)
|
||||
}
|
||||
|
||||
// CommitTo writes all nodes and the secure hash pre-images to the given database.
|
||||
|
@ -80,7 +80,7 @@ func checkTrieConsistency(db Database, root common.Hash) error {
|
||||
if err != nil {
|
||||
return nil // // Consider a non existent state consistent
|
||||
}
|
||||
it := NewNodeIterator(trie)
|
||||
it := trie.NodeIterator(nil)
|
||||
for it.Next(true) {
|
||||
}
|
||||
return it.Error()
|
||||
|
14
trie/trie.go
14
trie/trie.go
@ -125,9 +125,10 @@ func New(root common.Hash, db Database) (*Trie, error) {
|
||||
return trie, nil
|
||||
}
|
||||
|
||||
// Iterator returns an iterator over all mappings in the trie.
|
||||
func (t *Trie) Iterator() *Iterator {
|
||||
return NewIterator(t)
|
||||
// NodeIterator returns an iterator that returns nodes of the trie. Iteration starts at
|
||||
// the key after the given start key.
|
||||
func (t *Trie) NodeIterator(start []byte) NodeIterator {
|
||||
return newNodeIterator(t, start)
|
||||
}
|
||||
|
||||
// Get returns the value for key stored in the trie.
|
||||
@ -144,7 +145,7 @@ func (t *Trie) Get(key []byte) []byte {
|
||||
// The value bytes must not be modified by the caller.
|
||||
// If a node was not found in the database, a MissingNodeError is returned.
|
||||
func (t *Trie) TryGet(key []byte) ([]byte, error) {
|
||||
key = compactHexDecode(key)
|
||||
key = keybytesToHex(key)
|
||||
value, newroot, didResolve, err := t.tryGet(t.root, key, 0)
|
||||
if err == nil && didResolve {
|
||||
t.root = newroot
|
||||
@ -211,7 +212,7 @@ func (t *Trie) Update(key, value []byte) {
|
||||
//
|
||||
// If a node was not found in the database, a MissingNodeError is returned.
|
||||
func (t *Trie) TryUpdate(key, value []byte) error {
|
||||
k := compactHexDecode(key)
|
||||
k := keybytesToHex(key)
|
||||
if len(value) != 0 {
|
||||
_, n, err := t.insert(t.root, nil, k, valueNode(value))
|
||||
if err != nil {
|
||||
@ -307,7 +308,7 @@ func (t *Trie) Delete(key []byte) {
|
||||
// TryDelete removes any existing value for key from the trie.
|
||||
// If a node was not found in the database, a MissingNodeError is returned.
|
||||
func (t *Trie) TryDelete(key []byte) error {
|
||||
k := compactHexDecode(key)
|
||||
k := keybytesToHex(key)
|
||||
_, n, err := t.delete(t.root, nil, k)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -450,7 +451,6 @@ func (t *Trie) resolveHash(n hashNode, prefix, suffix []byte) (node, error) {
|
||||
return nil, &MissingNodeError{
|
||||
RootHash: t.originalRoot,
|
||||
NodeHash: common.BytesToHash(n),
|
||||
Key: compactHexEncode(append(prefix, suffix...)),
|
||||
PrefixLen: len(prefix),
|
||||
SuffixLen: len(suffix),
|
||||
}
|
||||
|
@ -439,7 +439,7 @@ func runRandTest(rt randTest) bool {
|
||||
tr = newtr
|
||||
case opItercheckhash:
|
||||
checktr, _ := New(common.Hash{}, nil)
|
||||
it := tr.Iterator()
|
||||
it := NewIterator(tr.NodeIterator(nil))
|
||||
for it.Next() {
|
||||
checktr.Update(it.Key, it.Value)
|
||||
}
|
||||
|
131
vendor/golang.org/x/crypto/curve25519/cswap_amd64.s
generated
vendored
131
vendor/golang.org/x/crypto/curve25519/cswap_amd64.s
generated
vendored
@ -2,87 +2,64 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This code was translated into a form compatible with 6a from the public
|
||||
// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
|
||||
|
||||
// +build amd64,!gccgo,!appengine
|
||||
|
||||
// func cswap(inout *[5]uint64, v uint64)
|
||||
// func cswap(inout *[4][5]uint64, v uint64)
|
||||
TEXT ·cswap(SB),7,$0
|
||||
MOVQ inout+0(FP),DI
|
||||
MOVQ v+8(FP),SI
|
||||
|
||||
CMPQ SI,$1
|
||||
MOVQ 0(DI),SI
|
||||
MOVQ 80(DI),DX
|
||||
MOVQ 8(DI),CX
|
||||
MOVQ 88(DI),R8
|
||||
MOVQ SI,R9
|
||||
CMOVQEQ DX,SI
|
||||
CMOVQEQ R9,DX
|
||||
MOVQ CX,R9
|
||||
CMOVQEQ R8,CX
|
||||
CMOVQEQ R9,R8
|
||||
MOVQ SI,0(DI)
|
||||
MOVQ DX,80(DI)
|
||||
MOVQ CX,8(DI)
|
||||
MOVQ R8,88(DI)
|
||||
MOVQ 16(DI),SI
|
||||
MOVQ 96(DI),DX
|
||||
MOVQ 24(DI),CX
|
||||
MOVQ 104(DI),R8
|
||||
MOVQ SI,R9
|
||||
CMOVQEQ DX,SI
|
||||
CMOVQEQ R9,DX
|
||||
MOVQ CX,R9
|
||||
CMOVQEQ R8,CX
|
||||
CMOVQEQ R9,R8
|
||||
MOVQ SI,16(DI)
|
||||
MOVQ DX,96(DI)
|
||||
MOVQ CX,24(DI)
|
||||
MOVQ R8,104(DI)
|
||||
MOVQ 32(DI),SI
|
||||
MOVQ 112(DI),DX
|
||||
MOVQ 40(DI),CX
|
||||
MOVQ 120(DI),R8
|
||||
MOVQ SI,R9
|
||||
CMOVQEQ DX,SI
|
||||
CMOVQEQ R9,DX
|
||||
MOVQ CX,R9
|
||||
CMOVQEQ R8,CX
|
||||
CMOVQEQ R9,R8
|
||||
MOVQ SI,32(DI)
|
||||
MOVQ DX,112(DI)
|
||||
MOVQ CX,40(DI)
|
||||
MOVQ R8,120(DI)
|
||||
MOVQ 48(DI),SI
|
||||
MOVQ 128(DI),DX
|
||||
MOVQ 56(DI),CX
|
||||
MOVQ 136(DI),R8
|
||||
MOVQ SI,R9
|
||||
CMOVQEQ DX,SI
|
||||
CMOVQEQ R9,DX
|
||||
MOVQ CX,R9
|
||||
CMOVQEQ R8,CX
|
||||
CMOVQEQ R9,R8
|
||||
MOVQ SI,48(DI)
|
||||
MOVQ DX,128(DI)
|
||||
MOVQ CX,56(DI)
|
||||
MOVQ R8,136(DI)
|
||||
MOVQ 64(DI),SI
|
||||
MOVQ 144(DI),DX
|
||||
MOVQ 72(DI),CX
|
||||
MOVQ 152(DI),R8
|
||||
MOVQ SI,R9
|
||||
CMOVQEQ DX,SI
|
||||
CMOVQEQ R9,DX
|
||||
MOVQ CX,R9
|
||||
CMOVQEQ R8,CX
|
||||
CMOVQEQ R9,R8
|
||||
MOVQ SI,64(DI)
|
||||
MOVQ DX,144(DI)
|
||||
MOVQ CX,72(DI)
|
||||
MOVQ R8,152(DI)
|
||||
MOVQ DI,AX
|
||||
MOVQ SI,DX
|
||||
SUBQ $1, SI
|
||||
NOTQ SI
|
||||
MOVQ SI, X15
|
||||
PSHUFD $0x44, X15, X15
|
||||
|
||||
MOVOU 0(DI), X0
|
||||
MOVOU 16(DI), X2
|
||||
MOVOU 32(DI), X4
|
||||
MOVOU 48(DI), X6
|
||||
MOVOU 64(DI), X8
|
||||
MOVOU 80(DI), X1
|
||||
MOVOU 96(DI), X3
|
||||
MOVOU 112(DI), X5
|
||||
MOVOU 128(DI), X7
|
||||
MOVOU 144(DI), X9
|
||||
|
||||
MOVO X1, X10
|
||||
MOVO X3, X11
|
||||
MOVO X5, X12
|
||||
MOVO X7, X13
|
||||
MOVO X9, X14
|
||||
|
||||
PXOR X0, X10
|
||||
PXOR X2, X11
|
||||
PXOR X4, X12
|
||||
PXOR X6, X13
|
||||
PXOR X8, X14
|
||||
PAND X15, X10
|
||||
PAND X15, X11
|
||||
PAND X15, X12
|
||||
PAND X15, X13
|
||||
PAND X15, X14
|
||||
PXOR X10, X0
|
||||
PXOR X10, X1
|
||||
PXOR X11, X2
|
||||
PXOR X11, X3
|
||||
PXOR X12, X4
|
||||
PXOR X12, X5
|
||||
PXOR X13, X6
|
||||
PXOR X13, X7
|
||||
PXOR X14, X8
|
||||
PXOR X14, X9
|
||||
|
||||
MOVOU X0, 0(DI)
|
||||
MOVOU X2, 16(DI)
|
||||
MOVOU X4, 32(DI)
|
||||
MOVOU X6, 48(DI)
|
||||
MOVOU X8, 64(DI)
|
||||
MOVOU X1, 80(DI)
|
||||
MOVOU X3, 96(DI)
|
||||
MOVOU X5, 112(DI)
|
||||
MOVOU X7, 128(DI)
|
||||
MOVOU X9, 144(DI)
|
||||
RET
|
||||
|
23
vendor/golang.org/x/crypto/curve25519/curve25519.go
generated
vendored
23
vendor/golang.org/x/crypto/curve25519/curve25519.go
generated
vendored
@ -8,6 +8,10 @@
|
||||
|
||||
package curve25519
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// This code is a port of the public domain, "ref10" implementation of
|
||||
// curve25519 from SUPERCOP 20130419 by D. J. Bernstein.
|
||||
|
||||
@ -50,17 +54,11 @@ func feCopy(dst, src *fieldElement) {
|
||||
//
|
||||
// Preconditions: b in {0,1}.
|
||||
func feCSwap(f, g *fieldElement, b int32) {
|
||||
var x fieldElement
|
||||
b = -b
|
||||
for i := range x {
|
||||
x[i] = b & (f[i] ^ g[i])
|
||||
}
|
||||
|
||||
for i := range f {
|
||||
f[i] ^= x[i]
|
||||
}
|
||||
for i := range g {
|
||||
g[i] ^= x[i]
|
||||
t := b & (f[i] ^ g[i])
|
||||
f[i] ^= t
|
||||
g[i] ^= t
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,12 +73,7 @@ func load3(in []byte) int64 {
|
||||
|
||||
// load4 reads a 32-bit, little-endian value from in.
|
||||
func load4(in []byte) int64 {
|
||||
var r int64
|
||||
r = int64(in[0])
|
||||
r |= int64(in[1]) << 8
|
||||
r |= int64(in[2]) << 16
|
||||
r |= int64(in[3]) << 24
|
||||
return r
|
||||
return int64(binary.LittleEndian.Uint32(in))
|
||||
}
|
||||
|
||||
func feFromBytes(dst *fieldElement, src *[32]byte) {
|
||||
|
2
vendor/golang.org/x/crypto/ssh/certs.go
generated
vendored
2
vendor/golang.org/x/crypto/ssh/certs.go
generated
vendored
@ -268,7 +268,7 @@ type CertChecker struct {
|
||||
// HostKeyFallback is called when CertChecker.CheckHostKey encounters a
|
||||
// public key that is not a certificate. It must implement host key
|
||||
// validation or else, if nil, all such keys are rejected.
|
||||
HostKeyFallback func(addr string, remote net.Addr, key PublicKey) error
|
||||
HostKeyFallback HostKeyCallback
|
||||
|
||||
// IsRevoked is called for each certificate so that revocation checking
|
||||
// can be implemented. It should return true if the given certificate
|
||||
|
56
vendor/golang.org/x/crypto/ssh/client.go
generated
vendored
56
vendor/golang.org/x/crypto/ssh/client.go
generated
vendored
@ -5,6 +5,7 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
@ -13,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// Client implements a traditional SSH client that supports shells,
|
||||
// subprocesses, port forwarding and tunneled dialing.
|
||||
// subprocesses, TCP port/streamlocal forwarding and tunneled dialing.
|
||||
type Client struct {
|
||||
Conn
|
||||
|
||||
@ -59,6 +60,7 @@ func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client {
|
||||
conn.forwards.closeAll()
|
||||
}()
|
||||
go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-tcpip"))
|
||||
go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-streamlocal@openssh.com"))
|
||||
return conn
|
||||
}
|
||||
|
||||
@ -68,6 +70,11 @@ func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client {
|
||||
func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) {
|
||||
fullConf := *config
|
||||
fullConf.SetDefaults()
|
||||
if fullConf.HostKeyCallback == nil {
|
||||
c.Close()
|
||||
return nil, nil, nil, errors.New("ssh: must specify HostKeyCallback")
|
||||
}
|
||||
|
||||
conn := &connection{
|
||||
sshConn: sshConn{conn: c},
|
||||
}
|
||||
@ -173,6 +180,13 @@ func Dial(network, addr string, config *ClientConfig) (*Client, error) {
|
||||
return NewClient(c, chans, reqs), nil
|
||||
}
|
||||
|
||||
// HostKeyCallback is the function type used for verifying server
|
||||
// keys. A HostKeyCallback must return nil if the host key is OK, or
|
||||
// an error to reject it. It receives the hostname as passed to Dial
|
||||
// or NewClientConn. The remote address is the RemoteAddr of the
|
||||
// net.Conn underlying the the SSH connection.
|
||||
type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
|
||||
|
||||
// A ClientConfig structure is used to configure a Client. It must not be
|
||||
// modified after having been passed to an SSH function.
|
||||
type ClientConfig struct {
|
||||
@ -188,10 +202,12 @@ type ClientConfig struct {
|
||||
// be used during authentication.
|
||||
Auth []AuthMethod
|
||||
|
||||
// HostKeyCallback, if not nil, is called during the cryptographic
|
||||
// handshake to validate the server's host key. A nil HostKeyCallback
|
||||
// implies that all host keys are accepted.
|
||||
HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
|
||||
// HostKeyCallback is called during the cryptographic
|
||||
// handshake to validate the server's host key. The client
|
||||
// configuration must supply this callback for the connection
|
||||
// to succeed. The functions InsecureIgnoreHostKey or
|
||||
// FixedHostKey can be used for simplistic host key checks.
|
||||
HostKeyCallback HostKeyCallback
|
||||
|
||||
// ClientVersion contains the version identification string that will
|
||||
// be used for the connection. If empty, a reasonable default is used.
|
||||
@ -209,3 +225,33 @@ type ClientConfig struct {
|
||||
// A Timeout of zero means no timeout.
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// InsecureIgnoreHostKey returns a function that can be used for
|
||||
// ClientConfig.HostKeyCallback to accept any host key. It should
|
||||
// not be used for production code.
|
||||
func InsecureIgnoreHostKey() HostKeyCallback {
|
||||
return func(hostname string, remote net.Addr, key PublicKey) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type fixedHostKey struct {
|
||||
key PublicKey
|
||||
}
|
||||
|
||||
func (f *fixedHostKey) check(hostname string, remote net.Addr, key PublicKey) error {
|
||||
if f.key == nil {
|
||||
return fmt.Errorf("ssh: required host key was nil")
|
||||
}
|
||||
if !bytes.Equal(key.Marshal(), f.key.Marshal()) {
|
||||
return fmt.Errorf("ssh: host key mismatch")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FixedHostKey returns a function for use in
|
||||
// ClientConfig.HostKeyCallback to accept only a specific host key.
|
||||
func FixedHostKey(key PublicKey) HostKeyCallback {
|
||||
hk := &fixedHostKey{key}
|
||||
return hk.check
|
||||
}
|
||||
|
49
vendor/golang.org/x/crypto/ssh/client_auth.go
generated
vendored
49
vendor/golang.org/x/crypto/ssh/client_auth.go
generated
vendored
@ -179,31 +179,26 @@ func (cb publicKeyCallback) method() string {
|
||||
}
|
||||
|
||||
func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
|
||||
// Authentication is performed in two stages. The first stage sends an
|
||||
// enquiry to test if each key is acceptable to the remote. The second
|
||||
// stage attempts to authenticate with the valid keys obtained in the
|
||||
// first stage.
|
||||
// Authentication is performed by sending an enquiry to test if a key is
|
||||
// acceptable to the remote. If the key is acceptable, the client will
|
||||
// attempt to authenticate with the valid key. If not the client will repeat
|
||||
// the process with the remaining keys.
|
||||
|
||||
signers, err := cb()
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
var validKeys []Signer
|
||||
for _, signer := range signers {
|
||||
if ok, err := validateKey(signer.PublicKey(), user, c); ok {
|
||||
validKeys = append(validKeys, signer)
|
||||
} else {
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// methods that may continue if this auth is not successful.
|
||||
var methods []string
|
||||
for _, signer := range validKeys {
|
||||
pub := signer.PublicKey()
|
||||
for _, signer := range signers {
|
||||
ok, err := validateKey(signer.PublicKey(), user, c)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
pub := signer.PublicKey()
|
||||
pubKey := pub.Marshal()
|
||||
sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{
|
||||
User: user,
|
||||
@ -236,13 +231,29 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if success {
|
||||
|
||||
// If authentication succeeds or the list of available methods does not
|
||||
// contain the "publickey" method, do not attempt to authenticate with any
|
||||
// other keys. According to RFC 4252 Section 7, the latter can occur when
|
||||
// additional authentication methods are required.
|
||||
if success || !containsMethod(methods, cb.method()) {
|
||||
return success, methods, err
|
||||
}
|
||||
}
|
||||
|
||||
return false, methods, nil
|
||||
}
|
||||
|
||||
func containsMethod(methods []string, method string) bool {
|
||||
for _, m := range methods {
|
||||
if m == method {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// validateKey validates the key provided is acceptable to the server.
|
||||
func validateKey(key PublicKey, user string, c packetConn) (bool, error) {
|
||||
pubKey := key.Marshal()
|
||||
|
14
vendor/golang.org/x/crypto/ssh/common.go
generated
vendored
14
vendor/golang.org/x/crypto/ssh/common.go
generated
vendored
@ -9,6 +9,7 @@ import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
_ "crypto/sha1"
|
||||
@ -40,7 +41,7 @@ var supportedKexAlgos = []string{
|
||||
kexAlgoDH14SHA1, kexAlgoDH1SHA1,
|
||||
}
|
||||
|
||||
// supportedKexAlgos specifies the supported host-key algorithms (i.e. methods
|
||||
// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
|
||||
// of authenticating servers) in preference order.
|
||||
var supportedHostKeyAlgos = []string{
|
||||
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
|
||||
@ -186,7 +187,7 @@ type Config struct {
|
||||
|
||||
// The maximum number of bytes sent or received after which a
|
||||
// new key is negotiated. It must be at least 256. If
|
||||
// unspecified, 1 gigabyte is used.
|
||||
// unspecified, a size suitable for the chosen cipher is used.
|
||||
RekeyThreshold uint64
|
||||
|
||||
// The allowed key exchanges algorithms. If unspecified then a
|
||||
@ -230,11 +231,12 @@ func (c *Config) SetDefaults() {
|
||||
}
|
||||
|
||||
if c.RekeyThreshold == 0 {
|
||||
// RFC 4253, section 9 suggests rekeying after 1G.
|
||||
c.RekeyThreshold = 1 << 30
|
||||
}
|
||||
if c.RekeyThreshold < minRekeyThreshold {
|
||||
// cipher specific default
|
||||
} else if c.RekeyThreshold < minRekeyThreshold {
|
||||
c.RekeyThreshold = minRekeyThreshold
|
||||
} else if c.RekeyThreshold >= math.MaxInt64 {
|
||||
// Avoid weirdness if somebody uses -1 as a threshold.
|
||||
c.RekeyThreshold = math.MaxInt64
|
||||
}
|
||||
}
|
||||
|
||||
|
3
vendor/golang.org/x/crypto/ssh/doc.go
generated
vendored
3
vendor/golang.org/x/crypto/ssh/doc.go
generated
vendored
@ -14,5 +14,8 @@ others.
|
||||
References:
|
||||
[PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD
|
||||
[SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1
|
||||
|
||||
This package does not fall under the stability promise of the Go language itself,
|
||||
so its API may be changed when pressing needs arise.
|
||||
*/
|
||||
package ssh // import "golang.org/x/crypto/ssh"
|
||||
|
53
vendor/golang.org/x/crypto/ssh/handshake.go
generated
vendored
53
vendor/golang.org/x/crypto/ssh/handshake.go
generated
vendored
@ -74,7 +74,7 @@ type handshakeTransport struct {
|
||||
startKex chan *pendingKex
|
||||
|
||||
// data for host key checking
|
||||
hostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
|
||||
hostKeyCallback HostKeyCallback
|
||||
dialAddress string
|
||||
remoteAddr net.Addr
|
||||
|
||||
@ -107,6 +107,8 @@ func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion,
|
||||
|
||||
config: config,
|
||||
}
|
||||
t.resetReadThresholds()
|
||||
t.resetWriteThresholds()
|
||||
|
||||
// We always start with a mandatory key exchange.
|
||||
t.requestKex <- struct{}{}
|
||||
@ -237,6 +239,17 @@ func (t *handshakeTransport) requestKeyExchange() {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *handshakeTransport) resetWriteThresholds() {
|
||||
t.writePacketsLeft = packetRekeyThreshold
|
||||
if t.config.RekeyThreshold > 0 {
|
||||
t.writeBytesLeft = int64(t.config.RekeyThreshold)
|
||||
} else if t.algorithms != nil {
|
||||
t.writeBytesLeft = t.algorithms.w.rekeyBytes()
|
||||
} else {
|
||||
t.writeBytesLeft = 1 << 30
|
||||
}
|
||||
}
|
||||
|
||||
func (t *handshakeTransport) kexLoop() {
|
||||
|
||||
write:
|
||||
@ -285,12 +298,8 @@ write:
|
||||
t.writeError = err
|
||||
t.sentInitPacket = nil
|
||||
t.sentInitMsg = nil
|
||||
t.writePacketsLeft = packetRekeyThreshold
|
||||
if t.config.RekeyThreshold > 0 {
|
||||
t.writeBytesLeft = int64(t.config.RekeyThreshold)
|
||||
} else if t.algorithms != nil {
|
||||
t.writeBytesLeft = t.algorithms.w.rekeyBytes()
|
||||
}
|
||||
|
||||
t.resetWriteThresholds()
|
||||
|
||||
// we have completed the key exchange. Since the
|
||||
// reader is still blocked, it is safe to clear out
|
||||
@ -344,6 +353,17 @@ write:
|
||||
// key exchange itself.
|
||||
const packetRekeyThreshold = (1 << 31)
|
||||
|
||||
func (t *handshakeTransport) resetReadThresholds() {
|
||||
t.readPacketsLeft = packetRekeyThreshold
|
||||
if t.config.RekeyThreshold > 0 {
|
||||
t.readBytesLeft = int64(t.config.RekeyThreshold)
|
||||
} else if t.algorithms != nil {
|
||||
t.readBytesLeft = t.algorithms.r.rekeyBytes()
|
||||
} else {
|
||||
t.readBytesLeft = 1 << 30
|
||||
}
|
||||
}
|
||||
|
||||
func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) {
|
||||
p, err := t.conn.readPacket()
|
||||
if err != nil {
|
||||
@ -391,12 +411,7 @@ func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t.readPacketsLeft = packetRekeyThreshold
|
||||
if t.config.RekeyThreshold > 0 {
|
||||
t.readBytesLeft = int64(t.config.RekeyThreshold)
|
||||
} else {
|
||||
t.readBytesLeft = t.algorithms.r.rekeyBytes()
|
||||
}
|
||||
t.resetReadThresholds()
|
||||
|
||||
// By default, a key exchange is hidden from higher layers by
|
||||
// translating it into msgIgnore.
|
||||
@ -574,7 +589,9 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
||||
}
|
||||
result.SessionID = t.sessionID
|
||||
|
||||
t.conn.prepareKeyChange(t.algorithms, result)
|
||||
if err := t.conn.prepareKeyChange(t.algorithms, result); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -614,11 +631,9 @@ func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics *
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if t.hostKeyCallback != nil {
|
||||
err = t.hostKeyCallback(t.dialAddress, t.remoteAddr, hostKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = t.hostKeyCallback(t.dialAddress, t.remoteAddr, hostKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
94
vendor/golang.org/x/crypto/ssh/keys.go
generated
vendored
94
vendor/golang.org/x/crypto/ssh/keys.go
generated
vendored
@ -824,7 +824,7 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
|
||||
|
||||
// Implemented based on the documentation at
|
||||
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
|
||||
func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) {
|
||||
func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
|
||||
magic := append([]byte("openssh-key-v1"), 0)
|
||||
if !bytes.Equal(magic, key[0:len(magic)]) {
|
||||
return nil, errors.New("ssh: invalid openssh private key format")
|
||||
@ -844,14 +844,15 @@ func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if w.KdfName != "none" || w.CipherName != "none" {
|
||||
return nil, errors.New("ssh: cannot decode encrypted private keys")
|
||||
}
|
||||
|
||||
pk1 := struct {
|
||||
Check1 uint32
|
||||
Check2 uint32
|
||||
Keytype string
|
||||
Pub []byte
|
||||
Priv []byte
|
||||
Comment string
|
||||
Pad []byte `ssh:"rest"`
|
||||
Rest []byte `ssh:"rest"`
|
||||
}{}
|
||||
|
||||
if err := Unmarshal(w.PrivKeyBlock, &pk1); err != nil {
|
||||
@ -862,24 +863,75 @@ func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) {
|
||||
return nil, errors.New("ssh: checkint mismatch")
|
||||
}
|
||||
|
||||
// we only handle ed25519 keys currently
|
||||
if pk1.Keytype != KeyAlgoED25519 {
|
||||
// we only handle ed25519 and rsa keys currently
|
||||
switch pk1.Keytype {
|
||||
case KeyAlgoRSA:
|
||||
// https://github.com/openssh/openssh-portable/blob/master/sshkey.c#L2760-L2773
|
||||
key := struct {
|
||||
N *big.Int
|
||||
E *big.Int
|
||||
D *big.Int
|
||||
Iqmp *big.Int
|
||||
P *big.Int
|
||||
Q *big.Int
|
||||
Comment string
|
||||
Pad []byte `ssh:"rest"`
|
||||
}{}
|
||||
|
||||
if err := Unmarshal(pk1.Rest, &key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, b := range key.Pad {
|
||||
if int(b) != i+1 {
|
||||
return nil, errors.New("ssh: padding not as expected")
|
||||
}
|
||||
}
|
||||
|
||||
pk := &rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{
|
||||
N: key.N,
|
||||
E: int(key.E.Int64()),
|
||||
},
|
||||
D: key.D,
|
||||
Primes: []*big.Int{key.P, key.Q},
|
||||
}
|
||||
|
||||
if err := pk.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pk.Precompute()
|
||||
|
||||
return pk, nil
|
||||
case KeyAlgoED25519:
|
||||
key := struct {
|
||||
Pub []byte
|
||||
Priv []byte
|
||||
Comment string
|
||||
Pad []byte `ssh:"rest"`
|
||||
}{}
|
||||
|
||||
if err := Unmarshal(pk1.Rest, &key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(key.Priv) != ed25519.PrivateKeySize {
|
||||
return nil, errors.New("ssh: private key unexpected length")
|
||||
}
|
||||
|
||||
for i, b := range key.Pad {
|
||||
if int(b) != i+1 {
|
||||
return nil, errors.New("ssh: padding not as expected")
|
||||
}
|
||||
}
|
||||
|
||||
pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize))
|
||||
copy(pk, key.Priv)
|
||||
return &pk, nil
|
||||
default:
|
||||
return nil, errors.New("ssh: unhandled key type")
|
||||
}
|
||||
|
||||
for i, b := range pk1.Pad {
|
||||
if int(b) != i+1 {
|
||||
return nil, errors.New("ssh: padding not as expected")
|
||||
}
|
||||
}
|
||||
|
||||
if len(pk1.Priv) != ed25519.PrivateKeySize {
|
||||
return nil, errors.New("ssh: private key unexpected length")
|
||||
}
|
||||
|
||||
pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize))
|
||||
copy(pk, pk1.Priv)
|
||||
return &pk, nil
|
||||
}
|
||||
|
||||
// FingerprintLegacyMD5 returns the user presentation of the key's
|
||||
|
33
vendor/golang.org/x/crypto/ssh/server.go
generated
vendored
33
vendor/golang.org/x/crypto/ssh/server.go
generated
vendored
@ -45,6 +45,12 @@ type ServerConfig struct {
|
||||
// authenticating.
|
||||
NoClientAuth bool
|
||||
|
||||
// MaxAuthTries specifies the maximum number of authentication attempts
|
||||
// permitted per connection. If set to a negative number, the number of
|
||||
// attempts are unlimited. If set to zero, the number of attempts are limited
|
||||
// to 6.
|
||||
MaxAuthTries int
|
||||
|
||||
// PasswordCallback, if non-nil, is called when a user
|
||||
// attempts to authenticate using a password.
|
||||
PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
|
||||
@ -141,6 +147,10 @@ type ServerConn struct {
|
||||
// Request and NewChannel channels must be serviced, or the connection
|
||||
// will hang.
|
||||
func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) {
|
||||
if config.MaxAuthTries == 0 {
|
||||
config.MaxAuthTries = 6
|
||||
}
|
||||
|
||||
fullConf := *config
|
||||
fullConf.SetDefaults()
|
||||
s := &connection{
|
||||
@ -267,8 +277,23 @@ func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, err
|
||||
var cache pubKeyCache
|
||||
var perms *Permissions
|
||||
|
||||
authFailures := 0
|
||||
|
||||
userAuthLoop:
|
||||
for {
|
||||
if authFailures >= config.MaxAuthTries && config.MaxAuthTries > 0 {
|
||||
discMsg := &disconnectMsg{
|
||||
Reason: 2,
|
||||
Message: "too many authentication failures",
|
||||
}
|
||||
|
||||
if err := s.transport.writePacket(Marshal(discMsg)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, discMsg
|
||||
}
|
||||
|
||||
var userAuthReq userAuthRequestMsg
|
||||
if packet, err := s.transport.readPacket(); err != nil {
|
||||
return nil, err
|
||||
@ -289,6 +314,11 @@ userAuthLoop:
|
||||
if config.NoClientAuth {
|
||||
authErr = nil
|
||||
}
|
||||
|
||||
// allow initial attempt of 'none' without penalty
|
||||
if authFailures == 0 {
|
||||
authFailures--
|
||||
}
|
||||
case "password":
|
||||
if config.PasswordCallback == nil {
|
||||
authErr = errors.New("ssh: password auth not configured")
|
||||
@ -360,6 +390,7 @@ userAuthLoop:
|
||||
if isQuery {
|
||||
// The client can query if the given public key
|
||||
// would be okay.
|
||||
|
||||
if len(payload) > 0 {
|
||||
return nil, parseError(msgUserAuthRequest)
|
||||
}
|
||||
@ -409,6 +440,8 @@ userAuthLoop:
|
||||
break userAuthLoop
|
||||
}
|
||||
|
||||
authFailures++
|
||||
|
||||
var failureMsg userAuthFailureMsg
|
||||
if config.PasswordCallback != nil {
|
||||
failureMsg.Methods = append(failureMsg.Methods, "password")
|
||||
|
115
vendor/golang.org/x/crypto/ssh/streamlocal.go
generated
vendored
Normal file
115
vendor/golang.org/x/crypto/ssh/streamlocal.go
generated
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
// streamLocalChannelOpenDirectMsg is a struct used for SSH_MSG_CHANNEL_OPEN message
|
||||
// with "direct-streamlocal@openssh.com" string.
|
||||
//
|
||||
// See openssh-portable/PROTOCOL, section 2.4. connection: Unix domain socket forwarding
|
||||
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL#L235
|
||||
type streamLocalChannelOpenDirectMsg struct {
|
||||
socketPath string
|
||||
reserved0 string
|
||||
reserved1 uint32
|
||||
}
|
||||
|
||||
// forwardedStreamLocalPayload is a struct used for SSH_MSG_CHANNEL_OPEN message
|
||||
// with "forwarded-streamlocal@openssh.com" string.
|
||||
type forwardedStreamLocalPayload struct {
|
||||
SocketPath string
|
||||
Reserved0 string
|
||||
}
|
||||
|
||||
// streamLocalChannelForwardMsg is a struct used for SSH2_MSG_GLOBAL_REQUEST message
|
||||
// with "streamlocal-forward@openssh.com"/"cancel-streamlocal-forward@openssh.com" string.
|
||||
type streamLocalChannelForwardMsg struct {
|
||||
socketPath string
|
||||
}
|
||||
|
||||
// ListenUnix is similar to ListenTCP but uses a Unix domain socket.
|
||||
func (c *Client) ListenUnix(socketPath string) (net.Listener, error) {
|
||||
m := streamLocalChannelForwardMsg{
|
||||
socketPath,
|
||||
}
|
||||
// send message
|
||||
ok, _, err := c.SendRequest("streamlocal-forward@openssh.com", true, Marshal(&m))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return nil, errors.New("ssh: streamlocal-forward@openssh.com request denied by peer")
|
||||
}
|
||||
ch := c.forwards.add(&net.UnixAddr{Name: socketPath, Net: "unix"})
|
||||
|
||||
return &unixListener{socketPath, c, ch}, nil
|
||||
}
|
||||
|
||||
func (c *Client) dialStreamLocal(socketPath string) (Channel, error) {
|
||||
msg := streamLocalChannelOpenDirectMsg{
|
||||
socketPath: socketPath,
|
||||
}
|
||||
ch, in, err := c.OpenChannel("direct-streamlocal@openssh.com", Marshal(&msg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go DiscardRequests(in)
|
||||
return ch, err
|
||||
}
|
||||
|
||||
type unixListener struct {
|
||||
socketPath string
|
||||
|
||||
conn *Client
|
||||
in <-chan forward
|
||||
}
|
||||
|
||||
// Accept waits for and returns the next connection to the listener.
|
||||
func (l *unixListener) Accept() (net.Conn, error) {
|
||||
s, ok := <-l.in
|
||||
if !ok {
|
||||
return nil, io.EOF
|
||||
}
|
||||
ch, incoming, err := s.newCh.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go DiscardRequests(incoming)
|
||||
|
||||
return &chanConn{
|
||||
Channel: ch,
|
||||
laddr: &net.UnixAddr{
|
||||
Name: l.socketPath,
|
||||
Net: "unix",
|
||||
},
|
||||
raddr: &net.UnixAddr{
|
||||
Name: "@",
|
||||
Net: "unix",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close closes the listener.
|
||||
func (l *unixListener) Close() error {
|
||||
// this also closes the listener.
|
||||
l.conn.forwards.remove(&net.UnixAddr{Name: l.socketPath, Net: "unix"})
|
||||
m := streamLocalChannelForwardMsg{
|
||||
l.socketPath,
|
||||
}
|
||||
ok, _, err := l.conn.SendRequest("cancel-streamlocal-forward@openssh.com", true, Marshal(&m))
|
||||
if err == nil && !ok {
|
||||
err = errors.New("ssh: cancel-streamlocal-forward@openssh.com failed")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *unixListener) Addr() net.Addr {
|
||||
return &net.UnixAddr{
|
||||
Name: l.socketPath,
|
||||
Net: "unix",
|
||||
}
|
||||
}
|
196
vendor/golang.org/x/crypto/ssh/tcpip.go
generated
vendored
196
vendor/golang.org/x/crypto/ssh/tcpip.go
generated
vendored
@ -20,12 +20,20 @@ import (
|
||||
// addr. Incoming connections will be available by calling Accept on
|
||||
// the returned net.Listener. The listener must be serviced, or the
|
||||
// SSH connection may hang.
|
||||
// N must be "tcp", "tcp4", "tcp6", or "unix".
|
||||
func (c *Client) Listen(n, addr string) (net.Listener, error) {
|
||||
laddr, err := net.ResolveTCPAddr(n, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
switch n {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
laddr, err := net.ResolveTCPAddr(n, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.ListenTCP(laddr)
|
||||
case "unix":
|
||||
return c.ListenUnix(addr)
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported protocol: %s", n)
|
||||
}
|
||||
return c.ListenTCP(laddr)
|
||||
}
|
||||
|
||||
// Automatic port allocation is broken with OpenSSH before 6.0. See
|
||||
@ -116,7 +124,7 @@ func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) {
|
||||
}
|
||||
|
||||
// Register this forward, using the port number we obtained.
|
||||
ch := c.forwards.add(*laddr)
|
||||
ch := c.forwards.add(laddr)
|
||||
|
||||
return &tcpListener{laddr, c, ch}, nil
|
||||
}
|
||||
@ -131,7 +139,7 @@ type forwardList struct {
|
||||
// forwardEntry represents an established mapping of a laddr on a
|
||||
// remote ssh server to a channel connected to a tcpListener.
|
||||
type forwardEntry struct {
|
||||
laddr net.TCPAddr
|
||||
laddr net.Addr
|
||||
c chan forward
|
||||
}
|
||||
|
||||
@ -139,16 +147,16 @@ type forwardEntry struct {
|
||||
// arguments to add/remove/lookup should be address as specified in
|
||||
// the original forward-request.
|
||||
type forward struct {
|
||||
newCh NewChannel // the ssh client channel underlying this forward
|
||||
raddr *net.TCPAddr // the raddr of the incoming connection
|
||||
newCh NewChannel // the ssh client channel underlying this forward
|
||||
raddr net.Addr // the raddr of the incoming connection
|
||||
}
|
||||
|
||||
func (l *forwardList) add(addr net.TCPAddr) chan forward {
|
||||
func (l *forwardList) add(addr net.Addr) chan forward {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
f := forwardEntry{
|
||||
addr,
|
||||
make(chan forward, 1),
|
||||
laddr: addr,
|
||||
c: make(chan forward, 1),
|
||||
}
|
||||
l.entries = append(l.entries, f)
|
||||
return f.c
|
||||
@ -176,44 +184,69 @@ func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) {
|
||||
|
||||
func (l *forwardList) handleChannels(in <-chan NewChannel) {
|
||||
for ch := range in {
|
||||
var payload forwardedTCPPayload
|
||||
if err := Unmarshal(ch.ExtraData(), &payload); err != nil {
|
||||
ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error())
|
||||
continue
|
||||
}
|
||||
var (
|
||||
laddr net.Addr
|
||||
raddr net.Addr
|
||||
err error
|
||||
)
|
||||
switch channelType := ch.ChannelType(); channelType {
|
||||
case "forwarded-tcpip":
|
||||
var payload forwardedTCPPayload
|
||||
if err = Unmarshal(ch.ExtraData(), &payload); err != nil {
|
||||
ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// RFC 4254 section 7.2 specifies that incoming
|
||||
// addresses should list the address, in string
|
||||
// format. It is implied that this should be an IP
|
||||
// address, as it would be impossible to connect to it
|
||||
// otherwise.
|
||||
laddr, err := parseTCPAddr(payload.Addr, payload.Port)
|
||||
if err != nil {
|
||||
ch.Reject(ConnectionFailed, err.Error())
|
||||
continue
|
||||
}
|
||||
raddr, err := parseTCPAddr(payload.OriginAddr, payload.OriginPort)
|
||||
if err != nil {
|
||||
ch.Reject(ConnectionFailed, err.Error())
|
||||
continue
|
||||
}
|
||||
// RFC 4254 section 7.2 specifies that incoming
|
||||
// addresses should list the address, in string
|
||||
// format. It is implied that this should be an IP
|
||||
// address, as it would be impossible to connect to it
|
||||
// otherwise.
|
||||
laddr, err = parseTCPAddr(payload.Addr, payload.Port)
|
||||
if err != nil {
|
||||
ch.Reject(ConnectionFailed, err.Error())
|
||||
continue
|
||||
}
|
||||
raddr, err = parseTCPAddr(payload.OriginAddr, payload.OriginPort)
|
||||
if err != nil {
|
||||
ch.Reject(ConnectionFailed, err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
if ok := l.forward(*laddr, *raddr, ch); !ok {
|
||||
case "forwarded-streamlocal@openssh.com":
|
||||
var payload forwardedStreamLocalPayload
|
||||
if err = Unmarshal(ch.ExtraData(), &payload); err != nil {
|
||||
ch.Reject(ConnectionFailed, "could not parse forwarded-streamlocal@openssh.com payload: "+err.Error())
|
||||
continue
|
||||
}
|
||||
laddr = &net.UnixAddr{
|
||||
Name: payload.SocketPath,
|
||||
Net: "unix",
|
||||
}
|
||||
raddr = &net.UnixAddr{
|
||||
Name: "@",
|
||||
Net: "unix",
|
||||
}
|
||||
default:
|
||||
panic(fmt.Errorf("ssh: unknown channel type %s", channelType))
|
||||
}
|
||||
if ok := l.forward(laddr, raddr, ch); !ok {
|
||||
// Section 7.2, implementations MUST reject spurious incoming
|
||||
// connections.
|
||||
ch.Reject(Prohibited, "no forward for address")
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// remove removes the forward entry, and the channel feeding its
|
||||
// listener.
|
||||
func (l *forwardList) remove(addr net.TCPAddr) {
|
||||
func (l *forwardList) remove(addr net.Addr) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
for i, f := range l.entries {
|
||||
if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port {
|
||||
if addr.Network() == f.laddr.Network() && addr.String() == f.laddr.String() {
|
||||
l.entries = append(l.entries[:i], l.entries[i+1:]...)
|
||||
close(f.c)
|
||||
return
|
||||
@ -231,12 +264,12 @@ func (l *forwardList) closeAll() {
|
||||
l.entries = nil
|
||||
}
|
||||
|
||||
func (l *forwardList) forward(laddr, raddr net.TCPAddr, ch NewChannel) bool {
|
||||
func (l *forwardList) forward(laddr, raddr net.Addr, ch NewChannel) bool {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
for _, f := range l.entries {
|
||||
if laddr.IP.Equal(f.laddr.IP) && laddr.Port == f.laddr.Port {
|
||||
f.c <- forward{ch, &raddr}
|
||||
if laddr.Network() == f.laddr.Network() && laddr.String() == f.laddr.String() {
|
||||
f.c <- forward{newCh: ch, raddr: raddr}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -262,7 +295,7 @@ func (l *tcpListener) Accept() (net.Conn, error) {
|
||||
}
|
||||
go DiscardRequests(incoming)
|
||||
|
||||
return &tcpChanConn{
|
||||
return &chanConn{
|
||||
Channel: ch,
|
||||
laddr: l.laddr,
|
||||
raddr: s.raddr,
|
||||
@ -277,7 +310,7 @@ func (l *tcpListener) Close() error {
|
||||
}
|
||||
|
||||
// this also closes the listener.
|
||||
l.conn.forwards.remove(*l.laddr)
|
||||
l.conn.forwards.remove(l.laddr)
|
||||
ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m))
|
||||
if err == nil && !ok {
|
||||
err = errors.New("ssh: cancel-tcpip-forward failed")
|
||||
@ -293,29 +326,52 @@ func (l *tcpListener) Addr() net.Addr {
|
||||
// Dial initiates a connection to the addr from the remote host.
|
||||
// The resulting connection has a zero LocalAddr() and RemoteAddr().
|
||||
func (c *Client) Dial(n, addr string) (net.Conn, error) {
|
||||
// Parse the address into host and numeric port.
|
||||
host, portString, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var ch Channel
|
||||
switch n {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
// Parse the address into host and numeric port.
|
||||
host, portString, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
port, err := strconv.ParseUint(portString, 10, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ch, err = c.dial(net.IPv4zero.String(), 0, host, int(port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Use a zero address for local and remote address.
|
||||
zeroAddr := &net.TCPAddr{
|
||||
IP: net.IPv4zero,
|
||||
Port: 0,
|
||||
}
|
||||
return &chanConn{
|
||||
Channel: ch,
|
||||
laddr: zeroAddr,
|
||||
raddr: zeroAddr,
|
||||
}, nil
|
||||
case "unix":
|
||||
var err error
|
||||
ch, err = c.dialStreamLocal(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &chanConn{
|
||||
Channel: ch,
|
||||
laddr: &net.UnixAddr{
|
||||
Name: "@",
|
||||
Net: "unix",
|
||||
},
|
||||
raddr: &net.UnixAddr{
|
||||
Name: addr,
|
||||
Net: "unix",
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported protocol: %s", n)
|
||||
}
|
||||
port, err := strconv.ParseUint(portString, 10, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Use a zero address for local and remote address.
|
||||
zeroAddr := &net.TCPAddr{
|
||||
IP: net.IPv4zero,
|
||||
Port: 0,
|
||||
}
|
||||
ch, err := c.dial(net.IPv4zero.String(), 0, host, int(port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tcpChanConn{
|
||||
Channel: ch,
|
||||
laddr: zeroAddr,
|
||||
raddr: zeroAddr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DialTCP connects to the remote address raddr on the network net,
|
||||
@ -332,7 +388,7 @@ func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tcpChanConn{
|
||||
return &chanConn{
|
||||
Channel: ch,
|
||||
laddr: laddr,
|
||||
raddr: raddr,
|
||||
@ -366,26 +422,26 @@ type tcpChan struct {
|
||||
Channel // the backing channel
|
||||
}
|
||||
|
||||
// tcpChanConn fulfills the net.Conn interface without
|
||||
// chanConn fulfills the net.Conn interface without
|
||||
// the tcpChan having to hold laddr or raddr directly.
|
||||
type tcpChanConn struct {
|
||||
type chanConn struct {
|
||||
Channel
|
||||
laddr, raddr net.Addr
|
||||
}
|
||||
|
||||
// LocalAddr returns the local network address.
|
||||
func (t *tcpChanConn) LocalAddr() net.Addr {
|
||||
func (t *chanConn) LocalAddr() net.Addr {
|
||||
return t.laddr
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
func (t *tcpChanConn) RemoteAddr() net.Addr {
|
||||
func (t *chanConn) RemoteAddr() net.Addr {
|
||||
return t.raddr
|
||||
}
|
||||
|
||||
// SetDeadline sets the read and write deadlines associated
|
||||
// with the connection.
|
||||
func (t *tcpChanConn) SetDeadline(deadline time.Time) error {
|
||||
func (t *chanConn) SetDeadline(deadline time.Time) error {
|
||||
if err := t.SetReadDeadline(deadline); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -396,12 +452,14 @@ func (t *tcpChanConn) SetDeadline(deadline time.Time) error {
|
||||
// A zero value for t means Read will not time out.
|
||||
// After the deadline, the error from Read will implement net.Error
|
||||
// with Timeout() == true.
|
||||
func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error {
|
||||
func (t *chanConn) SetReadDeadline(deadline time.Time) error {
|
||||
// for compatibility with previous version,
|
||||
// the error message contains "tcpChan"
|
||||
return errors.New("ssh: tcpChan: deadline not supported")
|
||||
}
|
||||
|
||||
// SetWriteDeadline exists to satisfy the net.Conn interface
|
||||
// but is not implemented by this type. It always returns an error.
|
||||
func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error {
|
||||
func (t *chanConn) SetWriteDeadline(deadline time.Time) error {
|
||||
return errors.New("ssh: tcpChan: deadline not supported")
|
||||
}
|
||||
|
63
vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
generated
vendored
63
vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
generated
vendored
@ -14,14 +14,12 @@ import (
|
||||
|
||||
// State contains the state of a terminal.
|
||||
type State struct {
|
||||
termios syscall.Termios
|
||||
state *unix.Termios
|
||||
}
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(fd int) bool {
|
||||
// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
|
||||
var termio unix.Termio
|
||||
err := unix.IoctlSetTermio(fd, unix.TCGETA, &termio)
|
||||
_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
@ -71,3 +69,60 @@ func ReadPassword(fd int) ([]byte, error) {
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// MakeRaw puts the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
// see http://cr.illumos.org/~webrev/andy_js/1060/
|
||||
func MakeRaw(fd int) (*State, error) {
|
||||
oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oldTermios := *oldTermiosPtr
|
||||
|
||||
newTermios := oldTermios
|
||||
newTermios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
|
||||
newTermios.Oflag &^= syscall.OPOST
|
||||
newTermios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
|
||||
newTermios.Cflag &^= syscall.CSIZE | syscall.PARENB
|
||||
newTermios.Cflag |= syscall.CS8
|
||||
newTermios.Cc[unix.VMIN] = 1
|
||||
newTermios.Cc[unix.VTIME] = 0
|
||||
|
||||
if err := unix.IoctlSetTermios(fd, unix.TCSETS, &newTermios); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &State{
|
||||
state: oldTermiosPtr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func Restore(fd int, oldState *State) error {
|
||||
return unix.IoctlSetTermios(fd, unix.TCSETS, oldState.state)
|
||||
}
|
||||
|
||||
// GetState returns the current state of a terminal which may be useful to
|
||||
// restore the terminal after a signal.
|
||||
func GetState(fd int) (*State, error) {
|
||||
oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &State{
|
||||
state: oldTermiosPtr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetSize returns the dimensions of the given terminal.
|
||||
func GetSize(fd int) (width, height int, err error) {
|
||||
ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(ws.Col), int(ws.Row), nil
|
||||
}
|
||||
|
66
vendor/vendor.json
vendored
66
vendor/vendor.json
vendored
@ -384,92 +384,92 @@
|
||||
{
|
||||
"checksumSHA1": "TT1rac6kpQp2vz24m5yDGUNQ/QQ=",
|
||||
"path": "golang.org/x/crypto/cast5",
|
||||
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
|
||||
"revisionTime": "2017-02-08T20:51:15Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "C1KKOxFoW7/W/NFNpiXK+boguNo=",
|
||||
"checksumSHA1": "nAu0XmCeC6WnUySyI8R7w4cxAqU=",
|
||||
"path": "golang.org/x/crypto/curve25519",
|
||||
"revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7",
|
||||
"revisionTime": "2017-03-17T13:29:17Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "wGb//LjBPNxYHqk+dcLo7BjPXK8=",
|
||||
"path": "golang.org/x/crypto/ed25519",
|
||||
"revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7",
|
||||
"revisionTime": "2017-03-17T13:29:17Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "LXFcVx8I587SnWmKycSDEq9yvK8=",
|
||||
"path": "golang.org/x/crypto/ed25519/internal/edwards25519",
|
||||
"revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7",
|
||||
"revisionTime": "2017-03-17T13:29:17Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "IIhFTrLlmlc6lEFSitqi4aw2lw0=",
|
||||
"path": "golang.org/x/crypto/openpgp",
|
||||
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
|
||||
"revisionTime": "2017-02-08T20:51:15Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "olOKkhrdkYQHZ0lf1orrFQPQrv4=",
|
||||
"path": "golang.org/x/crypto/openpgp/armor",
|
||||
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
|
||||
"revisionTime": "2017-02-08T20:51:15Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "eo/KtdjieJQXH7Qy+faXFcF70ME=",
|
||||
"path": "golang.org/x/crypto/openpgp/elgamal",
|
||||
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
|
||||
"revisionTime": "2017-02-08T20:51:15Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "rlxVSaGgqdAgwblsErxTxIfuGfg=",
|
||||
"path": "golang.org/x/crypto/openpgp/errors",
|
||||
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
|
||||
"revisionTime": "2017-02-08T20:51:15Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "LWdaR8Q9yn6eBCcnGl0HvJRDUBE=",
|
||||
"path": "golang.org/x/crypto/openpgp/packet",
|
||||
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
|
||||
"revisionTime": "2017-02-08T20:51:15Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "s2qT4UwvzBSkzXuiuMkowif1Olw=",
|
||||
"path": "golang.org/x/crypto/openpgp/s2k",
|
||||
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
|
||||
"revisionTime": "2017-02-08T20:51:15Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "1MGpGDQqnUoRpv7VEcQrXOBydXE=",
|
||||
"path": "golang.org/x/crypto/pbkdf2",
|
||||
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
|
||||
"revisionTime": "2017-02-08T20:51:15Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "y/oIaxq2d3WPizRZfVjo8RCRYTU=",
|
||||
"path": "golang.org/x/crypto/ripemd160",
|
||||
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
|
||||
"revisionTime": "2017-02-08T20:51:15Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "E8pDMGySfy5Mw+jzXOkOxo35bww=",
|
||||
"path": "golang.org/x/crypto/scrypt",
|
||||
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
|
||||
"revisionTime": "2017-02-08T20:51:15Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "fsrFs762jlaILyqqQImS1GfvIvw=",
|
||||
"checksumSHA1": "8sVsMTphul+B0sI0qAv4TE1ZxUk=",
|
||||
"path": "golang.org/x/crypto/ssh",
|
||||
"revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7",
|
||||
"revisionTime": "2017-03-17T13:29:17Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "xiderUuvye8Kpn7yX3niiJg32bE=",
|
||||
"checksumSHA1": "ZaU56svwLgiJD0y8JOB3+/mpYBA=",
|
||||
"path": "golang.org/x/crypto/ssh/terminal",
|
||||
"revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7",
|
||||
"revisionTime": "2017-03-17T13:29:17Z"
|
||||
"revision": "c7af5bf2638a1164f2eb5467c39c6cffbd13a02e",
|
||||
"revisionTime": "2017-04-25T18:31:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Y+HGqEkYM15ir+J93MEaHdyFy0c=",
|
||||
|
@ -58,15 +58,19 @@ func TestDBKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func generateEnvelope(t *testing.T) *whisper.Envelope {
|
||||
h := crypto.Keccak256Hash([]byte("test sample data"))
|
||||
params := &whisper.MessageParams{
|
||||
KeySym: []byte("test key"),
|
||||
KeySym: h[:],
|
||||
Topic: whisper.TopicType{},
|
||||
Payload: []byte("test payload"),
|
||||
PoW: powRequirement,
|
||||
WorkTime: 2,
|
||||
}
|
||||
|
||||
msg := whisper.NewSentMessage(params)
|
||||
msg, err := whisper.NewSentMessage(params)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||
}
|
||||
env, err := msg.Wrap(params)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to wrap with seed %d: %s.", seed, err)
|
||||
@ -188,7 +192,10 @@ func createRequest(t *testing.T, p *ServerTestParams) *whisper.Envelope {
|
||||
Src: p.key,
|
||||
}
|
||||
|
||||
msg := whisper.NewSentMessage(params)
|
||||
msg, err := whisper.NewSentMessage(params)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||
}
|
||||
env, err := msg.Wrap(params)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to wrap with seed %d: %s.", seed, err)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user