Merge branch 'master' into add-password-to-act

This commit is contained in:
Elad 2018-09-06 10:58:32 +02:00
commit bec7e9de0d
41 changed files with 641 additions and 376 deletions

View file

@ -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.

View file

@ -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:

View file

@ -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",

View file

@ -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 <keyfile> <message/file>`
### `ethkey signmessage <keyfile> <message/file>`
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 <address> <signature> <message/file>`
### `ethkey verifymessage <address> <signature> <message/file>`
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 <keyfile>`
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.

View file

@ -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 {

View file

@ -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)
}

View file

@ -151,6 +151,7 @@ var (
utils.WhisperEnabledFlag,
utils.WhisperMaxMessageSizeFlag,
utils.WhisperMinPOWFlag,
utils.WhisperRestrictConnectionBetweenLightClientsFlag,
}
metricsFlags = []cli.Flag{

View file

@ -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

View file

@ -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)

View file

@ -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.

View file

@ -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
)

View file

@ -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.

View file

@ -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)
}
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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)
}

View file

@ -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
}
}

195
mobile/shhclient.go Normal file
View 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
}

View file

@ -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 }

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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 {

View file

@ -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]<testType>.

View file

@ -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)

View file

@ -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)
}

@ -1 +1 @@
Subproject commit 2bb0c3da3bbb15c528bcef2a7e5ac4bd73f81f87
Subproject commit ad2184adca367c0b68c65b44519dba16e1d0b9e2

View file

@ -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
}

View file

@ -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).

View file

@ -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' __/_'>
( / _/ )_\'>
' "__/ /_/\_>
____/_/_/_/
/,---, _/ /
"" /_/_/_/
/_(_(_(_ \
( \_\_\\_ )\
\'__\_\_\_\__ ).\
//____|___\__) )_/
| _ \'___'_( /'
\_ (-'\'___'_\ __,'_'
__) \ \\___(_ __/.__,'
,((,-,__\ '", __\_/. __,'
'"./_._._-'
```

View file

@ -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()
}

View file

@ -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
View file

@ -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",

View file

@ -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

View file

@ -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,
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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)