mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
Merge branch 'master' into add-password-to-act
This commit is contained in:
commit
bec7e9de0d
41 changed files with 641 additions and 376 deletions
4
.github/no-response.yml
vendored
4
.github/no-response.yml
vendored
|
|
@ -7,5 +7,5 @@ closeComment: >
|
||||||
This issue has been automatically closed because there has been no response
|
This issue has been automatically closed because there has been no response
|
||||||
to our request for more information from the original author. With only the
|
to our request for more information from the original author. With only the
|
||||||
information that is currently in the issue, we don't have enough information
|
information that is currently in the issue, we don't have enough information
|
||||||
to take action. Please reach out if you have or find the answers we need so
|
to take action. Please reach out if you have more relevant information or
|
||||||
that we can investigate further.
|
answers to our questions so that we can investigate further.
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,17 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
maxUint256 = big.NewInt(0).Add(
|
||||||
|
big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), nil),
|
||||||
|
big.NewInt(-1))
|
||||||
|
maxInt256 = big.NewInt(0).Add(
|
||||||
|
big.NewInt(0).Exp(big.NewInt(2), big.NewInt(255), nil),
|
||||||
|
big.NewInt(-1))
|
||||||
|
)
|
||||||
|
|
||||||
// reads the integer based on its kind
|
// reads the integer based on its kind
|
||||||
func readInteger(kind reflect.Kind, b []byte) interface{} {
|
func readInteger(typ byte, kind reflect.Kind, b []byte) interface{} {
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Uint8:
|
case reflect.Uint8:
|
||||||
return b[len(b)-1]
|
return b[len(b)-1]
|
||||||
|
|
@ -45,7 +54,20 @@ func readInteger(kind reflect.Kind, b []byte) interface{} {
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
|
return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
|
||||||
default:
|
default:
|
||||||
return new(big.Int).SetBytes(b)
|
// the only case lefts for integer is int256/uint256.
|
||||||
|
// big.SetBytes can't tell if a number is negative, positive on itself.
|
||||||
|
// On EVM, if the returned number > max int256, it is negative.
|
||||||
|
ret := new(big.Int).SetBytes(b)
|
||||||
|
if typ == UintTy {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret.Cmp(maxInt256) > 0 {
|
||||||
|
ret.Add(maxUint256, big.NewInt(0).Neg(ret))
|
||||||
|
ret.Add(ret, big.NewInt(1))
|
||||||
|
ret.Neg(ret)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,7 +201,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||||
case StringTy: // variable arrays are written at the end of the return bytes
|
case StringTy: // variable arrays are written at the end of the return bytes
|
||||||
return string(output[begin : begin+end]), nil
|
return string(output[begin : begin+end]), nil
|
||||||
case IntTy, UintTy:
|
case IntTy, UintTy:
|
||||||
return readInteger(t.Kind, returnOutput), nil
|
return readInteger(t.T, t.Kind, returnOutput), nil
|
||||||
case BoolTy:
|
case BoolTy:
|
||||||
return readBool(returnOutput)
|
return readBool(returnOutput)
|
||||||
case AddressTy:
|
case AddressTy:
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,11 @@ var unpackTests = []unpackTest{
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
want: big.NewInt(1),
|
want: big.NewInt(1),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int256"}]`,
|
||||||
|
enc: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
want: big.NewInt(-1),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
def: `[{"type": "address"}]`,
|
def: `[{"type": "address"}]`,
|
||||||
enc: "0000000000000000000000000100000000000000000000000000000000000000",
|
enc: "0000000000000000000000000100000000000000000000000000000000000000",
|
||||||
|
|
|
||||||
|
|
@ -21,21 +21,33 @@ Private key information can be printed by using the `--private` flag;
|
||||||
make sure to use this feature with great caution!
|
make sure to use this feature with great caution!
|
||||||
|
|
||||||
|
|
||||||
### `ethkey sign <keyfile> <message/file>`
|
### `ethkey signmessage <keyfile> <message/file>`
|
||||||
|
|
||||||
Sign the message with a keyfile.
|
Sign the message with a keyfile.
|
||||||
It is possible to refer to a file containing the message.
|
It is possible to refer to a file containing the message.
|
||||||
|
To sign a message contained in a file, use the `--msgfile` flag.
|
||||||
|
|
||||||
|
|
||||||
### `ethkey verify <address> <signature> <message/file>`
|
### `ethkey verifymessage <address> <signature> <message/file>`
|
||||||
|
|
||||||
Verify the signature of the message.
|
Verify the signature of the message.
|
||||||
It is possible to refer to a file containing the message.
|
It is possible to refer to a file containing the message.
|
||||||
|
To sign a message contained in a file, use the --msgfile flag.
|
||||||
|
|
||||||
|
|
||||||
|
### `ethkey changepassphrase <keyfile>`
|
||||||
|
|
||||||
|
Change the passphrase of a keyfile.
|
||||||
|
use the `--newpasswordfile` to point to the new password file.
|
||||||
|
|
||||||
|
|
||||||
## Passphrases
|
## Passphrases
|
||||||
|
|
||||||
For every command that uses a keyfile, you will be prompted to provide the
|
For every command that uses a keyfile, you will be prompted to provide the
|
||||||
passphrase for decrypting the keyfile. To avoid this message, it is possible
|
passphrase for decrypting the keyfile. To avoid this message, it is possible
|
||||||
to pass the passphrase by using the `--passphrase` flag pointing to a file that
|
to pass the passphrase by using the `--passwordfile` flag pointing to a file that
|
||||||
contains the passphrase.
|
contains the passphrase.
|
||||||
|
|
||||||
|
## JSON
|
||||||
|
|
||||||
|
In case you need to output the result in a JSON format, you shall by using the `--json` flag.
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,8 @@ func main() {
|
||||||
if blob, err = ioutil.ReadFile(*accPassFlag); err != nil {
|
if blob, err = ioutil.ReadFile(*accPassFlag); err != nil {
|
||||||
log.Crit("Failed to read account password contents", "file", *accPassFlag, "err", err)
|
log.Crit("Failed to read account password contents", "file", *accPassFlag, "err", err)
|
||||||
}
|
}
|
||||||
pass := string(blob)
|
// Delete trailing newline in password
|
||||||
|
pass := strings.TrimSuffix(string(blob), "\n")
|
||||||
|
|
||||||
ks := keystore.NewKeyStore(filepath.Join(os.Getenv("HOME"), ".faucet", "keys"), keystore.StandardScryptN, keystore.StandardScryptP)
|
ks := keystore.NewKeyStore(filepath.Join(os.Getenv("HOME"), ".faucet", "keys"), keystore.StandardScryptN, keystore.StandardScryptP)
|
||||||
if blob, err = ioutil.ReadFile(*accJSONFlag); err != nil {
|
if blob, err = ioutil.ReadFile(*accJSONFlag); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,9 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
if ctx.GlobalIsSet(utils.WhisperMinPOWFlag.Name) {
|
if ctx.GlobalIsSet(utils.WhisperMinPOWFlag.Name) {
|
||||||
cfg.Shh.MinimumAcceptedPOW = ctx.Float64(utils.WhisperMinPOWFlag.Name)
|
cfg.Shh.MinimumAcceptedPOW = ctx.Float64(utils.WhisperMinPOWFlag.Name)
|
||||||
}
|
}
|
||||||
|
if ctx.GlobalIsSet(utils.WhisperRestrictConnectionBetweenLightClientsFlag.Name) {
|
||||||
|
cfg.Shh.RestrictConnectionBetweenLightClients = true
|
||||||
|
}
|
||||||
utils.RegisterShhService(stack, &cfg.Shh)
|
utils.RegisterShhService(stack, &cfg.Shh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,7 @@ var (
|
||||||
utils.WhisperEnabledFlag,
|
utils.WhisperEnabledFlag,
|
||||||
utils.WhisperMaxMessageSizeFlag,
|
utils.WhisperMaxMessageSizeFlag,
|
||||||
utils.WhisperMinPOWFlag,
|
utils.WhisperMinPOWFlag,
|
||||||
|
utils.WhisperRestrictConnectionBetweenLightClientsFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
metricsFlags = []cli.Flag{
|
metricsFlags = []cli.Flag{
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/swarm/api"
|
"github.com/ethereum/go-ethereum/swarm/api"
|
||||||
|
|
@ -45,6 +46,8 @@ const (
|
||||||
data = "notsorandomdata"
|
data = "notsorandomdata"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var DefaultCurve = crypto.S256()
|
||||||
|
|
||||||
// TestAccessPassword tests for the correct creation of an ACT manifest protected by a password.
|
// TestAccessPassword tests for the correct creation of an ACT manifest protected by a password.
|
||||||
// The test creates bogus content, uploads it encrypted, then creates the wrapping manifest with the Access entry
|
// The test creates bogus content, uploads it encrypted, then creates the wrapping manifest with the Access entry
|
||||||
// The parties participating - node (publisher), uploads to second node then disappears. Content which was uploaded
|
// The parties participating - node (publisher), uploads to second node then disappears. Content which was uploaded
|
||||||
|
|
@ -132,7 +135,9 @@ func TestAccessPassword(t *testing.T) {
|
||||||
if a.KdfParams == nil {
|
if a.KdfParams == nil {
|
||||||
t.Fatal("manifest access kdf params is nil")
|
t.Fatal("manifest access kdf params is nil")
|
||||||
}
|
}
|
||||||
|
if a.Publisher != "" {
|
||||||
|
t.Fatal("should be empty")
|
||||||
|
}
|
||||||
client := swarm.NewClient(cluster.Nodes[0].URL)
|
client := swarm.NewClient(cluster.Nodes[0].URL)
|
||||||
|
|
||||||
hash, err := client.UploadManifest(&m, false)
|
hash, err := client.UploadManifest(&m, false)
|
||||||
|
|
@ -205,7 +210,7 @@ func TestAccessPassword(t *testing.T) {
|
||||||
// the test will fail if the proxy's given private key is not granted on the ACT.
|
// the test will fail if the proxy's given private key is not granted on the ACT.
|
||||||
func TestAccessPK(t *testing.T) {
|
func TestAccessPK(t *testing.T) {
|
||||||
// Setup Swarm and upload a test file to it
|
// Setup Swarm and upload a test file to it
|
||||||
cluster := newTestCluster(t, 1)
|
cluster := newTestCluster(t, 2)
|
||||||
defer cluster.Shutdown()
|
defer cluster.Shutdown()
|
||||||
|
|
||||||
dataFilename := testutil.TempFileWithContent(t, data)
|
dataFilename := testutil.TempFileWithContent(t, data)
|
||||||
|
|
@ -263,6 +268,20 @@ func TestAccessPK(t *testing.T) {
|
||||||
t.Fatalf("stdout not matched")
|
t.Fatalf("stdout not matched")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//get the public key from the publisher directory
|
||||||
|
publicKeyFromDataDir := runSwarm(t,
|
||||||
|
"--bzzaccount",
|
||||||
|
publisherAccount.Address.String(),
|
||||||
|
"--password",
|
||||||
|
passwordFilename,
|
||||||
|
"--datadir",
|
||||||
|
publisherDir,
|
||||||
|
"print-keys",
|
||||||
|
"--compressed",
|
||||||
|
)
|
||||||
|
_, publicKeyString := publicKeyFromDataDir.ExpectRegexp(".+")
|
||||||
|
publicKeyFromDataDir.ExpectExit()
|
||||||
|
pkComp := strings.Split(publicKeyString[0], "=")[1]
|
||||||
var m api.Manifest
|
var m api.Manifest
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(matches[0]), &m)
|
err = json.Unmarshal([]byte(matches[0]), &m)
|
||||||
|
|
@ -296,7 +315,9 @@ func TestAccessPK(t *testing.T) {
|
||||||
if a.KdfParams != nil {
|
if a.KdfParams != nil {
|
||||||
t.Fatal("manifest access kdf params should be nil")
|
t.Fatal("manifest access kdf params should be nil")
|
||||||
}
|
}
|
||||||
|
if a.Publisher != pkComp {
|
||||||
|
t.Fatal("publisher key did not match")
|
||||||
|
}
|
||||||
client := swarm.NewClient(cluster.Nodes[0].URL)
|
client := swarm.NewClient(cluster.Nodes[0].URL)
|
||||||
|
|
||||||
hash, err := client.UploadManifest(&m, false)
|
hash, err := client.UploadManifest(&m, false)
|
||||||
|
|
@ -323,12 +344,22 @@ func TestAccessPK(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestAccessACT tests the creation of the ACT manifest end-to-end, without any bogus entries (i.e. default scenario = 3 nodes 1 unauthorized)
|
||||||
|
func TestAccessACT(t *testing.T) {
|
||||||
|
testAccessACT(t, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAccessACTScale tests the creation of the ACT manifest end-to-end, with 1000 bogus entries (i.e. 1000 EC keys + default scenario = 3 nodes 1 unauthorized = 1003 keys in the ACT manifest)
|
||||||
|
func TestAccessACTScale(t *testing.T) {
|
||||||
|
testAccessACT(t, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
// TestAccessACT tests the e2e creation, uploading and downloading of an ACT access control with both EC keys AND password protection
|
// TestAccessACT tests the e2e creation, uploading and downloading of an ACT access control with both EC keys AND password protection
|
||||||
// the test fires up a 3 node cluster, then randomly picks 2 nodes which will be acting as grantees to the data
|
// the test fires up a 3 node cluster, then randomly picks 2 nodes which will be acting as grantees to the data
|
||||||
// set and also protects the ACT with a password. the third node should fail decoding the reference as it will not be granted access.
|
// set and also protects the ACT with a password. the third node should fail decoding the reference as it will not be granted access.
|
||||||
// the third node then then tries to download using a correct password (and succeeds) then uses a wrong password and fails.
|
// the third node then then tries to download using a correct password (and succeeds) then uses a wrong password and fails.
|
||||||
// the publisher uploads through one of the nodes then disappears.
|
// the publisher uploads through one of the nodes then disappears.
|
||||||
func TestAccessACT(t *testing.T) {
|
func testAccessACT(t *testing.T, bogusEntries int) {
|
||||||
// Setup Swarm and upload a test file to it
|
// Setup Swarm and upload a test file to it
|
||||||
const clusterSize = 3
|
const clusterSize = 3
|
||||||
cluster := newTestCluster(t, clusterSize)
|
cluster := newTestCluster(t, clusterSize)
|
||||||
|
|
@ -367,6 +398,23 @@ func TestAccessACT(t *testing.T) {
|
||||||
grantees = append(grantees, hex.EncodeToString(granteePubKey))
|
grantees = append(grantees, hex.EncodeToString(granteePubKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bogusEntries > 0 {
|
||||||
|
bogusGrantees := []string{}
|
||||||
|
|
||||||
|
for i := 0; i < bogusEntries; i++ {
|
||||||
|
prv, err := ecies.GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
bogusGrantees = append(bogusGrantees, hex.EncodeToString(crypto.CompressPubkey(&prv.ExportECDSA().PublicKey)))
|
||||||
|
}
|
||||||
|
r2 := gorand.New(gorand.NewSource(time.Now().UnixNano()))
|
||||||
|
for i := 0; i < len(grantees); i++ {
|
||||||
|
insertAtIdx := r2.Intn(len(bogusGrantees))
|
||||||
|
bogusGrantees = append(bogusGrantees[:insertAtIdx], append([]string{grantees[i]}, bogusGrantees[insertAtIdx:]...)...)
|
||||||
|
}
|
||||||
|
grantees = bogusGrantees
|
||||||
|
}
|
||||||
granteesPubkeyListFile := testutil.TempFileWithContent(t, strings.Join(grantees, "\n"))
|
granteesPubkeyListFile := testutil.TempFileWithContent(t, strings.Join(grantees, "\n"))
|
||||||
defer os.RemoveAll(granteesPubkeyListFile)
|
defer os.RemoveAll(granteesPubkeyListFile)
|
||||||
|
|
||||||
|
|
@ -406,6 +454,22 @@ func TestAccessACT(t *testing.T) {
|
||||||
if len(matches) == 0 {
|
if len(matches) == 0 {
|
||||||
t.Fatalf("stdout not matched")
|
t.Fatalf("stdout not matched")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//get the public key from the publisher directory
|
||||||
|
publicKeyFromDataDir := runSwarm(t,
|
||||||
|
"--bzzaccount",
|
||||||
|
publisherAccount.Address.String(),
|
||||||
|
"--password",
|
||||||
|
passwordFilename,
|
||||||
|
"--datadir",
|
||||||
|
publisherDir,
|
||||||
|
"print-keys",
|
||||||
|
"--compressed",
|
||||||
|
)
|
||||||
|
_, publicKeyString := publicKeyFromDataDir.ExpectRegexp(".+")
|
||||||
|
publicKeyFromDataDir.ExpectExit()
|
||||||
|
pkComp := strings.Split(publicKeyString[0], "=")[1]
|
||||||
|
|
||||||
hash := matches[0]
|
hash := matches[0]
|
||||||
m, _, err := client.DownloadManifest(hash)
|
m, _, err := client.DownloadManifest(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -436,6 +500,9 @@ func TestAccessACT(t *testing.T) {
|
||||||
t.Fatalf(`got salt with length %v, expected not less the 32 bytes`, len(a.Salt))
|
t.Fatalf(`got salt with length %v, expected not less the 32 bytes`, len(a.Salt))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if a.Publisher != pkComp {
|
||||||
|
t.Fatal("publisher key did not match")
|
||||||
|
}
|
||||||
httpClient := &http.Client{}
|
httpClient := &http.Client{}
|
||||||
|
|
||||||
// all nodes except the skipped node should be able to decrypt the content
|
// all nodes except the skipped node should be able to decrypt the content
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -208,6 +209,10 @@ var (
|
||||||
Name: "data",
|
Name: "data",
|
||||||
Usage: "Initializes the resource with the given hex-encoded data. Data must be prefixed by 0x",
|
Usage: "Initializes the resource with the given hex-encoded data. Data must be prefixed by 0x",
|
||||||
}
|
}
|
||||||
|
SwarmCompressedFlag = cli.BoolFlag{
|
||||||
|
Name: "compressed",
|
||||||
|
Usage: "Prints encryption keys in compressed form",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
//declare a few constant error messages, useful for later error check comparisons in test
|
//declare a few constant error messages, useful for later error check comparisons in test
|
||||||
|
|
@ -252,6 +257,14 @@ func init() {
|
||||||
Usage: "Print version numbers",
|
Usage: "Print version numbers",
|
||||||
Description: "The output of this command is supposed to be machine-readable",
|
Description: "The output of this command is supposed to be machine-readable",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Action: keys,
|
||||||
|
CustomHelpTemplate: helpTemplate,
|
||||||
|
Name: "print-keys",
|
||||||
|
Flags: []cli.Flag{SwarmCompressedFlag},
|
||||||
|
Usage: "Print public key information",
|
||||||
|
Description: "The output of this command is supposed to be machine-readable",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Action: upload,
|
Action: upload,
|
||||||
CustomHelpTemplate: helpTemplate,
|
CustomHelpTemplate: helpTemplate,
|
||||||
|
|
@ -581,6 +594,17 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func keys(ctx *cli.Context) error {
|
||||||
|
privateKey := getPrivKey(ctx)
|
||||||
|
pub := hex.EncodeToString(crypto.FromECDSAPub(&privateKey.PublicKey))
|
||||||
|
pubCompressed := hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey))
|
||||||
|
if !ctx.Bool(SwarmCompressedFlag.Name) {
|
||||||
|
fmt.Println(fmt.Sprintf("publicKey=%s", pub))
|
||||||
|
}
|
||||||
|
fmt.Println(fmt.Sprintf("publicKeyCompressed=%s", pubCompressed))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func version(ctx *cli.Context) error {
|
func version(ctx *cli.Context) error {
|
||||||
fmt.Println(strings.Title(clientIdentifier))
|
fmt.Println(strings.Title(clientIdentifier))
|
||||||
fmt.Println("Version:", sv.VersionWithMeta)
|
fmt.Println("Version:", sv.VersionWithMeta)
|
||||||
|
|
|
||||||
|
|
@ -567,6 +567,10 @@ var (
|
||||||
Usage: "Minimum POW accepted",
|
Usage: "Minimum POW accepted",
|
||||||
Value: whisper.DefaultMinimumPoW,
|
Value: whisper.DefaultMinimumPoW,
|
||||||
}
|
}
|
||||||
|
WhisperRestrictConnectionBetweenLightClientsFlag = cli.BoolFlag{
|
||||||
|
Name: "shh.restrict-light",
|
||||||
|
Usage: "Restrict connection between two whisper light clients",
|
||||||
|
}
|
||||||
|
|
||||||
// Metrics flags
|
// Metrics flags
|
||||||
MetricsEnabledFlag = cli.BoolFlag{
|
MetricsEnabledFlag = cli.BoolFlag{
|
||||||
|
|
@ -1099,6 +1103,9 @@ func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) {
|
||||||
if ctx.GlobalIsSet(WhisperMinPOWFlag.Name) {
|
if ctx.GlobalIsSet(WhisperMinPOWFlag.Name) {
|
||||||
cfg.MinimumAcceptedPOW = ctx.GlobalFloat64(WhisperMinPOWFlag.Name)
|
cfg.MinimumAcceptedPOW = ctx.GlobalFloat64(WhisperMinPOWFlag.Name)
|
||||||
}
|
}
|
||||||
|
if ctx.GlobalIsSet(WhisperRestrictConnectionBetweenLightClientsFlag.Name) {
|
||||||
|
cfg.RestrictConnectionBetweenLightClients = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEthConfig applies eth-related command line flags to the config.
|
// SetEthConfig applies eth-related command line flags to the config.
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ import (
|
||||||
const (
|
const (
|
||||||
// HashLength is the expected length of the hash
|
// HashLength is the expected length of the hash
|
||||||
HashLength = 32
|
HashLength = 32
|
||||||
// AddressLength is the expected length of the adddress
|
// AddressLength is the expected length of the address
|
||||||
AddressLength = 20
|
AddressLength = 20
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ type Engine interface {
|
||||||
// the result into the given channel.
|
// the result into the given channel.
|
||||||
//
|
//
|
||||||
// Note, the method returns immediately and will send the result async. More
|
// Note, the method returns immediately and will send the result async. More
|
||||||
// than one result may also be returned depending on the consensus algorothm.
|
// than one result may also be returned depending on the consensus algorithm.
|
||||||
Seal(chain ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
|
Seal(chain ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
|
||||||
|
|
||||||
// SealHash returns the hash of a block prior to it being sealed.
|
// SealHash returns the hash of a block prior to it being sealed.
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,18 @@ func TestRemoteNotify(t *testing.T) {
|
||||||
|
|
||||||
go server.Serve(listener)
|
go server.Serve(listener)
|
||||||
|
|
||||||
|
// Wait for server to start listening
|
||||||
|
var tries int
|
||||||
|
for tries = 0; tries < 10; tries++ {
|
||||||
|
conn, _ := net.DialTimeout("tcp", listener.Addr().String(), 1*time.Second)
|
||||||
|
if conn != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tries == 10 {
|
||||||
|
t.Fatal("tcp listener not ready for more than 10 seconds")
|
||||||
|
}
|
||||||
|
|
||||||
// Create the custom ethash engine
|
// Create the custom ethash engine
|
||||||
ethash := NewTester([]string{"http://" + listener.Addr().String()}, false)
|
ethash := NewTester([]string{"http://" + listener.Addr().String()}, false)
|
||||||
defer ethash.Close()
|
defer ethash.Close()
|
||||||
|
|
@ -61,7 +73,7 @@ func TestRemoteNotify(t *testing.T) {
|
||||||
if want := common.BytesToHash(target.Bytes()).Hex(); work[2] != want {
|
if want := common.BytesToHash(target.Bytes()).Hex(); work[2] != want {
|
||||||
t.Errorf("work packet target mismatch: have %s, want %s", work[2], want)
|
t.Errorf("work packet target mismatch: have %s, want %s", work[2], want)
|
||||||
}
|
}
|
||||||
case <-time.After(time.Second):
|
case <-time.After(3 * time.Second):
|
||||||
t.Fatalf("notification timed out")
|
t.Fatalf("notification timed out")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +120,7 @@ func TestRemoteMultiNotify(t *testing.T) {
|
||||||
for i := 0; i < cap(sink); i++ {
|
for i := 0; i < cap(sink); i++ {
|
||||||
select {
|
select {
|
||||||
case <-sink:
|
case <-sink:
|
||||||
case <-time.After(250 * time.Millisecond):
|
case <-time.After(3 * time.Second):
|
||||||
t.Fatalf("notification %d timed out", i)
|
t.Fatalf("notification %d timed out", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/mclock"
|
"github.com/ethereum/go-ethereum/common/mclock"
|
||||||
|
"github.com/ethereum/go-ethereum/common/prque"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
|
@ -43,7 +44,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/hashicorp/golang-lru"
|
"github.com/hashicorp/golang-lru"
|
||||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -151,7 +151,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
||||||
chainConfig: chainConfig,
|
chainConfig: chainConfig,
|
||||||
cacheConfig: cacheConfig,
|
cacheConfig: cacheConfig,
|
||||||
db: db,
|
db: db,
|
||||||
triegc: prque.New(),
|
triegc: prque.New(nil),
|
||||||
stateCache: state.NewDatabase(db),
|
stateCache: state.NewDatabase(db),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
bodyCache: bodyCache,
|
bodyCache: bodyCache,
|
||||||
|
|
@ -915,7 +915,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
||||||
} else {
|
} else {
|
||||||
// Full but not archive node, do proper garbage collection
|
// Full but not archive node, do proper garbage collection
|
||||||
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
|
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
|
||||||
bc.triegc.Push(root, -float32(block.NumberU64()))
|
bc.triegc.Push(root, -int64(block.NumberU64()))
|
||||||
|
|
||||||
if current := block.NumberU64(); current > triesInMemory {
|
if current := block.NumberU64(); current > triesInMemory {
|
||||||
// If we exceeded our memory allowance, flush matured singleton nodes to disk
|
// If we exceeded our memory allowance, flush matured singleton nodes to disk
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
||||||
*usedGas += gas
|
*usedGas += gas
|
||||||
|
|
||||||
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
||||||
// based on the eip phase, we're passing wether the root touch-delete accounts.
|
// based on the eip phase, we're passing whether the root touch-delete accounts.
|
||||||
receipt := types.NewReceipt(root, failed, *usedGas)
|
receipt := types.NewReceipt(root, failed, *usedGas)
|
||||||
receipt.TxHash = tx.Hash()
|
receipt.TxHash = tx.Hash()
|
||||||
receipt.GasUsed = gas
|
receipt.GasUsed = gas
|
||||||
|
|
|
||||||
|
|
@ -26,13 +26,13 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/prque"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -987,11 +987,11 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) {
|
||||||
if pending > pool.config.GlobalSlots {
|
if pending > pool.config.GlobalSlots {
|
||||||
pendingBeforeCap := pending
|
pendingBeforeCap := pending
|
||||||
// Assemble a spam order to penalize large transactors first
|
// Assemble a spam order to penalize large transactors first
|
||||||
spammers := prque.New()
|
spammers := prque.New(nil)
|
||||||
for addr, list := range pool.pending {
|
for addr, list := range pool.pending {
|
||||||
// Only evict transactions from high rollers
|
// Only evict transactions from high rollers
|
||||||
if !pool.locals.contains(addr) && uint64(list.Len()) > pool.config.AccountSlots {
|
if !pool.locals.contains(addr) && uint64(list.Len()) > pool.config.AccountSlots {
|
||||||
spammers.Push(addr, float32(list.Len()))
|
spammers.Push(addr, int64(list.Len()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Gradually drop transactions from offenders
|
// Gradually drop transactions from offenders
|
||||||
|
|
|
||||||
|
|
@ -355,7 +355,7 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *
|
||||||
defer interpreter.intPool.put(shift) // First operand back into the pool
|
defer interpreter.intPool.put(shift) // First operand back into the pool
|
||||||
|
|
||||||
if shift.Cmp(common.Big256) >= 0 {
|
if shift.Cmp(common.Big256) >= 0 {
|
||||||
if value.Sign() > 0 {
|
if value.Sign() >= 0 {
|
||||||
value.SetUint64(0)
|
value.SetUint64(0)
|
||||||
} else {
|
} else {
|
||||||
value.SetInt64(-1)
|
value.SetInt64(-1)
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/prque"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -105,11 +105,11 @@ func newQueue() *queue {
|
||||||
headerPendPool: make(map[string]*fetchRequest),
|
headerPendPool: make(map[string]*fetchRequest),
|
||||||
headerContCh: make(chan bool),
|
headerContCh: make(chan bool),
|
||||||
blockTaskPool: make(map[common.Hash]*types.Header),
|
blockTaskPool: make(map[common.Hash]*types.Header),
|
||||||
blockTaskQueue: prque.New(),
|
blockTaskQueue: prque.New(nil),
|
||||||
blockPendPool: make(map[string]*fetchRequest),
|
blockPendPool: make(map[string]*fetchRequest),
|
||||||
blockDonePool: make(map[common.Hash]struct{}),
|
blockDonePool: make(map[common.Hash]struct{}),
|
||||||
receiptTaskPool: make(map[common.Hash]*types.Header),
|
receiptTaskPool: make(map[common.Hash]*types.Header),
|
||||||
receiptTaskQueue: prque.New(),
|
receiptTaskQueue: prque.New(nil),
|
||||||
receiptPendPool: make(map[string]*fetchRequest),
|
receiptPendPool: make(map[string]*fetchRequest),
|
||||||
receiptDonePool: make(map[common.Hash]struct{}),
|
receiptDonePool: make(map[common.Hash]struct{}),
|
||||||
resultCache: make([]*fetchResult, blockCacheItems),
|
resultCache: make([]*fetchResult, blockCacheItems),
|
||||||
|
|
@ -277,7 +277,7 @@ func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) {
|
||||||
}
|
}
|
||||||
// Schedule all the header retrieval tasks for the skeleton assembly
|
// Schedule all the header retrieval tasks for the skeleton assembly
|
||||||
q.headerTaskPool = make(map[uint64]*types.Header)
|
q.headerTaskPool = make(map[uint64]*types.Header)
|
||||||
q.headerTaskQueue = prque.New()
|
q.headerTaskQueue = prque.New(nil)
|
||||||
q.headerPeerMiss = make(map[string]map[uint64]struct{}) // Reset availability to correct invalid chains
|
q.headerPeerMiss = make(map[string]map[uint64]struct{}) // Reset availability to correct invalid chains
|
||||||
q.headerResults = make([]*types.Header, len(skeleton)*MaxHeaderFetch)
|
q.headerResults = make([]*types.Header, len(skeleton)*MaxHeaderFetch)
|
||||||
q.headerProced = 0
|
q.headerProced = 0
|
||||||
|
|
@ -288,7 +288,7 @@ func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) {
|
||||||
index := from + uint64(i*MaxHeaderFetch)
|
index := from + uint64(i*MaxHeaderFetch)
|
||||||
|
|
||||||
q.headerTaskPool[index] = header
|
q.headerTaskPool[index] = header
|
||||||
q.headerTaskQueue.Push(index, -float32(index))
|
q.headerTaskQueue.Push(index, -int64(index))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,11 +334,11 @@ func (q *queue) Schedule(headers []*types.Header, from uint64) []*types.Header {
|
||||||
}
|
}
|
||||||
// Queue the header for content retrieval
|
// Queue the header for content retrieval
|
||||||
q.blockTaskPool[hash] = header
|
q.blockTaskPool[hash] = header
|
||||||
q.blockTaskQueue.Push(header, -float32(header.Number.Uint64()))
|
q.blockTaskQueue.Push(header, -int64(header.Number.Uint64()))
|
||||||
|
|
||||||
if q.mode == FastSync {
|
if q.mode == FastSync {
|
||||||
q.receiptTaskPool[hash] = header
|
q.receiptTaskPool[hash] = header
|
||||||
q.receiptTaskQueue.Push(header, -float32(header.Number.Uint64()))
|
q.receiptTaskQueue.Push(header, -int64(header.Number.Uint64()))
|
||||||
}
|
}
|
||||||
inserts = append(inserts, header)
|
inserts = append(inserts, header)
|
||||||
q.headerHead = hash
|
q.headerHead = hash
|
||||||
|
|
@ -436,7 +436,7 @@ func (q *queue) ReserveHeaders(p *peerConnection, count int) *fetchRequest {
|
||||||
}
|
}
|
||||||
// Merge all the skipped batches back
|
// Merge all the skipped batches back
|
||||||
for _, from := range skip {
|
for _, from := range skip {
|
||||||
q.headerTaskQueue.Push(from, -float32(from))
|
q.headerTaskQueue.Push(from, -int64(from))
|
||||||
}
|
}
|
||||||
// Assemble and return the block download request
|
// Assemble and return the block download request
|
||||||
if send == 0 {
|
if send == 0 {
|
||||||
|
|
@ -542,7 +542,7 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common
|
||||||
}
|
}
|
||||||
// Merge all the skipped headers back
|
// Merge all the skipped headers back
|
||||||
for _, header := range skip {
|
for _, header := range skip {
|
||||||
taskQueue.Push(header, -float32(header.Number.Uint64()))
|
taskQueue.Push(header, -int64(header.Number.Uint64()))
|
||||||
}
|
}
|
||||||
if progress {
|
if progress {
|
||||||
// Wake WaitResults, resultCache was modified
|
// Wake WaitResults, resultCache was modified
|
||||||
|
|
@ -585,10 +585,10 @@ func (q *queue) cancel(request *fetchRequest, taskQueue *prque.Prque, pendPool m
|
||||||
defer q.lock.Unlock()
|
defer q.lock.Unlock()
|
||||||
|
|
||||||
if request.From > 0 {
|
if request.From > 0 {
|
||||||
taskQueue.Push(request.From, -float32(request.From))
|
taskQueue.Push(request.From, -int64(request.From))
|
||||||
}
|
}
|
||||||
for _, header := range request.Headers {
|
for _, header := range request.Headers {
|
||||||
taskQueue.Push(header, -float32(header.Number.Uint64()))
|
taskQueue.Push(header, -int64(header.Number.Uint64()))
|
||||||
}
|
}
|
||||||
delete(pendPool, request.Peer.id)
|
delete(pendPool, request.Peer.id)
|
||||||
}
|
}
|
||||||
|
|
@ -602,13 +602,13 @@ func (q *queue) Revoke(peerID string) {
|
||||||
|
|
||||||
if request, ok := q.blockPendPool[peerID]; ok {
|
if request, ok := q.blockPendPool[peerID]; ok {
|
||||||
for _, header := range request.Headers {
|
for _, header := range request.Headers {
|
||||||
q.blockTaskQueue.Push(header, -float32(header.Number.Uint64()))
|
q.blockTaskQueue.Push(header, -int64(header.Number.Uint64()))
|
||||||
}
|
}
|
||||||
delete(q.blockPendPool, peerID)
|
delete(q.blockPendPool, peerID)
|
||||||
}
|
}
|
||||||
if request, ok := q.receiptPendPool[peerID]; ok {
|
if request, ok := q.receiptPendPool[peerID]; ok {
|
||||||
for _, header := range request.Headers {
|
for _, header := range request.Headers {
|
||||||
q.receiptTaskQueue.Push(header, -float32(header.Number.Uint64()))
|
q.receiptTaskQueue.Push(header, -int64(header.Number.Uint64()))
|
||||||
}
|
}
|
||||||
delete(q.receiptPendPool, peerID)
|
delete(q.receiptPendPool, peerID)
|
||||||
}
|
}
|
||||||
|
|
@ -657,10 +657,10 @@ func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest,
|
||||||
|
|
||||||
// Return any non satisfied requests to the pool
|
// Return any non satisfied requests to the pool
|
||||||
if request.From > 0 {
|
if request.From > 0 {
|
||||||
taskQueue.Push(request.From, -float32(request.From))
|
taskQueue.Push(request.From, -int64(request.From))
|
||||||
}
|
}
|
||||||
for _, header := range request.Headers {
|
for _, header := range request.Headers {
|
||||||
taskQueue.Push(header, -float32(header.Number.Uint64()))
|
taskQueue.Push(header, -int64(header.Number.Uint64()))
|
||||||
}
|
}
|
||||||
// Add the peer to the expiry report along the number of failed requests
|
// Add the peer to the expiry report along the number of failed requests
|
||||||
expiries[id] = len(request.Headers)
|
expiries[id] = len(request.Headers)
|
||||||
|
|
@ -731,7 +731,7 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh
|
||||||
}
|
}
|
||||||
miss[request.From] = struct{}{}
|
miss[request.From] = struct{}{}
|
||||||
|
|
||||||
q.headerTaskQueue.Push(request.From, -float32(request.From))
|
q.headerTaskQueue.Push(request.From, -int64(request.From))
|
||||||
return 0, errors.New("delivery not accepted")
|
return 0, errors.New("delivery not accepted")
|
||||||
}
|
}
|
||||||
// Clean up a successful fetch and try to deliver any sub-results
|
// Clean up a successful fetch and try to deliver any sub-results
|
||||||
|
|
@ -854,7 +854,7 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQ
|
||||||
// Return all failed or missing fetches to the queue
|
// Return all failed or missing fetches to the queue
|
||||||
for _, header := range request.Headers {
|
for _, header := range request.Headers {
|
||||||
if header != nil {
|
if header != nil {
|
||||||
taskQueue.Push(header, -float32(header.Number.Uint64()))
|
taskQueue.Push(header, -int64(header.Number.Uint64()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Wake up WaitResults
|
// Wake up WaitResults
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/prque"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -160,7 +160,7 @@ func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, broadcastBloc
|
||||||
fetching: make(map[common.Hash]*announce),
|
fetching: make(map[common.Hash]*announce),
|
||||||
fetched: make(map[common.Hash][]*announce),
|
fetched: make(map[common.Hash][]*announce),
|
||||||
completing: make(map[common.Hash]*announce),
|
completing: make(map[common.Hash]*announce),
|
||||||
queue: prque.New(),
|
queue: prque.New(nil),
|
||||||
queues: make(map[string]int),
|
queues: make(map[string]int),
|
||||||
queued: make(map[common.Hash]*inject),
|
queued: make(map[common.Hash]*inject),
|
||||||
getBlock: getBlock,
|
getBlock: getBlock,
|
||||||
|
|
@ -299,7 +299,7 @@ func (f *Fetcher) loop() {
|
||||||
// If too high up the chain or phase, continue later
|
// If too high up the chain or phase, continue later
|
||||||
number := op.block.NumberU64()
|
number := op.block.NumberU64()
|
||||||
if number > height+1 {
|
if number > height+1 {
|
||||||
f.queue.Push(op, -float32(number))
|
f.queue.Push(op, -int64(number))
|
||||||
if f.queueChangeHook != nil {
|
if f.queueChangeHook != nil {
|
||||||
f.queueChangeHook(hash, true)
|
f.queueChangeHook(hash, true)
|
||||||
}
|
}
|
||||||
|
|
@ -624,7 +624,7 @@ func (f *Fetcher) enqueue(peer string, block *types.Block) {
|
||||||
}
|
}
|
||||||
f.queues[peer] = count
|
f.queues[peer] = count
|
||||||
f.queued[hash] = op
|
f.queued[hash] = op
|
||||||
f.queue.Push(op, -float32(block.NumberU64()))
|
f.queue.Push(op, -int64(block.NumberU64()))
|
||||||
if f.queueChangeHook != nil {
|
if f.queueChangeHook != nil {
|
||||||
f.queueChangeHook(op.block.Hash(), true)
|
f.queueChangeHook(op.block.Hash(), true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,9 @@ func (ec *EthereumClient) SubscribeNewHead(ctx *Context, handler NewHeadHandler,
|
||||||
handler.OnNewHead(&Header{header})
|
handler.OnNewHead(&Header{header})
|
||||||
|
|
||||||
case err := <-rawSub.Err():
|
case err := <-rawSub.Err():
|
||||||
handler.OnError(err.Error())
|
if err != nil {
|
||||||
|
handler.OnError(err.Error())
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -227,7 +229,9 @@ func (ec *EthereumClient) SubscribeFilterLogs(ctx *Context, query *FilterQuery,
|
||||||
handler.OnFilterLogs(&Log{&log})
|
handler.OnFilterLogs(&Log{&log})
|
||||||
|
|
||||||
case err := <-rawSub.Err():
|
case err := <-rawSub.Err():
|
||||||
handler.OnError(err.Error())
|
if err != nil {
|
||||||
|
handler.OnError(err.Error())
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
195
mobile/shhclient.go
Normal file
195
mobile/shhclient.go
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
// 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/>.
|
||||||
|
|
||||||
|
// Contains a wrapper for the Whisper client.
|
||||||
|
|
||||||
|
package geth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/whisper/shhclient"
|
||||||
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WhisperClient provides access to the Ethereum APIs.
|
||||||
|
type WhisperClient struct {
|
||||||
|
client *shhclient.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWhisperClient connects a client to the given URL.
|
||||||
|
func NewWhisperClient(rawurl string) (client *WhisperClient, _ error) {
|
||||||
|
rawClient, err := shhclient.Dial(rawurl)
|
||||||
|
return &WhisperClient{rawClient}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersion returns the Whisper sub-protocol version.
|
||||||
|
func (wc *WhisperClient) GetVersion(ctx *Context) (version string, _ error) {
|
||||||
|
return wc.client.Version(ctx.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info returns diagnostic information about the whisper node.
|
||||||
|
func (wc *WhisperClient) GetInfo(ctx *Context) (info *Info, _ error) {
|
||||||
|
rawInfo, err := wc.client.Info(ctx.context)
|
||||||
|
return &Info{&rawInfo}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMaxMessageSize sets the maximal message size allowed by this node. Incoming
|
||||||
|
// and outgoing messages with a larger size will be rejected. Whisper message size
|
||||||
|
// can never exceed the limit imposed by the underlying P2P protocol (10 Mb).
|
||||||
|
func (wc *WhisperClient) SetMaxMessageSize(ctx *Context, size int32) error {
|
||||||
|
return wc.client.SetMaxMessageSize(ctx.context, uint32(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMinimumPoW (experimental) sets the minimal PoW required by this node.
|
||||||
|
// This experimental function was introduced for the future dynamic adjustment of
|
||||||
|
// PoW requirement. If the node is overwhelmed with messages, it should raise the
|
||||||
|
// PoW requirement and notify the peers. The new value should be set relative to
|
||||||
|
// the old value (e.g. double). The old value could be obtained via shh_info call.
|
||||||
|
func (wc *WhisperClient) SetMinimumPoW(ctx *Context, pow float64) error {
|
||||||
|
return wc.client.SetMinimumPoW(ctx.context, pow)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marks specific peer trusted, which will allow it to send historic (expired) messages.
|
||||||
|
// Note This function is not adding new nodes, the node needs to exists as a peer.
|
||||||
|
func (wc *WhisperClient) MarkTrustedPeer(ctx *Context, enode string) error {
|
||||||
|
return wc.client.MarkTrustedPeer(ctx.context, enode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewKeyPair generates a new public and private key pair for message decryption and encryption.
|
||||||
|
// It returns an identifier that can be used to refer to the key.
|
||||||
|
func (wc *WhisperClient) NewKeyPair(ctx *Context) (string, error) {
|
||||||
|
return wc.client.NewKeyPair(ctx.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPrivateKey stored the key pair, and returns its ID.
|
||||||
|
func (wc *WhisperClient) AddPrivateKey(ctx *Context, key []byte) (string, error) {
|
||||||
|
return wc.client.AddPrivateKey(ctx.context, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteKeyPair delete the specifies key.
|
||||||
|
func (wc *WhisperClient) DeleteKeyPair(ctx *Context, id string) (string, error) {
|
||||||
|
return wc.client.DeleteKeyPair(ctx.context, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasKeyPair returns an indication if the node has a private key or
|
||||||
|
// key pair matching the given ID.
|
||||||
|
func (wc *WhisperClient) HasKeyPair(ctx *Context, id string) (bool, error) {
|
||||||
|
return wc.client.HasKeyPair(ctx.context, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicKey return the public key for a key ID.
|
||||||
|
func (wc *WhisperClient) GetPublicKey(ctx *Context, id string) ([]byte, error) {
|
||||||
|
return wc.client.PublicKey(ctx.context, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrivateKey return the private key for a key ID.
|
||||||
|
func (wc *WhisperClient) GetPrivateKey(ctx *Context, id string) ([]byte, error) {
|
||||||
|
return wc.client.PrivateKey(ctx.context, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSymmetricKey generates a random symmetric key and returns its identifier.
|
||||||
|
// Can be used encrypting and decrypting messages where the key is known to both parties.
|
||||||
|
func (wc *WhisperClient) NewSymmetricKey(ctx *Context) (string, error) {
|
||||||
|
return wc.client.NewSymmetricKey(ctx.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSymmetricKey stores the key, and returns its identifier.
|
||||||
|
func (wc *WhisperClient) AddSymmetricKey(ctx *Context, key []byte) (string, error) {
|
||||||
|
return wc.client.AddSymmetricKey(ctx.context, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateSymmetricKeyFromPassword generates the key from password, stores it, and returns its identifier.
|
||||||
|
func (wc *WhisperClient) GenerateSymmetricKeyFromPassword(ctx *Context, passwd string) (string, error) {
|
||||||
|
return wc.client.GenerateSymmetricKeyFromPassword(ctx.context, passwd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasSymmetricKey returns an indication if the key associated with the given id is stored in the node.
|
||||||
|
func (wc *WhisperClient) HasSymmetricKey(ctx *Context, id string) (bool, error) {
|
||||||
|
return wc.client.HasSymmetricKey(ctx.context, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSymmetricKey returns the symmetric key associated with the given identifier.
|
||||||
|
func (wc *WhisperClient) GetSymmetricKey(ctx *Context, id string) ([]byte, error) {
|
||||||
|
return wc.client.GetSymmetricKey(ctx.context, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSymmetricKey deletes the symmetric key associated with the given identifier.
|
||||||
|
func (wc *WhisperClient) DeleteSymmetricKey(ctx *Context, id string) error {
|
||||||
|
return wc.client.DeleteSymmetricKey(ctx.context, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post a message onto the network.
|
||||||
|
func (wc *WhisperClient) Post(ctx *Context, message *NewMessage) (string, error) {
|
||||||
|
return wc.client.Post(ctx.context, *message.newMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHeadHandler is a client-side subscription callback to invoke on events and
|
||||||
|
// subscription failure.
|
||||||
|
type NewMessageHandler interface {
|
||||||
|
OnNewMessage(message *Message)
|
||||||
|
OnError(failure string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeMessages subscribes to messages that match the given criteria. This method
|
||||||
|
// is only supported on bi-directional connections such as websockets and IPC.
|
||||||
|
// NewMessageFilter uses polling and is supported over HTTP.
|
||||||
|
func (wc *WhisperClient) SubscribeMessages(ctx *Context, criteria *Criteria, handler NewMessageHandler, buffer int) (*Subscription, error) {
|
||||||
|
// Subscribe to the event internally
|
||||||
|
ch := make(chan *whisper.Message, buffer)
|
||||||
|
rawSub, err := wc.client.SubscribeMessages(ctx.context, *criteria.criteria, ch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Start up a dispatcher to feed into the callback
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case message := <-ch:
|
||||||
|
handler.OnNewMessage(&Message{message})
|
||||||
|
|
||||||
|
case err := <-rawSub.Err():
|
||||||
|
if err != nil {
|
||||||
|
handler.OnError(err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return &Subscription{rawSub}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMessageFilter creates a filter within the node. This filter can be used to poll
|
||||||
|
// for new messages (see FilterMessages) that satisfy the given criteria. A filter can
|
||||||
|
// timeout when it was polled for in whisper.filterTimeout.
|
||||||
|
func (wc *WhisperClient) NewMessageFilter(ctx *Context, criteria *Criteria) (string, error) {
|
||||||
|
return wc.client.NewMessageFilter(ctx.context, *criteria.criteria)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMessageFilter removes the filter associated with the given id.
|
||||||
|
func (wc *WhisperClient) DeleteMessageFilter(ctx *Context, id string) error {
|
||||||
|
return wc.client.DeleteMessageFilter(ctx.context, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFilterMessages retrieves all messages that are received between the last call to
|
||||||
|
// this function and match the criteria that where given when the filter was created.
|
||||||
|
func (wc *WhisperClient) GetFilterMessages(ctx *Context, id string) (*Messages, error) {
|
||||||
|
rawFilterMessages, err := wc.client.FilterMessages(ctx.context, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res := make([]*whisper.Message, len(rawFilterMessages))
|
||||||
|
copy(res, rawFilterMessages)
|
||||||
|
return &Messages{res}, nil
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Nonce is a 64-bit hash which proves (combined with the mix-hash) that
|
// A Nonce is a 64-bit hash which proves (combined with the mix-hash) that
|
||||||
|
|
@ -334,3 +335,95 @@ func (r *Receipt) GetLogs() *Logs { return &Logs{r.receipt.Logs} }
|
||||||
func (r *Receipt) GetTxHash() *Hash { return &Hash{r.receipt.TxHash} }
|
func (r *Receipt) GetTxHash() *Hash { return &Hash{r.receipt.TxHash} }
|
||||||
func (r *Receipt) GetContractAddress() *Address { return &Address{r.receipt.ContractAddress} }
|
func (r *Receipt) GetContractAddress() *Address { return &Address{r.receipt.ContractAddress} }
|
||||||
func (r *Receipt) GetGasUsed() int64 { return int64(r.receipt.GasUsed) }
|
func (r *Receipt) GetGasUsed() int64 { return int64(r.receipt.GasUsed) }
|
||||||
|
|
||||||
|
// Info represents a diagnostic information about the whisper node.
|
||||||
|
type Info struct {
|
||||||
|
info *whisper.Info
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMessage represents a new whisper message that is posted through the RPC.
|
||||||
|
type NewMessage struct {
|
||||||
|
newMessage *whisper.NewMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNewMessage() *NewMessage {
|
||||||
|
nm := &NewMessage{
|
||||||
|
newMessage: new(whisper.NewMessage),
|
||||||
|
}
|
||||||
|
return nm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nm *NewMessage) GetSymKeyID() string { return nm.newMessage.SymKeyID }
|
||||||
|
func (nm *NewMessage) SetSymKeyID(symKeyID string) { nm.newMessage.SymKeyID = symKeyID }
|
||||||
|
func (nm *NewMessage) GetPublicKey() []byte { return nm.newMessage.PublicKey }
|
||||||
|
func (nm *NewMessage) SetPublicKey(publicKey []byte) {
|
||||||
|
nm.newMessage.PublicKey = common.CopyBytes(publicKey)
|
||||||
|
}
|
||||||
|
func (nm *NewMessage) GetSig() string { return nm.newMessage.Sig }
|
||||||
|
func (nm *NewMessage) SetSig(sig string) { nm.newMessage.Sig = sig }
|
||||||
|
func (nm *NewMessage) GetTTL() int64 { return int64(nm.newMessage.TTL) }
|
||||||
|
func (nm *NewMessage) SetTTL(ttl int64) { nm.newMessage.TTL = uint32(ttl) }
|
||||||
|
func (nm *NewMessage) GetPayload() []byte { return nm.newMessage.Payload }
|
||||||
|
func (nm *NewMessage) SetPayload(payload []byte) { nm.newMessage.Payload = common.CopyBytes(payload) }
|
||||||
|
func (nm *NewMessage) GetPowTime() int64 { return int64(nm.newMessage.PowTime) }
|
||||||
|
func (nm *NewMessage) SetPowTime(powTime int64) { nm.newMessage.PowTime = uint32(powTime) }
|
||||||
|
func (nm *NewMessage) GetPowTarget() float64 { return nm.newMessage.PowTarget }
|
||||||
|
func (nm *NewMessage) SetPowTarget(powTarget float64) { nm.newMessage.PowTarget = powTarget }
|
||||||
|
func (nm *NewMessage) GetTargetPeer() string { return nm.newMessage.TargetPeer }
|
||||||
|
func (nm *NewMessage) SetTargetPeer(targetPeer string) { nm.newMessage.TargetPeer = targetPeer }
|
||||||
|
func (nm *NewMessage) GetTopic() []byte { return nm.newMessage.Topic[:] }
|
||||||
|
func (nm *NewMessage) SetTopic(topic []byte) { nm.newMessage.Topic = whisper.BytesToTopic(topic) }
|
||||||
|
|
||||||
|
// Message represents a whisper message.
|
||||||
|
type Message struct {
|
||||||
|
message *whisper.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Message) GetSig() []byte { return m.message.Sig }
|
||||||
|
func (m *Message) GetTTL() int64 { return int64(m.message.TTL) }
|
||||||
|
func (m *Message) GetTimestamp() int64 { return int64(m.message.Timestamp) }
|
||||||
|
func (m *Message) GetPayload() []byte { return m.message.Payload }
|
||||||
|
func (m *Message) GetPoW() float64 { return m.message.PoW }
|
||||||
|
func (m *Message) GetHash() []byte { return m.message.Hash }
|
||||||
|
func (m *Message) GetDst() []byte { return m.message.Dst }
|
||||||
|
|
||||||
|
// Messages represents an array of messages.
|
||||||
|
type Messages struct {
|
||||||
|
messages []*whisper.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns the number of messages in the slice.
|
||||||
|
func (m *Messages) Size() int {
|
||||||
|
return len(m.messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the message at the given index from the slice.
|
||||||
|
func (m *Messages) Get(index int) (message *Message, _ error) {
|
||||||
|
if index < 0 || index >= len(m.messages) {
|
||||||
|
return nil, errors.New("index out of bounds")
|
||||||
|
}
|
||||||
|
return &Message{m.messages[index]}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Criteria holds various filter options for inbound messages.
|
||||||
|
type Criteria struct {
|
||||||
|
criteria *whisper.Criteria
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCriteria(topic []byte) *Criteria {
|
||||||
|
c := &Criteria{
|
||||||
|
criteria: new(whisper.Criteria),
|
||||||
|
}
|
||||||
|
encodedTopic := whisper.BytesToTopic(topic)
|
||||||
|
c.criteria.Topics = []whisper.TopicType{encodedTopic}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Criteria) GetSymKeyID() string { return c.criteria.SymKeyID }
|
||||||
|
func (c *Criteria) SetSymKeyID(symKeyID string) { c.criteria.SymKeyID = symKeyID }
|
||||||
|
func (c *Criteria) GetPrivateKeyID() string { return c.criteria.PrivateKeyID }
|
||||||
|
func (c *Criteria) SetPrivateKeyID(privateKeyID string) { c.criteria.PrivateKeyID = privateKeyID }
|
||||||
|
func (c *Criteria) GetSig() []byte { return c.criteria.Sig }
|
||||||
|
func (c *Criteria) SetSig(sig []byte) { c.criteria.Sig = common.CopyBytes(sig) }
|
||||||
|
func (c *Criteria) GetMinPow() float64 { return c.criteria.MinPow }
|
||||||
|
func (c *Criteria) SetMinPow(pow float64) { c.criteria.MinPow = pow }
|
||||||
|
|
|
||||||
|
|
@ -1228,7 +1228,7 @@ func (net *Network) checkTopicRegister(data *topicRegister) (*pong, error) {
|
||||||
if rlpHash(data.Topics) != pongpkt.data.(*pong).TopicHash {
|
if rlpHash(data.Topics) != pongpkt.data.(*pong).TopicHash {
|
||||||
return nil, errors.New("topic hash mismatch")
|
return nil, errors.New("topic hash mismatch")
|
||||||
}
|
}
|
||||||
if int(data.Idx) < 0 || int(data.Idx) >= len(data.Topics) {
|
if data.Idx >= uint(len(data.Topics)) {
|
||||||
return nil, errors.New("topic index out of range")
|
return nil, errors.New("topic index out of range")
|
||||||
}
|
}
|
||||||
return pongpkt.data.(*pong), nil
|
return pongpkt.data.(*pong), nil
|
||||||
|
|
|
||||||
|
|
@ -327,7 +327,7 @@ func (err *ConfigCompatError) Error() string {
|
||||||
return fmt.Sprintf("mismatching %s in database (have %d, want %d, rewindto %d)", err.What, err.StoredConfig, err.NewConfig, err.RewindTo)
|
return fmt.Sprintf("mismatching %s in database (have %d, want %d, rewindto %d)", err.What, err.StoredConfig, err.NewConfig, err.RewindTo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rules wraps ChainConfig and is merely syntatic sugar or can be used for functions
|
// Rules wraps ChainConfig and is merely syntactic sugar or can be used for functions
|
||||||
// that do not have or require information about the block.
|
// that do not have or require information about the block.
|
||||||
//
|
//
|
||||||
// Rules is a one time interface meaning that it shouldn't be used in between transition
|
// Rules is a one time interface meaning that it shouldn't be used in between transition
|
||||||
|
|
|
||||||
|
|
@ -487,6 +487,7 @@ func (c *Client) write(ctx context.Context, msg interface{}) error {
|
||||||
}
|
}
|
||||||
c.writeConn.SetWriteDeadline(deadline)
|
c.writeConn.SetWriteDeadline(deadline)
|
||||||
err := json.NewEncoder(c.writeConn).Encode(msg)
|
err := json.NewEncoder(c.writeConn).Encode(msg)
|
||||||
|
c.writeConn.SetWriteDeadline(time.Time{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.writeConn = nil
|
c.writeConn = nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,11 @@ func TestBlockchain(t *testing.T) {
|
||||||
bt.skipLoad(`^bcForgedTest/bcForkUncle\.json`)
|
bt.skipLoad(`^bcForgedTest/bcForkUncle\.json`)
|
||||||
bt.skipLoad(`^bcMultiChainTest/(ChainAtoChainB_blockorder|CallContractFromNotBestBlock)`)
|
bt.skipLoad(`^bcMultiChainTest/(ChainAtoChainB_blockorder|CallContractFromNotBestBlock)`)
|
||||||
bt.skipLoad(`^bcTotalDifficultyTest/(lotsOfLeafs|lotsOfBranches|sideChainWithMoreTransactions)`)
|
bt.skipLoad(`^bcTotalDifficultyTest/(lotsOfLeafs|lotsOfBranches|sideChainWithMoreTransactions)`)
|
||||||
// Constantinople is not implemented yet.
|
// This test is broken
|
||||||
bt.skipLoad(`(?i)(constantinople)`)
|
bt.fails(`blockhashNonConstArg_Constantinople`, "Broken test")
|
||||||
|
|
||||||
// Still failing tests
|
// Still failing tests
|
||||||
bt.skipLoad(`^bcWalletTest.*_Byzantium$`)
|
// bt.skipLoad(`^bcWalletTest.*_Byzantium$`)
|
||||||
|
|
||||||
bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) {
|
bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) {
|
||||||
if err := bt.checkFailure(t, name, test.Run()); err != nil {
|
if err := bt.checkFailure(t, name, test.Run()); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ type testMatcher struct {
|
||||||
failpat []testFailure
|
failpat []testFailure
|
||||||
skiploadpat []*regexp.Regexp
|
skiploadpat []*regexp.Regexp
|
||||||
skipshortpat []*regexp.Regexp
|
skipshortpat []*regexp.Regexp
|
||||||
|
whitelistpat *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
type testConfig struct {
|
type testConfig struct {
|
||||||
|
|
@ -121,6 +122,10 @@ func (tm *testMatcher) fails(pattern string, reason string) {
|
||||||
tm.failpat = append(tm.failpat, testFailure{regexp.MustCompile(pattern), reason})
|
tm.failpat = append(tm.failpat, testFailure{regexp.MustCompile(pattern), reason})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tm *testMatcher) whitelist(pattern string) {
|
||||||
|
tm.whitelistpat = regexp.MustCompile(pattern)
|
||||||
|
}
|
||||||
|
|
||||||
// config defines chain config for tests matching the pattern.
|
// config defines chain config for tests matching the pattern.
|
||||||
func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) {
|
func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) {
|
||||||
tm.configpat = append(tm.configpat, testConfig{regexp.MustCompile(pattern), cfg})
|
tm.configpat = append(tm.configpat, testConfig{regexp.MustCompile(pattern), cfg})
|
||||||
|
|
@ -208,6 +213,11 @@ func (tm *testMatcher) runTestFile(t *testing.T, path, name string, runTest inte
|
||||||
if r, _ := tm.findSkip(name); r != "" {
|
if r, _ := tm.findSkip(name); r != "" {
|
||||||
t.Skip(r)
|
t.Skip(r)
|
||||||
}
|
}
|
||||||
|
if tm.whitelistpat != nil {
|
||||||
|
if !tm.whitelistpat.MatchString(name) {
|
||||||
|
t.Skip("Skipped by whitelist")
|
||||||
|
}
|
||||||
|
}
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Load the file as map[string]<testType>.
|
// Load the file as map[string]<testType>.
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,6 @@ func TestState(t *testing.T) {
|
||||||
key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index)
|
key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index)
|
||||||
name := name + "/" + key
|
name := name + "/" + key
|
||||||
t.Run(key, func(t *testing.T) {
|
t.Run(key, func(t *testing.T) {
|
||||||
if subtest.Fork == "Constantinople" {
|
|
||||||
t.Skip("constantinople not supported yet")
|
|
||||||
}
|
|
||||||
withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error {
|
withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error {
|
||||||
_, err := test.Run(subtest, vmconfig)
|
_, err := test.Run(subtest, vmconfig)
|
||||||
return st.checkFailure(t, name, err)
|
return st.checkFailure(t, name, err)
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,18 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD
|
||||||
if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) {
|
if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) {
|
||||||
return statedb, fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs)
|
return statedb, fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs)
|
||||||
}
|
}
|
||||||
root, _ := statedb.Commit(config.IsEIP158(block.Number()))
|
// Commit block
|
||||||
|
statedb.Commit(config.IsEIP158(block.Number()))
|
||||||
|
// Add 0-value mining reward. This only makes a difference in the cases
|
||||||
|
// where
|
||||||
|
// - the coinbase suicided, or
|
||||||
|
// - there are only 'bad' transactions, which aren't executed. In those cases,
|
||||||
|
// the coinbase gets no txfee, so isn't created, and thus needs to be touched
|
||||||
|
statedb.AddBalance(block.Coinbase(), new(big.Int))
|
||||||
|
// And _now_ get the state root
|
||||||
|
root := statedb.IntermediateRoot(config.IsEIP158(block.Number()))
|
||||||
|
// N.B: We need to do this in a two-step process, because the first Commit takes care
|
||||||
|
// of suicides, and we need to touch the coinbase _after_ it has potentially suicided.
|
||||||
if root != common.Hash(post.Root) {
|
if root != common.Hash(post.Root) {
|
||||||
return statedb, fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root)
|
return statedb, fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 2bb0c3da3bbb15c528bcef2a7e5ac4bd73f81f87
|
Subproject commit ad2184adca367c0b68c65b44519dba16e1d0b9e2
|
||||||
|
|
@ -21,8 +21,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/prque"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrNotRequested is returned by the trie sync when it's requested to process a
|
// ErrNotRequested is returned by the trie sync when it's requested to process a
|
||||||
|
|
@ -84,7 +84,7 @@ func NewSync(root common.Hash, database DatabaseReader, callback LeafCallback) *
|
||||||
database: database,
|
database: database,
|
||||||
membatch: newSyncMemBatch(),
|
membatch: newSyncMemBatch(),
|
||||||
requests: make(map[common.Hash]*request),
|
requests: make(map[common.Hash]*request),
|
||||||
queue: prque.New(),
|
queue: prque.New(nil),
|
||||||
}
|
}
|
||||||
ts.AddSubTrie(root, 0, common.Hash{}, callback)
|
ts.AddSubTrie(root, 0, common.Hash{}, callback)
|
||||||
return ts
|
return ts
|
||||||
|
|
@ -242,7 +242,7 @@ func (s *Sync) schedule(req *request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Schedule the request for future retrieval
|
// Schedule the request for future retrieval
|
||||||
s.queue.Push(req.hash, float32(req.depth))
|
s.queue.Push(req.hash, int64(req.depth))
|
||||||
s.requests[req.hash] = req
|
s.requests[req.hash] = req
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
25
vendor/gopkg.in/karalabe/cookiejar.v2/LICENSE
generated
vendored
25
vendor/gopkg.in/karalabe/cookiejar.v2/LICENSE
generated
vendored
|
|
@ -1,25 +0,0 @@
|
||||||
Copyright (c) 2014 Péter Szilágyi. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
Alternatively, the CookieJar toolbox may be used in accordance with the terms
|
|
||||||
and conditions contained in a signed written agreement between you and the
|
|
||||||
author(s).
|
|
||||||
109
vendor/gopkg.in/karalabe/cookiejar.v2/README.md
generated
vendored
109
vendor/gopkg.in/karalabe/cookiejar.v2/README.md
generated
vendored
|
|
@ -1,109 +0,0 @@
|
||||||
CookieJar - A contestant's toolbox
|
|
||||||
======================================
|
|
||||||
|
|
||||||
CookieJar is a small collection of common algorithms, data structures and library extensions that were deemed handy for computing competitions at one point or another.
|
|
||||||
|
|
||||||
This toolbox is a work in progress for the time being. It may be lacking, and it may change drastically between commits (although every effort is made not to). You're welcome to use it, but it's your head on the line :)
|
|
||||||
|
|
||||||
Installation
|
|
||||||
----------------
|
|
||||||
|
|
||||||
To get the package, execute:
|
|
||||||
|
|
||||||
go get gopkg.in/karalabe/cookiejar.v2
|
|
||||||
|
|
||||||
To import this package, add the following line to your code:
|
|
||||||
|
|
||||||
import "gopkg.in/karalabe/cookiejar.v2"
|
|
||||||
|
|
||||||
For more details, see the [package documentation](http://godoc.org/gopkg.in/karalabe/cookiejar.v2).
|
|
||||||
|
|
||||||
Contents
|
|
||||||
------------
|
|
||||||
|
|
||||||
Algorithms:
|
|
||||||
- Graph
|
|
||||||
- [Breadth First Search](http://godoc.org/gopkg.in/karalabe/cookiejar.v2/graph/bfs)
|
|
||||||
- [Depth First Search](http://godoc.org/gopkg.in/karalabe/cookiejar.v2/graph/dfs)
|
|
||||||
|
|
||||||
Data structures:
|
|
||||||
- [Bag](http://godoc.org/gopkg.in/karalabe/cookiejar.v2/collections/bag)
|
|
||||||
- [Deque](http://godoc.org/gopkg.in/karalabe/cookiejar.v2/collections/deque)
|
|
||||||
- [Graph](http://godoc.org/gopkg.in/karalabe/cookiejar.v2/graph)
|
|
||||||
- [Priority Queue](http://godoc.org/gopkg.in/karalabe/cookiejar.v2/collections/prque)
|
|
||||||
- [Queue](http://godoc.org/gopkg.in/karalabe/cookiejar.v2/collections/queue)
|
|
||||||
- [Set](http://godoc.org/gopkg.in/karalabe/cookiejar.v2/collections/set)
|
|
||||||
- [Stack](http://godoc.org/gopkg.in/karalabe/cookiejar.v2/collections/stack)
|
|
||||||
|
|
||||||
Extensions:
|
|
||||||
- [fmt](http://godoc.org/gopkg.in/karalabe/cookiejar.v2/exts/fmtext)
|
|
||||||
- `Scan` and `Fscan` for `int`, `float64`, `string` and lines
|
|
||||||
- [math](http://godoc.org/gopkg.in/karalabe/cookiejar.v2/exts/mathext)
|
|
||||||
- `Abs` for `int`
|
|
||||||
- `Min` and `Max` for `int`, `big.Int` and `big.Rat`
|
|
||||||
- `Sign` for `int` and `float64`
|
|
||||||
- [os](http://godoc.org/gopkg.in/karalabe/cookiejar.v2/exts/osext)
|
|
||||||
- `Open` and `Create` without error codes
|
|
||||||
- [sort](http://godoc.org/gopkg.in/karalabe/cookiejar.v2/exts/sortext)
|
|
||||||
- `Sort` and `Search` for `big.Int` and `big.Rat`
|
|
||||||
- `Unique` for any `sort.Interface`
|
|
||||||
|
|
||||||
Below are the performance results for the data structures and the complexity analysis for the algorithms.
|
|
||||||
|
|
||||||
Performance
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz:
|
|
||||||
```
|
|
||||||
- bag
|
|
||||||
- BenchmarkInsert 309 ns/op
|
|
||||||
- BenchmarkRemove 197 ns/op
|
|
||||||
- BenchmarkDo 28.1 ns/op
|
|
||||||
- deque
|
|
||||||
- BenchmarkPush 25.4 ns/op
|
|
||||||
- BenchmarkPop 6.72 ns/op
|
|
||||||
- prque
|
|
||||||
- BenchmarkPush 171 ns/op
|
|
||||||
- BenchmarkPop 947 ns/op
|
|
||||||
- queue
|
|
||||||
- BenchmarkPush 23.0 ns/op
|
|
||||||
- BenchmarkPop 5.92 ns/op
|
|
||||||
- set
|
|
||||||
- BenchmarkInsert 259 ns/op
|
|
||||||
- BenchmarkRemove 115 ns/op
|
|
||||||
- BenchmarkDo 20.9 ns/op
|
|
||||||
- stack
|
|
||||||
- BenchmarkPush 16.4 ns/op
|
|
||||||
- BenchmarkPop 6.45 ns/op
|
|
||||||
```
|
|
||||||
|
|
||||||
Complexity
|
|
||||||
--------------
|
|
||||||
|
|
||||||
| Algorithm | Time complexity | Space complexity |
|
|
||||||
|:---------:|:---------------:|:----------------:|
|
|
||||||
| graph/bfs | O(E) | O(V) |
|
|
||||||
| graph/dfs | O(E) | O(E) |
|
|
||||||
|
|
||||||
Here be dragons :)
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
```
|
|
||||||
. _///_,
|
|
||||||
. / ` ' '>
|
|
||||||
) o' __/_'>
|
|
||||||
( / _/ )_\'>
|
|
||||||
' "__/ /_/\_>
|
|
||||||
____/_/_/_/
|
|
||||||
/,---, _/ /
|
|
||||||
"" /_/_/_/
|
|
||||||
/_(_(_(_ \
|
|
||||||
( \_\_\\_ )\
|
|
||||||
\'__\_\_\_\__ ).\
|
|
||||||
//____|___\__) )_/
|
|
||||||
| _ \'___'_( /'
|
|
||||||
\_ (-'\'___'_\ __,'_'
|
|
||||||
__) \ \\___(_ __/.__,'
|
|
||||||
,((,-,__\ '", __\_/. __,'
|
|
||||||
'"./_._._-'
|
|
||||||
```
|
|
||||||
66
vendor/gopkg.in/karalabe/cookiejar.v2/collections/prque/prque.go
generated
vendored
66
vendor/gopkg.in/karalabe/cookiejar.v2/collections/prque/prque.go
generated
vendored
|
|
@ -1,66 +0,0 @@
|
||||||
// CookieJar - A contestant's algorithm toolbox
|
|
||||||
// Copyright (c) 2013 Peter Szilagyi. All rights reserved.
|
|
||||||
//
|
|
||||||
// CookieJar is dual licensed: use of this source code is governed by a BSD
|
|
||||||
// license that can be found in the LICENSE file. Alternatively, the CookieJar
|
|
||||||
// toolbox may be used in accordance with the terms and conditions contained
|
|
||||||
// in a signed written agreement between you and the author(s).
|
|
||||||
|
|
||||||
// Package prque implements a priority queue data structure supporting arbitrary
|
|
||||||
// value types and float priorities.
|
|
||||||
//
|
|
||||||
// The reasoning behind using floats for the priorities vs. ints or interfaces
|
|
||||||
// was larger flexibility without sacrificing too much performance or code
|
|
||||||
// complexity.
|
|
||||||
//
|
|
||||||
// If you would like to use a min-priority queue, simply negate the priorities.
|
|
||||||
//
|
|
||||||
// Internally the queue is based on the standard heap package working on a
|
|
||||||
// sortable version of the block based stack.
|
|
||||||
package prque
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/heap"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Priority queue data structure.
|
|
||||||
type Prque struct {
|
|
||||||
cont *sstack
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new priority queue.
|
|
||||||
func New() *Prque {
|
|
||||||
return &Prque{newSstack()}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pushes a value with a given priority into the queue, expanding if necessary.
|
|
||||||
func (p *Prque) Push(data interface{}, priority float32) {
|
|
||||||
heap.Push(p.cont, &item{data, priority})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pops the value with the greates priority off the stack and returns it.
|
|
||||||
// Currently no shrinking is done.
|
|
||||||
func (p *Prque) Pop() (interface{}, float32) {
|
|
||||||
item := heap.Pop(p.cont).(*item)
|
|
||||||
return item.value, item.priority
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pops only the item from the queue, dropping the associated priority value.
|
|
||||||
func (p *Prque) PopItem() interface{} {
|
|
||||||
return heap.Pop(p.cont).(*item).value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks whether the priority queue is empty.
|
|
||||||
func (p *Prque) Empty() bool {
|
|
||||||
return p.cont.Len() == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the number of element in the priority queue.
|
|
||||||
func (p *Prque) Size() int {
|
|
||||||
return p.cont.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clears the contents of the priority queue.
|
|
||||||
func (p *Prque) Reset() {
|
|
||||||
*p = *New()
|
|
||||||
}
|
|
||||||
91
vendor/gopkg.in/karalabe/cookiejar.v2/collections/prque/sstack.go
generated
vendored
91
vendor/gopkg.in/karalabe/cookiejar.v2/collections/prque/sstack.go
generated
vendored
|
|
@ -1,91 +0,0 @@
|
||||||
// CookieJar - A contestant's algorithm toolbox
|
|
||||||
// Copyright (c) 2013 Peter Szilagyi. All rights reserved.
|
|
||||||
//
|
|
||||||
// CookieJar is dual licensed: use of this source code is governed by a BSD
|
|
||||||
// license that can be found in the LICENSE file. Alternatively, the CookieJar
|
|
||||||
// toolbox may be used in accordance with the terms and conditions contained
|
|
||||||
// in a signed written agreement between you and the author(s).
|
|
||||||
|
|
||||||
package prque
|
|
||||||
|
|
||||||
// The size of a block of data
|
|
||||||
const blockSize = 4096
|
|
||||||
|
|
||||||
// A prioritized item in the sorted stack.
|
|
||||||
type item struct {
|
|
||||||
value interface{}
|
|
||||||
priority float32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal sortable stack data structure. Implements the Push and Pop ops for
|
|
||||||
// the stack (heap) functionality and the Len, Less and Swap methods for the
|
|
||||||
// sortability requirements of the heaps.
|
|
||||||
type sstack struct {
|
|
||||||
size int
|
|
||||||
capacity int
|
|
||||||
offset int
|
|
||||||
|
|
||||||
blocks [][]*item
|
|
||||||
active []*item
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new, empty stack.
|
|
||||||
func newSstack() *sstack {
|
|
||||||
result := new(sstack)
|
|
||||||
result.active = make([]*item, blockSize)
|
|
||||||
result.blocks = [][]*item{result.active}
|
|
||||||
result.capacity = blockSize
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pushes a value onto the stack, expanding it if necessary. Required by
|
|
||||||
// heap.Interface.
|
|
||||||
func (s *sstack) Push(data interface{}) {
|
|
||||||
if s.size == s.capacity {
|
|
||||||
s.active = make([]*item, blockSize)
|
|
||||||
s.blocks = append(s.blocks, s.active)
|
|
||||||
s.capacity += blockSize
|
|
||||||
s.offset = 0
|
|
||||||
} else if s.offset == blockSize {
|
|
||||||
s.active = s.blocks[s.size/blockSize]
|
|
||||||
s.offset = 0
|
|
||||||
}
|
|
||||||
s.active[s.offset] = data.(*item)
|
|
||||||
s.offset++
|
|
||||||
s.size++
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pops a value off the stack and returns it. Currently no shrinking is done.
|
|
||||||
// Required by heap.Interface.
|
|
||||||
func (s *sstack) Pop() (res interface{}) {
|
|
||||||
s.size--
|
|
||||||
s.offset--
|
|
||||||
if s.offset < 0 {
|
|
||||||
s.offset = blockSize - 1
|
|
||||||
s.active = s.blocks[s.size/blockSize]
|
|
||||||
}
|
|
||||||
res, s.active[s.offset] = s.active[s.offset], nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the length of the stack. Required by sort.Interface.
|
|
||||||
func (s *sstack) Len() int {
|
|
||||||
return s.size
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compares the priority of two elements of the stack (higher is first).
|
|
||||||
// Required by sort.Interface.
|
|
||||||
func (s *sstack) Less(i, j int) bool {
|
|
||||||
return s.blocks[i/blockSize][i%blockSize].priority > s.blocks[j/blockSize][j%blockSize].priority
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swaps two elements in the stack. Required by sort.Interface.
|
|
||||||
func (s *sstack) Swap(i, j int) {
|
|
||||||
ib, io, jb, jo := i/blockSize, i%blockSize, j/blockSize, j%blockSize
|
|
||||||
s.blocks[ib][io], s.blocks[jb][jo] = s.blocks[jb][jo], s.blocks[ib][io]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resets the stack, effectively clearing its contents.
|
|
||||||
func (s *sstack) Reset() {
|
|
||||||
*s = *newSstack()
|
|
||||||
}
|
|
||||||
6
vendor/vendor.json
vendored
6
vendor/vendor.json
vendored
|
|
@ -891,12 +891,6 @@
|
||||||
"revision": "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec",
|
"revision": "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec",
|
||||||
"revisionTime": "2016-12-08T18:13:25Z"
|
"revisionTime": "2016-12-08T18:13:25Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"checksumSHA1": "DQXNV0EivoHm4q+bkdahYXrjjfE=",
|
|
||||||
"path": "gopkg.in/karalabe/cookiejar.v2/collections/prque",
|
|
||||||
"revision": "8dcd6a7f4951f6ff3ee9cbb919a06d8925822e57",
|
|
||||||
"revisionTime": "2015-07-24T13:16:13Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"checksumSHA1": "0xgs8lwcWLUffemlj+SsgKlxvDU=",
|
"checksumSHA1": "0xgs8lwcWLUffemlj+SsgKlxvDU=",
|
||||||
"path": "gopkg.in/natefinch/npipe.v2",
|
"path": "gopkg.in/natefinch/npipe.v2",
|
||||||
|
|
|
||||||
|
|
@ -195,14 +195,14 @@ func (api *PublicWhisperAPI) DeleteSymKey(ctx context.Context, id string) bool {
|
||||||
// MakeLightClient turns the node into light client, which does not forward
|
// MakeLightClient turns the node into light client, which does not forward
|
||||||
// any incoming messages, and sends only messages originated in this node.
|
// any incoming messages, and sends only messages originated in this node.
|
||||||
func (api *PublicWhisperAPI) MakeLightClient(ctx context.Context) bool {
|
func (api *PublicWhisperAPI) MakeLightClient(ctx context.Context) bool {
|
||||||
api.w.lightClient = true
|
api.w.SetLightClientMode(true)
|
||||||
return api.w.lightClient
|
return api.w.LightClientMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CancelLightClient cancels light client mode.
|
// CancelLightClient cancels light client mode.
|
||||||
func (api *PublicWhisperAPI) CancelLightClient(ctx context.Context) bool {
|
func (api *PublicWhisperAPI) CancelLightClient(ctx context.Context) bool {
|
||||||
api.w.lightClient = false
|
api.w.SetLightClientMode(false)
|
||||||
return !api.w.lightClient
|
return !api.w.LightClientMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate gencodec -type NewMessage -field-override newMessageOverride -out gen_newmessage_json.go
|
//go:generate gencodec -type NewMessage -field-override newMessageOverride -out gen_newmessage_json.go
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,14 @@ package whisperv6
|
||||||
|
|
||||||
// Config represents the configuration state of a whisper node.
|
// Config represents the configuration state of a whisper node.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
MaxMessageSize uint32 `toml:",omitempty"`
|
MaxMessageSize uint32 `toml:",omitempty"`
|
||||||
MinimumAcceptedPOW float64 `toml:",omitempty"`
|
MinimumAcceptedPOW float64 `toml:",omitempty"`
|
||||||
|
RestrictConnectionBetweenLightClients bool `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultConfig represents (shocker!) the default configuration.
|
// DefaultConfig represents (shocker!) the default configuration.
|
||||||
var DefaultConfig = Config{
|
var DefaultConfig = Config{
|
||||||
MaxMessageSize: DefaultMaxMessageSize,
|
MaxMessageSize: DefaultMaxMessageSize,
|
||||||
MinimumAcceptedPOW: DefaultMinimumPoW,
|
MinimumAcceptedPOW: DefaultMinimumPoW,
|
||||||
|
RestrictConnectionBetweenLightClients: true,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,11 +79,14 @@ func (peer *Peer) stop() {
|
||||||
func (peer *Peer) handshake() error {
|
func (peer *Peer) handshake() error {
|
||||||
// Send the handshake status message asynchronously
|
// Send the handshake status message asynchronously
|
||||||
errc := make(chan error, 1)
|
errc := make(chan error, 1)
|
||||||
|
isLightNode := peer.host.LightClientMode()
|
||||||
|
isRestrictedLightNodeConnection := peer.host.LightClientModeConnectionRestricted()
|
||||||
go func() {
|
go func() {
|
||||||
pow := peer.host.MinPow()
|
pow := peer.host.MinPow()
|
||||||
powConverted := math.Float64bits(pow)
|
powConverted := math.Float64bits(pow)
|
||||||
bloom := peer.host.BloomFilter()
|
bloom := peer.host.BloomFilter()
|
||||||
errc <- p2p.SendItems(peer.ws, statusCode, ProtocolVersion, powConverted, bloom)
|
|
||||||
|
errc <- p2p.SendItems(peer.ws, statusCode, ProtocolVersion, powConverted, bloom, isLightNode)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Fetch the remote status packet and verify protocol match
|
// Fetch the remote status packet and verify protocol match
|
||||||
|
|
@ -127,6 +130,11 @@ func (peer *Peer) handshake() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isRemotePeerLightNode, err := s.Bool()
|
||||||
|
if isRemotePeerLightNode && isLightNode && isRestrictedLightNodeConnection {
|
||||||
|
return fmt.Errorf("peer [%x] is useless: two light client communication restricted", peer.ID())
|
||||||
|
}
|
||||||
|
|
||||||
if err := <-errc; err != nil {
|
if err := <-errc; err != nil {
|
||||||
return fmt.Errorf("peer [%x] failed to send status packet: %v", peer.ID(), err)
|
return fmt.Errorf("peer [%x] failed to send status packet: %v", peer.ID(), err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var keys = []string{
|
var keys = []string{
|
||||||
|
|
@ -507,3 +508,63 @@ func waitForServersToStart(t *testing.T) {
|
||||||
}
|
}
|
||||||
t.Fatalf("Failed to start all the servers, running: %d", started)
|
t.Fatalf("Failed to start all the servers, running: %d", started)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//two generic whisper node handshake
|
||||||
|
func TestPeerHandshakeWithTwoFullNode(t *testing.T) {
|
||||||
|
w1 := Whisper{}
|
||||||
|
p1 := newPeer(&w1, p2p.NewPeer(discover.NodeID{}, "test", []p2p.Cap{}), &rwStub{[]interface{}{ProtocolVersion, uint64(123), make([]byte, BloomFilterSize), false}})
|
||||||
|
err := p1.handshake()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//two generic whisper node handshake. one don't send light flag
|
||||||
|
func TestHandshakeWithOldVersionWithoutLightModeFlag(t *testing.T) {
|
||||||
|
w1 := Whisper{}
|
||||||
|
p1 := newPeer(&w1, p2p.NewPeer(discover.NodeID{}, "test", []p2p.Cap{}), &rwStub{[]interface{}{ProtocolVersion, uint64(123), make([]byte, BloomFilterSize)}})
|
||||||
|
err := p1.handshake()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//two light nodes handshake. restriction disabled
|
||||||
|
func TestTwoLightPeerHandshakeRestrictionOff(t *testing.T) {
|
||||||
|
w1 := Whisper{}
|
||||||
|
w1.settings.Store(restrictConnectionBetweenLightClientsIdx, false)
|
||||||
|
w1.SetLightClientMode(true)
|
||||||
|
p1 := newPeer(&w1, p2p.NewPeer(discover.NodeID{}, "test", []p2p.Cap{}), &rwStub{[]interface{}{ProtocolVersion, uint64(123), make([]byte, BloomFilterSize), true}})
|
||||||
|
err := p1.handshake()
|
||||||
|
if err != nil {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//two light nodes handshake. restriction enabled
|
||||||
|
func TestTwoLightPeerHandshakeError(t *testing.T) {
|
||||||
|
w1 := Whisper{}
|
||||||
|
w1.settings.Store(restrictConnectionBetweenLightClientsIdx, true)
|
||||||
|
w1.SetLightClientMode(true)
|
||||||
|
p1 := newPeer(&w1, p2p.NewPeer(discover.NodeID{}, "test", []p2p.Cap{}), &rwStub{[]interface{}{ProtocolVersion, uint64(123), make([]byte, BloomFilterSize), true}})
|
||||||
|
err := p1.handshake()
|
||||||
|
if err == nil {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type rwStub struct {
|
||||||
|
payload []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stub *rwStub) ReadMsg() (p2p.Msg, error) {
|
||||||
|
size, r, err := rlp.EncodeToReader(stub.payload)
|
||||||
|
if err != nil {
|
||||||
|
return p2p.Msg{}, err
|
||||||
|
}
|
||||||
|
return p2p.Msg{Code: statusCode, Size: uint32(size), Payload: r}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stub *rwStub) WriteMsg(m p2p.Msg) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,12 +49,14 @@ type Statistics struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxMsgSizeIdx = iota // Maximal message length allowed by the whisper node
|
maxMsgSizeIdx = iota // Maximal message length allowed by the whisper node
|
||||||
overflowIdx // Indicator of message queue overflow
|
overflowIdx // Indicator of message queue overflow
|
||||||
minPowIdx // Minimal PoW required by the whisper node
|
minPowIdx // Minimal PoW required by the whisper node
|
||||||
minPowToleranceIdx // Minimal PoW tolerated by the whisper node for a limited time
|
minPowToleranceIdx // Minimal PoW tolerated by the whisper node for a limited time
|
||||||
bloomFilterIdx // Bloom filter for topics of interest for this node
|
bloomFilterIdx // Bloom filter for topics of interest for this node
|
||||||
bloomFilterToleranceIdx // Bloom filter tolerated by the whisper node for a limited time
|
bloomFilterToleranceIdx // Bloom filter tolerated by the whisper node for a limited time
|
||||||
|
lightClientModeIdx // Light client mode. (does not forward any messages)
|
||||||
|
restrictConnectionBetweenLightClientsIdx // Restrict connection between two light clients
|
||||||
)
|
)
|
||||||
|
|
||||||
// Whisper represents a dark communication interface through the Ethereum
|
// Whisper represents a dark communication interface through the Ethereum
|
||||||
|
|
@ -82,8 +84,6 @@ type Whisper struct {
|
||||||
|
|
||||||
syncAllowance int // maximum time in seconds allowed to process the whisper-related messages
|
syncAllowance int // maximum time in seconds allowed to process the whisper-related messages
|
||||||
|
|
||||||
lightClient bool // indicates is this node is pure light client (does not forward any messages)
|
|
||||||
|
|
||||||
statsMu sync.Mutex // guard stats
|
statsMu sync.Mutex // guard stats
|
||||||
stats Statistics // Statistics of whisper node
|
stats Statistics // Statistics of whisper node
|
||||||
|
|
||||||
|
|
@ -113,6 +113,7 @@ func New(cfg *Config) *Whisper {
|
||||||
whisper.settings.Store(minPowIdx, cfg.MinimumAcceptedPOW)
|
whisper.settings.Store(minPowIdx, cfg.MinimumAcceptedPOW)
|
||||||
whisper.settings.Store(maxMsgSizeIdx, cfg.MaxMessageSize)
|
whisper.settings.Store(maxMsgSizeIdx, cfg.MaxMessageSize)
|
||||||
whisper.settings.Store(overflowIdx, false)
|
whisper.settings.Store(overflowIdx, false)
|
||||||
|
whisper.settings.Store(restrictConnectionBetweenLightClientsIdx, cfg.RestrictConnectionBetweenLightClients)
|
||||||
|
|
||||||
// p2p whisper sub protocol handler
|
// p2p whisper sub protocol handler
|
||||||
whisper.protocol = p2p.Protocol{
|
whisper.protocol = p2p.Protocol{
|
||||||
|
|
@ -276,6 +277,31 @@ func (whisper *Whisper) SetMinimumPowTest(val float64) {
|
||||||
whisper.settings.Store(minPowToleranceIdx, val)
|
whisper.settings.Store(minPowToleranceIdx, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//SetLightClientMode makes node light client (does not forward any messages)
|
||||||
|
func (whisper *Whisper) SetLightClientMode(v bool) {
|
||||||
|
whisper.settings.Store(lightClientModeIdx, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
//LightClientMode indicates is this node is light client (does not forward any messages)
|
||||||
|
func (whisper *Whisper) LightClientMode() bool {
|
||||||
|
val, exist := whisper.settings.Load(lightClientModeIdx)
|
||||||
|
if !exist || val == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
v, ok := val.(bool)
|
||||||
|
return v && ok
|
||||||
|
}
|
||||||
|
|
||||||
|
//LightClientModeConnectionRestricted indicates that connection to light client in light client mode not allowed
|
||||||
|
func (whisper *Whisper) LightClientModeConnectionRestricted() bool {
|
||||||
|
val, exist := whisper.settings.Load(restrictConnectionBetweenLightClientsIdx)
|
||||||
|
if !exist || val == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
v, ok := val.(bool)
|
||||||
|
return v && ok
|
||||||
|
}
|
||||||
|
|
||||||
func (whisper *Whisper) notifyPeersAboutPowRequirementChange(pow float64) {
|
func (whisper *Whisper) notifyPeersAboutPowRequirementChange(pow float64) {
|
||||||
arr := whisper.getPeers()
|
arr := whisper.getPeers()
|
||||||
for _, p := range arr {
|
for _, p := range arr {
|
||||||
|
|
@ -672,7 +698,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
||||||
|
|
||||||
trouble := false
|
trouble := false
|
||||||
for _, env := range envelopes {
|
for _, env := range envelopes {
|
||||||
cached, err := whisper.add(env, whisper.lightClient)
|
cached, err := whisper.add(env, whisper.LightClientMode())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
trouble = true
|
trouble = true
|
||||||
log.Error("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err)
|
log.Error("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue