diff --git a/cmd/swarm/run_test.go b/cmd/swarm/run_test.go index 680d238d06..74c3a35e36 100644 --- a/cmd/swarm/run_test.go +++ b/cmd/swarm/run_test.go @@ -36,17 +36,22 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/internal/cmdtest" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/swarm" "github.com/ethereum/go-ethereum/swarm/api" swarmhttp "github.com/ethereum/go-ethereum/swarm/api/http" + colorable "github.com/mattn/go-colorable" ) var loglevel = flag.Int("loglevel", 3, "verbosity of logs") func init() { + log.PrintOrigins(true) + log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))) + // Run the app if we've been exec'd as "swarm-test" in runSwarm. reexec.Register("swarm-test", func() { if err := app.Run(os.Args); err != nil { diff --git a/cmd/swarm/upload_test.go b/cmd/swarm/upload_test.go index 616486e37c..03b14bca80 100644 --- a/cmd/swarm/upload_test.go +++ b/cmd/swarm/upload_test.go @@ -33,14 +33,8 @@ import ( "github.com/ethereum/go-ethereum/log" swarmapi "github.com/ethereum/go-ethereum/swarm/api/client" "github.com/ethereum/go-ethereum/swarm/testutil" - "github.com/mattn/go-colorable" ) -func init() { - log.PrintOrigins(true) - log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))) -} - func TestSwarmUp(t *testing.T) { if runtime.GOOS == "windows" { t.Skip() diff --git a/swarm/network/discovery.go b/swarm/network/discovery.go index 85b1fbf39f..3c9038cd64 100644 --- a/swarm/network/discovery.go +++ b/swarm/network/discovery.go @@ -57,8 +57,9 @@ func NewPeer(p *BzzPeer, kad *Kademlia) *Peer { exchange: &exchange{ peers: make(map[string]bool), }, - kad: kad, - BzzPeer: p, + kad: kad, + BzzPeer: p, + connectivity: &connectivity{seenAt: time.Now()}, } // record remote as seen so we never send a peer its own record d.seen(p.BzzAddr) diff --git a/swarm/network/hive.go b/swarm/network/hive.go index 2a4b845a43..0a386e63a3 100644 --- a/swarm/network/hive.go +++ b/swarm/network/hive.go @@ -17,6 +17,7 @@ package network import ( + "encoding/hex" "fmt" "math/rand" "sync" @@ -55,8 +56,7 @@ func NewHiveParams() *HiveParams { MaxPeersPerRequest: 5, KeepAliveInterval: 500 * time.Millisecond, RetryInterval: 4200000000, // 4.2 sec - //RetryExponent: 2, - RetryExponent: 3, + RetryExponent: 3, } } @@ -82,7 +82,6 @@ func NewHive(params *HiveParams, kad *Kademlia, store state.Store) *Hive { HiveParams: params, Kademlia: kad, Store: store, - //peers: make(map[enode.ID]*BzzPeer), // TODO bzzpeer addr should be fixed length so we can use it in indices without casting/formatting to string peers: make(map[string]*Peer), } @@ -138,7 +137,6 @@ func (h *Hive) Stop() error { // as well as advertises saturation depth if needed func (h *Hive) connect() { for range h.ticker.C { - addr, depth, changed := h.SuggestPeer() if h.Discovery && changed { NotifyDepth(uint8(depth), h.Kademlia) @@ -158,10 +156,28 @@ func (h *Hive) connect() { } } +func (h *Hive) registerPeer(p *Peer) (int, bool) { + addr := hex.EncodeToString(p.BzzPeer.OAddr) + log.Trace("hive.registerPeer", "addr", addr) + h.peers[addr] = p + return h.On(p) +} + +func (h *Hive) getPeer(bzzAddr *BzzAddr) *Peer { + addr := hex.EncodeToString(bzzAddr.OAddr) + log.Trace("hive.getPeer", "addr", addr) + p, ok := h.peers[addr] + if !ok { + panic("requested peer cannot be found in the known peer map") + } + + return p +} + // Run protocol run function func (h *Hive) Run(p *BzzPeer) error { dp := NewPeer(p, h.Kademlia) - depth, changed := h.On(dp) + depth, changed := h.registerPeer(dp) // if we want discovery, advertise change of depth if h.Discovery { if changed { @@ -259,6 +275,17 @@ func (h *Hive) suggestPeers() []*Peer { // // if no neighbours are found, peers from the shallowest bin that has unconnected pers // (who also have exceeded time delay) are returned +// peers are added in the following manner +// DEPTH +// + +// | ADDED FIRST +// | <------------------+ +// | +//SHALLOW +-------------------------------------+ D E E P +// (0) | (256) +// +----------> + +// ADDED LATER + func (h *Hive) getPotentialPeers() []*Peer { // record the peers to report back as callable @@ -266,24 +293,26 @@ func (h *Hive) getPotentialPeers() []*Peer { // our kademlia depth right now depth := h.Kademlia.NeighbourhoodDepth() - + log.Trace("getting potential peers", "depth", depth) // first check the neighbours potentialPeers := h.getPotentialNeighbours(depth) + for _, peer := range potentialPeers { if h.isTimeForRetry(peer) { callablePeers = append(callablePeers, peer) } } if len(callablePeers) > 0 { + log.Trace("got callable neighbours returning", "depth", depth) + return callablePeers } // lastPotentialBin keeps track of the last bin we got peers from - // initially setting it to -1 means it will always be run once - // and thus still process the neighbours if we have any. // if the depth is 0 then only neighbours are relevant // if we hit depth then we break out of the loop (with no peers) - for lastPotentialBin := -1; lastPotentialBin < depth; { + for lastPotentialBin := 0; lastPotentialBin < depth; lastPotentialBin++ { + potentialPeers, _ = h.getPotentialBinPeers(lastPotentialBin, depth) // among the latest retrieved potential peers // add any that have exceeded time delay for reconnect @@ -292,33 +321,39 @@ func (h *Hive) getPotentialPeers() []*Peer { callablePeers = append(callablePeers, peer) } } - - // if we now actally have peers that can be called - // then break out and return them - if len(callablePeers) > 0 { - break - } - - // - lastPotentialBin++ - - // otherwise, revert to bins shallower than depth - for len(potentialPeers) == 0 && lastPotentialBin < depth { - potentialPeers, lastPotentialBin = h.getPotentialBinPeers(lastPotentialBin, depth) - } } // return the catch of the day ... ehm ... tick return callablePeers } +func insertSort(arr []interface{}, comp func(interface{}, interface{}) bool) { + size := len(arr) + var temp, i, j interface{} + for i = 1; i < size; i++ { + temp = arr[i] + for j = i; j > 0 && comp(arr[j-1], temp); j-- { + arr[j] = arr[j-1] + } + arr[j] = temp + } +} + // returns all neighbors not connected to that should be func (h *Hive) getPotentialNeighbours(depth int) []*Peer { var neighbours []*Peer h.Kademlia.EachAddr(nil, 255, func(addr *BzzAddr, po int, _ bool) bool { + if po < depth { + return false + } + if !h.Kademlia.Connected(addr) { - p := h.peers[string(addr.Address())] + addr := hex.EncodeToString(addr.OAddr) + p, ok := h.peers[addr] + if !ok { + panic("not ok bro") + } neighbours = append(neighbours, p) } return true @@ -333,11 +368,12 @@ func (h *Hive) getPotentialNeighbours(depth int) []*Peer { // if the returned array is nil, we reached depth without finding any // TODO consider a separate pot where retrycount is part of the sorted value func (h *Hive) getPotentialBinPeers(offset int, depth int) ([]*Peer, int) { + log.Debug("hive.getPotentialBinPeers", "base", fmt.Sprintf("%08x", h.BaseAddr()[:4]), "offset", offset, "depth", depth) // record all peers that can and should be connected to var peers []*Peer - // keeps track of which po the last iteration matched + // keeps track of which po the last iteration matched in order to detect an empty bin seqPo := -1 // records the bin with the fewest peers @@ -345,21 +381,21 @@ func (h *Hive) getPotentialBinPeers(offset int, depth int) ([]*Peer, int) { // records the size of the slimBin slimBinSize := h.Kademlia.MinBinSize + //TODO seqPo and empty bin? isn't this just depth? why do we need this? // find the bin shallower than depth that has the LEAST peers // TODO refactor in order to avoid accessing hidden member of kademlia - h.Kademlia.conns.EachBin(nil, Pof, offset, func(po int, size int, f func(func(pot.Val, int) bool) bool) bool { + h.Kademlia.conns.EachBin(h.Kademlia.base, Pof, offset, func(po int, size int, f func(func(pot.Val, int) bool) bool) bool { seqPo++ - // stop if we reach depth, because peers from that point and deeper will be treated differently if depth >= po { - //TODO how to handle simbin -1 here return true } // if we detect an empty bin we can short circuit and return those results immetdiately if seqPo != po { - slimBin = po + log.Trace("detected empty bin", "seqPo", seqPo, "po", po) + slimBin = seqPo slimBinSize = 0 return false } @@ -367,29 +403,32 @@ func (h *Hive) getPotentialBinPeers(offset int, depth int) ([]*Peer, int) { // if size is less than optimal and smallest size we've seen so far // record this bin and its size if size < h.Kademlia.MinBinSize && size < slimBinSize { - slimBin = po + slimBin = seqPo slimBinSize = size } return true }) + log.Trace("processed conns", "slim bin", slimBin, "slimBinSize", slimBinSize, "seqPo", seqPo) + // if no bins have peers to match if slimBin == -1 { return nil, 0 } // if we found a bin to process, fill up with all unconnected peers in that bin - h.Kademlia.EachAddr(nil, slimBin, func(addr *BzzAddr, po int, _ bool) bool { + h.Kademlia.EachAddr(h.Kademlia.base, slimBin, func(addr *BzzAddr, po int, _ bool) bool { if po != slimBin { return false } if !h.Kademlia.Connected(addr) { - bzzAddrPeerIdx := string(addr.Address()) - peers = append(peers, h.peers[bzzAddrPeerIdx]) + p := h.getPeer(addr) + if h.isTimeForRetry(p) { + peers = append(peers, p) + } } return true }) - return peers, slimBin } @@ -401,76 +440,76 @@ func (h *Hive) SuggestPeer() (a *BzzAddr, o int, want bool) { defer h.lock.Unlock() return nil, 0, false - // minsize := k.MinBinSize - // depth := depthForPot(k.conns, k.MinProxBinSize, k.base) - // // if there is a callable neighbour within the current proxBin, connect - // // this makes sure nearest neighbour set is fully connected - // var ppo int - // k.addrs.EachNeighbour(k.base, Pof, func(val pot.Val, po int) bool { - // if po < depth { - // return false - // } - // e := val.(*entry) - // c := k.callable(e) - // if c { - // a = e.BzzAddr - // } - // ppo = po - // return !c - // }) - // if a != nil { - // log.Trace(fmt.Sprintf("%08x candidate nearest neighbour found: %v (%v)", k.BaseAddr()[:4], a, ppo)) - // return a, 0, false - // } - // - // var bpo []int - // prev := -1 - // k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool { - // prev++ - // for ; prev < po; prev++ { - // bpo = append(bpo, prev) - // minsize = 0 - // } - // if size < minsize { - // bpo = append(bpo, po) - // minsize = size - // } - // return size > 0 && po < depth - // }) - // // all buckets are full, ie., minsize == k.MinBinSize - // if len(bpo) == 0 { - // return nil, 0, false - // } - // // as long as we got candidate peers to connect to - // // dont ask for new peers (want = false) - // // try to select a candidate peer - // // find the first callable peer - // nxt := bpo[0] - // k.addrs.EachBin(k.base, Pof, nxt, func(po, _ int, f func(func(pot.Val, int) bool) bool) bool { - // // for each bin (up until depth) we find callable candidate peers - // if po >= depth { - // return false - // } - // return f(func(val pot.Val, _ int) bool { - // e := val.(*entry) - // c := k.callable(e) - // if c { - // a = e.BzzAddr - // } - // return !c - // }) - // }) - // // found a candidate - // if a != nil { - // return a, 0, false - // } - // // no candidate peer found, request for the short bin - // var changed bool - // if nxt < k.depth { - // k.depth = nxt - // changed = true - // } - // return a, nxt, changed + /* minsize := k.MinBinSize + lastbin = -1 + depth := depthForPot(k.conns, k.MinProxBinSize, k.base) + if there is a callable neighbour within the current proxBin, connect + this makes sure nearest neighbour set is fully connected + var ppo int + k.addrs.EachNeighbour(k.base, Pof, func(val pot.Val, po int) bool { + if lastbin > 0 && po != lastbin { return false} + e := val.(*entry) + if its connected => counter++, if == minProxBinSize, set the lastbin to po + c := k.callable(e) + if c { + a = e.BzzAddr + } + ppo = po + return !c + }) + if a != nil { + log.Trace(fmt.Sprintf("%08x candidate nearest neighbour found: %v (%v)", k.BaseAddr()[:4], a, ppo)) + return a, 0, false + } + + var bpo []int + prev := -1 + k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool { + prev++ + for ; prev < po; prev++ { + bpo = append(bpo, prev) + minsize = 0 + } + if size < minsize { + bpo = append(bpo, po) + minsize = size + } + return size > 0 && po < depth + }) + // all buckets are full, ie., minsize == k.MinBinSize + if len(bpo) == 0 { + return nil, 0, false + } + // as long as we got candidate peers to connect to + // // dont ask for new peers (want = false) + // // try to select a candidate peer + // // find the first callable peer + // nxt := bpo[0] + k.addrs.EachBin(k.base, Pof, nxt, func(po, _ int, f func(func(pot.Val, int) bool) bool) bool { + // // for each bin (up until depth) we find callable candidate peers + if po >= depth { + return false + } + return f(func(val pot.Val, _ int) bool { + e := val.(*entry) + c := k.callable(e) + if c { + a = e.BzzAddr + } + return !c + }) + }) + found a candidate + if a != nil { + return a, 0, false + } + // no candidate peer found, request for the short bin + var changed bool + if nxt < k.depth { + k.depth = nxt + changed = true + } + return a, nxt, changed*/ } // calculate the allowed number of retries based on time lapsed since last seen @@ -493,7 +532,7 @@ func (h *Hive) isTimeForRetry(d *Peer) bool { if isTime { log.Trace(fmt.Sprintf("%08x: peer %v is callable", Label(d)[:4], d)) } else { - log.Trace(fmt.Sprintf("%08x: %v long time since last try (at %v) needed before retry %v, wait only warrants %v", h.BaseAddr()[:4], d, timeAgo, d.retries, allowedRetryCountNow)) + log.Trace(fmt.Sprintf("%08x: %v long time since last try (at %v) needed before retry %v, wait only warrants %v, callable result: %t", h.BaseAddr()[:4], d, timeAgo, d.retries, allowedRetryCountNow, isTime)) } return isTime } diff --git a/swarm/network/hive_test.go b/swarm/network/hive_test.go index 56adc5a8e9..0fc629580f 100644 --- a/swarm/network/hive_test.go +++ b/swarm/network/hive_test.go @@ -17,10 +17,12 @@ package network import ( + "fmt" "io/ioutil" - "log" + golog "log" "os" "testing" + "time" p2ptest "github.com/ethereum/go-ethereum/p2p/testing" "github.com/ethereum/go-ethereum/swarm/state" @@ -61,7 +63,7 @@ func TestRegisterAndConnect(t *testing.T) { } func TestHiveStatePersistance(t *testing.T) { - log.SetOutput(os.Stdout) + golog.SetOutput(os.Stdout) dir, err := ioutil.TempDir("", "hive_test_store") if err != nil { @@ -115,3 +117,111 @@ func TestHiveStatePersistance(t *testing.T) { t.Fatalf("%d peers left over: %v", len(peers), peers) } } + +// TestSuggestPeer tests for the different moving parts of the kademlia SuggestPeer method +// We have to take several things in accoutn when testing this functionality: +// 1. Proximity order of the peers (duh) +// 2. Callability of peers (i.e. we tried to connect to a peer recently but couldn't so we have to make sure +// the exponential backoff kicks in (we won't test for its correctness here though) +// 3. +func TestSuggestPeerWTF(t *testing.T) { + base := "00000001" + hiveRetryInterval := int64(10 * time.Millisecond) + + for _, v := range []struct { + name string + ons []string + offs []string + expAddr []string + expDepth int + skip bool + wait time.Duration + }{ + { + name: "no peers to suggest (all ON)", + ons: []string{"00000010", "00010000", "00000011", "00000111"}, + offs: []string{}, + expAddr: []string{}, + expDepth: 0, + skip: true, + }, + { + name: "no peers (too early to try)", + ons: []string{"00100000", "00110000", "00111000", "00011011", "00010101"}, + offs: []string{"00110000", "00111000"}, + expAddr: []string{}, + expDepth: 0, + wait: 2 * time.Millisecond, //retry interval is set to 10ms, try before + skip: true, + }, + { + name: "suggest deeper then shallower", + ons: []string{"00100000", "00110000", "00111000", "00011011", "00010101"}, + offs: []string{"00110000", "00111000"}, + expAddr: []string{"00111000", "00110000"}, + expDepth: 0, + wait: 20 * time.Millisecond, + skip: true, + }, + { + name: "shallow bin ON (depth=1, bin 1 empty), suggest a peer with po > depth", + ons: []string{"10000000", "00100000", "00100001", "00100011"}, + offs: []string{"00100011"}, + expAddr: []string{"00100011"}, + expDepth: 1, + wait: 20 * time.Millisecond, + skip: true, + }, + { + name: "shallow bin ON (depth=3, bin 2 peer known but not connected), suggest a peer with po < depth", + ons: []string{"10000000", "01000000", "00100000", "00100001", "00001111", "00001011"}, + offs: []string{"00100001"}, + expAddr: []string{"00100001"}, + expDepth: 3, + wait: 20 * time.Millisecond, + }, + } { + if v.skip { + continue + } + t.Run(v.name, func(t *testing.T) { + params := NewHiveParams() + params.RetryInterval = hiveRetryInterval + k := newTestKademlia(base) + h := NewHive(params, k, nil) // hive + + for _, on := range v.ons { + piu := newTestKadPeer(k, on, false) + h.registerPeer(piu) + } + for _, off := range v.offs { + Off(k, off) + } + + if h.Kademlia.NeighbourhoodDepth() != v.expDepth { + t.Fatalf("wrong neighbourhood depth. got: %d, want: %d", k.NeighbourhoodDepth(), v.expDepth) + } + + if v.wait > 0 { + time.Sleep(v.wait) + } + + err := testSuggestPeerWTF(t, h, v.expAddr) + if err != nil { + t.Fatalf("%v", err.Error()) + } + }) + } +} +func testSuggestPeerWTF(t *testing.T, h *Hive, expAddr []string) error { + peers := h.suggestPeers() + if len(peers) != len(expAddr) { + t.Fatalf("expected %d suggested peers but got %d instead", len(expAddr), len(peers)) + } + for i, v := range peers { + if expAddr[i] != binStr(v.BzzAddr) { + return fmt.Errorf("incorrect peer address suggested. expected %v, got %v", expAddr[i], binStr(v.BzzAddr)) + } + } + return nil +} diff --git a/swarm/network/kademlia.go b/swarm/network/kademlia.go index e1d1da85a3..945e439464 100644 --- a/swarm/network/kademlia.go +++ b/swarm/network/kademlia.go @@ -250,7 +250,6 @@ func (k *Kademlia) Off(p *Peer) { } else { del = true } - if del { k.conns, _, _, _ = pot.Swap(k.conns, p, Pof, func(_ pot.Val) pot.Val { // v cannot be nil, but no need to check @@ -262,6 +261,9 @@ func (k *Kademlia) Off(p *Peer) { } k.sendNeighbourhoodDepthChange() } + log.Trace("OFF") + log.Trace(k.string()) + } // EachBin is a two level nested iterator @@ -380,6 +382,7 @@ func depthForPot(p *pot.Pot, minProxBinSize int, pivotAddr []byte) (depth int) { return true } + p.EachNeighbour(pivotAddr, Pof, f) // the second step is to test for empty bins in order from shallowest to deepest @@ -395,7 +398,6 @@ func depthForPot(p *pot.Pot, minProxBinSize int, pivotAddr []byte) (depth int) { } return false }) - return depth } @@ -610,6 +612,7 @@ func (k *Kademlia) connectedPotential() []int { return culprits } +/* func (k *Kademlia) connectedPotential() []uint8 { pk := make(map[uint8]int) pc := make(map[uint8]int) @@ -636,7 +639,7 @@ func (k *Kademlia) connectedPotential() []uint8 { } return culprits } - +*/ // Health state of the Kademlia // used for testing only type Health struct { diff --git a/swarm/network/kademlia_test.go b/swarm/network/kademlia_test.go index 418adf0dfa..0c3a0f0533 100644 --- a/swarm/network/kademlia_test.go +++ b/swarm/network/kademlia_test.go @@ -163,7 +163,6 @@ func TestNeighbourhoodDepth(t *testing.T) { t.Fatalf("%d expected depth 0, was %d", testNum, depth) } testNum++ - // add from 0 to 6 for i, p := range peers { kad.On(p) @@ -173,8 +172,10 @@ func TestNeighbourhoodDepth(t *testing.T) { } } testNum++ + kad.Off(sevenPeers[0]) kad.Off(sevenPeers[1]) + log.Trace(kad.String()) depth = kad.NeighbourhoodDepth() if depth != 6 { t.Fatalf("%d expected depth 6, was %d", testNum, depth) @@ -182,6 +183,7 @@ func TestNeighbourhoodDepth(t *testing.T) { testNum++ kad.Off(peers[4]) + log.Trace(kad.String()) depth = kad.NeighbourhoodDepth() if depth != 4 { t.Fatalf("%d expected depth 4, was %d", testNum, depth) @@ -189,6 +191,7 @@ func TestNeighbourhoodDepth(t *testing.T) { testNum++ kad.Off(peers[3]) + log.Trace(kad.String()) depth = kad.NeighbourhoodDepth() if depth != 3 { t.Fatalf("%d expected depth 3, was %d", testNum, depth) diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index 4b9b28cdc3..6430fb4336 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -357,6 +357,7 @@ func (a *BzzAddr) Under() []byte { func (a *BzzAddr) ID() enode.ID { n, err := enode.ParseV4(string(a.UAddr)) if err != nil { + panic("wtf") return enode.ID{} } return n.ID() diff --git a/swarm/network/protocol_test.go b/swarm/network/protocol_test.go index 53ceda7448..4bba51b607 100644 --- a/swarm/network/protocol_test.go +++ b/swarm/network/protocol_test.go @@ -17,13 +17,10 @@ package network import ( - "flag" "fmt" - "os" "sync" "testing" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/protocols" @@ -35,15 +32,6 @@ const ( TestProtocolNetworkID = 3 ) -var ( - loglevel = flag.Int("loglevel", 2, "verbosity of logs") -) - -func init() { - flag.Parse() - log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) -} - type testStore struct { sync.Mutex diff --git a/swarm/network/run_test.go b/swarm/network/run_test.go new file mode 100644 index 0000000000..32bb4366b2 --- /dev/null +++ b/swarm/network/run_test.go @@ -0,0 +1,32 @@ +// Copyright 2018 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 . + +package network + +import ( + "flag" + + "github.com/ethereum/go-ethereum/log" + colorable "github.com/mattn/go-colorable" +) + +var loglevel = flag.Int("loglevel", 3, "verbosity of logs") + +func init() { + log.PrintOrigins(true) + log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))) + +}