diff --git a/.github/no-response.yml b/.github/no-response.yml index a6227159d1..b6e96efdc6 100644 --- a/.github/no-response.yml +++ b/.github/no-response.yml @@ -7,5 +7,5 @@ closeComment: > 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 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 - that we can investigate further. + to take action. Please reach out if you have more relevant information or + answers to our questions so that we can investigate further. diff --git a/accounts/abi/unpack.go b/accounts/abi/unpack.go index 793d515adf..d5875140cc 100644 --- a/accounts/abi/unpack.go +++ b/accounts/abi/unpack.go @@ -25,8 +25,17 @@ import ( "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 -func readInteger(kind reflect.Kind, b []byte) interface{} { +func readInteger(typ byte, kind reflect.Kind, b []byte) interface{} { switch kind { case reflect.Uint8: return b[len(b)-1] @@ -45,7 +54,20 @@ func readInteger(kind reflect.Kind, b []byte) interface{} { case reflect.Int64: return int64(binary.BigEndian.Uint64(b[len(b)-8:])) 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 return string(output[begin : begin+end]), nil case IntTy, UintTy: - return readInteger(t.Kind, returnOutput), nil + return readInteger(t.T, t.Kind, returnOutput), nil case BoolTy: return readBool(returnOutput) case AddressTy: diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go index bdbab10b4f..97552b90cf 100644 --- a/accounts/abi/unpack_test.go +++ b/accounts/abi/unpack_test.go @@ -117,6 +117,11 @@ var unpackTests = []unpackTest{ enc: "0000000000000000000000000000000000000000000000000000000000000001", want: big.NewInt(1), }, + { + def: `[{"type": "int256"}]`, + enc: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: big.NewInt(-1), + }, { def: `[{"type": "address"}]`, enc: "0000000000000000000000000100000000000000000000000000000000000000", diff --git a/cmd/ethkey/README.md b/cmd/ethkey/README.md index cf72ba43d7..48d3c9e9b7 100644 --- a/cmd/ethkey/README.md +++ b/cmd/ethkey/README.md @@ -21,21 +21,33 @@ Private key information can be printed by using the `--private` flag; make sure to use this feature with great caution! -### `ethkey sign ` +### `ethkey signmessage ` Sign the message with a keyfile. 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
` +### `ethkey verifymessage
` Verify the signature of 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 ` + +Change the passphrase of a keyfile. +use the `--newpasswordfile` to point to the new password file. ## Passphrases 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 -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. + +## JSON + +In case you need to output the result in a JSON format, you shall by using the `--json` flag. diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go index 6799060275..cfe4e45f11 100644 --- a/cmd/faucet/faucet.go +++ b/cmd/faucet/faucet.go @@ -157,7 +157,8 @@ func main() { if blob, err = ioutil.ReadFile(*accPassFlag); err != nil { 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) if blob, err = ioutil.ReadFile(*accJSONFlag); err != nil { diff --git a/cmd/geth/config.go b/cmd/geth/config.go index e6bd4d5bef..b0749d2329 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -168,6 +168,9 @@ func makeFullNode(ctx *cli.Context) *node.Node { if ctx.GlobalIsSet(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) } diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 4d7e98698c..134d5a4c01 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -151,6 +151,7 @@ var ( utils.WhisperEnabledFlag, utils.WhisperMaxMessageSizeFlag, utils.WhisperMinPOWFlag, + utils.WhisperRestrictConnectionBetweenLightClientsFlag, } metricsFlags = []cli.Flag{ diff --git a/cmd/swarm/access_test.go b/cmd/swarm/access_test.go index aa2c0384a0..106e6dd912 100644 --- a/cmd/swarm/access_test.go +++ b/cmd/swarm/access_test.go @@ -33,6 +33,7 @@ import ( "time" "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/log" "github.com/ethereum/go-ethereum/swarm/api" @@ -45,6 +46,8 @@ const ( data = "notsorandomdata" ) +var DefaultCurve = crypto.S256() + // 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 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 { t.Fatal("manifest access kdf params is nil") } - + if a.Publisher != "" { + t.Fatal("should be empty") + } client := swarm.NewClient(cluster.Nodes[0].URL) 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. func TestAccessPK(t *testing.T) { // Setup Swarm and upload a test file to it - cluster := newTestCluster(t, 1) + cluster := newTestCluster(t, 2) defer cluster.Shutdown() dataFilename := testutil.TempFileWithContent(t, data) @@ -263,6 +268,20 @@ func TestAccessPK(t *testing.T) { 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 err = json.Unmarshal([]byte(matches[0]), &m) @@ -296,7 +315,9 @@ func TestAccessPK(t *testing.T) { if a.KdfParams != 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) 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 // 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. // 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. -func TestAccessACT(t *testing.T) { +func testAccessACT(t *testing.T, bogusEntries int) { // Setup Swarm and upload a test file to it const clusterSize = 3 cluster := newTestCluster(t, clusterSize) @@ -367,6 +398,23 @@ func TestAccessACT(t *testing.T) { 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")) defer os.RemoveAll(granteesPubkeyListFile) @@ -406,6 +454,22 @@ func TestAccessACT(t *testing.T) { if len(matches) == 0 { 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] m, _, err := client.DownloadManifest(hash) 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)) } + if a.Publisher != pkComp { + t.Fatal("publisher key did not match") + } httpClient := &http.Client{} // all nodes except the skipped node should be able to decrypt the content diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index bc87b533d3..c93344c420 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -18,6 +18,7 @@ package main import ( "crypto/ecdsa" + "encoding/hex" "fmt" "io/ioutil" "os" @@ -208,6 +209,10 @@ var ( Name: "data", 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 @@ -252,6 +257,14 @@ func init() { Usage: "Print version numbers", 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, 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 { fmt.Println(strings.Title(clientIdentifier)) fmt.Println("Version:", sv.VersionWithMeta) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 495bfe13e0..f431621baf 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -567,6 +567,10 @@ var ( Usage: "Minimum POW accepted", Value: whisper.DefaultMinimumPoW, } + WhisperRestrictConnectionBetweenLightClientsFlag = cli.BoolFlag{ + Name: "shh.restrict-light", + Usage: "Restrict connection between two whisper light clients", + } // Metrics flags MetricsEnabledFlag = cli.BoolFlag{ @@ -1099,6 +1103,9 @@ func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) { if ctx.GlobalIsSet(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. diff --git a/common/types.go b/common/types.go index 71fe5c95cd..a4b9995267 100644 --- a/common/types.go +++ b/common/types.go @@ -34,7 +34,7 @@ import ( const ( // HashLength is the expected length of the hash HashLength = 32 - // AddressLength is the expected length of the adddress + // AddressLength is the expected length of the address AddressLength = 20 ) diff --git a/consensus/consensus.go b/consensus/consensus.go index 12ede7ff46..487b07be77 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -90,7 +90,7 @@ type Engine interface { // the result into the given channel. // // 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 // SealHash returns the hash of a block prior to it being sealed. diff --git a/consensus/ethash/sealer_test.go b/consensus/ethash/sealer_test.go index 31d18b67c7..436359af7c 100644 --- a/consensus/ethash/sealer_test.go +++ b/consensus/ethash/sealer_test.go @@ -40,6 +40,18 @@ func TestRemoteNotify(t *testing.T) { 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 ethash := NewTester([]string{"http://" + listener.Addr().String()}, false) defer ethash.Close() @@ -61,7 +73,7 @@ func TestRemoteNotify(t *testing.T) { if want := common.BytesToHash(target.Bytes()).Hex(); 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") } } @@ -108,7 +120,7 @@ func TestRemoteMultiNotify(t *testing.T) { for i := 0; i < cap(sink); i++ { select { case <-sink: - case <-time.After(250 * time.Millisecond): + case <-time.After(3 * time.Second): t.Fatalf("notification %d timed out", i) } } diff --git a/core/blockchain.go b/core/blockchain.go index 0461da7fd9..63f60ca280 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "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/core/rawdb" "github.com/ethereum/go-ethereum/core/state" @@ -43,7 +44,6 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/hashicorp/golang-lru" - "gopkg.in/karalabe/cookiejar.v2/collections/prque" ) var ( @@ -151,7 +151,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par chainConfig: chainConfig, cacheConfig: cacheConfig, db: db, - triegc: prque.New(), + triegc: prque.New(nil), stateCache: state.NewDatabase(db), quit: make(chan struct{}), bodyCache: bodyCache, @@ -915,7 +915,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. } else { // Full but not archive node, do proper garbage collection 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 we exceeded our memory allowance, flush matured singleton nodes to disk diff --git a/core/state_processor.go b/core/state_processor.go index 1a91a57ab4..503a35d16a 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -110,7 +110,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo *usedGas += gas // 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.TxHash = tx.Hash() receipt.GasUsed = gas diff --git a/core/tx_pool.go b/core/tx_pool.go index 46ae2759bc..a0a6ff8510 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -26,13 +26,13 @@ import ( "time" "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/types" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" - "gopkg.in/karalabe/cookiejar.v2/collections/prque" ) const ( @@ -987,11 +987,11 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) { if pending > pool.config.GlobalSlots { pendingBeforeCap := pending // Assemble a spam order to penalize large transactors first - spammers := prque.New() + spammers := prque.New(nil) for addr, list := range pool.pending { // Only evict transactions from high rollers 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 diff --git a/core/vm/instructions.go b/core/vm/instructions.go index b7742e6551..ca9e775ac5 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -355,7 +355,7 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory * defer interpreter.intPool.put(shift) // First operand back into the pool if shift.Cmp(common.Big256) >= 0 { - if value.Sign() > 0 { + if value.Sign() >= 0 { value.SetUint64(0) } else { value.SetInt64(-1) diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 8529535ba7..a0e8a6d48c 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -26,10 +26,10 @@ import ( "time" "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/log" "github.com/ethereum/go-ethereum/metrics" - "gopkg.in/karalabe/cookiejar.v2/collections/prque" ) var ( @@ -105,11 +105,11 @@ func newQueue() *queue { headerPendPool: make(map[string]*fetchRequest), headerContCh: make(chan bool), blockTaskPool: make(map[common.Hash]*types.Header), - blockTaskQueue: prque.New(), + blockTaskQueue: prque.New(nil), blockPendPool: make(map[string]*fetchRequest), blockDonePool: make(map[common.Hash]struct{}), receiptTaskPool: make(map[common.Hash]*types.Header), - receiptTaskQueue: prque.New(), + receiptTaskQueue: prque.New(nil), receiptPendPool: make(map[string]*fetchRequest), receiptDonePool: make(map[common.Hash]struct{}), 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 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.headerResults = make([]*types.Header, len(skeleton)*MaxHeaderFetch) q.headerProced = 0 @@ -288,7 +288,7 @@ func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) { index := from + uint64(i*MaxHeaderFetch) 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 q.blockTaskPool[hash] = header - q.blockTaskQueue.Push(header, -float32(header.Number.Uint64())) + q.blockTaskQueue.Push(header, -int64(header.Number.Uint64())) if q.mode == FastSync { q.receiptTaskPool[hash] = header - q.receiptTaskQueue.Push(header, -float32(header.Number.Uint64())) + q.receiptTaskQueue.Push(header, -int64(header.Number.Uint64())) } inserts = append(inserts, header) q.headerHead = hash @@ -436,7 +436,7 @@ func (q *queue) ReserveHeaders(p *peerConnection, count int) *fetchRequest { } // Merge all the skipped batches back for _, from := range skip { - q.headerTaskQueue.Push(from, -float32(from)) + q.headerTaskQueue.Push(from, -int64(from)) } // Assemble and return the block download request if send == 0 { @@ -542,7 +542,7 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common } // Merge all the skipped headers back for _, header := range skip { - taskQueue.Push(header, -float32(header.Number.Uint64())) + taskQueue.Push(header, -int64(header.Number.Uint64())) } if progress { // Wake WaitResults, resultCache was modified @@ -585,10 +585,10 @@ func (q *queue) cancel(request *fetchRequest, taskQueue *prque.Prque, pendPool m defer q.lock.Unlock() if request.From > 0 { - taskQueue.Push(request.From, -float32(request.From)) + taskQueue.Push(request.From, -int64(request.From)) } for _, header := range request.Headers { - taskQueue.Push(header, -float32(header.Number.Uint64())) + taskQueue.Push(header, -int64(header.Number.Uint64())) } delete(pendPool, request.Peer.id) } @@ -602,13 +602,13 @@ func (q *queue) Revoke(peerID string) { if request, ok := q.blockPendPool[peerID]; ok { 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) } if request, ok := q.receiptPendPool[peerID]; ok { 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) } @@ -657,10 +657,10 @@ func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, // Return any non satisfied requests to the pool if request.From > 0 { - taskQueue.Push(request.From, -float32(request.From)) + taskQueue.Push(request.From, -int64(request.From)) } 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 expiries[id] = len(request.Headers) @@ -731,7 +731,7 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh } 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") } // 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 for _, header := range request.Headers { if header != nil { - taskQueue.Push(header, -float32(header.Number.Uint64())) + taskQueue.Push(header, -int64(header.Number.Uint64())) } } // Wake up WaitResults diff --git a/eth/fetcher/fetcher.go b/eth/fetcher/fetcher.go index 277f14b81a..f0b5e8064b 100644 --- a/eth/fetcher/fetcher.go +++ b/eth/fetcher/fetcher.go @@ -23,10 +23,10 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" - "gopkg.in/karalabe/cookiejar.v2/collections/prque" ) const ( @@ -160,7 +160,7 @@ func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, broadcastBloc fetching: make(map[common.Hash]*announce), fetched: make(map[common.Hash][]*announce), completing: make(map[common.Hash]*announce), - queue: prque.New(), + queue: prque.New(nil), queues: make(map[string]int), queued: make(map[common.Hash]*inject), getBlock: getBlock, @@ -299,7 +299,7 @@ func (f *Fetcher) loop() { // If too high up the chain or phase, continue later number := op.block.NumberU64() if number > height+1 { - f.queue.Push(op, -float32(number)) + f.queue.Push(op, -int64(number)) if f.queueChangeHook != nil { f.queueChangeHook(hash, true) } @@ -624,7 +624,7 @@ func (f *Fetcher) enqueue(peer string, block *types.Block) { } f.queues[peer] = count f.queued[hash] = op - f.queue.Push(op, -float32(block.NumberU64())) + f.queue.Push(op, -int64(block.NumberU64())) if f.queueChangeHook != nil { f.queueChangeHook(op.block.Hash(), true) } diff --git a/mobile/ethclient.go b/mobile/ethclient.go index 66399c6b56..662125c4ad 100644 --- a/mobile/ethclient.go +++ b/mobile/ethclient.go @@ -138,7 +138,9 @@ func (ec *EthereumClient) SubscribeNewHead(ctx *Context, handler NewHeadHandler, handler.OnNewHead(&Header{header}) case err := <-rawSub.Err(): - handler.OnError(err.Error()) + if err != nil { + handler.OnError(err.Error()) + } return } } @@ -227,7 +229,9 @@ func (ec *EthereumClient) SubscribeFilterLogs(ctx *Context, query *FilterQuery, handler.OnFilterLogs(&Log{&log}) case err := <-rawSub.Err(): - handler.OnError(err.Error()) + if err != nil { + handler.OnError(err.Error()) + } return } } diff --git a/mobile/shhclient.go b/mobile/shhclient.go new file mode 100644 index 0000000000..a069c9bd40 --- /dev/null +++ b/mobile/shhclient.go @@ -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 . + +// 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 +} diff --git a/mobile/types.go b/mobile/types.go index b2780f3076..443d07ea93 100644 --- a/mobile/types.go +++ b/mobile/types.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "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 @@ -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) GetContractAddress() *Address { return &Address{r.receipt.ContractAddress} } 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 } diff --git a/p2p/discv5/net.go b/p2p/discv5/net.go index b93c93d648..a6cabf0803 100644 --- a/p2p/discv5/net.go +++ b/p2p/discv5/net.go @@ -1228,7 +1228,7 @@ func (net *Network) checkTopicRegister(data *topicRegister) (*pong, error) { if rlpHash(data.Topics) != pongpkt.data.(*pong).TopicHash { 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 pongpkt.data.(*pong), nil diff --git a/params/config.go b/params/config.go index 70a1edead4..629720550a 100644 --- a/params/config.go +++ b/params/config.go @@ -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) } -// 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. // // Rules is a one time interface meaning that it shouldn't be used in between transition diff --git a/rpc/client.go b/rpc/client.go index a2ef2ed6b6..d96189a2d8 100644 --- a/rpc/client.go +++ b/rpc/client.go @@ -487,6 +487,7 @@ func (c *Client) write(ctx context.Context, msg interface{}) error { } c.writeConn.SetWriteDeadline(deadline) err := json.NewEncoder(c.writeConn).Encode(msg) + c.writeConn.SetWriteDeadline(time.Time{}) if err != nil { c.writeConn = nil } diff --git a/tests/block_test.go b/tests/block_test.go index 669d3ca08a..c911199299 100644 --- a/tests/block_test.go +++ b/tests/block_test.go @@ -30,11 +30,11 @@ func TestBlockchain(t *testing.T) { bt.skipLoad(`^bcForgedTest/bcForkUncle\.json`) bt.skipLoad(`^bcMultiChainTest/(ChainAtoChainB_blockorder|CallContractFromNotBestBlock)`) bt.skipLoad(`^bcTotalDifficultyTest/(lotsOfLeafs|lotsOfBranches|sideChainWithMoreTransactions)`) - // Constantinople is not implemented yet. - bt.skipLoad(`(?i)(constantinople)`) + // This test is broken + bt.fails(`blockhashNonConstArg_Constantinople`, "Broken test") // Still failing tests - bt.skipLoad(`^bcWalletTest.*_Byzantium$`) + // bt.skipLoad(`^bcWalletTest.*_Byzantium$`) bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) { if err := bt.checkFailure(t, name, test.Run()); err != nil { diff --git a/tests/init_test.go b/tests/init_test.go index 26e919d24b..90a74448a8 100644 --- a/tests/init_test.go +++ b/tests/init_test.go @@ -91,6 +91,7 @@ type testMatcher struct { failpat []testFailure skiploadpat []*regexp.Regexp skipshortpat []*regexp.Regexp + whitelistpat *regexp.Regexp } 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}) } +func (tm *testMatcher) whitelist(pattern string) { + tm.whitelistpat = regexp.MustCompile(pattern) +} + // config defines chain config for tests matching the pattern. func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) { 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 != "" { t.Skip(r) } + if tm.whitelistpat != nil { + if !tm.whitelistpat.MatchString(name) { + t.Skip("Skipped by whitelist") + } + } t.Parallel() // Load the file as map[string]. diff --git a/tests/state_test.go b/tests/state_test.go index adec4feb2b..b61a1ca285 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -44,9 +44,6 @@ func TestState(t *testing.T) { key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index) name := name + "/" + key 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 { _, err := test.Run(subtest, vmconfig) return st.checkFailure(t, name, err) diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 84581fae18..5d2251e529 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -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) { 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) { return statedb, fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root) } diff --git a/tests/testdata b/tests/testdata index 2bb0c3da3b..ad2184adca 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit 2bb0c3da3bbb15c528bcef2a7e5ac4bd73f81f87 +Subproject commit ad2184adca367c0b68c65b44519dba16e1d0b9e2 diff --git a/trie/sync.go b/trie/sync.go index 88d6eb7799..67dff5a8b6 100644 --- a/trie/sync.go +++ b/trie/sync.go @@ -21,8 +21,8 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/prque" "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 @@ -84,7 +84,7 @@ func NewSync(root common.Hash, database DatabaseReader, callback LeafCallback) * database: database, membatch: newSyncMemBatch(), requests: make(map[common.Hash]*request), - queue: prque.New(), + queue: prque.New(nil), } ts.AddSubTrie(root, 0, common.Hash{}, callback) return ts @@ -242,7 +242,7 @@ func (s *Sync) schedule(req *request) { return } // 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 } diff --git a/vendor/gopkg.in/karalabe/cookiejar.v2/LICENSE b/vendor/gopkg.in/karalabe/cookiejar.v2/LICENSE deleted file mode 100755 index 467d60878d..0000000000 --- a/vendor/gopkg.in/karalabe/cookiejar.v2/LICENSE +++ /dev/null @@ -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). diff --git a/vendor/gopkg.in/karalabe/cookiejar.v2/README.md b/vendor/gopkg.in/karalabe/cookiejar.v2/README.md deleted file mode 100755 index 44c420b60e..0000000000 --- a/vendor/gopkg.in/karalabe/cookiejar.v2/README.md +++ /dev/null @@ -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' __/_'> - ( / _/ )_\'> - ' "__/ /_/\_> - ____/_/_/_/ - /,---, _/ / - "" /_/_/_/ - /_(_(_(_ \ - ( \_\_\\_ )\ - \'__\_\_\_\__ ).\ - //____|___\__) )_/ - | _ \'___'_( /' - \_ (-'\'___'_\ __,'_' - __) \ \\___(_ __/.__,' - ,((,-,__\ '", __\_/. __,' - '"./_._._-' -``` diff --git a/vendor/gopkg.in/karalabe/cookiejar.v2/collections/prque/prque.go b/vendor/gopkg.in/karalabe/cookiejar.v2/collections/prque/prque.go deleted file mode 100755 index 5c1967c658..0000000000 --- a/vendor/gopkg.in/karalabe/cookiejar.v2/collections/prque/prque.go +++ /dev/null @@ -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() -} diff --git a/vendor/gopkg.in/karalabe/cookiejar.v2/collections/prque/sstack.go b/vendor/gopkg.in/karalabe/cookiejar.v2/collections/prque/sstack.go deleted file mode 100755 index 9f393196ef..0000000000 --- a/vendor/gopkg.in/karalabe/cookiejar.v2/collections/prque/sstack.go +++ /dev/null @@ -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() -} diff --git a/vendor/vendor.json b/vendor/vendor.json index fea2c804ea..6e5610460a 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -891,12 +891,6 @@ "revision": "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec", "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=", "path": "gopkg.in/natefinch/npipe.v2", diff --git a/whisper/whisperv6/api.go b/whisper/whisperv6/api.go index d729e79c35..e1dab7b226 100644 --- a/whisper/whisperv6/api.go +++ b/whisper/whisperv6/api.go @@ -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 // any incoming messages, and sends only messages originated in this node. func (api *PublicWhisperAPI) MakeLightClient(ctx context.Context) bool { - api.w.lightClient = true - return api.w.lightClient + api.w.SetLightClientMode(true) + return api.w.LightClientMode() } // CancelLightClient cancels light client mode. func (api *PublicWhisperAPI) CancelLightClient(ctx context.Context) bool { - api.w.lightClient = false - return !api.w.lightClient + api.w.SetLightClientMode(false) + return !api.w.LightClientMode() } //go:generate gencodec -type NewMessage -field-override newMessageOverride -out gen_newmessage_json.go diff --git a/whisper/whisperv6/config.go b/whisper/whisperv6/config.go index 61419de007..38eb9551cc 100644 --- a/whisper/whisperv6/config.go +++ b/whisper/whisperv6/config.go @@ -18,12 +18,14 @@ package whisperv6 // Config represents the configuration state of a whisper node. type Config struct { - MaxMessageSize uint32 `toml:",omitempty"` - MinimumAcceptedPOW float64 `toml:",omitempty"` + MaxMessageSize uint32 `toml:",omitempty"` + MinimumAcceptedPOW float64 `toml:",omitempty"` + RestrictConnectionBetweenLightClients bool `toml:",omitempty"` } // DefaultConfig represents (shocker!) the default configuration. var DefaultConfig = Config{ - MaxMessageSize: DefaultMaxMessageSize, - MinimumAcceptedPOW: DefaultMinimumPoW, + MaxMessageSize: DefaultMaxMessageSize, + MinimumAcceptedPOW: DefaultMinimumPoW, + RestrictConnectionBetweenLightClients: true, } diff --git a/whisper/whisperv6/peer.go b/whisper/whisperv6/peer.go index 79cc212708..621d512081 100644 --- a/whisper/whisperv6/peer.go +++ b/whisper/whisperv6/peer.go @@ -79,11 +79,14 @@ func (peer *Peer) stop() { func (peer *Peer) handshake() error { // Send the handshake status message asynchronously errc := make(chan error, 1) + isLightNode := peer.host.LightClientMode() + isRestrictedLightNodeConnection := peer.host.LightClientModeConnectionRestricted() go func() { pow := peer.host.MinPow() powConverted := math.Float64bits(pow) 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 @@ -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 { return fmt.Errorf("peer [%x] failed to send status packet: %v", peer.ID(), err) } diff --git a/whisper/whisperv6/peer_test.go b/whisper/whisperv6/peer_test.go index 0c9b380901..fe31922cb2 100644 --- a/whisper/whisperv6/peer_test.go +++ b/whisper/whisperv6/peer_test.go @@ -34,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/rlp" ) var keys = []string{ @@ -507,3 +508,63 @@ func waitForServersToStart(t *testing.T) { } 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 +} diff --git a/whisper/whisperv6/whisper.go b/whisper/whisperv6/whisper.go index be633e7ff6..eb713f84ee 100644 --- a/whisper/whisperv6/whisper.go +++ b/whisper/whisperv6/whisper.go @@ -49,12 +49,14 @@ type Statistics struct { } const ( - maxMsgSizeIdx = iota // Maximal message length allowed by the whisper node - overflowIdx // Indicator of message queue overflow - minPowIdx // Minimal PoW required by the whisper node - minPowToleranceIdx // Minimal PoW tolerated by the whisper node for a limited time - bloomFilterIdx // Bloom filter for topics of interest for this node - bloomFilterToleranceIdx // Bloom filter tolerated by the whisper node for a limited time + maxMsgSizeIdx = iota // Maximal message length allowed by the whisper node + overflowIdx // Indicator of message queue overflow + minPowIdx // Minimal PoW required by the whisper node + minPowToleranceIdx // Minimal PoW tolerated by the whisper node for a limited time + bloomFilterIdx // Bloom filter for topics of interest for this node + 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 @@ -82,8 +84,6 @@ type Whisper struct { 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 stats Statistics // Statistics of whisper node @@ -113,6 +113,7 @@ func New(cfg *Config) *Whisper { whisper.settings.Store(minPowIdx, cfg.MinimumAcceptedPOW) whisper.settings.Store(maxMsgSizeIdx, cfg.MaxMessageSize) whisper.settings.Store(overflowIdx, false) + whisper.settings.Store(restrictConnectionBetweenLightClientsIdx, cfg.RestrictConnectionBetweenLightClients) // p2p whisper sub protocol handler whisper.protocol = p2p.Protocol{ @@ -276,6 +277,31 @@ func (whisper *Whisper) SetMinimumPowTest(val float64) { 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) { arr := whisper.getPeers() for _, p := range arr { @@ -672,7 +698,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { trouble := false for _, env := range envelopes { - cached, err := whisper.add(env, whisper.lightClient) + cached, err := whisper.add(env, whisper.LightClientMode()) if err != nil { trouble = true log.Error("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err)