');
+ this.$body.append(this.$elm);
+ remove = function(event, modal) { modal.elm.remove(); };
+ this.showSpinner();
+ el.trigger($.modal.AJAX_SEND);
+ $.get(target).done(function(html) {
+ if (!$.modal.isActive()) return;
+ el.trigger($.modal.AJAX_SUCCESS);
+ var current = getCurrent();
+ current.$elm.empty().append(html).on($.modal.CLOSE, remove);
+ current.hideSpinner();
+ current.open();
+ el.trigger($.modal.AJAX_COMPLETE);
+ }).fail(function() {
+ el.trigger($.modal.AJAX_FAIL);
+ var current = getCurrent();
+ current.hideSpinner();
+ modals.pop(); // remove expected modal from the list
+ el.trigger($.modal.AJAX_COMPLETE);
+ });
+ }
+ } else {
+ this.$elm = el;
+ this.$body.append(this.$elm);
+ this.open();
+ }
+ };
+
+ $.modal.prototype = {
+ constructor: $.modal,
+
+ open: function() {
+ var m = this;
+ this.block();
+ if(this.options.doFade) {
+ setTimeout(function() {
+ m.show();
+ }, this.options.fadeDuration * this.options.fadeDelay);
+ } else {
+ this.show();
+ }
+ $(document).off('keydown.modal').on('keydown.modal', function(event) {
+ var current = getCurrent();
+ if (event.which == 27 && current.options.escapeClose) current.close();
+ });
+ if (this.options.clickClose)
+ this.$blocker.click(function(e) {
+ if (e.target==this)
+ $.modal.close();
+ });
+ },
+
+ close: function() {
+ modals.pop();
+ this.unblock();
+ this.hide();
+ if (!$.modal.isActive())
+ $(document).off('keydown.modal');
+ },
+
+ block: function() {
+ this.$elm.trigger($.modal.BEFORE_BLOCK, [this._ctx()]);
+ this.$body.css('overflow','hidden');
+ this.$blocker = $('
').appendTo(this.$body);
+ selectCurrent();
+ if(this.options.doFade) {
+ this.$blocker.css('opacity',0).animate({opacity: 1}, this.options.fadeDuration);
+ }
+ this.$elm.trigger($.modal.BLOCK, [this._ctx()]);
+ },
+
+ unblock: function(now) {
+ if (!now && this.options.doFade)
+ this.$blocker.fadeOut(this.options.fadeDuration, this.unblock.bind(this,true));
+ else {
+ this.$blocker.children().appendTo(this.$body);
+ this.$blocker.remove();
+ this.$blocker = null;
+ selectCurrent();
+ if (!$.modal.isActive())
+ this.$body.css('overflow','');
+ }
+ },
+
+ show: function() {
+ this.$elm.trigger($.modal.BEFORE_OPEN, [this._ctx()]);
+ if (this.options.showClose) {
+ this.closeButton = $('
' + this.options.closeText + '');
+ this.$elm.append(this.closeButton);
+ }
+ this.$elm.addClass(this.options.modalClass).appendTo(this.$blocker);
+ if(this.options.doFade) {
+ this.$elm.css('opacity',0).show().animate({opacity: 1}, this.options.fadeDuration);
+ } else {
+ this.$elm.show();
+ }
+ this.$elm.trigger($.modal.OPEN, [this._ctx()]);
+ },
+
+ hide: function() {
+ this.$elm.trigger($.modal.BEFORE_CLOSE, [this._ctx()]);
+ if (this.closeButton) this.closeButton.remove();
+ var _this = this;
+ if(this.options.doFade) {
+ this.$elm.fadeOut(this.options.fadeDuration, function () {
+ _this.$elm.trigger($.modal.AFTER_CLOSE, [_this._ctx()]);
+ });
+ } else {
+ this.$elm.hide(0, function () {
+ _this.$elm.trigger($.modal.AFTER_CLOSE, [_this._ctx()]);
+ });
+ }
+ this.$elm.trigger($.modal.CLOSE, [this._ctx()]);
+ },
+
+ showSpinner: function() {
+ if (!this.options.showSpinner) return;
+ this.spinner = this.spinner || $('
')
+ .append(this.options.spinnerHtml);
+ this.$body.append(this.spinner);
+ this.spinner.show();
+ },
+
+ hideSpinner: function() {
+ if (this.spinner) this.spinner.remove();
+ },
+
+ //Return context for custom events
+ _ctx: function() {
+ return { elm: this.$elm, $blocker: this.$blocker, options: this.options };
+ }
+ };
+
+ $.modal.close = function(event) {
+ if (!$.modal.isActive()) return;
+ if (event) event.preventDefault();
+ var current = getCurrent();
+ current.close();
+ return current.$elm;
+ };
+
+ // Returns if there currently is an active modal
+ $.modal.isActive = function () {
+ return modals.length > 0;
+ }
+
+ $.modal.defaults = {
+ closeExisting: true,
+ escapeClose: true,
+ clickClose: true,
+ closeText: 'Close',
+ closeClass: '',
+ modalClass: "modal",
+ spinnerHtml: null,
+ showSpinner: true,
+ showClose: true,
+ fadeDuration: null, // Number of milliseconds the fade animation takes.
+ fadeDelay: 1.0 // Point during the overlay's fade-in that the modal begins to fade in (.5 = 50%, 1.5 = 150%, etc.)
+ };
+
+ // Event constants
+ $.modal.BEFORE_BLOCK = 'modal:before-block';
+ $.modal.BLOCK = 'modal:block';
+ $.modal.BEFORE_OPEN = 'modal:before-open';
+ $.modal.OPEN = 'modal:open';
+ $.modal.BEFORE_CLOSE = 'modal:before-close';
+ $.modal.CLOSE = 'modal:close';
+ $.modal.AFTER_CLOSE = 'modal:after-close';
+ $.modal.AJAX_SEND = 'modal:ajax:send';
+ $.modal.AJAX_SUCCESS = 'modal:ajax:success';
+ $.modal.AJAX_FAIL = 'modal:ajax:fail';
+ $.modal.AJAX_COMPLETE = 'modal:ajax:complete';
+
+ $.fn.modal = function(options){
+ if (this.length === 1) {
+ new $.modal(this, options);
+ }
+ return this;
+ };
+
+ // Automatically bind links with rel="modal:close" to, well, close the modal.
+ $(document).on('click.modal', 'a[rel="modal:close"]', $.modal.close);
+ $(document).on('click.modal', 'a[rel="modal:open"]', function(event) {
+ event.preventDefault();
+ $(this).modal();
+ });
+})(jQuery);
diff --git a/swarm/network/hive.go b/swarm/network/hive.go
index 9dccef1400..904a687ad4 100644
--- a/swarm/network/hive.go
+++ b/swarm/network/hive.go
@@ -30,6 +30,7 @@ type Hive struct {
addr kademlia.Address
kad *kademlia.Kademlia
path string
+ quit chan bool
toggle chan bool
more chan bool
@@ -106,6 +107,7 @@ func (self *Hive) Addr() kademlia.Address {
func (self *Hive) Start(id discover.NodeID, listenAddr func() string, connectPeer func(string) error) (err error) {
self.toggle = make(chan bool)
self.more = make(chan bool)
+ self.quit = make(chan bool)
self.id = id
self.listenAddr = listenAddr
err = self.kad.Load(self.path, nil)
@@ -123,13 +125,15 @@ func (self *Hive) Start(id discover.NodeID, listenAddr func() string, connectPee
// to attempt to write to more (remove Peer when shutting down)
return
}
- node, proxLimit := self.kad.FindBest()
+ node, need, proxLimit := self.kad.Suggest()
+
if node != nil && len(node.Url) > 0 {
- glog.V(logger.Detail).Infof("[BZZ] KΛÐΞMLIΛ hive: call for bee %v", node.Url)
+ glog.V(logger.Debug).Infof("[BZZ] KΛÐΞMLIΛ hive: call known bee %v", node.Url)
// enode or any lower level connection address is unnecessary in future
// discovery table is used to look it up.
connectPeer(node.Url)
- } else if proxLimit > -1 {
+ }
+ if need {
// a random peer is taken from the table
peers := self.kad.FindClosest(kademlia.RandomAddressAt(self.addr, rand.Intn(self.kad.MaxProx)), 1)
if len(peers) > 0 {
@@ -138,15 +142,21 @@ func (self *Hive) Start(id discover.NodeID, listenAddr func() string, connectPee
req := &retrieveRequestMsgData{
Key: storage.Key(randAddr[:]),
}
- glog.V(logger.Detail).Infof("[BZZ] KΛÐΞMLIΛ hive: call any bee in area %v messenger bee %v", randAddr, peers[0])
+ glog.V(logger.Debug).Infof("[BZZ] KΛÐΞMLIΛ hive: call any bee near %v (PO%03d) - messenger bee: %v", randAddr, proxLimit, peers[0])
peers[0].(*peer).retrieve(req)
+ } else {
+ glog.V(logger.Warn).Infof("[BZZ] KΛÐΞMLIΛ hive: no peer")
}
- self.toggle <- true
glog.V(logger.Detail).Infof("[BZZ] KΛÐΞMLIΛ hive: buzz kept alive")
} else {
- self.toggle <- false
+ glog.V(logger.Info).Infof("[BZZ] KΛÐΞMLIΛ hive: no need for more bees")
}
- glog.V(logger.Detail).Infof("[BZZ] KΛÐΞMLIΛ hive: queen's address: %v, population: %d (%d)", self.addr, self.kad.Count(), self.kad.DBCount())
+ select {
+ case self.toggle <- need:
+ case <-self.quit:
+ return
+ }
+ glog.V(logger.Debug).Infof("[BZZ] KΛÐΞMLIΛ hive: queen's address: %v, population: %d (%d)", self.addr, self.kad.Count(), self.kad.DBCount())
}
}()
return
@@ -165,14 +175,11 @@ func (self *Hive) keepAlive() {
if self.kad.DBCount() > 0 {
select {
case self.more <- true:
+ glog.V(logger.Debug).Infof("[BZZ] KΛÐΞMLIΛ hive: buzz wakeup")
default:
}
}
- case need, alive := <-self.toggle:
- if !alive {
- self.more <- false
- return
- }
+ case need := <-self.toggle:
if alarm == nil && need {
alarm = time.NewTicker(time.Duration(self.callInterval)).C
}
@@ -180,20 +187,31 @@ func (self *Hive) keepAlive() {
alarm = nil
}
+ case <-self.quit:
+ return
}
}
}
func (self *Hive) Stop() error {
// closing toggle channel quits the updateloop
- close(self.toggle)
+ close(self.quit)
return self.kad.Save(self.path, saveSync)
}
// called at the end of a successful protocol handshake
-func (self *Hive) addPeer(p *peer) {
+func (self *Hive) addPeer(p *peer) error {
+ defer func() {
+ select {
+ case self.more <- true:
+ default:
+ }
+ }()
glog.V(logger.Detail).Infof("[BZZ] KΛÐΞMLIΛ hive: hi new bee %v", p)
- self.kad.On(p, loadSync)
+ err := self.kad.On(p, loadSync)
+ if err != nil {
+ return err
+ }
// self lookup (can be encoded as nil/zero key since peers addr known) + no id ()
// the most common way of saying hi in bzz is initiation of gossip
// let me know about anyone new from my hood , here is the storageradius
@@ -201,10 +219,8 @@ func (self *Hive) addPeer(p *peer) {
// we do not record as request or forward it, just reply with peers
p.retrieve(&retrieveRequestMsgData{})
glog.V(logger.Detail).Infof("[BZZ] KΛÐΞMLIΛ hive: 'whatsup wheresdaparty' sent to %v", p)
- select {
- case self.more <- true:
- default:
- }
+
+ return nil
}
// called after peer disconnected
@@ -241,7 +257,7 @@ func (self *Hive) DropAll() {
// contructor for kademlia.NodeRecord based on peer address alone
// TODO: should go away and only addr passed to kademlia
func newNodeRecord(addr *peerAddr) *kademlia.NodeRecord {
- now := kademlia.Time(time.Now())
+ now := time.Now()
return &kademlia.NodeRecord{
Addr: addr.Addr,
Url: addr.String(),
@@ -336,7 +352,7 @@ func (self *Hive) peers(req *retrieveRequestMsgData) {
for _, peer := range self.getPeers(key, int(req.MaxPeers)) {
addrs = append(addrs, peer.remoteAddr)
}
- glog.V(logger.Detail).Infof("[BZZ] Hive sending %d peer addresses to %v. req.Id: %v, req.Key: %x", len(addrs), req.from, req.Id, req.Key.Log())
+ glog.V(logger.Debug).Infof("[BZZ] Hive sending %d peer addresses to %v. req.Id: %v, req.Key: %v", len(addrs), req.from, req.Id, req.Key.Log())
peersData := &peersMsgData{
Peers: addrs,
diff --git a/swarm/network/kademlia/kaddb.go b/swarm/network/kademlia/kaddb.go
index d7c6a5be13..33cae221cb 100644
--- a/swarm/network/kademlia/kaddb.go
+++ b/swarm/network/kademlia/kaddb.go
@@ -12,26 +12,6 @@ import (
"github.com/ethereum/go-ethereum/logger/glog"
)
-type Time time.Time
-
-func (t *Time) MarshalJSON() (out []byte, err error) {
- return []byte(fmt.Sprintf("%d", t.Unix())), nil
-}
-
-func (t *Time) UnmarshalJSON(value []byte) error {
- var i int64
- _, err := fmt.Sscanf(string(value), "%d", &i)
- if err != nil {
- return err
- }
- *t = Time(time.Unix(i, 0))
- return nil
-}
-
-func (t Time) Unix() int64 {
- return time.Time(t).Unix()
-}
-
type NodeData interface {
json.Marshaler
json.Unmarshaler
@@ -41,17 +21,17 @@ type NodeData interface {
type NodeRecord struct {
Addr Address // address of node
Url string // Url, used to connect to node
- After Time // next call after time
- Seen Time // last connected at time
+ After time.Time // next call after time
+ Seen time.Time // last connected at time
Meta *json.RawMessage // arbitrary metadata saved for a peer
- node Node
- connected bool
+ node Node
}
-// set checked to current time,
func (self *NodeRecord) setSeen() {
- self.Seen = Time(time.Now())
+ t := time.Now()
+ self.Seen = t
+ self.After = t
}
func (self *NodeRecord) String() string {
@@ -64,7 +44,7 @@ type KadDb struct {
Nodes [][]*NodeRecord
index map[Address]*NodeRecord
cursors []int
- lock sync.Mutex
+ lock sync.RWMutex
purgeInterval time.Duration
initialRetryInterval time.Duration
connRetryExp int
@@ -142,16 +122,19 @@ This is used to pick candidates for live nodes that are most wanted for
a higly connected low centrality network structure for Swarm which best suits
for a Kademlia-style routing.
-The candidate is chosen using the following strategy.
+* Starting as naive node with empty db, this implements Kademlia bootstrapping
+* As a mature node, it fills short lines. All on demand.
+
+The candidate is chosen using the following strategy:
We check for missing online nodes in the buckets for 1 upto Max BucketSize rounds.
On each round we proceed from the low to high proximity order buckets.
If the number of active nodes (=connected peers) is < rounds, then start looking
for a known candidate. To determine if there is a candidate to recommend the
-node record database row corresponding to the bucket is checked.
+kaddb node record database row corresponding to the bucket is checked.
If the row cursor is on position i, the ith element in the row is chosen.
If the record is scheduled not to be retried before NOW, the next element is taken.
-If the record is scheduled can be retried, it is set as checked, scheduled for
+If the record is scheduled to be retried, it is set as checked, scheduled for
checking and is returned. The time of the next check is in X (duration) such that
X = ConnRetryExp * delta where delta is the time past since the last check and
ConnRetryExp is constant obsoletion factor. (Note that when node records are added
@@ -167,121 +150,109 @@ offline past peer)
|| (proxBin(a) < proxBin(b) && |proxBin(a)| == |proxBin(b)|)
|| (proxBin(a) == proxBin(b) && lastChecked(a) < lastChecked(b))
-This has double role. Starting as naive node with empty db, this implements
-Kademlia bootstrapping
-As a mature node, it fills short lines. All on demand.
The second argument returned names the first missing slot found
*/
-func (self *KadDb) findBest(bucketSize int, binsize func(int) int) (node *NodeRecord, proxLimit int) {
- // return value -1 indicates that buckets are filled in all
- proxLimit = -1
+func (self *KadDb) findBest(maxBinSize int, binSize func(int) int) (node *NodeRecord, need bool, proxLimit int) {
+ // return nil, proxLimit indicates that all buckets are filled
defer self.lock.Unlock()
self.lock.Lock()
- var interval int64
+ var interval time.Duration
var found bool
- for rounds := 1; rounds <= bucketSize; rounds++ {
+ var purge []bool
+ var delta time.Duration
+ var cursor int
+ var count int
+ var after time.Time
+
+ // iterate over columns maximum bucketsize times
+ for rounds := 1; rounds <= maxBinSize; rounds++ {
ROUND:
+ // iterate over rows from PO 0 upto MaxProx
for po, dbrow := range self.Nodes {
- if po > len(self.Nodes) {
- break ROUND
+ // if row has rounds connected peers, then take the next
+ if binSize(po) >= rounds {
+ continue ROUND
}
- size := binsize(po)
- if size < rounds {
- if proxLimit < 0 {
- // set the first missing slot found
- proxLimit = po
+ if !need {
+ // set proxlimit to the PO where the first missing slot is found
+ proxLimit = po
+ need = true
+ }
+ purge = make([]bool, len(dbrow))
+
+ // there is a missing slot - finding a node to connect to
+ // select a node record from the relavant kaddb row (of identical prox order)
+ ROW:
+ for cursor = self.cursors[po]; !found && count < len(dbrow); cursor = (cursor + 1) % len(dbrow) {
+ count++
+ node = dbrow[cursor]
+
+ // skip already connected nodes
+ if node.node != nil {
+ glog.V(logger.Debug).Infof("[KΛÐ]: kaddb record %v (PO%03d:%d/%d) already connected", node.Addr, po, cursor, len(dbrow))
+ continue ROW
}
- var count int
- var purge []int
- n := self.cursors[po]
- // try node records in the relavant kaddb row (of identical prox order)
- // if they are ripe for checking
- ROW:
- for count < len(dbrow) {
- node = dbrow[n]
-
- // skip already connected nodes
- if !node.connected {
-
- glog.V(logger.Detail).Infof("[KΛÐ]: kaddb record %v (PO%03d:%d) not to be retried before %v", node.Addr, po, n, node.After)
-
- // time since last known connection attempt
- delta := node.After.Unix() - node.Seen.Unix()
- // if delta < 4 {
- // node.After = Time(time.Time{})
- // }
-
- // if node is scheduled to connect
- if time.Time(node.After).Before(time.Now()) {
-
- // if checked longer than purge interval
- if time.Time(node.Seen).Add(self.purgeInterval).Before(time.Now()) {
- // delete node
- purge = append(purge, n)
- glog.V(logger.Detail).Infof("[KΛÐ]: inactive node record %v (PO%03d:%d) last check: %v, next check: %v", node.Addr, po, n, node.Seen, node.After)
- } else {
- // scheduling next check
- if (node.After == Time(time.Time{})) {
- node.After = Time(time.Now().Add(self.initialRetryInterval))
- } else {
- interval = delta * int64(self.connRetryExp)
- node.After = Time(time.Unix(time.Now().Unix()+interval, 0))
- }
-
- glog.V(logger.Detail).Infof("[KΛÐ]: serve node record %v (PO%03d:%d), last check: %v, next check: %v", node.Addr, po, n, node.Seen, node.After)
- }
- found = true
- break ROW
- }
- glog.V(logger.Detail).Infof("[KΛÐ]: kaddb record %v (PO%03d:%d) not ready. skipped. not to be retried before: %v", node.Addr, po, n, node.After)
- } // if node.node == nil
- n++
- count++
- // cycle: n = n % len(dbrow)
- if n >= len(dbrow) {
- n = 0
- }
+ // if node is scheduled to connect
+ if time.Time(node.After).After(time.Now()) {
+ glog.V(logger.Debug).Infof("[KΛÐ]: kaddb record %v (PO%03d:%d) skipped. seen at %v (%v ago), scheduled at %v", node.Addr, po, cursor, node.Seen, delta, node.After)
+ continue ROW
}
- self.cursors[po] = n
- self.delete(po, purge...)
- if found {
- glog.V(logger.Detail).Infof("[KΛÐ]: rounds %d: prox limit: PO%03d\n%v", rounds, proxLimit, node)
- node.setSeen()
- return
- }
- } // if len < rounds
- } // for po-s
- glog.V(logger.Detail).Infof("[KΛÐ]: rounds %d: proxlimit: PO%03d", rounds, proxLimit)
- if proxLimit == 0 || proxLimit < 0 && bucketSize == rounds {
- return
- }
- } // for round
- return
+ delta = time.Since(time.Time(node.Seen))
+ if delta < self.initialRetryInterval {
+ delta = self.initialRetryInterval
+ }
+ if delta > self.purgeInterval {
+ // remove node
+ purge[cursor] = true
+ glog.V(logger.Debug).Infof("[KΛÐ]: kaddb record %v (PO%03d:%d) unreachable since %v. Removed", node.Addr, po, cursor, node.Seen)
+ continue ROW
+ }
+
+ glog.V(logger.Debug).Infof("[KΛÐ]: kaddb record %v (PO%03d:%d) ready to be tried. seen at %v (%v ago), scheduled at %v", node.Addr, po, cursor, node.Seen, delta, node.After)
+
+ // scheduling next check
+ interval = time.Duration(delta * time.Duration(self.connRetryExp))
+ after = time.Now().Add(interval)
+
+ glog.V(logger.Debug).Infof("[KΛÐ]: kaddb record %v (PO%03d:%d) selected as candidate connection %v. seen at %v (%v ago), selectable since %v, retry after %v (in %v)", node.Addr, po, cursor, rounds, node.Seen, delta, node.After, after, interval)
+ node.After = after
+ found = true
+ } // ROW
+ self.cursors[po] = cursor
+ self.delete(po, purge)
+ if found {
+ return node, need, proxLimit
+ }
+ } // ROUND
+ } // ROUNDS
+
+ return nil, need, proxLimit
}
// deletes the noderecords of a kaddb row corresponding to the indexes
// caller must hold the dblock
// the call is unsafe, no index checks
-func (self *KadDb) delete(row int, indexes ...int) {
- var prev int
+func (self *KadDb) delete(row int, purge []bool) {
var nodes []*NodeRecord
dbrow := self.Nodes[row]
- for _, next := range indexes {
- // need to adjust dbcursor
- if next > 0 {
- if next <= self.cursors[row] {
- self.cursors[row]--
- }
- nodes = append(nodes, dbrow[prev:next]...)
+ for i, del := range purge {
+ if i == self.cursors[row] {
+ //reset cursor
+ self.cursors[row] = len(nodes)
}
- prev = next + 1
- delete(self.index, dbrow[next].Addr)
+ // delete the entry to be purged
+ if del {
+ delete(self.index, dbrow[i].Addr)
+ continue
+ }
+ // otherwise append to new list
+ nodes = append(nodes, dbrow[i])
}
- self.Nodes[row] = append(nodes, dbrow[prev:]...)
+ self.Nodes[row] = nodes
}
// save persists kaddb on disk (written to file on path in json format.
@@ -294,8 +265,8 @@ func (self *KadDb) save(path string, cb func(*NodeRecord, Node)) error {
for _, b := range self.Nodes {
for _, node := range b {
n++
- node.After = Time(time.Now())
- node.Seen = Time(time.Now())
+ node.After = time.Now()
+ node.Seen = time.Now()
if cb != nil {
cb(node, node.node)
}
@@ -331,24 +302,25 @@ func (self *KadDb) load(path string, cb func(*NodeRecord, Node) error) (err erro
return
}
var n int
- var purge []int
+ var purge []bool
for po, b := range self.Nodes {
+ purge = make([]bool, len(b))
ROW:
for i, node := range b {
if cb != nil {
err = cb(node, node.node)
if err != nil {
- purge = append(purge, i)
+ purge[i] = true
continue ROW
}
}
n++
- if (node.After == Time(time.Time{})) {
- node.After = Time(time.Now())
+ if (node.After == time.Time{}) {
+ node.After = time.Now()
}
self.index[node.Addr] = node
}
- self.delete(po, purge...)
+ self.delete(po, purge)
}
glog.V(logger.Info).Infof("[KΛÐ] loaded kaddb with %v nodes from %v", n, path)
diff --git a/swarm/network/kademlia/kademlia.go b/swarm/network/kademlia/kademlia.go
index 602db1445d..8fcfdc08e1 100644
--- a/swarm/network/kademlia/kademlia.go
+++ b/swarm/network/kademlia/kademlia.go
@@ -12,16 +12,17 @@ import (
)
const (
- bucketSize = 3
- proxBinSize = 4
+ bucketSize = 4
+ proxBinSize = 2
maxProx = 8
connRetryExp = 2
+ maxPeers = 100
)
var (
purgeInterval = 42 * time.Hour
- initialRetryInterval = 42 * 100 * time.Millisecond
- maxIdleInterval = 42 * 10 * time.Second
+ initialRetryInterval = 42 * time.Millisecond
+ maxIdleInterval = 42 * 100 * time.Millisecond
)
type KadParams struct {
@@ -31,6 +32,7 @@ type KadParams struct {
BucketSize int
PurgeInterval time.Duration
InitialRetryInterval time.Duration
+ MaxIdleInterval time.Duration
ConnRetryExp int
}
@@ -41,6 +43,7 @@ func NewKadParams() *KadParams {
BucketSize: bucketSize,
PurgeInterval: purgeInterval,
InitialRetryInterval: initialRetryInterval,
+ MaxIdleInterval: maxIdleInterval,
ConnRetryExp: connRetryExp,
}
}
@@ -52,7 +55,7 @@ type Kademlia struct {
proxLimit int // state, the PO of the first row of the most proximate bin
proxSize int // state, the number of peers in the most proximate bin
count int // number of active peers (w live connection)
- buckets []*bucket // the actual bins
+ buckets [][]Node // the actual bins
db *KadDb // kaddb, node record database
lock sync.RWMutex // mutex to access buckets
}
@@ -68,14 +71,7 @@ type Node interface {
// add is the base address of the table
// params is KadParams configuration
func New(addr Address, params *KadParams) *Kademlia {
- buckets := make([]*bucket, params.MaxProx+1)
- for i, _ := range buckets {
- buckets[i] = &bucket{size: params.BucketSize} // will initialise bucket{int(0),[]Node(nil),sync.Mutex}
- }
- glog.V(logger.Info).Infof("[KΛÐ] base address %v", addr)
-
- // ! temporary hack fixme:
- params.ProxBinSize = 4
+ buckets := make([][]Node, params.MaxProx+1)
return &Kademlia{
addr: addr,
KadParams: params,
@@ -104,14 +100,12 @@ func (self *Kademlia) DBCount() int {
// On is the entry point called when a new nodes is added
// unsafe in that node is not checked to be already active node (to be called once)
func (self *Kademlia) On(node Node, cb func(*NodeRecord, Node) error) (err error) {
+ glog.V(logger.Warn).Infof("[KΛÐ]: %v", self)
defer self.lock.Unlock()
self.lock.Lock()
index := self.proximityBin(node.Addr())
record := self.db.findOrCreate(index, node.Addr(), node.Url())
- // callback on add node
- // setting the node on the record, set it checked (for connectivity)
- record.node = node
if cb != nil {
err = cb(record, node)
@@ -119,33 +113,46 @@ func (self *Kademlia) On(node Node, cb func(*NodeRecord, Node) error) (err error
if err != nil {
return fmt.Errorf("unable to add node %v, callback error: %v", node.Addr(), err)
}
- glog.V(logger.Info).Infof("[KΛÐ]: add node record %v with node %v", record, node)
+ glog.V(logger.Debug).Infof("[KΛÐ]: add node record %v with node %v", record, node)
}
- record.connected = true
// insert in kademlia table of active nodes
bucket := self.buckets[index]
// if bucket is full insertion replaces the worst node
// TODO: give priority to peers with active traffic
- replaced, err := bucket.insert(node)
- if err != nil {
- glog.V(logger.Debug).Infof("[KΛÐ]: node %v not needed: %v", node, err)
- return err
- // no prox adjustment needed
- // do not change count
+ if len(bucket) >= self.BucketSize { // >= allows us to add peers beyond the bucketsize limitation
+ // always rotate peers
+ idle := self.MaxIdleInterval
+ var pos int
+ var replaced Node
+ for i, p := range bucket {
+ idleInt := time.Since(p.LastActive())
+ if idleInt > idle {
+ idle = idleInt
+ pos = i
+ replaced = p
+ }
+ }
+ if replaced == nil {
+ glog.V(logger.Debug).Infof("[KΛÐ]: all peers wanted, PO%03d bucket full", index)
+ return fmt.Errorf("bucket full")
+ }
+ glog.V(logger.Debug).Infof("[KΛÐ]: node %v replaced by %v (idle for %v > %v)", replaced, node, idle, self.MaxIdleInterval)
+ replaced.Drop()
+ self.buckets[index] = append(bucket[:pos], bucket[(pos+1):]...)
+ // there is no change in bucket cardinalities so no prox limit adjustment is needed
+ return nil
+ } else {
+ self.buckets[index] = append(bucket, node)
+ glog.V(logger.Debug).Infof("[KΛÐ]: add node %v to table", node)
+ self.count++
+ self.setProxLimit(index, true)
}
- if replaced != nil {
- glog.V(logger.Debug).Infof("[KΛÐ]: node %v replaced by %v ", replaced, node)
- return
- }
- // new node added
- glog.V(logger.Info).Infof("[KΛÐ]: add node %v to table", node)
- self.count++
- self.setProxLimit(index, false)
- return
+ record.node = node
+ return nil
}
-// is the entrypoint called when a node is taken offline
+// Off is the called when a node is taken offline (from the protocol main loop exit)
func (self *Kademlia) Off(node Node, cb func(*NodeRecord, Node)) (err error) {
self.lock.Lock()
defer self.lock.Unlock()
@@ -153,70 +160,73 @@ func (self *Kademlia) Off(node Node, cb func(*NodeRecord, Node)) (err error) {
var found bool
index := self.proximityBin(node.Addr())
bucket := self.buckets[index]
- for i := 0; i < len(bucket.nodes); i++ {
- if node.Addr() == bucket.nodes[i].Addr() {
+ for i := 0; i < len(bucket); i++ {
+ if node.Addr() == bucket[i].Addr() {
found = true
- bucket.nodes = append(bucket.nodes[:i], bucket.nodes[(i+1):]...)
+ self.buckets[index] = append(bucket[:i], bucket[(i+1):]...)
+ break
}
}
if !found {
- return
+ // gracefully return without error if peer already unregistered
+ glog.V(logger.Warn).Infof("[KΛÐ]: remove node %v not in table, population now is %v", node, self.count)
+ return nil
}
- glog.V(logger.Info).Infof("[KΛÐ]: remove node %v from table", node)
self.count--
- if len(bucket.nodes) < bucket.size {
- err = fmt.Errorf("insufficient nodes (%v) in bucket %v", len(bucket.nodes), index)
- }
+ glog.V(logger.Debug).Infof("[KΛÐ]: remove node %v from table, population now is %v", node, self.count)
+ self.setProxLimit(index, false)
- self.setProxLimit(index, true)
-
- r := self.db.index[node.Addr()]
+ record := self.db.index[node.Addr()]
// callback on remove
if cb != nil {
- cb(r, r.node)
+ cb(record, record.node)
}
- r.node = nil
- r.connected = false
+ record.node = nil
return
}
// proxLimit is dynamically adjusted so that
// 1) there is no empty buckets in bin < proxLimit and
-// 2) the sum of all items sare the maximpossible but lower than ProxBinSize
+// 2) the sum of all items are the minimum possible but higher than ProxBinSize
// adjust Prox (proxLimit and proxSize after an insertion/removal of nodes)
// caller holds the lock
-func (self *Kademlia) setProxLimit(r int, off bool) {
- // glog.V(logger.Info).Infof("[KΛÐ]: adjust proxbin for (bin: %v, off: %v)", r, off)
- if r < self.proxLimit && len(self.buckets[r].nodes) > 0 {
+func (self *Kademlia) setProxLimit(r int, on bool) {
+ // if the change is outside the core (PO lower)
+ // and the change does not leave a bucket empty then
+ // no adjustment needed
+ if r < self.proxLimit && len(self.buckets[r]) > 0 {
return
}
- glog.V(logger.Detail).Infof("[KΛÐ]: set proxbin (size: %v, limit: %v, bin: %v, off: %v)", self.proxSize, self.proxLimit, r, off)
- if off {
- self.proxSize--
- for (self.proxSize < self.ProxBinSize || r < self.proxLimit) &&
- self.proxLimit > 0 {
- //
- self.proxLimit--
- self.proxSize += len(self.buckets[self.proxLimit].nodes)
- glog.V(logger.Detail).Infof("[KΛÐ]: proxbin expansion (size: %v, limit: %v, bin: %v, off: %v)", self.proxSize, self.proxLimit, r, off)
- }
- glog.V(logger.Detail).Infof("%v", self)
- return
- }
- self.proxSize++
- for self.proxLimit < self.MaxProx &&
- len(self.buckets[self.proxLimit].nodes) > 0 &&
- self.proxSize-len(self.buckets[self.proxLimit].nodes) >= self.ProxBinSize {
- //
- self.proxSize -= len(self.buckets[self.proxLimit].nodes)
- self.proxLimit++
- glog.V(logger.Detail).Infof("[KΛÐ]: proxbin contraction (size: %v, limit: %v, bin: %v, off: %v)", self.proxSize, self.proxLimit, r, off)
- }
- glog.V(logger.Detail).Infof("%v", self)
+ // if on=a node was added, then r must be within prox limit so increment cardinality
+ if on {
+ self.proxSize++
+ curr := len(self.buckets[self.proxLimit])
+ // if now core is big enough without the furthest bucket, then contract
+ // this can result in more than one bucket change
+ for self.proxSize >= self.ProxBinSize+curr && curr > 0 {
+ self.proxSize -= curr
+ self.proxLimit++
+ curr = len(self.buckets[self.proxLimit])
+ glog.V(logger.Detail).Infof("[KΛÐ]: proxbin contraction (size: %v, limit: %v, bin: %v)", self.proxSize, self.proxLimit, r)
+ }
+ return
+ }
+ // otherwise
+ if r >= self.proxLimit {
+ self.proxSize--
+ }
+ // expand core by lowering prox limit until hit zero or cover the empty bucket or reached target cardinality
+ for (self.proxSize < self.ProxBinSize || r < self.proxLimit) &&
+ self.proxLimit > 0 {
+ //
+ self.proxLimit--
+ self.proxSize += len(self.buckets[self.proxLimit])
+ glog.V(logger.Detail).Infof("[KΛÐ]: proxbin expansion (size: %v, limit: %v, bin: %v)", self.proxSize, self.proxLimit, r)
+ }
}
/*
@@ -225,62 +235,54 @@ as the target. The most proximate bin will be the union of the bins between
proxLimit and MaxProx.
*/
func (self *Kademlia) FindClosest(target Address, max int) []Node {
- defer self.lock.RUnlock()
- self.lock.RLock()
+ self.lock.Lock()
+ defer self.lock.Unlock()
+
r := nodesByDistance{
target: target,
}
- index := self.proximityBin(target)
- start := index
- var down bool
- if index >= self.proxLimit {
- index = self.proxLimit
- start = self.MaxProx
- down = true
- }
- var n int
+ po := self.proximityBin(target)
+ index := po
+ step := 1
+ glog.V(logger.Detail).Infof("[KΛÐ]: serving %v nodes at %v (PO%02d)", max, index, po)
+
+ // if max is set to 0, just want a full bucket, dynamic number
+ min := max
+ // set limit to max
limit := max
if max == 0 {
- limit = 1000
+ min = 1
+ limit = maxPeers
}
- for {
- bucket := self.buckets[start].nodes
- for i := 0; i < len(bucket); i++ {
- r.push(bucket[i], limit)
+ var n int
+ for index >= 0 {
+ // add entire bucket
+ for _, p := range self.buckets[index] {
+ r.push(p, limit)
n++
}
- if max == 0 && start <= index && (n > 0 || start == 0) || max > 0 && down && start <= index && (n >= limit || n == self.count || start == 0) {
+ // terminate if index reached the bottom or enough peers > min
+ glog.V(logger.Detail).Infof("[KΛÐ]: add %v -> %v (PO%02d, PO%03d)", len(self.buckets[index]), n, index, po)
+ if n >= min && (step < 0 || max == 0) {
break
}
- if down {
- start--
- } else {
- if start == self.MaxProx {
- if index == 0 {
- break
- }
- start = index - 1
- down = true
- } else {
- start++
- }
+ // reach top most non-empty PO bucket, turn around
+ if index == self.MaxProx {
+ index = po
+ step = -1
}
+ index += step
}
- glog.V(logger.Detail).Infof("[KΛÐ]: serve %d (=<%d) nodes for target lookup %v (PO%d)", n, self.MaxProx, target, index)
+ glog.V(logger.Detail).Infof("[KΛÐ]: serve %d (<=%d) nodes for target lookup %v (PO%03d)", n, max, target, po)
return r.nodes
}
-func (self *Kademlia) binsize(p int) int {
- b := self.buckets[p]
- defer b.lock.RUnlock()
- b.lock.RLock()
- return len(b.nodes)
-}
-
-func (self *Kademlia) FindBest() (node *NodeRecord, proxLimit int) {
- return self.db.findBest(self.BucketSize, self.binsize)
+func (self *Kademlia) Suggest() (*NodeRecord, bool, int) {
+ defer self.lock.RUnlock()
+ self.lock.RLock()
+ return self.db.findBest(self.BucketSize, func(i int) int { return len(self.buckets[i]) })
}
// adds node records to kaddb (persisted node record db)
@@ -288,13 +290,6 @@ func (self *Kademlia) Add(nrs []*NodeRecord) {
self.db.add(nrs, self.proximityBin)
}
-// in situ mutable bucket
-type bucket struct {
- size int
- nodes []Node
- lock sync.RWMutex
-}
-
// nodesByDistance is a list of nodes, ordered by distance to target.
type nodesByDistance struct {
nodes []Node
@@ -331,27 +326,6 @@ func (h *nodesByDistance) push(node Node, max int) {
}
}
-// insert adds a peer to a bucket either by appending to existing items if
-// bucket length does not exceed bucketSize, or by replacing the worst
-// Node in the bucket
-func (self *bucket) insert(node Node) (replaced Node, err error) {
- self.lock.Lock()
- defer self.lock.Unlock()
- if len(self.nodes) >= self.size { // >= allows us to add peers beyond the bucketsize limitation
- // dev p2p kicks out nodes idle for > 30 s, so here we never replace nodes if
- // bucket is full
- return nil, fmt.Errorf("bucket full")
- }
- self.nodes = append(self.nodes, node)
- return
-}
-
-func (self *bucket) length(node Node) int {
- self.lock.Lock()
- defer self.lock.Unlock()
- return len(self.nodes)
-}
-
/*
Taking the proximity order relative to a fix point x classifies the points in
the space (n byte long byte sequences) into bins. Items in each are at
@@ -396,43 +370,46 @@ func (self *Kademlia) Load(path string, cb func(*NodeRecord, Node) error) (err e
}
// kademlia table + kaddb table displayed with ascii
-// callerholds the lock
func (self *Kademlia) String() string {
+ defer self.lock.RUnlock()
+ self.lock.RLock()
+ defer self.db.lock.RUnlock()
+ self.db.lock.RLock()
var rows []string
rows = append(rows, "=========================================================================")
- rows = append(rows, fmt.Sprintf("KΛÐΞMLIΛ hive: queen's address: %v, population: %d (%d)", self.addr, self.Count(), self.DBCount()))
- rows = append(rows, fmt.Sprintf("%v : MaxProx: %d, ProxBinSize: %d, BucketSize: %d, proxLimit: %d, proxSize: %d", time.Now(), self.MaxProx, self.ProxBinSize, self.BucketSize, self.proxLimit, self.proxSize))
+ rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %v", time.Now().UTC().Format(time.UnixDate), self.addr.String()[:6]))
+ rows = append(rows, fmt.Sprintf("population: %d (%d), proxLimit: %d, proxSize: %d", self.count, len(self.db.index), self.proxLimit, self.proxSize))
+ rows = append(rows, fmt.Sprintf("MaxProx: %d, ProxBinSize: %d, BucketSize: %d", self.MaxProx, self.ProxBinSize, self.BucketSize))
- for i, b := range self.buckets {
+ for i, bucket := range self.buckets {
if i == self.proxLimit {
- rows = append(rows, fmt.Sprintf("===================== PROX LIMIT: %d =================================", i))
+ rows = append(rows, fmt.Sprintf("============ PROX LIMIT: %d ==========================================", i))
}
- row := []string{fmt.Sprintf("%03d", i), fmt.Sprintf("%2d", len(b.nodes))}
+ row := []string{fmt.Sprintf("%03d", i), fmt.Sprintf("%2d", len(bucket))}
var k int
c := self.db.cursors[i]
- for ; k < len(b.nodes); k++ {
- p := b.nodes[(c+k)%len(b.nodes)]
- row = append(row, fmt.Sprintf("%s", p.Addr().String()[:8]))
- if k == 3 {
+ for ; k < len(bucket); k++ {
+ p := bucket[(c+k)%len(bucket)]
+ row = append(row, p.Addr().String()[:6])
+ if k == 4 {
break
}
}
- for ; k < 3; k++ {
- row = append(row, " ")
+ for ; k < 4; k++ {
+ row = append(row, " ")
}
row = append(row, fmt.Sprintf("| %2d %2d", len(self.db.Nodes[i]), self.db.cursors[i]))
for j, p := range self.db.Nodes[i] {
- row = append(row, fmt.Sprintf("%08x", p.Addr[:4]))
- if j == 2 {
+ row = append(row, p.Addr.String()[:6])
+ if j == 3 {
break
}
}
rows = append(rows, strings.Join(row, " "))
if i == self.MaxProx {
- break
}
}
rows = append(rows, "=========================================================================")
diff --git a/swarm/network/kademlia/kademlia_test.go b/swarm/network/kademlia/kademlia_test.go
index f36158c378..0e162e99b8 100644
--- a/swarm/network/kademlia/kademlia_test.go
+++ b/swarm/network/kademlia/kademlia_test.go
@@ -2,6 +2,7 @@ package kademlia
import (
"fmt"
+ "math"
"math/rand"
"reflect"
"testing"
@@ -11,8 +12,8 @@ import (
var (
quickrand = rand.New(rand.NewSource(time.Now().Unix()))
- quickcfgFindClosest = &quick.Config{MaxCount: 5000, Rand: quickrand}
- quickcfgBootStrap = &quick.Config{MaxCount: 1000, Rand: quickrand}
+ quickcfgFindClosest = &quick.Config{MaxCount: 50, Rand: quickrand}
+ quickcfgBootStrap = &quick.Config{MaxCount: 100, Rand: quickrand}
)
type testNode struct {
@@ -60,43 +61,36 @@ func TestBootstrap(t *testing.T) {
kad := New(test.Self, params)
var err error
- addr := RandomAddress()
- prox := proximity(addr, test.Self)
-
- for p := 0; p <= prox; p++ {
+ for p := 0; p < 9; p++ {
var nrs []*NodeRecord
- for i := 0; i < 3; i++ {
+ n := math.Pow(float64(2), float64(7-p))
+ for i := 0; i < int(n); i++ {
+ addr := RandomAddressAt(test.Self, p)
nrs = append(nrs, &NodeRecord{
- Addr: RandomAddressAt(test.Self, p),
+ Addr: addr,
})
}
kad.Add(nrs)
}
- node := &testNode{addr}
+ node := &testNode{test.Self}
n := 0
for n < 100 {
err = kad.On(node, nil)
if err != nil {
- t.Errorf("backend not accepting node")
- return false
+ t.Fatalf("backend not accepting node: %v", err)
}
- var nrs []*NodeRecord
- prox := proximity(test.Self, node.addr)
- for i := 0; i < 13; i++ {
- nrs = append(nrs, &NodeRecord{
- Addr: RandomAddressAt(test.Self, prox+1),
- })
- }
- kad.Add(nrs)
- record, _ := kad.FindBest()
- if record == nil {
+ record, need, _ := kad.Suggest()
+ if !need {
break
}
- node = &testNode{record.Addr}
n++
+ if record == nil {
+ continue
+ }
+ node = &testNode{record.Addr}
}
exp := test.BucketSize * (test.MaxProx + 1)
if kad.Count() != exp {
@@ -116,15 +110,13 @@ func TestFindClosest(t *testing.T) {
test := func(test *FindClosestTest) bool {
// for any node kad.le, Target and N
params := NewKadParams()
- params.MaxProx = 10
+ params.MaxProx = 7
kad := New(test.Self, params)
var err error
- // t.Logf("FindClosestTest %v: %v\n", len(test.All), test)
for _, node := range test.All {
err = kad.On(node, nil)
- if err != nil {
- t.Errorf("backend not accepting node")
- return false
+ if err != nil && err.Error() != "bucket full" {
+ t.Fatalf("backend not accepting node: %v", err)
}
}
@@ -157,17 +149,13 @@ func TestFindClosest(t *testing.T) {
// check that the result nodes have minimum distance to target.
farthestResult := nodes[len(nodes)-1].Addr()
for i, b := range kad.buckets {
- for j, n := range b.nodes {
+ for j, n := range b {
if contains(nodes, n.Addr()) {
continue // don't run the check below for nodes in result
}
if test.Target.ProxCmp(n.Addr(), farthestResult) < 0 {
_ = i * j
t.Errorf("kad.le contains node that is closer to target but it's not in result")
- // t.Logf("bucket %v, item %v\n", i, j)
- // t.Logf(" Target: %x", test.Target)
- // t.Logf(" Farthest Result: %x", farthestResult)
- // t.Logf(" ID: %x (%d)", n.Addr(), kad.proximityBin(n.Addr()))
return false
}
}
@@ -193,7 +181,7 @@ func TestProxAdjust(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
self := gen(Address{}, r).(Address)
params := NewKadParams()
- params.MaxProx = 10
+ params.MaxProx = 7
kad := New(self, params)
var err error
@@ -201,15 +189,13 @@ func TestProxAdjust(t *testing.T) {
a := gen(Address{}, r).(Address)
addresses = append(addresses, a)
err = kad.On(&testNode{addr: a}, nil)
- if err != nil {
- t.Errorf("backend not accepting node")
- return
+ if err != nil && err.Error() != "bucket full" {
+ t.Fatalf("backend not accepting node: %v", err)
}
if !kad.proxCheck(t) {
return
}
}
-
test := func(test *proxTest) bool {
node := &testNode{test.addr}
if test.add {
@@ -229,35 +215,33 @@ func TestSaveLoad(t *testing.T) {
addresses := gen([]Address{}, r).([]Address)
self := RandomAddress()
params := NewKadParams()
- params.MaxProx = 10
+ params.MaxProx = 7
kad := New(self, params)
var err error
for _, a := range addresses {
err = kad.On(&testNode{addr: a}, nil)
- if err != nil {
- t.Errorf("backend not accepting node")
- return
+ if err != nil && err.Error() != "bucket full" {
+ t.Fatalf("backend not accepting node: %v", err)
}
}
nodes := kad.FindClosest(self, 100)
path := "/tmp/bzz.peers"
err = kad.Save(path, nil)
- if err != nil {
+ if err != nil && err.Error() != "bucket full" {
t.Fatalf("unepected error saving kaddb: %v", err)
}
kad = New(self, params)
err = kad.Load(path, nil)
- if err != nil {
+ if err != nil && err.Error() != "bucket full" {
t.Fatalf("unepected error loading kaddb: %v", err)
}
for _, b := range kad.db.Nodes {
for _, node := range b {
err = kad.On(&testNode{node.Addr}, nil)
- if err != nil {
- t.Errorf("backend not accepting node")
- return
+ if err != nil && err.Error() != "bucket full" {
+ t.Fatalf("backend not accepting node: %v", err)
}
}
}
@@ -270,30 +254,30 @@ func TestSaveLoad(t *testing.T) {
}
func (self *Kademlia) proxCheck(t *testing.T) bool {
- var sum, i int
- var b *bucket
- for i, b = range self.buckets {
- l := len(b.nodes)
+ var sum int
+ for i, b := range self.buckets {
+ l := len(b)
// if we are in the high prox multibucket
if i >= self.proxLimit {
sum += l
} else if l == 0 {
- t.Errorf("bucket %d empty, yet proxLimit is %d\n%v", len(b.nodes), self.proxLimit, self)
+ t.Errorf("bucket %d empty, yet proxLimit is %d\n%v", len(b), self.proxLimit, self)
return false
}
}
// check if merged high prox bucket does not exceed size
if sum > 0 {
- // if sum > self.ProxBinSize {
- // t.Errorf("bucket %d is empty, yet proxSize is %d\n%v", i, self.proxSize, self)
- // return false
- // }
if sum != self.proxSize {
t.Errorf("proxSize incorrect, expected %v, got %v", sum, self.proxSize)
return false
}
- if self.proxLimit > 0 && sum+len(self.buckets[self.proxLimit-1].nodes) < self.ProxBinSize {
- t.Errorf("proxBinSize incorrect, expected %v got %v", sum, self.proxSize)
+ last := len(self.buckets[self.proxLimit])
+ if last > 0 && sum >= self.ProxBinSize+last {
+ t.Errorf("proxLimit %v incorrect, redundant non-empty bucket %d added to proxBin with %v (target %v)\n%v", self.proxLimit, last, sum-last, self.ProxBinSize, self)
+ return false
+ }
+ if self.proxLimit > 0 && sum < self.ProxBinSize {
+ t.Errorf("proxLimit %v incorrect. proxSize %v is less than target %v, yet there is more peers", self.proxLimit, sum, self.ProxBinSize)
return false
}
}
@@ -309,7 +293,7 @@ type bootstrapTest struct {
func (*bootstrapTest) Generate(rand *rand.Rand, size int) reflect.Value {
t := &bootstrapTest{
Self: gen(Address{}, rand).(Address),
- MaxProx: 10 + rand.Intn(3),
+ MaxProx: 5 + rand.Intn(2),
BucketSize: rand.Intn(3) + 1,
}
return reflect.ValueOf(t)
diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go
index 6f27f68b26..7ba7755e34 100644
--- a/swarm/network/protocol.go
+++ b/swarm/network/protocol.go
@@ -49,6 +49,7 @@ const (
ErrExtraStatusMsg
ErrSwap
ErrSync
+ ErrUnwanted
)
var errorToString = map[int]string{
@@ -61,6 +62,7 @@ var errorToString = map[int]string{
ErrExtraStatusMsg: "Extra status message",
ErrSwap: "SWAP error",
ErrSync: "Sync error",
+ ErrUnwanted: "Unwanted peer",
}
// bzz represents the swarm wire protocol
@@ -179,7 +181,8 @@ func run(requestDb *storage.LDBDatabase, depo StorageHandler, backend bind.Backe
// the main forever loop that handles incoming requests
for {
if self.hive.blockRead {
- time.Sleep(1 * time.Second)
+ glog.V(logger.Warn).Infof("[BZZ] Cannot read network")
+ time.Sleep(100 * time.Millisecond)
continue
}
err = self.handle()
@@ -223,7 +226,7 @@ func (self *bzz) handle() error {
if err := msg.Decode(&req); err != nil {
return self.protoError(ErrDecode, "<- %v: %v", msg, err)
}
- glog.V(logger.Debug).Infof("[BZZ] incoming store request: %s", req.String())
+ glog.V(logger.Detail).Infof("[BZZ] incoming store request: %s", req.String())
// swap accounting is done within forwarding
self.storage.HandleStoreRequestMsg(&req, &peer{bzz: self})
@@ -254,7 +257,7 @@ func (self *bzz) handle() error {
return self.protoError(ErrDecode, "<- %v: %v", msg, err)
}
req.from = &peer{bzz: self}
- glog.V(logger.Debug).Infof("[BZZ] <- peer addresses: %v", req)
+ glog.V(logger.Detail).Infof("[BZZ] <- peer addresses: %v", req)
self.hive.HandlePeersMsg(&req, &peer{bzz: self})
case syncRequestMsg:
@@ -366,7 +369,10 @@ func (self *bzz) handleStatus() (err error) {
}
glog.V(logger.Info).Infof("[BZZ] Peer %08x is [bzz] capable (%d/%d)", self.remoteAddr.Addr[:4], status.Version, status.NetworkId)
- self.hive.addPeer(&peer{bzz: self})
+ err = self.hive.addPeer(&peer{bzz: self})
+ if err != nil {
+ return self.protoError(ErrUnwanted, "%v", err)
+ }
// hive sets syncstate so sync should start after node added
glog.V(logger.Info).Infof("[BZZ] syncronisation request sent with %v", self.syncState)
@@ -516,7 +522,6 @@ func (self *bzz) send(msg uint64, data interface{}) error {
if self.hive.blockWrite {
return fmt.Errorf("network write blocked")
}
- // self.messages = append(self.messages, "")
glog.V(logger.Detail).Infof("[BZZ] -> %v: %v (%T) to %v", msg, data, data, self)
err := p2p.Send(self.rw, msg, data)
if err != nil {
diff --git a/swarm/network/syncdb.go b/swarm/network/syncdb.go
index f91ed9a50d..aabc2d9b03 100644
--- a/swarm/network/syncdb.go
+++ b/swarm/network/syncdb.go
@@ -126,7 +126,7 @@ LOOP:
// if syncdb is stopped. In this case we need to save the item to the db
more = deliver(req, self.quit)
if !more {
- glog.V(logger.Debug).Infof("[BZZ] syncDb[%v] quit: switching to db. session tally (db/total): %v/%v", self.priority, self.dbTotal, self.total)
+ glog.V(logger.Debug).Infof("[BZZ] syncDb[%v/%v] quit: switching to db. session tally (db/total): %v/%v", self.key.Log(), self.priority, self.dbTotal, self.total)
// received quit signal, save request currently waiting delivery
// by switching to db mode and closing the buffer
buffer = nil
@@ -136,12 +136,12 @@ LOOP:
break // break from select, this item will be written to the db
}
self.total++
- glog.V(logger.Detail).Infof("[BZZ] syncDb[%v] deliver (db/total): %v/%v", self.priority, self.dbTotal, self.total)
+ glog.V(logger.Detail).Infof("[BZZ] syncDb[%v/%v] deliver (db/total): %v/%v", self.key.Log(), self.priority, self.dbTotal, self.total)
// by the time deliver returns, there were new writes to the buffer
// if buffer contention is detected, switch to db mode which drains
// the buffer so no process will block on pushing store requests
if len(buffer) == cap(buffer) {
- glog.V(logger.Debug).Infof("[BZZ] syncDb[%v] buffer full %v: switching to db. session tally (db/total): %v/%v", self.priority, cap(buffer), self.dbTotal, self.total)
+ glog.V(logger.Debug).Infof("[BZZ] syncDb[%v/%v] buffer full %v: switching to db. session tally (db/total): %v/%v", self.key.Log(), self.priority, cap(buffer), self.dbTotal, self.total)
buffer = nil
db = self.buffer
}
@@ -154,18 +154,18 @@ LOOP:
binary.BigEndian.PutUint64(counterValue, counter)
batch.Put(self.counterKey, counterValue) // persist counter in batch
self.writeSyncBatch(batch) // save batch
- glog.V(logger.Detail).Infof("[BZZ] syncDb[%v] quitting: save current batch to db", self.priority)
+ glog.V(logger.Detail).Infof("[BZZ] syncDb[%v/%v] quitting: save current batch to db", self.key.Log(), self.priority)
break LOOP
}
self.dbTotal++
self.total++
- // otherwise break after selec
+ // otherwise break after select
case dbSize = <-self.batch:
// explicit request for batch
if inBatch == 0 && quit != nil {
// there was no writes since the last batch so db depleted
// switch to buffer mode
- glog.V(logger.Debug).Infof("[BZZ] syncDb[%v] empty db: switching to buffer", self.priority)
+ glog.V(logger.Debug).Infof("[BZZ] syncDb[%v/%v] empty db: switching to buffer", self.key.Log(), self.priority)
db = nil
buffer = self.buffer
dbSize <- 0 // indicates to 'caller' that batch has been written
@@ -174,7 +174,7 @@ LOOP:
}
binary.BigEndian.PutUint64(counterValue, counter)
batch.Put(self.counterKey, counterValue)
- glog.V(logger.Debug).Infof("[BZZ] syncDb[%v] write batch %v/%v - %x - %x", self.priority, inBatch, counter, self.counterKey, counterValue)
+ glog.V(logger.Debug).Infof("[BZZ] syncDb[%v/%v] write batch %v/%v - %x - %x", self.key.Log(), self.priority, inBatch, counter, self.counterKey, counterValue)
batch = self.writeSyncBatch(batch)
dbSize <- inBatch // indicates to 'caller' that batch has been written
inBatch = 0
@@ -186,7 +186,7 @@ LOOP:
db = self.buffer
buffer = nil
quit = nil
- glog.V(logger.Detail).Infof("[BZZ] syncDb[%v] quitting: save buffer to db", self.priority)
+ glog.V(logger.Detail).Infof("[BZZ] syncDb[%v/%v] quitting: save buffer to db", self.key.Log(), self.priority)
close(db)
continue LOOP
}
@@ -194,15 +194,15 @@ LOOP:
// only get here if we put req into db
entry, err = self.newSyncDbEntry(req, counter)
if err != nil {
- glog.V(logger.Warn).Infof("[BZZ] syncDb[%v] saving request %v (#%v/%v) failed: %v", self.priority, req, inBatch, inDb, err)
+ glog.V(logger.Warn).Infof("[BZZ] syncDb[%v/%v] saving request %v (#%v/%v) failed: %v", self.key.Log(), self.priority, req, inBatch, inDb, err)
continue LOOP
}
batch.Put(entry.key, entry.val)
- glog.V(logger.Detail).Infof("[BZZ] syncDb[%v] to batch %v '%v' (#%v/%v/%v)", self.priority, req, entry, inBatch, inDb, counter)
+ glog.V(logger.Detail).Infof("[BZZ] syncDb[%v/%v] to batch %v '%v' (#%v/%v/%v)", self.key.Log(), self.priority, req, entry, inBatch, inDb, counter)
// if just switched to db mode and not quitting, then launch dbRead
// in a parallel go routine to send deliveries from db
if inDb == 0 && quit != nil {
- glog.V(logger.Detail).Infof("[BZZ] syncDb[%v] start dbRead")
+ glog.V(logger.Detail).Infof("[BZZ] syncDb[%v/%v] start dbRead")
go self.dbRead(true, counter, deliver)
}
inDb++
@@ -221,7 +221,7 @@ LOOP:
func (self *syncDb) writeSyncBatch(batch *leveldb.Batch) *leveldb.Batch {
err := self.db.Write(batch)
if err != nil {
- glog.V(logger.Warn).Infof("[BZZ] syncDb[%v] saving batch to db failed: %v", self.priority, err)
+ glog.V(logger.Warn).Infof("[BZZ] syncDb[%v/%v] saving batch to db failed: %v", self.key.Log(), self.priority, err)
return batch
}
return new(leveldb.Batch)
@@ -295,7 +295,7 @@ func (self *syncDb) dbRead(useBatches bool, counter uint64, fun func(interface{}
continue
}
del = new(leveldb.Batch)
- glog.V(logger.Detail).Infof("[BZZ] syncDb[%v]: new iterator: %x (batch %v, count %v)", self.priority, key, batches, cnt)
+ glog.V(logger.Detail).Infof("[BZZ] syncDb[%v/%v]: new iterator: %x (batch %v, count %v)", self.key.Log(), self.priority, key, batches, cnt)
for n = 0; !useBatches || n < cnt; it.Next() {
copy(key, it.Key())
@@ -307,11 +307,11 @@ func (self *syncDb) dbRead(useBatches bool, counter uint64, fun func(interface{}
val := make([]byte, 40)
copy(val, it.Value())
entry = &syncDbEntry{key, val}
- // glog.V(logger.Detail).Infof("[BZZ] syncDb[%v] - %v, batches: %v, total: %v, session total from db: %v/%v", self.priority, self.key.Log(), batches, total, self.dbTotal, self.total)
+ // glog.V(logger.Detail).Infof("[BZZ] syncDb[%v/%v] - %v, batches: %v, total: %v, session total from db: %v/%v", self.key.Log(), self.priority, self.key.Log(), batches, total, self.dbTotal, self.total)
more = fun(entry, self.quit)
if !more {
// quit received when waiting to deliver entry, the entry will not be deleted
- glog.V(logger.Detail).Infof("[BZZ] syncDb[%v] batch %v quit after %v/%v items", self.priority, batches, n, cnt)
+ glog.V(logger.Detail).Infof("[BZZ] syncDb[%v/%v] batch %v quit after %v/%v items", self.key.Log(), self.priority, batches, n, cnt)
break
}
// since subsequent batches of the same db session are indexed incrementally
diff --git a/swarm/network/syncer.go b/swarm/network/syncer.go
index e540e236f5..1d94ee4331 100644
--- a/swarm/network/syncer.go
+++ b/swarm/network/syncer.go
@@ -298,6 +298,7 @@ func (self *syncer) sync() {
if state.LastSeenAt < state.SessionAt {
state.Last = state.SessionAt
glog.V(logger.Debug).Infof("[BZZ] syncer[%v]: start syncronising history since last disconnect at %v up until session start at %v: %v", self.key.Log(), state.LastSeenAt, state.SessionAt, state)
+ // blocks until state syncing is finished
self.syncState(state)
}
glog.V(logger.Info).Infof("[BZZ] syncer[%v]: syncing all history complete", self.key.Log())
@@ -316,7 +317,7 @@ func (self *syncer) syncState(state *syncState) {
// stop quits both request processor and saves the request cache to disk
func (self *syncer) stop() {
close(self.quit)
- glog.V(logger.Detail).Infof("[BZZ] syncer[%v]: stopand save sync request db backlog", self.key.Log())
+ glog.V(logger.Detail).Infof("[BZZ] syncer[%v]: stop and save sync request db backlog", self.key.Log())
for _, db := range self.queues {
db.stop()
}
@@ -358,19 +359,18 @@ func (self *syncer) syncHistory(state *syncState) chan interface{} {
IT:
for {
key := it.Next()
- if key != nil {
- select {
- // blocking until history channel is read from
- case history <- storage.Key(key):
- n++
- glog.V(logger.Detail).Infof("[BZZ] syncer[%v]: history: %v (%v keys)", self.key.Log(), key.Log(), n)
- state.Latest = key
- case <-self.quit:
- return
- }
- } else {
+ if key == nil {
break IT
}
+ select {
+ // blocking until history channel is read from
+ case history <- storage.Key(key):
+ n++
+ glog.V(logger.Detail).Infof("[BZZ] syncer[%v]: history: %v (%v keys)", self.key.Log(), key.Log(), n)
+ state.Latest = key
+ case <-self.quit:
+ return
+ }
}
glog.V(logger.Debug).Infof("[BZZ] syncer[%v]: finished syncing history between %v - %v for chunk addresses %v - %v (at %v) (chunks = %v)", self.key.Log(), state.First, state.Last, state.Start, state.Stop, state.Latest, n)
}()
@@ -416,25 +416,31 @@ LOOP:
// are checked first - integrity can only be guaranteed if writing
// is locked while selecting
if priority != High || len(keys) == 0 {
+ // selection is not needed if the High priority queue has items
keys = nil
+ PRIORITIES:
for priority = High; priority >= 0; priority-- {
+ // the first priority channel that is non-empty will be assigned to keys
if len(self.keys[priority]) > 0 {
glog.V(logger.Detail).Infof("[BZZ] syncer[%v]: reading request with priority %v", self.key.Log(), priority)
keys = self.keys[priority]
- break
+ break PRIORITIES
}
+ glog.V(logger.Debug).Infof("[BZZ] syncer[%v/%v]: queue: [%v, %v, %v]", self.key.Log(), priority, len(self.keys[High]), len(self.keys[Medium]), len(self.keys[Low]))
+ // if the input queue is empty on this level, resort to history if there is any
if uint(priority) == histPrior && history != nil {
glog.V(logger.Detail).Infof("[BZZ] syncer[%v]: reading history for %v", self.key.Log(), self.key)
keys = history
- break
+ break PRIORITIES
}
}
- // if peer ready to receive but nothing to send
- if keys == nil && deliveryRequest == nil {
- // if no items left and switch to waiting mode
- glog.V(logger.Detail).Infof("[BZZ] syncer[%v]: buffers consumed. Waiting", self.key.Log())
- newUnsyncedKeys = self.newUnsyncedKeys
- }
+ }
+
+ // if peer ready to receive but nothing to send
+ if keys == nil && deliveryRequest == nil {
+ // if no items left and switch to waiting mode
+ glog.V(logger.Detail).Infof("[BZZ] syncer[%v]: buffers consumed. Waiting", self.key.Log())
+ newUnsyncedKeys = self.newUnsyncedKeys
}
// send msg iff
@@ -447,7 +453,7 @@ LOOP:
len(unsynced) > 0 && keys == nil ||
len(unsynced) == int(self.SyncBatchSize)) {
justSynced = false
- // listen to requests again
+ // listen to requests
deliveryRequest = self.deliveryRequest
newUnsyncedKeys = nil // not care about data until next req comes in
// set sync to current counter
@@ -458,11 +464,11 @@ LOOP:
// send the unsynced keys
stateCopy := *state
err := self.unsyncedKeys(unsynced, &stateCopy)
- self.state = state
- glog.V(logger.Debug).Infof("[BZZ] syncer[%v]: --> %v keys sent: (total: %v (%v), history: %v), sent sync state: %v", self.key.Log(), len(unsynced), keyCounts, keyCount, historyCnt, stateCopy)
if err != nil {
glog.V(logger.Warn).Infof("[BZZ] syncer[%v]: unable to send unsynced keys: %v", err)
}
+ self.state = state
+ glog.V(logger.Debug).Infof("[BZZ] syncer[%v]: --> %v keys sent: (total: %v (%v), history: %v), sent sync state: %v", self.key.Log(), len(unsynced), keyCounts, keyCount, historyCnt, stateCopy)
unsynced = nil
keys = nil
}
@@ -477,7 +483,11 @@ LOOP:
// history channel is closed, waiting for new state (called from sync())
syncStates = self.syncStates
state.Synced = true // this signals that the current segment is complete
- state.synced <- false
+ select {
+ case state.synced <- false:
+ case <-self.quit:
+ break LOOP
+ }
justSynced = true
history = nil
}
@@ -575,7 +585,7 @@ func (self *syncer) syncDeliveries() {
total++
msg, err = self.newStoreRequestMsgData(req)
if err != nil {
- glog.V(logger.Warn).Infof("[BZZ] syncer[%v]: failed to deliver %v: %v", self.key.Log(), req, err)
+ glog.V(logger.Warn).Infof("[BZZ] syncer[%v]: failed to create store request for %v: %v", self.key.Log(), req, err)
} else {
err = self.store(msg)
if err != nil {
diff --git a/swarm/services/chequebook/cheque.go b/swarm/services/chequebook/cheque.go
index eb2150b468..7bbd2e9392 100644
--- a/swarm/services/chequebook/cheque.go
+++ b/swarm/services/chequebook/cheque.go
@@ -131,8 +131,10 @@ func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.Priva
}
func (self *Chequebook) setBalanceFromBlockChain() {
- balance := self.backend.BalanceAt(self.contractAddr)
- self.balance.Set(balance)
+ balance, err := self.backend.BalanceAt(self.contractAddr)
+ if err != nil {
+ self.balance.Set(balance)
+ }
}
// LoadChequebook(path, prvKey, backend) loads a chequebook from disk (file path)
diff --git a/swarm/services/chequebook/cheque_test.go b/swarm/services/chequebook/cheque_test.go
index c74b64be43..44db5091aa 100644
--- a/swarm/services/chequebook/cheque_test.go
+++ b/swarm/services/chequebook/cheque_test.go
@@ -10,7 +10,6 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/swarm/services/chequebook/contract"
)
@@ -42,16 +41,16 @@ func newTestBackend() *testBackend {
return &testBackend{SimulatedBackend: backends.NewSimulatedBackend(accs...)}
}
-func (b *testBackend) GetTxReceipt(txhash common.Hash) *types.Receipt {
- return nil
+func (b *testBackend) GetTxReceipt(txhash common.Hash) (map[string]interface{}, error) {
+ return nil, nil
}
-func (b *testBackend) CodeAt(address common.Address) string {
- return ""
+func (b *testBackend) CodeAt(address common.Address) (string, error) {
+ return "", nil
}
-func (b *testBackend) BalanceAt(address common.Address) *big.Int {
- return big.NewInt(0)
+func (b *testBackend) BalanceAt(address common.Address) (*big.Int, error) {
+ return big.NewInt(0), nil
}
func deploy(prvKey *ecdsa.PrivateKey, amount *big.Int, backend *backends.SimulatedBackend) (common.Address, error) {
diff --git a/swarm/services/ens/contract/ens.go b/swarm/services/ens/contract/ens.go
index 52611a58f8..b647fa7f56 100644
--- a/swarm/services/ens/contract/ens.go
+++ b/swarm/services/ens/contract/ens.go
@@ -12,108 +12,108 @@ import (
"github.com/ethereum/go-ethereum/core/types"
)
-// ENSABI is the input ABI used to generate the binding from.
-const ENSABI = `[{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"Owners","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"Registry","outputs":[{"name":"","type":"bytes32"}],"type":"function"},{"constant":false,"inputs":[{"name":"host","type":"bytes32"},{"name":"content","type":"bytes32"}],"name":"Set","outputs":[],"type":"function"}]`
+// ResolverABI is the input ABI used to generate the binding from.
+const ResolverABI = `[{"constant":false,"inputs":[{"name":"rootNodeId","type":"bytes12"},{"name":"name","type":"bytes32[]"}],"name":"deletePrivateRR","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"isPersonalResolver","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"label","type":"bytes32"},{"name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"id","type":"bytes32"}],"name":"getExtended","outputs":[{"name":"data","type":"bytes"}],"type":"function"},{"constant":false,"inputs":[{"name":"rootNodeId","type":"bytes12"},{"name":"name","type":"string"},{"name":"rtype","type":"bytes16"},{"name":"ttl","type":"uint32"},{"name":"len","type":"uint16"},{"name":"data","type":"bytes32"}],"name":"setRR","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"rootNodeId","type":"bytes12"},{"name":"name","type":"bytes32[]"},{"name":"rtype","type":"bytes16"},{"name":"ttl","type":"uint32"},{"name":"len","type":"uint16"},{"name":"data","type":"bytes32"}],"name":"setPrivateRR","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"nodeId","type":"bytes12"},{"name":"qtype","type":"bytes32"},{"name":"index","type":"uint16"}],"name":"resolve","outputs":[{"name":"rcode","type":"uint16"},{"name":"rtype","type":"bytes16"},{"name":"ttl","type":"uint32"},{"name":"len","type":"uint16"},{"name":"data","type":"bytes32"}],"type":"function"},{"constant":false,"inputs":[{"name":"label","type":"bytes32"},{"name":"resolver","type":"address"},{"name":"nodeId","type":"bytes12"}],"name":"register","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"label","type":"bytes32"},{"name":"resolver","type":"address"},{"name":"nodeId","type":"bytes12"}],"name":"setResolver","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"rootNodeId","type":"bytes12"},{"name":"name","type":"string"}],"name":"deleteRR","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"label","type":"bytes32"}],"name":"getOwner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"nodeId","type":"bytes12"},{"name":"label","type":"bytes32"}],"name":"findResolver","outputs":[{"name":"rcode","type":"uint16"},{"name":"ttl","type":"uint32"},{"name":"rnode","type":"bytes12"},{"name":"raddress","type":"address"}],"type":"function"}]`
-// ENSBin is the compiled bytecode used for deploying new contracts.
-const ENSBin = `0x606060405260008054600160a060020a03191633179055610148806100246000396000f3606060405260e060020a60003504633d14b257811461003c57806341c0e1b51461005d578063a0d03d3614610085578063be36e6761461009d575b005b61013c600435600260205260009081526040902054600160a060020a031681565b61003a60005433600160a060020a039081169116141561014657600054600160a060020a0316ff5b61013c60043560016020526000908152604090205481565b61003a600435602435600082815260026020526040812054600160a060020a031614156100e7576040600020805473ffffffffffffffffffffffffffffffffffffffff1916321790555b32600160a060020a03166002600050600084815260200190815260200160002060009054906101000a9004600160a060020a0316600160a060020a0316141561013857600160205260406000208190555b5050565b6060908152602090f35b56`
+// ResolverBin is the compiled bytecode used for deploying new contracts.
+const ResolverBin = `0x`
-// DeployENS deploys a new Ethereum contract, binding an instance of ENS to it.
-func DeployENS(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ENS, error) {
- parsed, err := abi.JSON(strings.NewReader(ENSABI))
+// DeployResolver deploys a new Ethereum contract, binding an instance of Resolver to it.
+func DeployResolver(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Resolver, error) {
+ parsed, err := abi.JSON(strings.NewReader(ResolverABI))
if err != nil {
return common.Address{}, nil, nil, err
}
- address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ENSBin), backend)
+ address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ResolverBin), backend)
if err != nil {
return common.Address{}, nil, nil, err
}
- return address, tx, &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}}, nil
+ return address, tx, &Resolver{ResolverCaller: ResolverCaller{contract: contract}, ResolverTransactor: ResolverTransactor{contract: contract}}, nil
}
-// ENS is an auto generated Go binding around an Ethereum contract.
-type ENS struct {
- ENSCaller // Read-only binding to the contract
- ENSTransactor // Write-only binding to the contract
+// Resolver is an auto generated Go binding around an Ethereum contract.
+type Resolver struct {
+ ResolverCaller // Read-only binding to the contract
+ ResolverTransactor // Write-only binding to the contract
}
-// ENSCaller is an auto generated read-only Go binding around an Ethereum contract.
-type ENSCaller struct {
+// ResolverCaller is an auto generated read-only Go binding around an Ethereum contract.
+type ResolverCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
-// ENSTransactor is an auto generated write-only Go binding around an Ethereum contract.
-type ENSTransactor struct {
+// ResolverTransactor is an auto generated write-only Go binding around an Ethereum contract.
+type ResolverTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
-// ENSSession is an auto generated Go binding around an Ethereum contract,
+// ResolverSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
-type ENSSession struct {
- Contract *ENS // Generic contract binding to set the session for
+type ResolverSession struct {
+ Contract *Resolver // Generic contract binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
-// ENSCallerSession is an auto generated read-only Go binding around an Ethereum contract,
+// ResolverCallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
-type ENSCallerSession struct {
- Contract *ENSCaller // Generic contract caller binding to set the session for
- CallOpts bind.CallOpts // Call options to use throughout this session
+type ResolverCallerSession struct {
+ Contract *ResolverCaller // Generic contract caller binding to set the session for
+ CallOpts bind.CallOpts // Call options to use throughout this session
}
-// ENSTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
+// ResolverTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
-type ENSTransactorSession struct {
- Contract *ENSTransactor // Generic contract transactor binding to set the session for
- TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
+type ResolverTransactorSession struct {
+ Contract *ResolverTransactor // Generic contract transactor binding to set the session for
+ TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
-// ENSRaw is an auto generated low-level Go binding around an Ethereum contract.
-type ENSRaw struct {
- Contract *ENS // Generic contract binding to access the raw methods on
+// ResolverRaw is an auto generated low-level Go binding around an Ethereum contract.
+type ResolverRaw struct {
+ Contract *Resolver // Generic contract binding to access the raw methods on
}
-// ENSCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
-type ENSCallerRaw struct {
- Contract *ENSCaller // Generic read-only contract binding to access the raw methods on
+// ResolverCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
+type ResolverCallerRaw struct {
+ Contract *ResolverCaller // Generic read-only contract binding to access the raw methods on
}
-// ENSTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
-type ENSTransactorRaw struct {
- Contract *ENSTransactor // Generic write-only contract binding to access the raw methods on
+// ResolverTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
+type ResolverTransactorRaw struct {
+ Contract *ResolverTransactor // Generic write-only contract binding to access the raw methods on
}
-// NewENS creates a new instance of ENS, bound to a specific deployed contract.
-func NewENS(address common.Address, backend bind.ContractBackend) (*ENS, error) {
- contract, err := bindENS(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))
+// NewResolver creates a new instance of Resolver, bound to a specific deployed contract.
+func NewResolver(address common.Address, backend bind.ContractBackend) (*Resolver, error) {
+ contract, err := bindResolver(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))
if err != nil {
return nil, err
}
- return &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}}, nil
+ return &Resolver{ResolverCaller: ResolverCaller{contract: contract}, ResolverTransactor: ResolverTransactor{contract: contract}}, nil
}
-// NewENSCaller creates a new read-only instance of ENS, bound to a specific deployed contract.
-func NewENSCaller(address common.Address, caller bind.ContractCaller) (*ENSCaller, error) {
- contract, err := bindENS(address, caller, nil)
+// NewResolverCaller creates a new read-only instance of Resolver, bound to a specific deployed contract.
+func NewResolverCaller(address common.Address, caller bind.ContractCaller) (*ResolverCaller, error) {
+ contract, err := bindResolver(address, caller, nil)
if err != nil {
return nil, err
}
- return &ENSCaller{contract: contract}, nil
+ return &ResolverCaller{contract: contract}, nil
}
-// NewENSTransactor creates a new write-only instance of ENS, bound to a specific deployed contract.
-func NewENSTransactor(address common.Address, transactor bind.ContractTransactor) (*ENSTransactor, error) {
- contract, err := bindENS(address, nil, transactor)
+// NewResolverTransactor creates a new write-only instance of Resolver, bound to a specific deployed contract.
+func NewResolverTransactor(address common.Address, transactor bind.ContractTransactor) (*ResolverTransactor, error) {
+ contract, err := bindResolver(address, nil, transactor)
if err != nil {
return nil, err
}
- return &ENSTransactor{contract: contract}, nil
+ return &ResolverTransactor{contract: contract}, nil
}
-// bindENS binds a generic wrapper to an already deployed contract.
-func bindENS(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {
- parsed, err := abi.JSON(strings.NewReader(ENSABI))
+// bindResolver binds a generic wrapper to an already deployed contract.
+func bindResolver(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {
+ parsed, err := abi.JSON(strings.NewReader(ResolverABI))
if err != nil {
return nil, err
}
@@ -124,443 +124,353 @@ func bindENS(address common.Address, caller bind.ContractCaller, transactor bind
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
-func (_ENS *ENSRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
- return _ENS.Contract.ENSCaller.contract.Call(opts, result, method, params...)
+func (_Resolver *ResolverRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
+ return _Resolver.Contract.ResolverCaller.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
-func (_ENS *ENSRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
- return _ENS.Contract.ENSTransactor.contract.Transfer(opts)
+func (_Resolver *ResolverRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _Resolver.Contract.ResolverTransactor.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
-func (_ENS *ENSRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
- return _ENS.Contract.ENSTransactor.contract.Transact(opts, method, params...)
+func (_Resolver *ResolverRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ return _Resolver.Contract.ResolverTransactor.contract.Transact(opts, method, params...)
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
-func (_ENS *ENSCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
- return _ENS.Contract.contract.Call(opts, result, method, params...)
+func (_Resolver *ResolverCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
+ return _Resolver.Contract.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
-func (_ENS *ENSTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
- return _ENS.Contract.contract.Transfer(opts)
+func (_Resolver *ResolverTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _Resolver.Contract.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
-func (_ENS *ENSTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
- return _ENS.Contract.contract.Transact(opts, method, params...)
+func (_Resolver *ResolverTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ return _Resolver.Contract.contract.Transact(opts, method, params...)
}
-// Owners is a free data retrieval call binding the contract method 0x3d14b257.
+// FindResolver is a free data retrieval call binding the contract method 0xedc0277c.
//
-// Solidity: function Owners( bytes32) constant returns(address)
-func (_ENS *ENSCaller) Owners(opts *bind.CallOpts, arg0 [32]byte) (common.Address, error) {
+// Solidity: function findResolver(nodeId bytes12, label bytes32) constant returns(rcode uint16, ttl uint32, rnode bytes12, raddress address)
+func (_Resolver *ResolverCaller) FindResolver(opts *bind.CallOpts, nodeId [12]byte, label [32]byte) (struct {
+ Rcode uint16
+ Ttl uint32
+ Rnode [12]byte
+ Raddress common.Address
+}, error) {
+ ret := new(struct {
+ Rcode uint16
+ Ttl uint32
+ Rnode [12]byte
+ Raddress common.Address
+ })
+ out := ret
+ err := _Resolver.contract.Call(opts, out, "findResolver", nodeId, label)
+ return *ret, err
+}
+
+// FindResolver is a free data retrieval call binding the contract method 0xedc0277c.
+//
+// Solidity: function findResolver(nodeId bytes12, label bytes32) constant returns(rcode uint16, ttl uint32, rnode bytes12, raddress address)
+func (_Resolver *ResolverSession) FindResolver(nodeId [12]byte, label [32]byte) (struct {
+ Rcode uint16
+ Ttl uint32
+ Rnode [12]byte
+ Raddress common.Address
+}, error) {
+ return _Resolver.Contract.FindResolver(&_Resolver.CallOpts, nodeId, label)
+}
+
+// FindResolver is a free data retrieval call binding the contract method 0xedc0277c.
+//
+// Solidity: function findResolver(nodeId bytes12, label bytes32) constant returns(rcode uint16, ttl uint32, rnode bytes12, raddress address)
+func (_Resolver *ResolverCallerSession) FindResolver(nodeId [12]byte, label [32]byte) (struct {
+ Rcode uint16
+ Ttl uint32
+ Rnode [12]byte
+ Raddress common.Address
+}, error) {
+ return _Resolver.Contract.FindResolver(&_Resolver.CallOpts, nodeId, label)
+}
+
+// GetExtended is a free data retrieval call binding the contract method 0x8021061c.
+//
+// Solidity: function getExtended(id bytes32) constant returns(data bytes)
+func (_Resolver *ResolverCaller) GetExtended(opts *bind.CallOpts, id [32]byte) ([]byte, error) {
+ var (
+ ret0 = new([]byte)
+ )
+ out := ret0
+ err := _Resolver.contract.Call(opts, out, "getExtended", id)
+ return *ret0, err
+}
+
+// GetExtended is a free data retrieval call binding the contract method 0x8021061c.
+//
+// Solidity: function getExtended(id bytes32) constant returns(data bytes)
+func (_Resolver *ResolverSession) GetExtended(id [32]byte) ([]byte, error) {
+ return _Resolver.Contract.GetExtended(&_Resolver.CallOpts, id)
+}
+
+// GetExtended is a free data retrieval call binding the contract method 0x8021061c.
+//
+// Solidity: function getExtended(id bytes32) constant returns(data bytes)
+func (_Resolver *ResolverCallerSession) GetExtended(id [32]byte) ([]byte, error) {
+ return _Resolver.Contract.GetExtended(&_Resolver.CallOpts, id)
+}
+
+// GetOwner is a free data retrieval call binding the contract method 0xdeb931a2.
+//
+// Solidity: function getOwner(label bytes32) constant returns(address)
+func (_Resolver *ResolverCaller) GetOwner(opts *bind.CallOpts, label [32]byte) (common.Address, error) {
var (
ret0 = new(common.Address)
)
out := ret0
- err := _ENS.contract.Call(opts, out, "Owners", arg0)
+ err := _Resolver.contract.Call(opts, out, "getOwner", label)
return *ret0, err
}
-// Owners is a free data retrieval call binding the contract method 0x3d14b257.
+// GetOwner is a free data retrieval call binding the contract method 0xdeb931a2.
//
-// Solidity: function Owners( bytes32) constant returns(address)
-func (_ENS *ENSSession) Owners(arg0 [32]byte) (common.Address, error) {
- return _ENS.Contract.Owners(&_ENS.CallOpts, arg0)
+// Solidity: function getOwner(label bytes32) constant returns(address)
+func (_Resolver *ResolverSession) GetOwner(label [32]byte) (common.Address, error) {
+ return _Resolver.Contract.GetOwner(&_Resolver.CallOpts, label)
}
-// Owners is a free data retrieval call binding the contract method 0x3d14b257.
+// GetOwner is a free data retrieval call binding the contract method 0xdeb931a2.
//
-// Solidity: function Owners( bytes32) constant returns(address)
-func (_ENS *ENSCallerSession) Owners(arg0 [32]byte) (common.Address, error) {
- return _ENS.Contract.Owners(&_ENS.CallOpts, arg0)
+// Solidity: function getOwner(label bytes32) constant returns(address)
+func (_Resolver *ResolverCallerSession) GetOwner(label [32]byte) (common.Address, error) {
+ return _Resolver.Contract.GetOwner(&_Resolver.CallOpts, label)
}
-// Registry is a free data retrieval call binding the contract method 0xa0d03d36.
+// IsPersonalResolver is a free data retrieval call binding the contract method 0x3f5665e7.
//
-// Solidity: function Registry( bytes32) constant returns(bytes32)
-func (_ENS *ENSCaller) Registry(opts *bind.CallOpts, arg0 [32]byte) ([32]byte, error) {
+// Solidity: function isPersonalResolver() constant returns(bool)
+func (_Resolver *ResolverCaller) IsPersonalResolver(opts *bind.CallOpts) (bool, error) {
var (
- ret0 = new([32]byte)
+ ret0 = new(bool)
)
out := ret0
- err := _ENS.contract.Call(opts, out, "Registry", arg0)
+ err := _Resolver.contract.Call(opts, out, "isPersonalResolver")
return *ret0, err
}
-// Registry is a free data retrieval call binding the contract method 0xa0d03d36.
+// IsPersonalResolver is a free data retrieval call binding the contract method 0x3f5665e7.
//
-// Solidity: function Registry( bytes32) constant returns(bytes32)
-func (_ENS *ENSSession) Registry(arg0 [32]byte) ([32]byte, error) {
- return _ENS.Contract.Registry(&_ENS.CallOpts, arg0)
+// Solidity: function isPersonalResolver() constant returns(bool)
+func (_Resolver *ResolverSession) IsPersonalResolver() (bool, error) {
+ return _Resolver.Contract.IsPersonalResolver(&_Resolver.CallOpts)
}
-// Registry is a free data retrieval call binding the contract method 0xa0d03d36.
+// IsPersonalResolver is a free data retrieval call binding the contract method 0x3f5665e7.
//
-// Solidity: function Registry( bytes32) constant returns(bytes32)
-func (_ENS *ENSCallerSession) Registry(arg0 [32]byte) ([32]byte, error) {
- return _ENS.Contract.Registry(&_ENS.CallOpts, arg0)
+// Solidity: function isPersonalResolver() constant returns(bool)
+func (_Resolver *ResolverCallerSession) IsPersonalResolver() (bool, error) {
+ return _Resolver.Contract.IsPersonalResolver(&_Resolver.CallOpts)
}
-// Set is a paid mutator transaction binding the contract method 0xbe36e676.
+// Resolve is a free data retrieval call binding the contract method 0xa16fdafa.
//
-// Solidity: function Set(host bytes32, content bytes32) returns()
-func (_ENS *ENSTransactor) Set(opts *bind.TransactOpts, host [32]byte, content [32]byte) (*types.Transaction, error) {
- return _ENS.contract.Transact(opts, "Set", host, content)
+// Solidity: function resolve(nodeId bytes12, qtype bytes32, index uint16) constant returns(rcode uint16, rtype bytes16, ttl uint32, len uint16, data bytes32)
+func (_Resolver *ResolverCaller) Resolve(opts *bind.CallOpts, nodeId [12]byte, qtype [32]byte, index uint16) (struct {
+ Rcode uint16
+ Rtype [16]byte
+ Ttl uint32
+ Len uint16
+ Data [32]byte
+}, error) {
+ ret := new(struct {
+ Rcode uint16
+ Rtype [16]byte
+ Ttl uint32
+ Len uint16
+ Data [32]byte
+ })
+ out := ret
+ err := _Resolver.contract.Call(opts, out, "resolve", nodeId, qtype, index)
+ return *ret, err
}
-// Set is a paid mutator transaction binding the contract method 0xbe36e676.
+// Resolve is a free data retrieval call binding the contract method 0xa16fdafa.
//
-// Solidity: function Set(host bytes32, content bytes32) returns()
-func (_ENS *ENSSession) Set(host [32]byte, content [32]byte) (*types.Transaction, error) {
- return _ENS.Contract.Set(&_ENS.TransactOpts, host, content)
+// Solidity: function resolve(nodeId bytes12, qtype bytes32, index uint16) constant returns(rcode uint16, rtype bytes16, ttl uint32, len uint16, data bytes32)
+func (_Resolver *ResolverSession) Resolve(nodeId [12]byte, qtype [32]byte, index uint16) (struct {
+ Rcode uint16
+ Rtype [16]byte
+ Ttl uint32
+ Len uint16
+ Data [32]byte
+}, error) {
+ return _Resolver.Contract.Resolve(&_Resolver.CallOpts, nodeId, qtype, index)
}
-// Set is a paid mutator transaction binding the contract method 0xbe36e676.
+// Resolve is a free data retrieval call binding the contract method 0xa16fdafa.
//
-// Solidity: function Set(host bytes32, content bytes32) returns()
-func (_ENS *ENSTransactorSession) Set(host [32]byte, content [32]byte) (*types.Transaction, error) {
- return _ENS.Contract.Set(&_ENS.TransactOpts, host, content)
+// Solidity: function resolve(nodeId bytes12, qtype bytes32, index uint16) constant returns(rcode uint16, rtype bytes16, ttl uint32, len uint16, data bytes32)
+func (_Resolver *ResolverCallerSession) Resolve(nodeId [12]byte, qtype [32]byte, index uint16) (struct {
+ Rcode uint16
+ Rtype [16]byte
+ Ttl uint32
+ Len uint16
+ Data [32]byte
+}, error) {
+ return _Resolver.Contract.Resolve(&_Resolver.CallOpts, nodeId, qtype, index)
}
-// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
+// DeletePrivateRR is a paid mutator transaction binding the contract method 0x1b370194.
//
-// Solidity: function kill() returns()
-func (_ENS *ENSTransactor) Kill(opts *bind.TransactOpts) (*types.Transaction, error) {
- return _ENS.contract.Transact(opts, "kill")
+// Solidity: function deletePrivateRR(rootNodeId bytes12, name bytes32[]) returns()
+func (_Resolver *ResolverTransactor) DeletePrivateRR(opts *bind.TransactOpts, rootNodeId [12]byte, name [][32]byte) (*types.Transaction, error) {
+ return _Resolver.contract.Transact(opts, "deletePrivateRR", rootNodeId, name)
}
-// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
+// DeletePrivateRR is a paid mutator transaction binding the contract method 0x1b370194.
//
-// Solidity: function kill() returns()
-func (_ENS *ENSSession) Kill() (*types.Transaction, error) {
- return _ENS.Contract.Kill(&_ENS.TransactOpts)
+// Solidity: function deletePrivateRR(rootNodeId bytes12, name bytes32[]) returns()
+func (_Resolver *ResolverSession) DeletePrivateRR(rootNodeId [12]byte, name [][32]byte) (*types.Transaction, error) {
+ return _Resolver.Contract.DeletePrivateRR(&_Resolver.TransactOpts, rootNodeId, name)
}
-// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
+// DeletePrivateRR is a paid mutator transaction binding the contract method 0x1b370194.
//
-// Solidity: function kill() returns()
-func (_ENS *ENSTransactorSession) Kill() (*types.Transaction, error) {
- return _ENS.Contract.Kill(&_ENS.TransactOpts)
+// Solidity: function deletePrivateRR(rootNodeId bytes12, name bytes32[]) returns()
+func (_Resolver *ResolverTransactorSession) DeletePrivateRR(rootNodeId [12]byte, name [][32]byte) (*types.Transaction, error) {
+ return _Resolver.Contract.DeletePrivateRR(&_Resolver.TransactOpts, rootNodeId, name)
}
-// MortalABI is the input ABI used to generate the binding from.
-const MortalABI = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"}]`
-
-// MortalBin is the compiled bytecode used for deploying new contracts.
-const MortalBin = `0x606060405260008054600160a060020a03191633179055605c8060226000396000f3606060405260e060020a600035046341c0e1b58114601a575b005b60186000543373ffffffffffffffffffffffffffffffffffffffff90811691161415605a5760005473ffffffffffffffffffffffffffffffffffffffff16ff5b56`
-
-// DeployMortal deploys a new Ethereum contract, binding an instance of Mortal to it.
-func DeployMortal(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Mortal, error) {
- parsed, err := abi.JSON(strings.NewReader(MortalABI))
- if err != nil {
- return common.Address{}, nil, nil, err
- }
- address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(MortalBin), backend)
- if err != nil {
- return common.Address{}, nil, nil, err
- }
- return address, tx, &Mortal{MortalCaller: MortalCaller{contract: contract}, MortalTransactor: MortalTransactor{contract: contract}}, nil
-}
-
-// Mortal is an auto generated Go binding around an Ethereum contract.
-type Mortal struct {
- MortalCaller // Read-only binding to the contract
- MortalTransactor // Write-only binding to the contract
-}
-
-// MortalCaller is an auto generated read-only Go binding around an Ethereum contract.
-type MortalCaller struct {
- contract *bind.BoundContract // Generic contract wrapper for the low level calls
-}
-
-// MortalTransactor is an auto generated write-only Go binding around an Ethereum contract.
-type MortalTransactor struct {
- contract *bind.BoundContract // Generic contract wrapper for the low level calls
-}
-
-// MortalSession is an auto generated Go binding around an Ethereum contract,
-// with pre-set call and transact options.
-type MortalSession struct {
- Contract *Mortal // Generic contract binding to set the session for
- CallOpts bind.CallOpts // Call options to use throughout this session
- TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
-}
-
-// MortalCallerSession is an auto generated read-only Go binding around an Ethereum contract,
-// with pre-set call options.
-type MortalCallerSession struct {
- Contract *MortalCaller // Generic contract caller binding to set the session for
- CallOpts bind.CallOpts // Call options to use throughout this session
-}
-
-// MortalTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
-// with pre-set transact options.
-type MortalTransactorSession struct {
- Contract *MortalTransactor // Generic contract transactor binding to set the session for
- TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
-}
-
-// MortalRaw is an auto generated low-level Go binding around an Ethereum contract.
-type MortalRaw struct {
- Contract *Mortal // Generic contract binding to access the raw methods on
-}
-
-// MortalCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
-type MortalCallerRaw struct {
- Contract *MortalCaller // Generic read-only contract binding to access the raw methods on
-}
-
-// MortalTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
-type MortalTransactorRaw struct {
- Contract *MortalTransactor // Generic write-only contract binding to access the raw methods on
-}
-
-// NewMortal creates a new instance of Mortal, bound to a specific deployed contract.
-func NewMortal(address common.Address, backend bind.ContractBackend) (*Mortal, error) {
- contract, err := bindMortal(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))
- if err != nil {
- return nil, err
- }
- return &Mortal{MortalCaller: MortalCaller{contract: contract}, MortalTransactor: MortalTransactor{contract: contract}}, nil
-}
-
-// NewMortalCaller creates a new read-only instance of Mortal, bound to a specific deployed contract.
-func NewMortalCaller(address common.Address, caller bind.ContractCaller) (*MortalCaller, error) {
- contract, err := bindMortal(address, caller, nil)
- if err != nil {
- return nil, err
- }
- return &MortalCaller{contract: contract}, nil
-}
-
-// NewMortalTransactor creates a new write-only instance of Mortal, bound to a specific deployed contract.
-func NewMortalTransactor(address common.Address, transactor bind.ContractTransactor) (*MortalTransactor, error) {
- contract, err := bindMortal(address, nil, transactor)
- if err != nil {
- return nil, err
- }
- return &MortalTransactor{contract: contract}, nil
-}
-
-// bindMortal binds a generic wrapper to an already deployed contract.
-func bindMortal(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {
- parsed, err := abi.JSON(strings.NewReader(MortalABI))
- if err != nil {
- return nil, err
- }
- return bind.NewBoundContract(address, parsed, caller, transactor), nil
-}
-
-// Call invokes the (constant) contract method with params as input values and
-// sets the output to result. The result type might be a single field for simple
-// returns, a slice of interfaces for anonymous returns and a struct for named
-// returns.
-func (_Mortal *MortalRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
- return _Mortal.Contract.MortalCaller.contract.Call(opts, result, method, params...)
-}
-
-// Transfer initiates a plain transaction to move funds to the contract, calling
-// its default method if one is available.
-func (_Mortal *MortalRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
- return _Mortal.Contract.MortalTransactor.contract.Transfer(opts)
-}
-
-// Transact invokes the (paid) contract method with params as input values.
-func (_Mortal *MortalRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
- return _Mortal.Contract.MortalTransactor.contract.Transact(opts, method, params...)
-}
-
-// Call invokes the (constant) contract method with params as input values and
-// sets the output to result. The result type might be a single field for simple
-// returns, a slice of interfaces for anonymous returns and a struct for named
-// returns.
-func (_Mortal *MortalCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
- return _Mortal.Contract.contract.Call(opts, result, method, params...)
-}
-
-// Transfer initiates a plain transaction to move funds to the contract, calling
-// its default method if one is available.
-func (_Mortal *MortalTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
- return _Mortal.Contract.contract.Transfer(opts)
-}
-
-// Transact invokes the (paid) contract method with params as input values.
-func (_Mortal *MortalTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
- return _Mortal.Contract.contract.Transact(opts, method, params...)
-}
-
-// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
+// DeleteRR is a paid mutator transaction binding the contract method 0xbc06183d.
//
-// Solidity: function kill() returns()
-func (_Mortal *MortalTransactor) Kill(opts *bind.TransactOpts) (*types.Transaction, error) {
- return _Mortal.contract.Transact(opts, "kill")
+// Solidity: function deleteRR(rootNodeId bytes12, name string) returns()
+func (_Resolver *ResolverTransactor) DeleteRR(opts *bind.TransactOpts, rootNodeId [12]byte, name string) (*types.Transaction, error) {
+ return _Resolver.contract.Transact(opts, "deleteRR", rootNodeId, name)
}
-// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
+// DeleteRR is a paid mutator transaction binding the contract method 0xbc06183d.
//
-// Solidity: function kill() returns()
-func (_Mortal *MortalSession) Kill() (*types.Transaction, error) {
- return _Mortal.Contract.Kill(&_Mortal.TransactOpts)
+// Solidity: function deleteRR(rootNodeId bytes12, name string) returns()
+func (_Resolver *ResolverSession) DeleteRR(rootNodeId [12]byte, name string) (*types.Transaction, error) {
+ return _Resolver.Contract.DeleteRR(&_Resolver.TransactOpts, rootNodeId, name)
}
-// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
+// DeleteRR is a paid mutator transaction binding the contract method 0xbc06183d.
//
-// Solidity: function kill() returns()
-func (_Mortal *MortalTransactorSession) Kill() (*types.Transaction, error) {
- return _Mortal.Contract.Kill(&_Mortal.TransactOpts)
+// Solidity: function deleteRR(rootNodeId bytes12, name string) returns()
+func (_Resolver *ResolverTransactorSession) DeleteRR(rootNodeId [12]byte, name string) (*types.Transaction, error) {
+ return _Resolver.Contract.DeleteRR(&_Resolver.TransactOpts, rootNodeId, name)
}
-// OwnedABI is the input ABI used to generate the binding from.
-const OwnedABI = `[{"inputs":[],"type":"constructor"}]`
-
-// OwnedBin is the compiled bytecode used for deploying new contracts.
-const OwnedBin = `0x606060405260008054600160a060020a0319163317905560068060226000396000f3606060405200`
-
-// DeployOwned deploys a new Ethereum contract, binding an instance of Owned to it.
-func DeployOwned(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Owned, error) {
- parsed, err := abi.JSON(strings.NewReader(OwnedABI))
- if err != nil {
- return common.Address{}, nil, nil, err
- }
- address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(OwnedBin), backend)
- if err != nil {
- return common.Address{}, nil, nil, err
- }
- return address, tx, &Owned{OwnedCaller: OwnedCaller{contract: contract}, OwnedTransactor: OwnedTransactor{contract: contract}}, nil
+// Register is a paid mutator transaction binding the contract method 0xa1f8f8f0.
+//
+// Solidity: function register(label bytes32, resolver address, nodeId bytes12) returns()
+func (_Resolver *ResolverTransactor) Register(opts *bind.TransactOpts, label [32]byte, resolver common.Address, nodeId [12]byte) (*types.Transaction, error) {
+ return _Resolver.contract.Transact(opts, "register", label, resolver, nodeId)
}
-// Owned is an auto generated Go binding around an Ethereum contract.
-type Owned struct {
- OwnedCaller // Read-only binding to the contract
- OwnedTransactor // Write-only binding to the contract
+// Register is a paid mutator transaction binding the contract method 0xa1f8f8f0.
+//
+// Solidity: function register(label bytes32, resolver address, nodeId bytes12) returns()
+func (_Resolver *ResolverSession) Register(label [32]byte, resolver common.Address, nodeId [12]byte) (*types.Transaction, error) {
+ return _Resolver.Contract.Register(&_Resolver.TransactOpts, label, resolver, nodeId)
}
-// OwnedCaller is an auto generated read-only Go binding around an Ethereum contract.
-type OwnedCaller struct {
- contract *bind.BoundContract // Generic contract wrapper for the low level calls
+// Register is a paid mutator transaction binding the contract method 0xa1f8f8f0.
+//
+// Solidity: function register(label bytes32, resolver address, nodeId bytes12) returns()
+func (_Resolver *ResolverTransactorSession) Register(label [32]byte, resolver common.Address, nodeId [12]byte) (*types.Transaction, error) {
+ return _Resolver.Contract.Register(&_Resolver.TransactOpts, label, resolver, nodeId)
}
-// OwnedTransactor is an auto generated write-only Go binding around an Ethereum contract.
-type OwnedTransactor struct {
- contract *bind.BoundContract // Generic contract wrapper for the low level calls
+// SetOwner is a paid mutator transaction binding the contract method 0x5b0fc9c3.
+//
+// Solidity: function setOwner(label bytes32, newOwner address) returns()
+func (_Resolver *ResolverTransactor) SetOwner(opts *bind.TransactOpts, label [32]byte, newOwner common.Address) (*types.Transaction, error) {
+ return _Resolver.contract.Transact(opts, "setOwner", label, newOwner)
}
-// OwnedSession is an auto generated Go binding around an Ethereum contract,
-// with pre-set call and transact options.
-type OwnedSession struct {
- Contract *Owned // Generic contract binding to set the session for
- CallOpts bind.CallOpts // Call options to use throughout this session
- TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
+// SetOwner is a paid mutator transaction binding the contract method 0x5b0fc9c3.
+//
+// Solidity: function setOwner(label bytes32, newOwner address) returns()
+func (_Resolver *ResolverSession) SetOwner(label [32]byte, newOwner common.Address) (*types.Transaction, error) {
+ return _Resolver.Contract.SetOwner(&_Resolver.TransactOpts, label, newOwner)
}
-// OwnedCallerSession is an auto generated read-only Go binding around an Ethereum contract,
-// with pre-set call options.
-type OwnedCallerSession struct {
- Contract *OwnedCaller // Generic contract caller binding to set the session for
- CallOpts bind.CallOpts // Call options to use throughout this session
+// SetOwner is a paid mutator transaction binding the contract method 0x5b0fc9c3.
+//
+// Solidity: function setOwner(label bytes32, newOwner address) returns()
+func (_Resolver *ResolverTransactorSession) SetOwner(label [32]byte, newOwner common.Address) (*types.Transaction, error) {
+ return _Resolver.Contract.SetOwner(&_Resolver.TransactOpts, label, newOwner)
}
-// OwnedTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
-// with pre-set transact options.
-type OwnedTransactorSession struct {
- Contract *OwnedTransactor // Generic contract transactor binding to set the session for
- TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
+// SetPrivateRR is a paid mutator transaction binding the contract method 0x91c8e7b9.
+//
+// Solidity: function setPrivateRR(rootNodeId bytes12, name bytes32[], rtype bytes16, ttl uint32, len uint16, data bytes32) returns()
+func (_Resolver *ResolverTransactor) SetPrivateRR(opts *bind.TransactOpts, rootNodeId [12]byte, name [][32]byte, rtype [16]byte, ttl uint32, len uint16, data [32]byte) (*types.Transaction, error) {
+ return _Resolver.contract.Transact(opts, "setPrivateRR", rootNodeId, name, rtype, ttl, len, data)
}
-// OwnedRaw is an auto generated low-level Go binding around an Ethereum contract.
-type OwnedRaw struct {
- Contract *Owned // Generic contract binding to access the raw methods on
+// SetPrivateRR is a paid mutator transaction binding the contract method 0x91c8e7b9.
+//
+// Solidity: function setPrivateRR(rootNodeId bytes12, name bytes32[], rtype bytes16, ttl uint32, len uint16, data bytes32) returns()
+func (_Resolver *ResolverSession) SetPrivateRR(rootNodeId [12]byte, name [][32]byte, rtype [16]byte, ttl uint32, len uint16, data [32]byte) (*types.Transaction, error) {
+ return _Resolver.Contract.SetPrivateRR(&_Resolver.TransactOpts, rootNodeId, name, rtype, ttl, len, data)
}
-// OwnedCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
-type OwnedCallerRaw struct {
- Contract *OwnedCaller // Generic read-only contract binding to access the raw methods on
+// SetPrivateRR is a paid mutator transaction binding the contract method 0x91c8e7b9.
+//
+// Solidity: function setPrivateRR(rootNodeId bytes12, name bytes32[], rtype bytes16, ttl uint32, len uint16, data bytes32) returns()
+func (_Resolver *ResolverTransactorSession) SetPrivateRR(rootNodeId [12]byte, name [][32]byte, rtype [16]byte, ttl uint32, len uint16, data [32]byte) (*types.Transaction, error) {
+ return _Resolver.Contract.SetPrivateRR(&_Resolver.TransactOpts, rootNodeId, name, rtype, ttl, len, data)
}
-// OwnedTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
-type OwnedTransactorRaw struct {
- Contract *OwnedTransactor // Generic write-only contract binding to access the raw methods on
+// SetRR is a paid mutator transaction binding the contract method 0x8bba944d.
+//
+// Solidity: function setRR(rootNodeId bytes12, name string, rtype bytes16, ttl uint32, len uint16, data bytes32) returns()
+func (_Resolver *ResolverTransactor) SetRR(opts *bind.TransactOpts, rootNodeId [12]byte, name string, rtype [16]byte, ttl uint32, len uint16, data [32]byte) (*types.Transaction, error) {
+ return _Resolver.contract.Transact(opts, "setRR", rootNodeId, name, rtype, ttl, len, data)
}
-// NewOwned creates a new instance of Owned, bound to a specific deployed contract.
-func NewOwned(address common.Address, backend bind.ContractBackend) (*Owned, error) {
- contract, err := bindOwned(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))
- if err != nil {
- return nil, err
- }
- return &Owned{OwnedCaller: OwnedCaller{contract: contract}, OwnedTransactor: OwnedTransactor{contract: contract}}, nil
+// SetRR is a paid mutator transaction binding the contract method 0x8bba944d.
+//
+// Solidity: function setRR(rootNodeId bytes12, name string, rtype bytes16, ttl uint32, len uint16, data bytes32) returns()
+func (_Resolver *ResolverSession) SetRR(rootNodeId [12]byte, name string, rtype [16]byte, ttl uint32, len uint16, data [32]byte) (*types.Transaction, error) {
+ return _Resolver.Contract.SetRR(&_Resolver.TransactOpts, rootNodeId, name, rtype, ttl, len, data)
}
-// NewOwnedCaller creates a new read-only instance of Owned, bound to a specific deployed contract.
-func NewOwnedCaller(address common.Address, caller bind.ContractCaller) (*OwnedCaller, error) {
- contract, err := bindOwned(address, caller, nil)
- if err != nil {
- return nil, err
- }
- return &OwnedCaller{contract: contract}, nil
+// SetRR is a paid mutator transaction binding the contract method 0x8bba944d.
+//
+// Solidity: function setRR(rootNodeId bytes12, name string, rtype bytes16, ttl uint32, len uint16, data bytes32) returns()
+func (_Resolver *ResolverTransactorSession) SetRR(rootNodeId [12]byte, name string, rtype [16]byte, ttl uint32, len uint16, data [32]byte) (*types.Transaction, error) {
+ return _Resolver.Contract.SetRR(&_Resolver.TransactOpts, rootNodeId, name, rtype, ttl, len, data)
}
-// NewOwnedTransactor creates a new write-only instance of Owned, bound to a specific deployed contract.
-func NewOwnedTransactor(address common.Address, transactor bind.ContractTransactor) (*OwnedTransactor, error) {
- contract, err := bindOwned(address, nil, transactor)
- if err != nil {
- return nil, err
- }
- return &OwnedTransactor{contract: contract}, nil
+// SetResolver is a paid mutator transaction binding the contract method 0xa9f2a1b2.
+//
+// Solidity: function setResolver(label bytes32, resolver address, nodeId bytes12) returns()
+func (_Resolver *ResolverTransactor) SetResolver(opts *bind.TransactOpts, label [32]byte, resolver common.Address, nodeId [12]byte) (*types.Transaction, error) {
+ return _Resolver.contract.Transact(opts, "setResolver", label, resolver, nodeId)
}
-// bindOwned binds a generic wrapper to an already deployed contract.
-func bindOwned(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {
- parsed, err := abi.JSON(strings.NewReader(OwnedABI))
- if err != nil {
- return nil, err
- }
- return bind.NewBoundContract(address, parsed, caller, transactor), nil
+// SetResolver is a paid mutator transaction binding the contract method 0xa9f2a1b2.
+//
+// Solidity: function setResolver(label bytes32, resolver address, nodeId bytes12) returns()
+func (_Resolver *ResolverSession) SetResolver(label [32]byte, resolver common.Address, nodeId [12]byte) (*types.Transaction, error) {
+ return _Resolver.Contract.SetResolver(&_Resolver.TransactOpts, label, resolver, nodeId)
}
-// Call invokes the (constant) contract method with params as input values and
-// sets the output to result. The result type might be a single field for simple
-// returns, a slice of interfaces for anonymous returns and a struct for named
-// returns.
-func (_Owned *OwnedRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
- return _Owned.Contract.OwnedCaller.contract.Call(opts, result, method, params...)
-}
-
-// Transfer initiates a plain transaction to move funds to the contract, calling
-// its default method if one is available.
-func (_Owned *OwnedRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
- return _Owned.Contract.OwnedTransactor.contract.Transfer(opts)
-}
-
-// Transact invokes the (paid) contract method with params as input values.
-func (_Owned *OwnedRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
- return _Owned.Contract.OwnedTransactor.contract.Transact(opts, method, params...)
-}
-
-// Call invokes the (constant) contract method with params as input values and
-// sets the output to result. The result type might be a single field for simple
-// returns, a slice of interfaces for anonymous returns and a struct for named
-// returns.
-func (_Owned *OwnedCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
- return _Owned.Contract.contract.Call(opts, result, method, params...)
-}
-
-// Transfer initiates a plain transaction to move funds to the contract, calling
-// its default method if one is available.
-func (_Owned *OwnedTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
- return _Owned.Contract.contract.Transfer(opts)
-}
-
-// Transact invokes the (paid) contract method with params as input values.
-func (_Owned *OwnedTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
- return _Owned.Contract.contract.Transact(opts, method, params...)
+// SetResolver is a paid mutator transaction binding the contract method 0xa9f2a1b2.
+//
+// Solidity: function setResolver(label bytes32, resolver address, nodeId bytes12) returns()
+func (_Resolver *ResolverTransactorSession) SetResolver(label [32]byte, resolver common.Address, nodeId [12]byte) (*types.Transaction, error) {
+ return _Resolver.Contract.SetResolver(&_Resolver.TransactOpts, label, resolver, nodeId)
}
diff --git a/swarm/services/ens/contract/ens.sol b/swarm/services/ens/contract/ens.sol
index 9ef19c5e80..7e0f3e73bb 100644
--- a/swarm/services/ens/contract/ens.sol
+++ b/swarm/services/ens/contract/ens.sol
@@ -1,19 +1,31 @@
-import "mortal";
-/// @title Swarm Distributed Preimage Archive
-/// @author Viktor Tron
-contract ENS is mortal
-{
+/**
+ * ENS resolver interface.
+ */
+contract Resolver {
+ bytes32 constant TYPE_STAR = "*";
+
+ // Response codes.
+ uint16 constant RCODE_OK = 0;
+ uint16 constant RCODE_NXDOMAIN = 3;
- mapping (bytes32 => bytes32) public Registry;
- mapping (bytes32 => address) public Owners;
+ // These methods are shared by all resolvers
+ function findResolver(bytes12 nodeId, bytes32 label) constant
+ returns (uint16 rcode, uint32 ttl, bytes12 rnode, address raddress);
+ function resolve(bytes12 nodeId, bytes32 qtype, uint16 index) constant
+ returns (uint16 rcode, bytes16 rtype, uint32 ttl, uint16 len,
+ bytes32 data);
+ function getExtended(bytes32 id) constant returns (bytes data);
- function Set(bytes32 host, bytes32 content) {
- if (Owners[host] == 0x0) {
- Owners[host] = tx.origin;
- }
- if (Owners[host] == tx.origin) {
- Registry[host] = content;
- }
- }
+ // These methods are implemented by personal resolvers
+ function isPersonalResolver() constant returns (bool);
+ function setRR(bytes12 rootNodeId, string name, bytes16 rtype, uint32 ttl, uint16 len, bytes32 data);
+ function setPrivateRR(bytes12 rootNodeId, bytes32[] name, bytes16 rtype, uint32 ttl, uint16 len, bytes32 data);
+ function deleteRR(bytes12 rootNodeId, string name);
+ function deletePrivateRR(bytes12 rootNodeId, bytes32[] name);
-}
\ No newline at end of file
+ // These methods are implemented by open registrar implementations.
+ function register(bytes32 label, address resolver, bytes12 nodeId);
+ function setOwner(bytes32 label, address newOwner);
+ function setResolver(bytes32 label, address resolver, bytes12 nodeId);
+ function getOwner(bytes32 label) constant returns (address);
+}
diff --git a/swarm/services/ens/ens.go b/swarm/services/ens/ens.go
index 715d06d0ea..65022982c6 100644
--- a/swarm/services/ens/ens.go
+++ b/swarm/services/ens/ens.go
@@ -3,82 +3,183 @@ package ens
import (
"fmt"
- "math/big"
"regexp"
+ "strings"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/swarm/services/ens/contract"
"github.com/ethereum/go-ethereum/swarm/storage"
)
var domainAndVersion = regexp.MustCompile("[@:;,]+")
+var qtypeChash = [32]byte{ 0x43, 0x48, 0x41, 0x53, 0x48}
+var rtypeChash = [16]byte{ 0x43, 0x48, 0x41, 0x53, 0x48}
// swarm domain name registry and resolver
// the ENS instance can be directly wrapped in rpc.Api
type ENS struct {
- *contract.ENSSession
+ transactOpts *bind.TransactOpts;
+ contractBackend bind.ContractBackend;
+ rootAddress common.Address;
}
-// NewENS creates a proxy instance wrapping the abigen interface to the ENS contract
-// using the transaction options passed as first argument, it sets up a session
func NewENS(transactOpts *bind.TransactOpts, contractAddr common.Address, contractBackend bind.ContractBackend) *ENS {
- ens, err := contract.NewENS(contractAddr, contractBackend)
- if err != nil {
- glog.V(logger.Debug).Infof("error setting up name server on %v, skipping: %v", contractAddr.Hex(), err)
- }
return &ENS{
- &contract.ENSSession{
- Contract: ens,
- TransactOpts: *transactOpts,
- },
+ transactOpts: transactOpts,
+ contractBackend: contractBackend,
+ rootAddress: contractAddr,
}
}
-// Register(name, hash )
-//involves sending a transaction (sent by sender specified as From of Transact)
-func (self *ENS) Register(name string, hash common.Hash) (*types.Transaction, error) {
- namehash := crypto.Sha3Hash([]byte(name))
- owner, err := self.Owners(namehash)
+func (self *ENS) newResolver(contractAddr common.Address) (*contract.ResolverSession, error) {
+ resolver, err := contract.NewResolver(contractAddr, self.contractBackend)
if err != nil {
- return nil, fmt.Errorf("error registering '%s': %v", name, err)
+ return nil, err
}
- if (owner != common.Address{} && owner != self.TransactOpts.From) {
- return nil, fmt.Errorf("error registering '%s': already set as %", name)
- }
- glog.V(logger.Debug).Infof("[ENS]: host '%s' (hash: '%v') to be registered as '%v'", name, namehash.Hex(), hash.Hex())
- return self.Set(namehash, hash)
-}
-
-func (self *ENS) WhoseIs(name string) (common.Address, error) {
- namehash := crypto.Sha3Hash([]byte(name))
- return self.Owners(namehash)
+ return &contract.ResolverSession{
+ Contract: resolver,
+ TransactOpts: *self.transactOpts,
+ }, nil
}
// resolve is a non-tranasctional call, returns hash as storage.Key
func (self *ENS) Resolve(hostPort string) (storage.Key, error) {
host := hostPort
- var version *big.Int
parts := domainAndVersion.Split(host, 3)
if len(parts) > 1 && parts[1] != "" {
host = parts[0]
- version = common.Big(parts[1])
}
- hash := crypto.Sha3Hash([]byte(host))
- _ = version
- // hash, err = self.registrar.Resolver(version).HashToHash(hostHash)
- hash, err := self.Registry(hash)
- if err != nil {
- return nil, fmt.Errorf("error resolving '%v': %v", hash.Hex(), err)
- }
- if (hash == common.Hash{}) {
- return nil, fmt.Errorf("unable to resolve '%v': not found", hash)
- }
- contentHash := storage.Key(hash.Bytes())
- glog.V(logger.Debug).Infof("[ENS] resolve host '%v' to contentHash: '%v'", hash, contentHash)
- return contentHash, nil
+ return self.resolveName(self.rootAddress, host)
+}
+
+func (self *ENS) nextResolver(resolver *contract.ResolverSession, nodeId [12]byte, label string) (*contract.ResolverSession, [12]byte, error) {
+ hash := crypto.Sha3Hash([]byte(label))
+ ret, err := resolver.FindResolver(nodeId, hash)
+ if err != nil {
+ err = fmt.Errorf("error resolving label '%v': %v", label, err)
+ return nil, [12]byte{}, err
+ }
+ if ret.Rcode != 0 {
+ err = fmt.Errorf("error resolving label '%v': got response code %v", label, ret.Rcode)
+ return nil, [12]byte{}, err
+ }
+ nodeId = ret.Rnode;
+ resolver, err = self.newResolver(ret.Raddress)
+ if err != nil {
+ return nil, [12]byte{}, err
+ }
+
+ return resolver, nodeId, nil
+}
+
+func (self *ENS) findResolver(rootAddress common.Address, host string) (*contract.ResolverSession, [12]byte, error) {
+ resolver, err := self.newResolver(self.rootAddress)
+ if err != nil {
+ return nil, [12]byte{}, err
+ }
+
+ if len(host) == 0 {
+ return resolver, [12]byte{}, nil
+ }
+
+ labels := strings.Split(host, ".")
+
+ var nodeId [12]byte
+ for i := len(labels) - 1; i >= 0; i-- {
+ var err error
+ resolver, nodeId, err = self.nextResolver(resolver, nodeId, labels[i])
+ if err != nil {
+ return nil, [12]byte{}, err
+ }
+ }
+
+ return resolver, nodeId, nil
+}
+
+func (self *ENS) resolveName(rootAddress common.Address, host string) (storage.Key, error) {
+ resolver, nodeId, err := self.findResolver(rootAddress, host)
+ if err != nil {
+ return nil, err
+ }
+
+ ret, err := resolver.Resolve(nodeId, qtypeChash, 0)
+ if err != nil {
+ return nil, fmt.Errorf("error looking up RR on '%v': %v", host, err)
+ }
+ if ret.Rcode != 0 {
+ return nil, fmt.Errorf("error looking up RR on '%v': got response code %v", host, ret.Rcode)
+ }
+ return storage.Key(ret.Data[:]), nil
+}
+
+/**
+ * Registers a new domain name for the caller, making them the owner of the new name.
+ */
+func (self *ENS) Register(name string, resolverAddress common.Address) (*types.Transaction, error) {
+ // Find the resolver that we should register with (the one that controls the parent domain)
+ parts := strings.SplitN(name, ".", 2)
+
+ baseName := ""
+ if len(parts) > 1 {
+ baseName = parts[1]
+ }
+
+ resolver, nodeId, err := self.findResolver(self.rootAddress, baseName)
+ if err != nil {
+ return nil, err
+ }
+ if nodeId != [12]byte{} {
+ return nil, fmt.Errorf("cannot register domains on %v: not a root node", baseName)
+ }
+
+ // Send it a register transaction
+ hash := crypto.Sha3Hash([]byte(parts[0]))
+ return resolver.Register(hash, resolverAddress, [12]byte{})
+}
+
+/**
+ * Steps through name components until it finds a PersonalResolver contract.
+ * Returns the resolver, the node ID, and the remaining name components.
+ */
+func (self *ENS) findPersonalResolver(name string) (*contract.ResolverSession, [12]byte, string, error) {
+ var nodeId [12]byte
+
+ resolver, err := self.newResolver(self.rootAddress)
+ if err != nil {
+ return nil, [12]byte{}, "", err
+ }
+
+ labels := strings.Split(name, ".")
+
+ for i := len(labels) - 1; i >= 0; i-- {
+ if personal, _ := resolver.IsPersonalResolver(); personal {
+ return resolver, nodeId, strings.Join(labels[0:i + 1], "."), nil
+ }
+
+ resolver, nodeId, err = self.nextResolver(resolver, nodeId, labels[i])
+ if err != nil {
+ return nil, [12]byte{}, "", err
+ }
+ }
+
+ if personal, _ := resolver.IsPersonalResolver(); !personal {
+ return nil, [12]byte{}, "", fmt.Errorf("Personal resolver not found in any name component")
+ } else {
+ return resolver, nodeId, "", nil
+ }
+}
+
+/**
+ * Sets the content hash associated with a name.
+ */
+func (self *ENS) SetContentHash(name string, hash common.Hash) (*types.Transaction, error) {
+ resolver, nodeId, name, err := self.findPersonalResolver(name)
+ if err != nil {
+ return nil, err
+ }
+
+ return resolver.SetRR(nodeId, name, rtypeChash, 3600, 20, [32]byte(hash))
}
diff --git a/swarm/services/ens/ens_test.go b/swarm/services/ens/ens_test.go
index 0b99730b46..7480d802d1 100644
--- a/swarm/services/ens/ens_test.go
+++ b/swarm/services/ens/ens_test.go
@@ -10,9 +10,15 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/swarm/services/ens/contract"
)
+func init() {
+ glog.SetV(6)
+ glog.SetToStderr(true)
+}
+
var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
name = "my name on ENS"
@@ -23,7 +29,7 @@ var (
func deploy(prvKey *ecdsa.PrivateKey, amount *big.Int, backend *backends.SimulatedBackend) (common.Address, error) {
deployTransactor := bind.NewKeyedTransactor(prvKey)
deployTransactor.Value = amount
- addr, _, _, err := contract.DeployENS(deployTransactor, backend)
+ addr, _, _, err := contract.DeployResolver(deployTransactor, backend)
if err != nil {
return common.Address{}, err
}
@@ -38,12 +44,25 @@ func TestENS(t *testing.T) {
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
+
+ resolverAddr, err := deploy(key, big.NewInt(0), contractBackend)
+ if err != nil {
+ t.Fatalf("expected no error, got %v", err)
+ }
+
ens := NewENS(transactOpts, contractAddr, contractBackend)
- _, err = ens.Register(name, hash)
+ _, err = ens.Register(name, resolverAddr)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
contractBackend.Commit()
+
+ _, err = ens.SetContentHash(name, hash)
+ if err != nil {
+ t.Fatalf("expected no error, got %v", err)
+ }
+ contractBackend.Commit()
+
vhost, err := ens.Resolve(name)
if err != nil {
t.Fatalf("expected no error, got %v", err)
diff --git a/swarm/storage/chunker.go b/swarm/storage/chunker.go
index c19bb622ec..490f5b432e 100644
--- a/swarm/storage/chunker.go
+++ b/swarm/storage/chunker.go
@@ -2,10 +2,11 @@ package storage
import (
"encoding/binary"
+ "errors"
"fmt"
+ "hash"
"io"
"sync"
- "time"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -37,11 +38,9 @@ data_{i} := size(subtree_{i}) || key_{j} || key_{j+1} .... || key_{j+n-1}
*/
const (
- // defaultHash = "SHA3" // http://golang.org/pkg/hash/#Hash
- defaultHash = "SHA256" // http://golang.org/pkg/hash/#Hash
+ defaultHash = "SHA3" // http://golang.org/pkg/hash/#Hash
+ // defaultHash = "SHA256" // http://golang.org/pkg/hash/#Hash
defaultBranches int64 = 128
- joinTimeout = 120 // second
- splitTimeout = 120 // second
// hashSize int64 = hasherfunc.New().Size() // hasher knows about its own length in bytes
// chunksize int64 = branches * hashSize // chunk is defined as this
)
@@ -55,130 +54,107 @@ The hashing itself does use extra copies and allocation though, since it does ne
*/
type ChunkerParams struct {
- Branches int64
- Hash string
- JoinTimeout time.Duration
- SplitTimeout time.Duration
+ Branches int64
+ Hash string
}
func NewChunkerParams() *ChunkerParams {
return &ChunkerParams{
- Branches: defaultBranches,
- Hash: defaultHash,
- JoinTimeout: joinTimeout,
- SplitTimeout: splitTimeout,
+ Branches: defaultBranches,
+ Hash: defaultHash,
}
}
type TreeChunker struct {
- branches int64
- hashFunc Hasher
- joinTimeout time.Duration
- splitTimeout time.Duration
+ branches int64
+ hashFunc Hasher
// calculated
- hashSize int64 // self.hashFunc.New().Size()
- chunkSize int64 // hashSize* branches
+ hashSize int64 // self.hashFunc.New().Size()
+ chunkSize int64 // hashSize* branches
+ workerCount int
}
func NewTreeChunker(params *ChunkerParams) (self *TreeChunker) {
self = &TreeChunker{}
self.hashFunc = MakeHashFunc(params.Hash)
self.branches = params.Branches
- self.joinTimeout = params.JoinTimeout * time.Second
- self.splitTimeout = params.SplitTimeout * time.Second
self.hashSize = int64(self.hashFunc().Size())
self.chunkSize = self.hashSize * self.branches
+ self.workerCount = 1
return
}
-func (self *TreeChunker) KeySize() int64 {
- return self.hashSize
-}
+// func (self *TreeChunker) KeySize() int64 {
+// return self.hashSize
+// }
// String() for pretty printing
func (self *Chunk) String() string {
return fmt.Sprintf("Key: %v TreeSize: %v Chunksize: %v", self.Key.Log(), self.Size, len(self.SData))
}
-// The treeChunkers own Hash hashes together
-// - the size (of the subtree encoded in the Chunk)
-// - the Chunk, ie. the contents read from the input reader
-func (self *TreeChunker) Hash(input []byte) []byte {
- hasher := self.hashFunc()
- hasher.Write(input)
- return hasher.Sum(nil)
+type hashJob struct {
+ key Key
+ chunk []byte
+ size int64
+ parentWg *sync.WaitGroup
}
-func (self *TreeChunker) Split(key Key, data SectionReader, chunkC chan *Chunk, swg *sync.WaitGroup) (errC chan error) {
-
- if swg != nil {
- swg.Add(1)
- defer swg.Done()
- }
+func (self *TreeChunker) Split(data io.Reader, size int64, chunkC chan *Chunk, swg, wwg *sync.WaitGroup) (Key, error) {
if self.chunkSize <= 0 {
panic("chunker must be initialised")
}
- if int64(len(key)) != self.hashSize {
- panic(fmt.Sprintf("root key buffer must be allocated byte slice of length %d", self.hashSize))
+ jobC := make(chan *hashJob, 2*processors)
+ wg := &sync.WaitGroup{}
+ errC := make(chan error)
+
+ // wwg = workers waitgroup keeps track of hashworkers spawned by this split call
+ if wwg != nil {
+ wwg.Add(1)
+ }
+ go self.hashWorker(jobC, chunkC, errC, swg, wwg)
+
+ depth := 0
+ treeSize := self.chunkSize
+
+ // takes lowest depth such that chunksize*HashCount^(depth+1) > size
+ // power series, will find the order of magnitude of the data size in base hashCount or numbers of levels of branching in the resulting tree.
+ for ; treeSize < size; treeSize *= self.branches {
+ depth++
}
- wg := &sync.WaitGroup{}
- errC = make(chan error)
- rerrC := make(chan error)
- timeout := time.After(self.splitTimeout)
-
+ key := make([]byte, self.hashFunc().Size())
+ // glog.V(logger.Detail).Infof("[BZZ] split request received for data (%v bytes, depth: %v)", size, depth)
+ // this waitgroup member is released after the root hash is calculated
wg.Add(1)
- go func() {
-
- depth := 0
- treeSize := self.chunkSize
- size := data.Size()
- // takes lowest depth such that chunksize*HashCount^(depth+1) > size
- // power series, will find the order of magnitude of the data size in base hashCount or numbers of levels of branching in the resulting tree.
-
- for ; treeSize < size; treeSize *= self.branches {
- depth++
- }
-
- // glog.V(logger.Detail).Infof("[BZZ] split request received for data (%v bytes, depth: %v)", size, depth)
-
- //launch actual recursive function passing the workgroup
- self.split(depth, treeSize/self.branches, key, data, chunkC, rerrC, wg, swg)
- }()
+ //launch actual recursive function passing the waitgroups
+ go self.split(depth, treeSize/self.branches, key, data, size, jobC, chunkC, errC, wg, swg, wwg)
// closes internal error channel if all subprocesses in the workgroup finished
go func() {
+ // waiting for all threads to finish
wg.Wait()
- close(rerrC)
-
- }()
-
- // waiting for request to end with wg finishing, error, or timeout
- go func() {
- select {
- case err := <-rerrC:
- if err != nil {
- errC <- err
- } // otherwise splitting is complete
- case <-timeout:
- errC <- fmt.Errorf("split time out")
+ // if storage waitgroup is non-nil, we wait for storage to finish too
+ if swg != nil {
+ // glog.V(logger.Detail).Infof("Waiting for storage to finish")
+ swg.Wait()
}
close(errC)
}()
- return
+ select {
+ case err := <-errC:
+ if err != nil {
+ return nil, err
+ }
+ //
+ }
+ return key, nil
}
-func (self *TreeChunker) split(depth int, treeSize int64, key Key, data SectionReader, chunkC chan *Chunk, errc chan error, parentWg *sync.WaitGroup, swg *sync.WaitGroup) {
-
- defer parentWg.Done()
-
- size := data.Size()
- var newChunk *Chunk
- var hash Key
- // glog.V(logger.Detail).Infof("[BZZ] depth: %v, max subtree size: %v, data size: %v", depth, treeSize, size)
+func (self *TreeChunker) split(depth int, treeSize int64, key Key, data io.Reader, size int64, jobC chan *hashJob, chunkC chan *Chunk, errC chan error, parentWg, swg, wwg *sync.WaitGroup) {
for depth > 0 && size < treeSize {
treeSize /= self.branches
@@ -187,185 +163,233 @@ func (self *TreeChunker) split(depth int, treeSize int64, key Key, data SectionR
if depth == 0 {
// leaf nodes -> content chunks
- chunkData := make([]byte, data.Size()+8)
+ chunkData := make([]byte, size+8)
binary.LittleEndian.PutUint64(chunkData[0:8], uint64(size))
- data.ReadAt(chunkData[8:], 0)
- hash = self.Hash(chunkData)
- // glog.V(logger.Detail).Infof("[BZZ] content chunk: max subtree size: %v, data size: %v", treeSize, size)
- newChunk = &Chunk{
- Key: hash,
- SData: chunkData,
- Size: size,
+ data.Read(chunkData[8:])
+ select {
+ case jobC <- &hashJob{key, chunkData, size, parentWg}:
+ case <-errC:
}
- } else {
- // intermediate chunk containing child nodes hashes
- branchCnt := int64((size + treeSize - 1) / treeSize)
- // glog.V(logger.Detail).Infof("[BZZ] intermediate node: setting branches: %v, depth: %v, max subtree size: %v, data size: %v", branches, depth, treeSize, size)
+ // glog.V(logger.Detail).Infof("[BZZ] read %v", size)
+ return
+ }
+ // intermediate chunk containing child nodes hashes
+ branchCnt := int64((size + treeSize - 1) / treeSize)
+ // glog.V(logger.Detail).Infof("[BZZ] intermediate node: setting branches: %v, depth: %v, max subtree size: %v, data size: %v", branches, depth, treeSize, size)
- var chunk []byte = make([]byte, branchCnt*self.hashSize+8)
- var pos, i int64
+ var chunk []byte = make([]byte, branchCnt*self.hashSize+8)
+ var pos, i int64
- binary.LittleEndian.PutUint64(chunk[0:8], uint64(size))
+ binary.LittleEndian.PutUint64(chunk[0:8], uint64(size))
- childrenWg := &sync.WaitGroup{}
- var secSize int64
- for i < branchCnt {
- // the last item can have shorter data
- if size-pos < treeSize {
- secSize = size - pos
- } else {
- secSize = treeSize
+ childrenWg := &sync.WaitGroup{}
+ var secSize int64
+ for i < branchCnt {
+ // the last item can have shorter data
+ if size-pos < treeSize {
+ secSize = size - pos
+ } else {
+ secSize = treeSize
+ }
+ // the hash of that data
+ subTreeKey := chunk[8+i*self.hashSize : 8+(i+1)*self.hashSize]
+
+ childrenWg.Add(1)
+ self.split(depth-1, treeSize/self.branches, subTreeKey, data, secSize, jobC, chunkC, errC, childrenWg, swg, wwg)
+
+ i++
+ pos += treeSize
+ }
+ // wait for all the children to complete calculating their hashes and copying them onto sections of the chunk
+ // parentWg.Add(1)
+ // go func() {
+ childrenWg.Wait()
+ if len(jobC) > self.workerCount && self.workerCount < processors {
+ if wwg != nil {
+ wwg.Add(1)
+ }
+ self.workerCount++
+ go self.hashWorker(jobC, chunkC, errC, swg, wwg)
+ }
+ select {
+ case jobC <- &hashJob{key, chunk, size, parentWg}:
+ case <-errC:
+ }
+}
+
+func (self *TreeChunker) hashWorker(jobC chan *hashJob, chunkC chan *Chunk, errC chan error, swg, wwg *sync.WaitGroup) {
+ hasher := self.hashFunc()
+ if wwg != nil {
+ defer wwg.Done()
+ }
+ for {
+ select {
+
+ case job, ok := <-jobC:
+ if !ok {
+ return
}
- // take the section of the data encoded in the subTree
- subTreeData := NewChunkReader(data, pos, secSize)
- // the hash of that data
- subTreeKey := chunk[8+i*self.hashSize : 8+(i+1)*self.hashSize]
-
- childrenWg.Add(1)
- go self.split(depth-1, treeSize/self.branches, subTreeKey, subTreeData, chunkC, errc, childrenWg, swg)
-
- i++
- pos += treeSize
- }
- // wait for all the children to complete calculating their hashes and copying them onto sections of the chunk
- childrenWg.Wait()
- // now we got the hashes in the chunk, then hash the chunks
- hash = self.Hash(chunk)
- newChunk = &Chunk{
- Key: hash,
- SData: chunk,
- Size: size,
- wg: swg,
+ // now we got the hashes in the chunk, then hash the chunks
+ hasher.Reset()
+ self.hashChunk(hasher, job, chunkC, swg)
+ // glog.V(logger.Detail).Infof("[BZZ] hash chunk (%v)", job.size)
+ case <-errC:
+ return
}
+ }
+}
+// The treeChunkers own Hash hashes together
+// - the size (of the subtree encoded in the Chunk)
+// - the Chunk, ie. the contents read from the input reader
+func (self *TreeChunker) hashChunk(hasher hash.Hash, job *hashJob, chunkC chan *Chunk, swg *sync.WaitGroup) {
+ hasher.Write(job.chunk)
+ h := hasher.Sum(nil)
+ newChunk := &Chunk{
+ Key: h,
+ SData: job.chunk,
+ Size: job.size,
+ wg: swg,
+ }
+
+ // report hash of this chunk one level up (keys corresponds to the proper subslice of the parent chunk)
+ copy(job.key, h)
+ // send off new chunk to storage
+ if chunkC != nil {
if swg != nil {
swg.Add(1)
}
}
-
- // send off new chunk to storage
+ job.parentWg.Done()
if chunkC != nil {
chunkC <- newChunk
}
- // report hash of this chunk one level up (keys corresponds to the proper subslice of the parent chunk)x
- copy(key, hash)
-
-}
-
-func (self *TreeChunker) Join(key Key, chunkC chan *Chunk) SectionReader {
-
- return &LazyChunkReader{
- key: key,
- chunkC: chunkC,
- quitC: make(chan bool),
- errC: make(chan error),
- chunker: self,
- }
}
// LazyChunkReader implements LazySectionReader
type LazyChunkReader struct {
- key Key // root key
- chunkC chan *Chunk // chunk channel to send retrieve requests on
- size int64 // size of the entire subtree
- off int64 // offset
- quitC chan bool // channel to abort retrieval
- errC chan error // error channel to monitor retrieve errors
- chunker *TreeChunker // needs TreeChunker params TODO: should just take
- // the chunkSize, branches etc as params
+ key Key // root key
+ chunkC chan *Chunk // chunk channel to send retrieve requests on
+ chunk *Chunk // size of the entire subtree
+ off int64 // offset
+ chunkSize int64 // inherit from chunker
+ branches int64 // inherit from chunker
+ hashSize int64 // inherit from chunker
}
-func (self *LazyChunkReader) ReadAt(b []byte, off int64) (read int, err error) {
- self.errC = make(chan error)
- chunk := &Chunk{
- Key: self.key,
- C: make(chan bool), // close channel to signal data delivery
- }
- self.chunkC <- chunk // submit retrieval request, someone should be listening on the other side (or we will time out globally)
- glog.V(logger.Detail).Infof("[BZZ] readAt: reading %v into %d bytes at offset %d.", chunk.Key.Log(), len(b), off)
+// implements the Joiner interface
+func (self *TreeChunker) Join(key Key, chunkC chan *Chunk) LazySectionReader {
- // waiting for the chunk retrieval
- select {
- case <-self.quitC:
- // this is how we control process leakage (quitC is closed once join is finished (after timeout))
- // glog.V(logger.Detail).Infof("[BZZ] quit")
- return
- case <-chunk.C: // bells are ringing, data have been delivered
- // glog.V(logger.Detail).Infof("[BZZ] chunk data received for %v", chunk.Key.Log())
+ return &LazyChunkReader{
+ key: key,
+ chunkC: chunkC,
+ chunkSize: self.chunkSize,
+ branches: self.branches,
+ hashSize: self.hashSize,
}
- if len(chunk.SData) == 0 {
- // glog.V(logger.Detail).Infof("[BZZ] No payload in %v", chunk.Key.Log())
- return 0, notFound
+}
+
+// Size is meant to be called on the LazySectionReader
+func (self *LazyChunkReader) Size(quitC chan bool) (n int64, err error) {
+ if self.chunk != nil {
+ return self.chunk.Size, nil
}
- chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
- self.size = chunk.Size
- if b == nil {
+ chunk := retrieve(self.key, self.chunkC, quitC)
+ if chunk == nil {
+ select {
+ case <-quitC:
+ return 0, errors.New("aborted")
+ default:
+ return 0, fmt.Errorf("root chunk not found for %v", self.key.Hex())
+ }
+ }
+ self.chunk = chunk
+ return chunk.Size, nil
+}
+
+// read at can be called numerous times
+// concurrent reads are allowed
+// Size() needs to be called synchronously on the LazyChunkReader first
+func (self *LazyChunkReader) ReadAt(b []byte, off int64) (read int, err error) {
+ // this is correct, a swarm doc cannot be zero length, so no EOF is expected
+ if len(b) == 0 {
// glog.V(logger.Detail).Infof("[BZZ] Size query for %v", chunk.Key.Log())
- return
+ return 0, nil
}
- want := int64(len(b))
- if off+want > self.size {
- want = self.size - off
+ quitC := make(chan bool)
+ size, err := self.Size(quitC)
+ if err != nil {
+ return 0, err
}
+ glog.V(logger.Detail).Infof("readAt: len(b): %v, off: %v, size: %v ", len(b), off, size)
+
+ errC := make(chan error)
+ // glog.V(logger.Detail).Infof("[BZZ] readAt: reading %v into %d bytes at offset %d.", self.chunk.Key.Log(), len(b), off)
+
+ // }
+ // glog.V(logger.Detail).Infof("-> want: %v, off: %v size: %v ", want, off, self.size)
var treeSize int64
var depth int
// calculate depth and max treeSize
- treeSize = self.chunker.chunkSize
- for ; treeSize < chunk.Size; treeSize *= self.chunker.branches {
+ treeSize = self.chunkSize
+ for ; treeSize < size; treeSize *= self.branches {
depth++
}
wg := sync.WaitGroup{}
wg.Add(1)
- go self.join(b, off, off+want, depth, treeSize/self.chunker.branches, chunk, &wg)
+ go self.join(b, off, off+int64(len(b)), depth, treeSize/self.branches, self.chunk, &wg, errC, quitC)
go func() {
wg.Wait()
- close(self.errC)
+ close(errC)
}()
- select {
- case err = <-self.errC:
- // glog.V(logger.Detail).Infof("[BZZ] ReadAt received %v", err)
- read = len(b)
- if off+int64(read) == self.size {
- err = io.EOF
- }
- // glog.V(logger.Detail).Infof("[BZZ] ReadAt returning at %d: %v", read, err)
- case <-self.quitC:
- // glog.V(logger.Detail).Infof("[BZZ] ReadAt aborted at %d: %v", read, err)
+
+ err = <-errC
+ if err != nil {
+ close(quitC)
+
+ return 0, err
}
- return
+ // glog.V(logger.Detail).Infof("[BZZ] ReadAt received %v", err)
+ glog.V(logger.Detail).Infof("end: len(b): %v, off: %v, size: %v ", len(b), off, size)
+ if off+int64(len(b)) >= size {
+ glog.V(logger.Detail).Infof(" len(b): %v EOF", len(b))
+ return len(b), io.EOF
+ }
+ // glog.V(logger.Detail).Infof("[BZZ] ReadAt returning at %d: %v", read, err)
+ return len(b), nil
}
-func (self *LazyChunkReader) join(b []byte, off int64, eoff int64, depth int, treeSize int64, chunk *Chunk, parentWg *sync.WaitGroup) {
+func (self *LazyChunkReader) join(b []byte, off int64, eoff int64, depth int, treeSize int64, chunk *Chunk, parentWg *sync.WaitGroup, errC chan error, quitC chan bool) {
defer parentWg.Done()
+ // return NewDPA(&LocalStore{})
+ glog.V(logger.Detail).Infof("inh len(b): %v, off: %v eoff: %v ", len(b), off, eoff)
// glog.V(logger.Detail).Infof("[BZZ] depth: %v, loff: %v, eoff: %v, chunk.Size: %v, treeSize: %v", depth, off, eoff, chunk.Size, treeSize)
- chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
+ // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
// find appropriate block level
for chunk.Size < treeSize && depth > 0 {
- treeSize /= self.chunker.branches
+ treeSize /= self.branches
depth--
}
+ // leaf chunk found
if depth == 0 {
- // glog.V(logger.Detail).Infof("[BZZ] depth: %v, len(b): %v, off: %v, eoff: %v, chunk.Size: %v, treeSize: %v", depth, len(b), off, eoff, chunk.Size, treeSize)
- if int64(len(b)) != eoff-off {
- //fmt.Printf("len(b) = %v off = %v eoff = %v", len(b), off, eoff)
- panic("len(b) does not match")
- }
-
+ glog.V(logger.Detail).Infof("[BZZ] depth: %v, len(b): %v, off: %v, eoff: %v, chunk.Size: %v, treeSize: %v", depth, len(b), off, eoff, chunk.Size, treeSize)
copy(b, chunk.SData[8+off:8+eoff])
return // simply give back the chunks reader for content chunks
}
- // subtree index
+ // subtree
start := off / treeSize
end := (eoff + treeSize - 1) / treeSize
- wg := sync.WaitGroup{}
+
+ wg := &sync.WaitGroup{}
+ defer wg.Wait()
+ glog.V(logger.Detail).Infof("[BZZ] start %v,end %v", start, end)
for i := start; i < end; i++ {
-
soff := i * treeSize
roff := soff
seoff := soff + treeSize
@@ -376,36 +400,91 @@ func (self *LazyChunkReader) join(b []byte, off int64, eoff int64, depth int, tr
if seoff > eoff {
seoff = eoff
}
-
+ if depth > 1 {
+ wg.Wait()
+ }
wg.Add(1)
go func(j int64) {
- childKey := chunk.SData[8+j*self.chunker.hashSize : 8+(j+1)*self.chunker.hashSize]
- // glog.V(logger.Detail).Infof("[BZZ] subtree index: %v -> %v", j, childKey.Log())
-
- ch := &Chunk{
- Key: childKey,
- C: make(chan bool), // close channel to signal data delivery
- }
- // glog.V(logger.Detail).Infof("[BZZ] chunk data sent for %v (key interval in chunk %v-%v)", ch.Key.Log(), j*self.chunker.hashSize, (j+1)*self.chunker.hashSize)
- self.chunkC <- ch // submit retrieval request, someone should be listening on the other side (or we will time out globally)
-
- // waiting for the chunk retrieval
- select {
- case <-self.quitC:
- // this is how we control process leakage (quitC is closed once join is finished (after timeout))
+ childKey := chunk.SData[8+j*self.hashSize : 8+(j+1)*self.hashSize]
+ // glog.V(logger.Detail).Infof("[BZZ] subtree ind.ex: %v -> %v", j, childKey.Log())
+ chunk := retrieve(childKey, self.chunkC, quitC)
+ if chunk == nil {
+ select {
+ case errC <- fmt.Errorf("chunk %v-%v not found", off, off+treeSize):
+ case <-quitC:
+ }
return
- case <-ch.C: // bells are ringing, data have been delivered
- // glog.V(logger.Detail).Infof("[BZZ] chunk data received")
}
if soff < off {
soff = off
}
- if len(ch.SData) == 0 {
- self.errC <- fmt.Errorf("chunk %v-%v not found", off, off+treeSize)
- return
- }
- self.join(b[soff-off:seoff-off], soff-roff, seoff-roff, depth-1, treeSize/self.chunker.branches, ch, &wg)
+ self.join(b[soff-off:seoff-off], soff-roff, seoff-roff, depth-1, treeSize/self.branches, chunk, wg, errC, quitC)
}(i)
} //for
- wg.Wait()
+}
+
+// the helper method submits chunks for a key to a oueue (DPA) and
+// block until they time out or arrive
+// abort if quitC is readable
+func retrieve(key Key, chunkC chan *Chunk, quitC chan bool) *Chunk {
+ chunk := &Chunk{
+ Key: key,
+ C: make(chan bool), // close channel to signal data delivery
+ }
+ // glog.V(logger.Detail).Infof("[BZZ] chunk data sent for %v (key interval in chunk %v-%v)", ch.Key.Log(), j*self.chunker.hashSize, (j+1)*self.chunker.hashSize)
+ // submit chunk for retrieval
+ select {
+ case chunkC <- chunk: // submit retrieval request, someone should be listening on the other side (or we will time out globally)
+ case <-quitC:
+ return nil
+ }
+ // waiting for the chunk retrieval
+ select { // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
+
+ case <-quitC:
+ // this is how we control process leakage (quitC is closed once join is finished (after timeout))
+ return nil
+ case <-chunk.C: // bells are ringing, data have been delivered
+ // glog.V(logger.Detail).Infof("[BZZ] chunk data received")
+ }
+ if len(chunk.SData) == 0 {
+ return nil // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
+
+ }
+ return chunk
+}
+
+// Read keeps a cursor so cannot be called simulateously, see ReadAt
+func (self *LazyChunkReader) Read(b []byte) (read int, err error) {
+ read, err = self.ReadAt(b, self.off)
+ glog.V(logger.Detail).Infof("[BZZ] read: %v, off: %v, error: %v", read, self.off, err)
+
+ self.off += int64(read)
+ return
+}
+
+// completely analogous to standard SectionReader implementation
+var errWhence = errors.New("Seek: invalid whence")
+var errOffset = errors.New("Seek: invalid offset")
+
+func (s *LazyChunkReader) Seek(offset int64, whence int) (int64, error) {
+ switch whence {
+ default:
+ return 0, errWhence
+ case 0:
+ offset += 0
+ case 1:
+ offset += s.off
+ case 2:
+ if s.chunk == nil {
+ return 0, fmt.Errorf("seek from the end requires rootchunk for size. call Size first")
+ }
+ offset += s.chunk.Size
+ }
+
+ if offset < 0 {
+ return 0, errOffset
+ }
+ s.off = offset
+ return offset, nil
}
diff --git a/swarm/storage/chunker_test.go b/swarm/storage/chunker_test.go
index 7dd301fff5..cdf549ff45 100644
--- a/swarm/storage/chunker_test.go
+++ b/swarm/storage/chunker_test.go
@@ -2,20 +2,33 @@ package storage
import (
"bytes"
- // "fmt"
+ "fmt"
"io"
+ "runtime"
+ "sync"
"testing"
"time"
+
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
)
+func init() {
+ glog.SetV(logger.Info)
+ glog.SetToStderr(true)
+}
+
/*
Tests TreeChunker by splitting and joining a random byte slice
*/
+type test interface {
+ Fatalf(string, ...interface{})
+}
+
type chunkerTester struct {
- errors []error
- chunks []*Chunk
- timeout bool
+ chunks []*Chunk
+ t test
}
func (self *chunkerTester) checkChunks(t *testing.T, want int) {
@@ -25,77 +38,70 @@ func (self *chunkerTester) checkChunks(t *testing.T, want int) {
}
}
-func (self *chunkerTester) Split(chunker *TreeChunker, l int) (key Key, input []byte) {
+func (self *chunkerTester) Split(chunker Splitter, data io.Reader, size int64, chunkC chan *Chunk, swg *sync.WaitGroup) (key Key) {
// reset
- self.errors = nil
self.chunks = nil
- self.timeout = false
-
- data, slice := testDataReader(l)
- input = slice
- key = make([]byte, 32)
- chunkC := make(chan *Chunk, 1000)
- errC := chunker.Split(key, data, chunkC, nil)
quitC := make(chan bool)
timeout := time.After(600 * time.Second)
+ if chunkC != nil {
+ go func() {
+ for {
+ select {
+ case <-timeout:
+ self.t.Fatalf("Join timeout error")
- go func() {
- LOOP:
- for {
- select {
- case <-timeout:
- self.timeout = true
- break LOOP
-
- case chunk := <-chunkC:
- if chunk != nil {
+ case chunk, ok := <-chunkC:
+ if !ok {
+ // glog.V(logger.Info).Infof("chunkC closed quitting")
+ close(quitC)
+ return
+ }
+ // glog.V(logger.Info).Infof("chunk %v received", len(self.chunks))
self.chunks = append(self.chunks, chunk)
- } else {
- break LOOP
- }
-
- case err, ok := <-errC:
- if err != nil {
- self.errors = append(self.errors, err)
- }
- // fmt.Printf("err %v", err)
- if !ok {
- close(chunkC)
- errC = nil
+ if chunk.wg != nil {
+ chunk.wg.Done()
+ }
}
}
+ }()
+ }
+ key, err := chunker.Split(data, size, chunkC, swg, nil)
+ if err != nil {
+ self.t.Fatalf("Split error: %v", err)
+ }
+ if chunkC != nil {
+ if swg != nil {
+ // glog.V(logger.Info).Infof("Waiting for storage to finish")
+ swg.Wait()
+ // glog.V(logger.Info).Infof("St orage finished")
}
- close(quitC)
- }()
- <-quitC // waiting for it to finish
+ close(chunkC)
+ }
+ if chunkC != nil {
+ <-quitC
+ }
return
}
-func (self *chunkerTester) Join(chunker *TreeChunker, key Key, c int) SectionReader {
+func (self *chunkerTester) Join(chunker *TreeChunker, key Key, c int, chunkC chan *Chunk, quitC chan bool) LazySectionReader {
// reset but not the chunks
- self.errors = nil
- self.timeout = false
- chunkC := make(chan *Chunk, 1000)
reader := chunker.Join(key, chunkC)
- quitC := make(chan bool)
timeout := time.After(600 * time.Second)
i := 0
go func() {
- LOOP:
for {
select {
- case <-quitC:
- break LOOP
-
case <-timeout:
- self.timeout = true
- break LOOP
+ self.t.Fatalf("Join timeout error")
- case chunk := <-chunkC:
+ case chunk, ok := <-chunkC:
+ if !ok {
+ close(quitC)
+ return
+ }
i++
- // dpaLogger.DebugDetailf("TESTER: chunk request %x", chunk.Key[:4])
// this just mocks the behaviour of a chunk store retrieval
var found bool
for _, ch := range self.chunks {
@@ -106,56 +112,58 @@ func (self *chunkerTester) Join(chunker *TreeChunker, key Key, c int) SectionRea
}
}
if !found {
- // fmt.Printf("TESTER: chunk unknown for %x", chunk.Key[:4])
+ self.t.Fatalf("not found ")
}
close(chunk.C)
- // dpaLogger.DebugDetailf("TESTER: chunk request served %x", chunk.Key[:4])
}
}
}()
return reader
}
-func testRandomData(chunker *TreeChunker, tester *chunkerTester, n int, chunks int, t *testing.T) {
- key, input := tester.Split(chunker, n)
+func testRandomData(n int, chunks int, t *testing.T) {
+ chunker := NewTreeChunker(&ChunkerParams{
+ Branches: 128,
+ Hash: "SHA3",
+ })
+ tester := &chunkerTester{t: t}
+ data, input := testDataReaderAndSlice(n)
- t.Logf(" Key = %x\n", key)
+ chunkC := make(chan *Chunk, 1000)
+ swg := &sync.WaitGroup{}
- tester.checkChunks(t, chunks)
- time.Sleep(100 * time.Millisecond)
+ splitter := chunker
+ key := tester.Split(splitter, data, int64(n), chunkC, swg)
- reader := tester.Join(chunker, key, 0)
+ // t.Logf(" Key = %v\n", key)
+
+ // tester.checkChunks(t, chunks)
+ chunkC = make(chan *Chunk, 1000)
+ quitC := make(chan bool)
+
+ reader := tester.Join(chunker, key, 0, chunkC, quitC)
output := make([]byte, n)
r, err := reader.Read(output)
if r != n || err != io.EOF {
- t.Errorf("read error read: %v n = %v err = %v\n", r, n, err)
+ t.Fatalf("read error read: %v n = %v err = %v\n", r, n, err)
}
- // t.Logf(" IN: %x\nOUT: %x\n", input, output)
- if !bytes.Equal(output, input) {
- t.Errorf("input and output mismatch\n IN: %x\nOUT: %x\n", input, output)
+ if input != nil {
+ if !bytes.Equal(output, input) {
+ t.Fatalf("input and output mismatch\n IN: %v\nOUT: %v\n", input, output)
+ }
}
+ close(chunkC)
+ <-quitC
}
func TestRandomData(t *testing.T) {
- chunker, tester := chunkerAndTester()
- testRandomData(chunker, tester, 60, 1, t)
- testRandomData(chunker, tester, 179, 5, t)
- testRandomData(chunker, tester, 253, 7, t)
- // t.Logf("chunks %v", tester.chunks)
+ testRandomData(60, 1, t)
+ testRandomData(83, 3, t)
+ testRandomData(179, 5, t)
+ testRandomData(253, 7, t)
}
-func chunkerAndTester() (chunker *TreeChunker, tester *chunkerTester) {
- chunker = NewTreeChunker(&ChunkerParams{
- Branches: 2,
- Hash: "SHA256",
- SplitTimeout: 10,
- JoinTimeout: 10,
- })
- tester = &chunkerTester{}
- return
-}
-
-func readAll(reader SectionReader, result []byte) {
+func readAll(reader LazySectionReader, result []byte) {
size := int64(len(result))
var end int64
@@ -169,46 +177,98 @@ func readAll(reader SectionReader, result []byte) {
}
}
-func benchReadAll(reader SectionReader) {
- size := reader.Size()
+func benchReadAll(reader LazySectionReader) {
+ size, _ := reader.Size(nil)
output := make([]byte, 1000)
for pos := int64(0); pos < size; pos += 1000 {
reader.ReadAt(output, pos)
}
}
-func benchmarkJoinRandomData(n int, chunks int, t *testing.B) {
- t.StopTimer()
+func benchmarkJoin(n int, t *testing.B) {
for i := 0; i < t.N; i++ {
- // fmt.Printf("round %v\n", i)
- chunker, tester := chunkerAndTester()
- key, _ := tester.Split(chunker, n)
- // fmt.Printf("split done %v, joining...\n", i)
+ chunker := NewTreeChunker(&ChunkerParams{
+ Branches: 128,
+ Hash: "SHA3",
+ })
+ tester := &chunkerTester{t: t}
+ data := testDataReader(n)
+
+ chunkC := make(chan *Chunk, 1000)
+ swg := &sync.WaitGroup{}
+
+ key := tester.Split(chunker, data, int64(n), chunkC, swg)
t.StartTimer()
- reader := tester.Join(chunker, key, i)
- // fmt.Printf("join done %v, reading...\n", i)
+ chunkC = make(chan *Chunk, 1000)
+ quitC := make(chan bool)
+ reader := tester.Join(chunker, key, i, chunkC, quitC)
+ t.StopTimer()
benchReadAll(reader)
+ close(chunkC)
+ <-quitC
}
}
-func benchmarkSplitRandomData(n int, chunks int, t *testing.B) {
+func benchmarkSplitTree(n int, t *testing.B) {
+ t.ReportAllocs()
for i := 0; i < t.N; i++ {
- chunker, tester := chunkerAndTester()
- tester.Split(chunker, n)
+ chunker := NewTreeChunker(&ChunkerParams{
+ Branches: 128,
+ Hash: "SHA3",
+ })
+ tester := &chunkerTester{t: t}
+ data := testDataReader(n)
+ // glog.V(logger.Info).Infof("splitting data of length %v", n)
+ tester.Split(chunker, data, int64(n), nil, nil)
}
+ stats := new(runtime.MemStats)
+ runtime.ReadMemStats(stats)
+ fmt.Println(stats.Sys)
}
-func BenchmarkJoinRandomData_100_2(t *testing.B) { benchmarkJoinRandomData(100, 3, t) }
-func BenchmarkJoinRandomData_1000_2(t *testing.B) { benchmarkJoinRandomData(1000, 3, t) }
-func BenchmarkJoinRandomData_10000_2(t *testing.B) { benchmarkJoinRandomData(10000, 3, t) }
-func BenchmarkJoinRandomData_100000_2(t *testing.B) { benchmarkJoinRandomData(100000, 3, t) }
-func BenchmarkJoinRandomData_1000000_2(t *testing.B) { benchmarkJoinRandomData(1000000, 3, t) }
+func benchmarkSplitPyramid(n int, t *testing.B) {
+ t.ReportAllocs()
+ for i := 0; i < t.N; i++ {
+ splitter := NewPyramidChunker(&ChunkerParams{
+ Branches: 128,
+ Hash: "SHA3",
+ })
+ tester := &chunkerTester{t: t}
+ data := testDataReader(n)
+ // glog.V(logger.Info).Infof("splitting data of length %v", n)
+ tester.Split(splitter, data, int64(n), nil, nil)
+ }
+ stats := new(runtime.MemStats)
+ runtime.ReadMemStats(stats)
+ fmt.Println(stats.Sys)
+}
-func BenchmarkSplitRandomData_100_2(t *testing.B) { benchmarkSplitRandomData(100, 3, t) }
-func BenchmarkSplitRandomData_1000_2(t *testing.B) { benchmarkSplitRandomData(1000, 3, t) }
-func BenchmarkSplitRandomData_10000_2(t *testing.B) { benchmarkSplitRandomData(10000, 3, t) }
-func BenchmarkSplitRandomData_100000_2(t *testing.B) { benchmarkSplitRandomData(100000, 3, t) }
-func BenchmarkSplitRandomData_1000000_2(t *testing.B) { benchmarkSplitRandomData(1000000, 3, t) }
-func BenchmarkSplitRandomData_10000000_2(t *testing.B) { benchmarkSplitRandomData(10000000, 3, t) }
+func BenchmarkJoin_100_2(t *testing.B) { benchmarkJoin(100, t) }
+func BenchmarkJoin_1000_2(t *testing.B) { benchmarkJoin(1000, t) }
+func BenchmarkJoin_10000_2(t *testing.B) { benchmarkJoin(10000, t) }
+func BenchmarkJoin_100000_2(t *testing.B) { benchmarkJoin(100000, t) }
+func BenchmarkJoin_1000000_2(t *testing.B) { benchmarkJoin(1000000, t) }
-// go test -bench ./bzz -cpuprofile cpu.out -memprofile mem.out
+func BenchmarkSplitTree_2(t *testing.B) { benchmarkSplitTree(100, t) }
+func BenchmarkSplitTree_2h(t *testing.B) { benchmarkSplitTree(500, t) }
+func BenchmarkSplitTree_3(t *testing.B) { benchmarkSplitTree(1000, t) }
+func BenchmarkSplitTree_3h(t *testing.B) { benchmarkSplitTree(5000, t) }
+func BenchmarkSplitTree_4(t *testing.B) { benchmarkSplitTree(10000, t) }
+func BenchmarkSplitTree_4h(t *testing.B) { benchmarkSplitTree(50000, t) }
+func BenchmarkSplitTree_5(t *testing.B) { benchmarkSplitTree(100000, t) }
+func BenchmarkSplitTree_6(t *testing.B) { benchmarkSplitTree(1000000, t) }
+func BenchmarkSplitTree_7(t *testing.B) { benchmarkSplitTree(10000000, t) }
+func BenchmarkSplitTree_8(t *testing.B) { benchmarkSplitTree(100000000, t) }
+
+func BenchmarkSplitPyramid_2(t *testing.B) { benchmarkSplitPyramid(100, t) }
+func BenchmarkSplitPyramid_2h(t *testing.B) { benchmarkSplitPyramid(500, t) }
+func BenchmarkSplitPyramid_3(t *testing.B) { benchmarkSplitPyramid(1000, t) }
+func BenchmarkSplitPyramid_3h(t *testing.B) { benchmarkSplitPyramid(5000, t) }
+func BenchmarkSplitPyramid_4(t *testing.B) { benchmarkSplitPyramid(10000, t) }
+func BenchmarkSplitPyramid_4h(t *testing.B) { benchmarkSplitPyramid(50000, t) }
+func BenchmarkSplitPyramid_5(t *testing.B) { benchmarkSplitPyramid(100000, t) }
+func BenchmarkSplitPyramid_6(t *testing.B) { benchmarkSplitPyramid(1000000, t) }
+func BenchmarkSplitPyramid_7(t *testing.B) { benchmarkSplitPyramid(10000000, t) }
+func BenchmarkSplitPyramid_8(t *testing.B) { benchmarkSplitPyramid(100000000, t) }
+
+// godep go test -bench ./swarm/storage -cpuprofile cpu.out -memprofile mem.out
diff --git a/swarm/storage/chunkreader.go b/swarm/storage/chunkreader.go
deleted file mode 100644
index b147c85bf8..0000000000
--- a/swarm/storage/chunkreader.go
+++ /dev/null
@@ -1,194 +0,0 @@
-package storage
-
-import (
- "bytes"
- "errors"
- "io"
-)
-
-type Bounded interface {
- Size() int64
-}
-
-type Sliced interface {
- Slice(int64, int64) (b []byte, err error)
-}
-
-// Size, Seek, Read, ReadAt
-type SectionReader interface {
- Bounded
- io.Seeker
- io.Reader
- io.ReaderAt
-}
-
-// ChunkReader implements SectionReader on a section
-// of an underlying ReaderAt.
-type ChunkReader struct {
- r io.ReaderAt
- base int64
- off int64
- limit int64
-}
-
-// NewChunkReader returns a ChunkReader that reads from r
-// starting at offset off and stops with EOF after n bytes.
-func NewChunkReader(r io.ReaderAt, off int64, n int64) *ChunkReader {
- return &ChunkReader{r: r, base: off, off: off, limit: off + n}
-}
-
-// ByteSliceReader just extends byte.Reader to make base slice accessible
-type ByteSliceReader struct {
- *bytes.Reader
- base []byte
-}
-
-func NewByteSliceReader(b []byte) *ByteSliceReader {
- return &ByteSliceReader{
- base: b,
- Reader: bytes.NewReader(b),
- }
-}
-
-// ByteSliceReader implements the Sliced interface
-func (self *ByteSliceReader) Slice(from, to int64) (b []byte, err error) {
- if from < 0 || to >= int64(self.Len()) {
- err = io.EOF
- } else {
- b = self.base[from:to]
- }
- return
-}
-
-// NewChunkReaderFromBytes is a convenience shortcut to get a SectionReader over a byte slice
-func NewChunkReaderFromBytes(b []byte) *ChunkReader {
- return NewChunkReader(NewByteSliceReader(b), 0, int64(len(b)))
-}
-
-/*
-The following is adapted from io.SectionReader
-*/
-
-func (s *ChunkReader) Size() int64 {
- return s.limit - s.base
-}
-
-var errWhence = errors.New("Seek: invalid whence")
-var errOffset = errors.New("Seek: invalid offset")
-
-func (s *ChunkReader) Seek(offset int64, whence int) (int64, error) {
- switch whence {
- default:
- return 0, errWhence
- case 0:
- offset += s.base
- case 1:
- offset += s.off
- case 2:
- offset += s.limit
- }
- if offset < s.base {
- return 0, errOffset
- }
- s.off = offset
- return offset - s.base, nil
-}
-
-func (s *ChunkReader) Read(p []byte) (n int, err error) {
- if s.off >= s.limit {
- return 0, io.EOF
- }
- if max := s.limit - s.off; int64(len(p)) > max {
- p = p[0:max]
- }
- n, err = s.r.ReadAt(p, s.off)
- s.off += int64(n)
- return
-}
-
-func (s *ChunkReader) ReadAt(p []byte, off int64) (n int, err error) {
- if off < 0 || off >= s.limit-s.base {
- return 0, io.EOF
- }
- off += s.base
- if max := s.limit - off; int64(len(p)) > max {
- p = p[0:max]
- n, err = s.r.ReadAt(p, off)
- if err == nil {
- err = io.EOF
- }
- return n, err
- }
- n, err = s.r.ReadAt(p, off)
- return
-}
-
-// added methods to that ChunkReader implements the Sliced interface
-func (s *ChunkReader) Slice(from, to int64) (b []byte, err error) {
- if from < 0 || to >= s.Size() {
- err = io.EOF
- } else {
- if sl, ok := s.r.(Sliced); ok {
- b, err = sl.Slice(s.base+from, s.base+to)
- } else {
- err = errors.New("not sliceable base")
- }
- }
- return
-}
-
-// added method so that ChunkReader implements the io.WriterTo interface
-// WriteTo method is used by io.Copy
-// This is so that we avoid one extra step of allocation (if the underlying initial Reader implements Sliced
-func (r *ChunkReader) WriteTo(w io.Writer) (n int64, err error) {
- var b []byte
- var m int
- // if b, _ := r.Slice(r.off-r.base, r.limit-r.base); b == nil {
- // if slices not available we do it with extra allocation
- b = make([]byte, r.limit-r.off)
- m, err = r.Read(b)
- if err != nil {
- return
- }
- // }
- m, err = w.Write(b)
- if m > len(b) {
- panic("bytes.Reader.WriteTo: invalid Write count")
- }
- r.off = r.base + int64(m)
- n = int64(m)
- if m != len(b) && err == nil {
- err = io.ErrShortWrite
- }
- // w
- return
-}
-
-func (self *LazyChunkReader) Size() (n int64) {
- self.ReadAt(nil, 0)
- return self.size
-}
-
-func (self *LazyChunkReader) Read(b []byte) (read int, err error) {
- read, err = self.ReadAt(b, self.off)
- self.off += int64(read)
- return
-}
-
-func (s *LazyChunkReader) Seek(offset int64, whence int) (int64, error) {
- switch whence {
- default:
- return 0, errWhence
- case 0:
- offset += 0
- case 1:
- offset += s.off
- case 2:
- offset += s.size
- }
- if offset < 0 {
- return 0, errOffset
- }
- s.off = offset
- return offset, nil
-}
diff --git a/swarm/storage/common_test.go b/swarm/storage/common_test.go
index 40dc35fc69..55fcbfd409 100644
--- a/swarm/storage/common_test.go
+++ b/swarm/storage/common_test.go
@@ -1,6 +1,7 @@
package storage
import (
+ "bytes"
"crypto/rand"
"io"
"sync"
@@ -10,61 +11,39 @@ import (
"github.com/ethereum/go-ethereum/logger/glog"
)
-func testDataReader(l int) (r *ChunkReader, slice []byte) {
+func testDataReader(l int) (r io.Reader) {
+ return io.LimitReader(rand.Reader, int64(l))
+}
+
+func testDataReaderAndSlice(l int) (r io.Reader, slice []byte) {
slice = make([]byte, l)
if _, err := rand.Read(slice); err != nil {
panic("rand error")
}
- r = NewChunkReaderFromBytes(slice)
- return
-}
-
-func randomChunks(l int64, branches int64, chunkC chan *Chunk) (key Key, errC chan error) {
- chunker := NewTreeChunker(&ChunkerParams{
- Branches: branches,
- Hash: defaultHash,
- SplitTimeout: splitTimeout,
- })
- key = make([]byte, 32)
- b := make([]byte, l)
- _, err := rand.Read(b)
- if err != nil {
- panic("no rand")
- }
- wg := &sync.WaitGroup{}
- errC = chunker.Split(key, NewChunkReaderFromBytes(b), chunkC, wg)
- wg.Wait()
+ r = bytes.NewReader(slice)
return
}
func testStore(m ChunkStore, l int64, branches int64, t *testing.T) {
chunkC := make(chan *Chunk)
- key, errC := randomChunks(l, branches, chunkC)
-
-SPLIT:
- for {
- select {
- case chunk := <-chunkC:
+ go func() {
+ for chunk := range chunkC {
m.Put(chunk)
- case err, ok := <-errC:
- if err != nil {
- t.Errorf("Chunker error: %v", err)
- return
- }
- if !ok {
- break SPLIT
+ if chunk.wg != nil {
+ chunk.wg.Done()
}
}
- }
+ }()
chunker := NewTreeChunker(&ChunkerParams{
- Branches: branches,
- Hash: defaultHash,
- SplitTimeout: splitTimeout,
+ Branches: branches,
+ Hash: defaultHash,
})
+ swg := &sync.WaitGroup{}
+ key, err := chunker.Split(rand.Reader, l, chunkC, swg, nil)
+ swg.Wait()
+ close(chunkC)
chunkC = make(chan *Chunk)
- var r SectionReader
- r = chunker.Join(key, chunkC)
quit := make(chan bool)
@@ -73,22 +52,26 @@ SPLIT:
go func(chunk *Chunk) {
storedChunk, err := m.Get(chunk.Key)
if err == notFound {
- glog.V(logger.Detail).Infof("[BZZ] chunk '%x' not found", chunk.Key)
+ glog.V(logger.Detail).Infof("[BZZ] chunk '%v' not found", chunk.Key.Log())
} else if err != nil {
- glog.V(logger.Detail).Infof("[BZZ] error retrieving chunk %x: %v", chunk.Key, err)
+ glog.V(logger.Detail).Infof("[BZZ] error retrieving chunk %v: %v", chunk.Key.Log(), err)
} else {
chunk.SData = storedChunk.SData
+ chunk.Size = storedChunk.Size
}
- glog.V(logger.Detail).Infof("[BZZ] chunk '%x' not found", chunk.Key[:4])
+ glog.V(logger.Detail).Infof("[BZZ] chunk '%v' not found", chunk.Key.Log())
close(chunk.C)
}(ch)
}
+ close(quit)
}()
+ r := chunker.Join(key, chunkC)
b := make([]byte, l)
n, err := r.ReadAt(b, 0)
if err != io.EOF {
- t.Errorf("read error (%v/%v) %v", n, l, err)
- close(quit)
+ t.Fatalf("read error (%v/%v) %v", n, l, err)
}
+ close(chunkC)
+ <-quit
}
diff --git a/swarm/storage/dpa.go b/swarm/storage/dpa.go
index 922ff3bec2..34a4639a6d 100644
--- a/swarm/storage/dpa.go
+++ b/swarm/storage/dpa.go
@@ -2,6 +2,7 @@ package storage
import (
"errors"
+ "io"
"sync"
"time"
@@ -72,33 +73,14 @@ func NewDPA(store ChunkStore, params *ChunkerParams) *DPA {
// FS-aware API and httpaccess
// Chunk retrieval blocks on netStore requests with a timeout so reader will
// report error if retrieval of chunks within requested range time out.
-func (self *DPA) Retrieve(key Key) SectionReader {
+func (self *DPA) Retrieve(key Key) LazySectionReader {
return self.Chunker.Join(key, self.retrieveC)
}
// Public API. Main entry point for document storage directly. Used by the
// FS-aware API and httpaccess
-func (self *DPA) Store(data SectionReader, wg *sync.WaitGroup) (key Key, err error) {
- key = make([]byte, self.Chunker.KeySize())
- errC := self.Chunker.Split(key, data, self.storeC, wg)
-
-SPLIT:
- for {
- select {
- case err, ok := <-errC:
- if err != nil {
- glog.V(logger.Error).Infof("[BZZ] chunker split error: %v", err)
- }
- if !ok {
- break SPLIT
- }
-
- case <-self.quitC:
- break SPLIT
- }
- }
- return
-
+func (self *DPA) Store(data io.Reader, size int64, wg *sync.WaitGroup) (key Key, err error) {
+ return self.Chunker.Split(data, size, self.storeC, nil, wg)
}
func (self *DPA) Start() {
@@ -164,7 +146,7 @@ func (self *DPA) storeLoop() {
go func(chunk *Chunk) {
self.Put(chunk)
if chunk.wg != nil {
- glog.V(logger.Detail).Infof("[BZZ] DPA.storeLoop %v", chunk.Key.Log())
+ glog.V(logger.Detail).Infof("[BZZ] dpa: store loop %v", chunk.Key.Log())
chunk.wg.Done()
}
}(ch)
diff --git a/swarm/storage/dpa_test.go b/swarm/storage/dpa_test.go
index a4400783fb..4c50a7214f 100644
--- a/swarm/storage/dpa_test.go
+++ b/swarm/storage/dpa_test.go
@@ -29,9 +29,9 @@ func TestDPArandom(t *testing.T) {
ChunkStore: localStore,
}
dpa.Start()
- reader, slice := testDataReader(testDataSize)
+ reader, slice := testDataReaderAndSlice(testDataSize)
wg := &sync.WaitGroup{}
- key, err := dpa.Store(reader, wg)
+ key, err := dpa.Store(reader, testDataSize, wg)
if err != nil {
t.Errorf("Store error: %v", err)
}
@@ -85,9 +85,9 @@ func TestDPA_capacity(t *testing.T) {
ChunkStore: localStore,
}
dpa.Start()
- reader, slice := testDataReader(testDataSize)
+ reader, slice := testDataReaderAndSlice(testDataSize)
wg := &sync.WaitGroup{}
- key, err := dpa.Store(reader, wg)
+ key, err := dpa.Store(reader, testDataSize, wg)
if err != nil {
t.Errorf("Store error: %v", err)
}
diff --git a/swarm/storage/localstore.go b/swarm/storage/localstore.go
index 81d4a6f2d0..68487368f1 100644
--- a/swarm/storage/localstore.go
+++ b/swarm/storage/localstore.go
@@ -1,5 +1,9 @@
package storage
+import (
+ "encoding/binary"
+)
+
// LocalStore is a combination of inmemory db over a disk persisted db
// implements a Get/Put with fallback (caching) logic using any 2 ChunkStores
type LocalStore struct {
@@ -48,6 +52,7 @@ func (self *LocalStore) Get(key Key) (chunk *Chunk, err error) {
if err != nil {
return
}
+ chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
self.memStore.Put(chunk)
return
}
diff --git a/swarm/storage/memstore.go b/swarm/storage/memstore.go
index f415bdfa59..c5e0f6227f 100644
--- a/swarm/storage/memstore.go
+++ b/swarm/storage/memstore.go
@@ -3,7 +3,6 @@
package storage
import (
- "bytes"
"sync"
)
@@ -44,41 +43,6 @@ func NewMemStore(d *DbStore, capacity uint) (m *MemStore) {
return
}
-func (x Key) Size() uint {
- return uint(len(x))
-}
-
-func (x Key) isEqual(y Key) bool {
- return bytes.Compare(x, y) == 0
-}
-
-func (h Key) bits(i, j uint) uint {
- ii := i >> 3
- jj := i & 7
- if ii >= h.Size() {
- return 0
- }
-
- if jj+j <= 8 {
- return uint((h[ii] >> jj) & ((1 << j) - 1))
- }
-
- res := uint(h[ii] >> jj)
- jj = 8 - jj
- j -= jj
- for j != 0 {
- ii++
- if j < 8 {
- res += uint(h[ii]&((1< task %v (%v)", index, n)
+ select {
+ case tasks <- &Task{Index: int64(index), Data: buffer[:n+8], Last: last}:
+ case <-abortC:
+ return nil, err
+ }
+ if last {
+ // glog.V(logger.Info).Infof("last task %v (%v)", index, n)
+ break
+ }
+ }
+ // Wait for the workers and return
+ close(tasks)
+ pend.Wait()
+
+ // glog.V(logger.Info).Infof("len: %v", results.Levels[0][0])
+ key := results.Levels[0][0].Children[0][:]
+ return key, nil
+}
+
+func (self *PyramidChunker) processor(pend *sync.WaitGroup, tasks chan *Task, results *Tree) {
+ defer pend.Done()
+
+ // glog.V(logger.Info).Infof("processor started")
+ // Start processing leaf chunks ad infinitum
+ hasher := self.hashFunc()
+ for task := range tasks {
+ depth, pow := len(results.Levels)-1, self.branches
+ // glog.V(logger.Info).Infof("task: %v, last: %v", task.Index, task.Last)
+
+ var node *Node
+ for depth >= 0 {
+ // New chunk received, reset the hasher and start processing
+ hasher.Reset()
+
+ if node == nil { // Leaf node, hash the data chunk
+ hasher.Write(task.Data)
+ } else { // Internal node, hash the children
+ for _, hash := range node.Children {
+ hasher.Write(hash[:])
+ }
+ }
+ hash := hasher.Sum(nil)
+ last := task.Last || (node != nil) && node.Last
+ // Insert the subresult into the memoization tree
+ results.Lock.Lock()
+ if node = results.Levels[depth][task.Index/pow]; node == nil {
+ // Figure out the pending tasks
+ pending := self.branches
+ if task.Index/pow == results.Chunks/pow {
+ pending = (results.Chunks + pow/self.branches - 1) / (pow / self.branches) % self.branches
+ }
+ node = &Node{pending, make([]common.Hash, pending), last}
+ results.Levels[depth][task.Index/pow] = node
+ }
+ node.Pending--
+ i := task.Index / (pow / self.branches) % self.branches
+ if last {
+ node.Pending -= self.branches - i
+ node.Children = node.Children[:i+1]
+ node.Last = true
+ }
+ copy(node.Children[i][:], hash)
+ left := node.Pending
+
+ if depth+1 < len(results.Levels) {
+ delete(results.Levels[depth+1], task.Index/(pow/self.branches))
+ }
+ results.Lock.Unlock()
+ // If there's more work to be done, leave for others
+ // glog.V(logger.Info).Infof("left %v", left)
+ if left > 0 {
+ break
+ }
+ // We're the last ones in this batch, merge the children together
+ depth--
+ pow *= self.branches
+ }
+ pend.Done()
+ }
+}
diff --git a/swarm/storage/types.go b/swarm/storage/types.go
index 124a56a085..8d7e7fdd38 100644
--- a/swarm/storage/types.go
+++ b/swarm/storage/types.go
@@ -5,6 +5,7 @@ import (
"crypto"
"fmt"
"hash"
+ "io"
"sync"
"github.com/ethereum/go-ethereum/common"
@@ -13,10 +14,47 @@ import (
type Hasher func() hash.Hash
+// Peer is the recorded as Source on the chunk
+// should probably not be here? but network should wrap chunk object
type Peer interface{}
type Key []byte
+func (x Key) Size() uint {
+ return uint(len(x))
+}
+
+func (x Key) isEqual(y Key) bool {
+ return bytes.Compare(x, y) == 0
+}
+
+func (h Key) bits(i, j uint) uint {
+ ii := i >> 3
+ jj := i & 7
+ if ii >= h.Size() {
+ return 0
+ }
+
+ if jj+j <= 8 {
+ return uint((h[ii] >> jj) & ((1 << j) - 1))
+ }
+
+ res := uint(h[ii] >> jj)
+ jj = 8 - jj
+ j -= jj
+ for j != 0 {
+ ii++
+ if j < 8 {
+ res += uint(h[ii]&((1< Swarm Domain Name Registrar")
+ self.dns = ens.NewENS(transactOpts, config.EnsRoot, self.backend)
+ glog.V(logger.Debug).Infof("[BZZ] -> Swarm Domain Name Registrar @ address %v", config.EnsRoot)
self.api = api.NewApi(self.dpa, self.dns)
// Manifests for Smart Hosting
diff --git a/swarm/test/connections/00.sh b/swarm/test/connections/00.sh
index 7f4c4445e9..b080d51cfe 100644
--- a/swarm/test/connections/00.sh
+++ b/swarm/test/connections/00.sh
@@ -1,33 +1,25 @@
#!/bin/bash
-dir=`dirname $0`
-source $dir/../../cmd/swarm/test.sh
-
swarm init 4
echo "expect each node to have 3 peers"
-cmd="'net.peerCount'"
+cmd="net.peerCount"
sleep 5
-swarm attach 00 --exec "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
-swarm attach 01 --exec "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
-swarm attach 02 --exec "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
-swarm attach 03 --exec "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
+swarm execute 00 "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
+swarm execute 01 "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
+swarm execute 02 "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
+swarm execute 03 "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
swarm stop all
-echo "after static nodes is deleted, connections are recovered from kaddb in bzz-peers.json"
-# echo rm -rf $DATA_ROOT/enodes\*
-# echo rm -rf $DATA_ROOT/data/\*/static-nodes.json
-rm -rf $DATA_ROOT/enodes*
-rm -rf $DATA_ROOT/data/*/static-nodes.json
+echo "connections are recovered from kaddb in bzz-peers.json"
swarm cluster 4
echo "expect each node to have 3 peers"
-cmd="'net.peerCount'"
-sleep 10
-swarm attach 00 --exec "$cmd" |tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
-swarm attach 01 --exec "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
-swarm attach 02 --exec "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
-swarm attach 03 --exec "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
+sleep 5
+swarm execute 00 "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
+swarm execute 01 "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
+swarm execute 02 "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
+swarm execute 03 "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
swarm stop all
diff --git a/swarm/test/swap/00.sh b/swarm/test/swap/00.sh
index 0cb0b05a4d..81006a8d6f 100644
--- a/swarm/test/swap/00.sh
+++ b/swarm/test/swap/00.sh
@@ -4,7 +4,7 @@ echo " two nodes that do not sync and do not have any funds"
echo " cannot retrieve content from each other"
dir=`dirname $0`
-source $dir/../../cmd/swarm/test.sh
+source $dir/../test.sh
FILE_00=/tmp/1K.0
randomfile 1 > $FILE_00
diff --git a/swarm/test/swap/01.sh b/swarm/test/swap/01.sh
index 362f0da73a..d9d50fa2cc 100644
--- a/swarm/test/swap/01.sh
+++ b/swarm/test/swap/01.sh
@@ -3,19 +3,20 @@ echo " two nodes that do not sync but have enough funds"
echo " can retrieve content from each other"
dir=`dirname $0`
-source $dir/../../cmd/swarm/test.sh
+source $dir/..s/test.sh
file=/tmp/test.file
mininginterval=120
key=/tmp/key
-logargs="--verbosity=0 --vmodule='swarm/*=6'"
-# logargs='--verbosity=6'
+# logargs="--verbosity=0 --vmodule='swarm/*=6'"
+logargs='--verbosity=6'
+# swarm init 2 --mine --bzznosync --bzznoswap=false $logargsc
+# echo "Mining some ether..."
+# sleep $mininginterval
-swarm init 2 --mine --bzznosync $logargs
+swarm cluster 2 --mine --bzznosync --bzznoswap=false $logargsc
-echo "Mining some ether..."
-sleep $mininginterval
randomfile 10 > $file
swarm up 00 $file|tail -n1 > $key
diff --git a/swarm/test/syncing/00.sh b/swarm/test/syncing/00.sh
index a793fea916..3785ba1e2f 100644
--- a/swarm/test/syncing/00.sh
+++ b/swarm/test/syncing/00.sh
@@ -4,7 +4,7 @@ echo " two nodes that sync (no swap and do not have any funds)"
echo " can be in sync content with each other"
dir=`dirname $0`
-source $dir/../../cmd/swarm/test.sh
+source $dir/../test.sh
mkdir -p /tmp/swarm-test-files
FILE_00=/tmp/swarm-test-files/00
@@ -28,7 +28,6 @@ swarm needs 00 $key $FILE_00
swarm needs 01 $key $FILE_00
swarm stop 01
-# exit 1;
swarm up 00 $FILE_01|tail -n1 > $key
swarm needs 00 $key $FILE_01
@@ -46,7 +45,8 @@ swarm needs 00 $key $FILE_03
swarm stop 00
swarm up 01 $FILE_04|tail -n1 > $key
swarm needs 01 $key $FILE_04
-swarm start 00 #--bzznoswap
+swarm start 00
+sleep $wait
swarm needs 00 $key $FILE_04
swarm stop all
diff --git a/swarm/test/syncing/01.sh b/swarm/test/syncing/01.sh
index 4e152dcd9f..bd090d391a 100644
--- a/swarm/test/syncing/01.sh
+++ b/swarm/test/syncing/01.sh
@@ -4,7 +4,7 @@ echo " two nodes that do not have any funds"
echo " can still sync content with each other"
dir=`dirname $0`
-source $dir/../../cmd/swarm/test.sh
+source $dir/../test.sh
key=/tmp/key
long=/tmp/10M
diff --git a/swarm/test/syncing/02.sh b/swarm/test/syncing/02.sh
index 48760d6e97..2b0d23443c 100644
--- a/swarm/test/syncing/02.sh
+++ b/swarm/test/syncing/02.sh
@@ -5,26 +5,26 @@ echo " two nodes that sync (no swap and do not have any funds)"
echo " can sync content with each other even with intermittent network connection"
dir=`dirname $0`
-source $dir/../../cmd/swarm/test.sh
+source $dir/../test.sh
long=/tmp/10M
key=/tmp/key
-randomfile 10000 > $long
+randomfile 100000 > $long
ls -l $long
-swarm init 2
-sleep $wait
+swarm init 2 --vmodule='swarm/*=5'
swarm up 00 $long |tail -n1 > $key &
-sleep $wait
-swarm attach 01 -exec "'bzz.blockNetworkRead(true)'"
-sleep $wait
-swarm attach 01 -exec "'bzz.blockNetworkRead(false)'"
-sleep $wait
-swarm attach 01 -exec "'bzz.blockNetworkRead(true)'"
-sleep $wait
+sleep 1
+swarm execute 01 'bzz.blockNetworkRead(true)'
+sleep 3
+swarm execute 01 'bzz.blockNetworkRead(false)'
+# sleep $wait
+# swarm attach 01 -exec "'bzz.blockNetworkRead(true)'"
+# sleep $wait
swarm stop 01
-swarm start 01
-swarm needs 01 $key $long
-
+# swarm start 01
+# sleep $wait
+# swarm needs 01 $key $long
+# sleep 3
swarm stop all
\ No newline at end of file
diff --git a/swarm/test/test.sh b/swarm/test/test.sh
new file mode 100644
index 0000000000..01703452a5
--- /dev/null
+++ b/swarm/test/test.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+TEST_DIR=`dirname $0`
+TEST_NAME=`basename $0 .sh`
+TEST_TYPE=`basename $TEST_DIR`
+export IP_ADDR="[::]"
+
+
+export SWARM_NETWORK_ID=322$TEST_NAME
+export SWARM_DIR=~/bzz/test/$TEST_TYPE
+
+rm -rf $SWARM_DIR/$SWARM_NETWORK_ID
+
+wait=1
+
+
+
+function randomfile {
+ dd if=/dev/urandom of=/dev/stdout bs=1024 count=$1 2>/dev/null
+}
\ No newline at end of file