syncbase/sync: remove obsolete sync code.

Remove the obsolete sync code from Syncbase.

Change-Id: I1d7303db725c36bd5dd8488fe0a3bc537ef6e2ae
diff --git a/x/ref/services/syncbase/sync/dag.go b/x/ref/services/syncbase/sync/dag.go
deleted file mode 100644
index 72d78cf..0000000
--- a/x/ref/services/syncbase/sync/dag.go
+++ /dev/null
@@ -1,1183 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-// Veyron Sync DAG (directed acyclic graph) utility functions.
-// The DAG is used to track the version history of objects in order to
-// detect and resolve conflicts (concurrent changes on different devices).
-//
-// Terminology:
-// * An object is a unique value in the Veyron Store represented by its UID.
-// * As an object mutates, its version number is updated by the Store.
-// * Each (object, version) tuple is represented by a node in the Sync DAG.
-// * The previous version of an object is its parent in the DAG, i.e. the
-//   new version is derived from that parent.
-// * When there are no conflicts, the node has a single reference back to
-//   a parent node.
-// * When a conflict between two concurrent object versions is resolved,
-//   the new version has references back to each of the two parents to
-//   indicate that it is derived from both nodes.
-// * During a sync operation from a source device to a target device, the
-//   target receives a DAG fragment from the source.  That fragment has to
-//   be incorporated (grafted) into the target device's DAG.  It may be a
-//   continuation of the DAG of an object, with the attachment (graft) point
-//   being the current head of DAG, in which case there are no conflicts.
-//   Or the graft point(s) may be older nodes, which means the new fragment
-//   is a divergence in the graph causing a conflict that must be resolved
-//   in order to re-converge the two DAG fragments.
-//
-// In the diagrams below:
-// (h) represents the head node in the local device.
-// (nh) represents the new head node received from the remote device.
-// (g) represents a graft node, where new nodes attach to the existing DAG.
-// <- represents a derived-from mutation, i.e. a child-to-parent pointer
-//
-// a- No-conflict example: the new nodes (v3, v4) attach to the head node (v2).
-//    In this case the new head becomes the head node, the new DAG fragment
-//    being a continuation of the existing DAG.
-//
-//    Before:
-//    v0 <- v1 <- v2(h)
-//
-//    Sync updates applied, no conflict detected:
-//    v0 <- v1 <- v2(h,g) <- v3 <- v4 (nh)
-//
-//    After:
-//    v0 <- v1 <- v2 <- v3 <- v4 (h)
-//
-// b- Conflict example: the new nodes (v3, v4) attach to an old node (v1).
-//    The current head node (v2) and the new head node (v4) are divergent
-//    (concurrent) mutations that need to be resolved.  The conflict
-//    resolution function is passed the old head (v2), new head (v4), and
-//    the common ancestor (v1) and resolves the conflict with (v5) which
-//    is represented in the DAG as derived from both v2 and v4 (2 parents).
-//
-//    Before:
-//    v0 <- v1 <- v2(h)
-//
-//    Sync updates applied, conflict detected (v2 not a graft node):
-//    v0 <- v1(g) <- v2(h)
-//                <- v3 <- v4 (nh)
-//
-//    After, conflict resolver creates v5 having 2 parents (v2, v4):
-//    v0 <- v1(g) <- v2 <------- v5(h)
-//                <- v3 <- v4 <-
-//
-// Note: the DAG does not grow indefinitely.  During a sync operation each
-// device learns what the other device already knows -- where it's at in
-// the version history for the objects.  When a device determines that all
-// devices that sync an object (as per the definitions of replication groups
-// in the Veyron Store) have moved past some version for that object, the
-// DAG for that object can be pruned, deleting all prior (ancestor) nodes.
-//
-// The DAG DB contains four tables persisted to disk (nodes, heads, trans,
-// priv) and three in-memory (ephemeral) maps (graft, txSet, txGC):
-//   * nodes: one entry per (object, version) with references to the
-//            parent node(s) it is derived from, a reference to the
-//            log record identifying that change, a reference to its
-//            transaction set (or NoTxId if none), and a boolean to
-//            indicate whether this change was a deletion of the object.
-//   * heads: one entry per object pointing to its most recent version
-//            in the nodes table
-//   * trans: one entry per transaction ID containing the set of objects
-//            that forms the transaction and their versions.
-//   * priv:  one entry per object ID for objects that are private to the
-//            store, not shared through any SyncGroup.
-//   * graft: during a sync operation, it tracks the nodes where the new
-//            DAG fragments are attached to the existing graph for each
-//            mutated object.  This map is used to determine whether a
-//            conflict happened for an object and, if yes, select the most
-//            recent common ancestor from these graft points to use when
-//            resolving the conflict.  At the end of a sync operation the
-//            graft map is destroyed.
-//   * txSet: used to incrementally construct the transaction sets that
-//            are stored in the "trans" table once all the nodes of a
-//            transaction have been added.  Multiple transaction sets
-//            can be constructed to support the concurrency between the
-//            Sync Initiator and Watcher threads.
-//   * txGC:  used to track the transactions impacted by objects being
-//            pruned.  At the end of the pruning operation the records
-//            of the "trans" table are updated from the txGC information.
-//
-// Note: for regular (no-conflict) changes, a node has a reference to
-// one parent from which it was derived.  When a conflict is resolved,
-// the new node has references to the two concurrent parents that triggered
-// the conflict.  The states of the parents[] array are:
-//   * []            The earliest/first version of an object
-//   * [XYZ]         Regular non-conflict version derived from XYZ
-//   * [XYZ, ABC]    Resolution version caused by XYZ-vs-ABC conflict
-
-import (
-	"container/list"
-	"errors"
-	"fmt"
-	"math/rand"
-	"strconv"
-	"time"
-
-	"v.io/x/lib/vlog"
-	"v.io/x/ref/lib/stats"
-)
-
-const (
-	NoTxId = TxId(0)
-)
-
-var (
-	errBadDAG = errors.New("invalid DAG")
-)
-
-type dagTxMap map[ObjId]Version
-
-// dagTxState tracks the state of a transaction.
-type dagTxState struct {
-	TxMap   dagTxMap
-	TxCount uint32
-}
-
-type dag struct {
-	fname string               // file pathname
-	store *kvdb                // underlying K/V store
-	heads *kvtable             // pointer to "heads" table in the store
-	nodes *kvtable             // pointer to "nodes" table in the store
-	trans *kvtable             // pointer to "trans" table in the store
-	priv  *kvtable             // pointer to "priv" table in the store
-	graft map[ObjId]*graftInfo // in-memory state of DAG object grafting
-	txSet map[TxId]*dagTxState // in-memory construction of transaction sets
-	txGC  map[TxId]dagTxMap    // in-memory tracking of transaction sets to cleanup
-	txGen *rand.Rand           // transaction ID random number generator
-
-	// DAG stats
-	numObj  *stats.Integer // number of objects
-	numNode *stats.Integer // number of versions across all objects
-	numTx   *stats.Integer // number of transactions tracked
-	numPriv *stats.Integer // number of private objects
-}
-
-type dagNode struct {
-	Level   uint64    // node distance from root
-	Parents []Version // references to parent versions
-	Logrec  string    // reference to log record change
-	TxId    TxId      // ID of a transaction set
-	Deleted bool      // true if the change was a delete
-}
-
-type graftInfo struct {
-	newNodes   map[Version]struct{} // set of newly added nodes during a sync
-	graftNodes map[Version]uint64   // set of graft nodes and their level
-	newHeads   map[Version]struct{} // set of candidate new head nodes
-}
-
-type privNode struct {
-	//Mutation *raw.Mutation // most recent store mutation for a private (unshared) object
-	PathIDs  []ObjId // store IDs in the path from the object to the root of the store
-	SyncTime int64   // SyncTime is the timestamp of the mutation when it arrives at the Sync server.
-	TxId     TxId    // ID of the transaction in which this mutation was done
-	TxCount  uint32  // total number of object mutations in that transaction
-}
-
-// openDAG opens or creates a DAG for the given filename.
-func openDAG(filename string) (*dag, error) {
-	// Open the file and create it if it does not exist.
-	// Also initialize the store and its tables.
-	db, tbls, err := kvdbOpen(filename, []string{"heads", "nodes", "trans", "priv"})
-	if err != nil {
-		return nil, err
-	}
-
-	d := &dag{
-		fname:   filename,
-		store:   db,
-		heads:   tbls[0],
-		nodes:   tbls[1],
-		trans:   tbls[2],
-		priv:    tbls[3],
-		txGen:   rand.New(rand.NewSource(time.Now().UTC().UnixNano())),
-		txSet:   make(map[TxId]*dagTxState),
-		numObj:  stats.NewInteger(statsNumDagObj),
-		numNode: stats.NewInteger(statsNumDagNode),
-		numTx:   stats.NewInteger(statsNumDagTx),
-		numPriv: stats.NewInteger(statsNumDagPrivNode),
-	}
-
-	// Initialize the stats counters from the tables.
-	d.numObj.Set(int64(d.heads.getNumKeys()))
-	d.numNode.Set(int64(d.nodes.getNumKeys()))
-	d.numTx.Set(int64(d.trans.getNumKeys()))
-	d.numPriv.Set(int64(d.priv.getNumKeys()))
-
-	d.clearGraft()
-	d.clearTxGC()
-
-	return d, nil
-}
-
-// close closes the DAG and invalidates its structure.
-func (d *dag) close() {
-	if d.store != nil {
-		d.store.close() // this also closes the tables
-		stats.Delete(statsNumDagObj)
-		stats.Delete(statsNumDagNode)
-		stats.Delete(statsNumDagTx)
-		stats.Delete(statsNumDagPrivNode)
-	}
-	*d = dag{} // zero out the DAG struct
-}
-
-// flush flushes the DAG store to disk.
-func (d *dag) flush() {
-	if d.store != nil {
-		d.store.flush()
-	}
-}
-
-// clearGraft clears the temporary in-memory grafting maps.
-func (d *dag) clearGraft() {
-	if d.store != nil {
-		d.graft = make(map[ObjId]*graftInfo)
-	}
-}
-
-// clearTxGC clears the temporary in-memory transaction garbage collection maps.
-func (d *dag) clearTxGC() {
-	if d.store != nil {
-		d.txGC = make(map[TxId]dagTxMap)
-	}
-}
-
-// getObjectGraft returns the graft structure for an object ID.
-// The graftInfo struct for an object is ephemeral (in-memory) and it
-// tracks the following information:
-// - newNodes:   the set of newly added nodes used to detect the type of
-//               edges between nodes (new-node to old-node or vice versa).
-// - newHeads:   the set of new candidate head nodes used to detect conflicts.
-// - graftNodes: the set of nodes used to find common ancestors between
-//               conflicting nodes.
-//
-// After the received Sync logs are applied, if there are two new heads in
-// the newHeads set, there is a conflict to be resolved for this object.
-// Otherwise if there is only one head, no conflict was triggered and the
-// new head becomes the current version for the object.
-//
-// In case of conflict, the graftNodes set is used to select the common
-// ancestor to pass to the conflict resolver.
-//
-// Note: if an object's graft structure does not exist only create it
-// if the "create" parameter is set to true.
-func (d *dag) getObjectGraft(oid ObjId, create bool) *graftInfo {
-	graft := d.graft[oid]
-	if graft == nil && create {
-		graft = &graftInfo{
-			newNodes:   make(map[Version]struct{}),
-			graftNodes: make(map[Version]uint64),
-			newHeads:   make(map[Version]struct{}),
-		}
-
-		// If a current head node exists for this object, initialize
-		// the set of candidate new heads to include it.
-		head, err := d.getHead(oid)
-		if err == nil {
-			graft.newHeads[head] = struct{}{}
-		}
-
-		d.graft[oid] = graft
-	}
-	return graft
-}
-
-// addNodeTxStart generates a transaction ID and returns it to the
-// caller if a TxId is not specified.  This transaction ID is stored
-// as part of each log record. If a TxId is specified by the caller,
-// state corresponding to that TxId is instantiated. TxId is used to
-// track DAG nodes that are part of the same transaction.
-func (d *dag) addNodeTxStart(tid TxId) TxId {
-	if d.store == nil {
-		return NoTxId
-	}
-
-	// Check if "tid" already exists.
-	if tid != NoTxId {
-		if _, ok := d.txSet[tid]; ok {
-			return tid
-		}
-		txSt, err := d.getTransaction(tid)
-		if err == nil {
-			d.txSet[tid] = txSt
-			return tid
-		}
-	} else {
-		// Generate a random 64-bit transaction ID different than NoTxId.
-		// Also make sure the ID is not already being used.
-		for (tid == NoTxId) || (d.txSet[tid] != nil) {
-			// Generate an unsigned 64-bit random value by combining a
-			// random 63-bit value and a random 1-bit value.
-			tid = (TxId(d.txGen.Int63()) << 1) | TxId(d.txGen.Int63n(2))
-		}
-	}
-
-	// Initialize the in-memory object/version map for that transaction ID.
-	d.txSet[tid] = &dagTxState{TxMap: make(dagTxMap), TxCount: 0}
-	return tid
-}
-
-// addNodeTxEnd marks the end of a given transaction.
-// The DAG completes its internal tracking of the transaction information.
-func (d *dag) addNodeTxEnd(tid TxId, count uint32) error {
-	if d.store == nil {
-		return errBadDAG
-	}
-	if tid == NoTxId || count == 0 {
-		return fmt.Errorf("invalid TxState: %v %v", tid, count)
-	}
-
-	txSt, ok := d.txSet[tid]
-	if !ok {
-		return fmt.Errorf("unknown transaction ID: %v", tid)
-	}
-
-	// The first time a transaction (TxId) is ended, TxCount is
-	// zero while "count" is not. Subsequently if this TxId is
-	// started and ended, TxCount should be the same as the
-	// incoming "count".
-	if txSt.TxCount != 0 && txSt.TxCount != count {
-		return fmt.Errorf("incorrect counts for transaction: %v (%v %v)", tid, txSt.TxCount, count)
-	}
-
-	// Only save non-empty transactions, i.e. those that have at least
-	// one mutation on a shared (non-private) object.
-	if len(txSt.TxMap) > 0 {
-		txSt.TxCount = count
-		if err := d.setTransaction(tid, txSt); err != nil {
-			return err
-		}
-	}
-
-	delete(d.txSet, tid)
-	return nil
-}
-
-// addNode adds a new node for an object in the DAG, linking it to its parent nodes.
-// It verifies that this node does not exist and that its parent nodes are valid.
-// It also determines the DAG level of the node from its parent nodes (max() + 1).
-//
-// If the node is due to a local change (from the Watcher API), no need to
-// update the grafting structure.  Otherwise the node is due to a remote change
-// (from the Sync protocol) being grafted on the DAG:
-// - If a parent node is not new, mark it as a DAG graft point.
-// - Mark this version as a new node.
-// - Update the new head node pointer of the grafted DAG.
-//
-// If the transaction ID is set to NoTxId, this node is not part of a transaction.
-// Otherwise, track its membership in the given transaction ID.
-func (d *dag) addNode(oid ObjId, version Version, remote, deleted bool,
-	parents []Version, logrec string, tid TxId) error {
-	if d.store == nil {
-		return errBadDAG
-	}
-
-	if parents != nil {
-		if len(parents) > 2 {
-			return fmt.Errorf("cannot have more than 2 parents, not %d", len(parents))
-		}
-		if len(parents) == 0 {
-			// Replace an empty array with a nil.
-			parents = nil
-		}
-	}
-
-	// The new node must not exist.
-	if d.hasNode(oid, version) {
-		return fmt.Errorf("node %v:%d already exists in the DAG", oid, version)
-	}
-
-	// A new root node (no parents) is allowed only for new objects.
-	if parents == nil {
-		_, err := d.getHead(oid)
-		if err == nil {
-			return fmt.Errorf("cannot add another root node %v:%d for this object in the DAG", oid, version)
-		}
-	}
-
-	// For a remote change, make sure the object has a graft info entry.
-	// During a sync operation, each mutated object gets new nodes added
-	// in its DAG.  These new nodes are either derived from nodes that
-	// were previously known on this device (i.e. their parent nodes are
-	// pre-existing), or they are derived from other new DAG nodes being
-	// discovered during this sync (i.e. their parent nodes were also
-	// just added to the DAG).
-	//
-	// To detect a conflict and find the most recent common ancestor to
-	// pass to the conflict resolver callback, the DAG keeps track of the
-	// new nodes that have old parent nodes.  These old-to-new edges are
-	// the points where new DAG fragments are attached (grafted) onto the
-	// existing DAG.  The old nodes are the "graft nodes" and they form
-	// the set of possible common ancestors to use in case of conflict:
-	// 1- A conflict happens when the current "head node" for an object
-	//    is not in the set of graft nodes.  It means the object mutations
-	//    were not derived from what the device knows, but where divergent
-	//    changes from a prior point (from one of the graft nodes).
-	// 2- The most recent common ancestor to use in resolving the conflict
-	//    is the object graft node with the deepest level (furthest from
-	//    the origin root node), representing the most up-to-date common
-	//    knowledge between this device and the divergent changes.
-	//
-	// Note: at the end of a sync operation between 2 devices, the whole
-	// graft info is cleared (Syncd calls clearGraft()) to prepare it for
-	// the new pairwise sync operation.
-	graft := d.getObjectGraft(oid, remote)
-
-	// Verify the parents and determine the node level.
-	// Update the graft info in the DAG for this object.
-	var level uint64
-	for _, parent := range parents {
-		node, err := d.getNode(oid, parent)
-		if err != nil {
-			return err
-		}
-		if level <= node.Level {
-			level = node.Level + 1
-		}
-		if remote {
-			// If this parent is an old node, it's a graft point in the DAG
-			// and may be a common ancestor used during conflict resolution.
-			if _, ok := graft.newNodes[parent]; !ok {
-				graft.graftNodes[parent] = node.Level
-			}
-
-			// The parent nodes can no longer be candidates for new head versions.
-			if _, ok := graft.newHeads[parent]; ok {
-				delete(graft.newHeads, parent)
-			}
-		}
-	}
-
-	if remote {
-		// This new node is a candidate for new head version.
-		graft.newNodes[version] = struct{}{}
-		graft.newHeads[version] = struct{}{}
-	}
-
-	// If this node is part of a transaction, add it to that set.
-	if tid != NoTxId {
-		txSt, ok := d.txSet[tid]
-		if !ok {
-			return fmt.Errorf("unknown transaction ID: %v", tid)
-		}
-
-		txSt.TxMap[oid] = version
-	}
-
-	// Insert the new node in the kvdb.
-	node := &dagNode{Level: level, Parents: parents, Logrec: logrec, TxId: tid, Deleted: deleted}
-	if err := d.setNode(oid, version, node); err != nil {
-		return err
-	}
-
-	d.numNode.Incr(1)
-	if parents == nil {
-		d.numObj.Incr(1)
-	}
-	return nil
-}
-
-// hasNode returns true if the node (oid, version) exists in the DAG DB.
-func (d *dag) hasNode(oid ObjId, version Version) bool {
-	if d.store == nil {
-		return false
-	}
-	key := objNodeKey(oid, version)
-	return d.nodes.hasKey(key)
-}
-
-// addParent adds to the DAG node (oid, version) linkage to this parent node.
-// If the parent linkage is due to a local change (from conflict resolution
-// by blessing an existing version), no need to update the grafting structure.
-// Otherwise a remote change (from the Sync protocol) updates the graft.
-//
-// TODO(rdaoud): recompute the levels of reachable child-nodes if the new
-// parent's level is greater or equal to the node's current level.
-func (d *dag) addParent(oid ObjId, version, parent Version, remote bool) error {
-	if version == parent {
-		return fmt.Errorf("addParent: object %v: node %d cannot be its own parent", oid, version)
-	}
-
-	node, err := d.getNode(oid, version)
-	if err != nil {
-		return err
-	}
-
-	pnode, err := d.getNode(oid, parent)
-	if err != nil {
-		vlog.VI(1).Infof("addParent: object %v, node %d, parent %d: parent node not found", oid, version, parent)
-		return err
-	}
-
-	// Check if the parent is already linked to this node.
-	found := false
-	for i := range node.Parents {
-		if node.Parents[i] == parent {
-			found = true
-			break
-		}
-	}
-
-	// If the parent is not yet linked (local or remote) add it.
-	if !found {
-		// Make sure that adding the link does not create a cycle in the DAG.
-		// This is done by verifying that the node is not an ancestor of the
-		// parent that it is being linked to.
-		err = d.ancestorIter(oid, pnode.Parents, func(oid ObjId, v Version, nd *dagNode) error {
-			if v == version {
-				return fmt.Errorf("addParent: cycle on object %v: node %d is an ancestor of parent node %d",
-					oid, version, parent)
-			}
-			return nil
-		})
-		if err != nil {
-			return err
-		}
-		node.Parents = append(node.Parents, parent)
-		err = d.setNode(oid, version, node)
-		if err != nil {
-			return err
-		}
-	}
-
-	// For local changes we are done, the grafting structure is not updated.
-	if !remote {
-		return nil
-	}
-
-	// If the node and its parent are new/old or old/new then add
-	// the parent as a graft point (a potential common ancestor).
-	graft := d.getObjectGraft(oid, true)
-
-	_, nodeNew := graft.newNodes[version]
-	_, parentNew := graft.newNodes[parent]
-	if (nodeNew && !parentNew) || (!nodeNew && parentNew) {
-		graft.graftNodes[parent] = pnode.Level
-	}
-
-	// The parent node can no longer be a candidate for a new head version.
-	// The addParent() function only removes candidates from newHeads that
-	// have become parents.  It does not add the child nodes to newHeads
-	// because they are not necessarily new-head candidates.  If they are
-	// new nodes, the addNode() function handles adding them to newHeads.
-	// For old nodes, only the current head could be a candidate and it is
-	// added to newHeads when the graft struct is initialized.
-	if _, ok := graft.newHeads[parent]; ok {
-		delete(graft.newHeads, parent)
-	}
-
-	return nil
-}
-
-// moveHead moves the object head node in the DAG.
-func (d *dag) moveHead(oid ObjId, head Version) error {
-	if d.store == nil {
-		return errBadDAG
-	}
-
-	// Verify that the node exists.
-	if !d.hasNode(oid, head) {
-		return fmt.Errorf("node %v:%d does not exist in the DAG", oid, head)
-	}
-
-	return d.setHead(oid, head)
-}
-
-// hasConflict determines if there is a conflict for this object between its
-// new and old head nodes.
-// - Yes: return (true, newHead, oldHead, ancestor)
-// - No:  return (false, newHead, oldHead, NoVersion)
-// A conflict exists when there are two new-head nodes.  It means the newly
-// added object versions are not derived in part from this device's current
-// knowledge.  If there is a single new-head, the object changes were applied
-// without triggering a conflict.
-func (d *dag) hasConflict(oid ObjId) (isConflict bool, newHead, oldHead, ancestor Version, err error) {
-	oldHead = NoVersion
-	newHead = NoVersion
-	ancestor = NoVersion
-	if d.store == nil {
-		err = errBadDAG
-		return
-	}
-
-	graft := d.graft[oid]
-	if graft == nil {
-		err = fmt.Errorf("node %v has no DAG graft information", oid)
-		return
-	}
-
-	numHeads := len(graft.newHeads)
-	if numHeads < 1 || numHeads > 2 {
-		err = fmt.Errorf("node %v has invalid number of new head candidates %d: %v", oid, numHeads, graft.newHeads)
-		return
-	}
-
-	// Fetch the current head for this object if it exists.  The error from getHead()
-	// is ignored because a newly received object is not yet known on this device and
-	// will not trigger a conflict.
-	oldHead, _ = d.getHead(oid)
-
-	// If there is only one new head node there is no conflict.
-	// The new head is that single one, even if it might also be the same old node.
-	if numHeads == 1 {
-		for k := range graft.newHeads {
-			newHead = k
-		}
-		return
-	}
-
-	// With two candidate head nodes, the new one is the node that is
-	// not the current (old) head node.
-	for k := range graft.newHeads {
-		if k != oldHead {
-			newHead = k
-			break
-		}
-	}
-
-	// There is a conflict: the best choice ancestor is the graft point
-	// node with the largest level (farthest from the root).  It is
-	// possible in some corner cases to have multiple graft nodes at
-	// the same level.  This would still be a single conflict, but the
-	// multiple same-level graft points representing equivalent conflict
-	// resolutions on different devices that are now merging their
-	// resolutions.  In such a case it does not matter which node is
-	// chosen as the ancestor because the conflict resolver function
-	// is assumed to be convergent.  However it's nicer to make that
-	// selection deterministic so all devices see the same choice.
-	// For this the version number is used as a tie-breaker.
-	isConflict = true
-	var maxLevel uint64
-	for node, level := range graft.graftNodes {
-		if maxLevel < level ||
-			(maxLevel == level && ancestor < node) {
-			maxLevel = level
-			ancestor = node
-		}
-	}
-	return
-}
-
-// ancestorIter iterates over the DAG ancestor nodes for an object in a
-// breadth-first traversal starting from given version node(s).  In its
-// traversal it invokes the callback function once for each node, passing
-// the object ID, version number and a pointer to the dagNode.
-func (d *dag) ancestorIter(oid ObjId, startVersions []Version,
-	cb func(ObjId, Version, *dagNode) error) error {
-	visited := make(map[Version]bool)
-	queue := list.New()
-	for _, version := range startVersions {
-		queue.PushBack(version)
-		visited[version] = true
-	}
-
-	for queue.Len() > 0 {
-		version := queue.Remove(queue.Front()).(Version)
-		node, err := d.getNode(oid, version)
-		if err != nil {
-			// Ignore it, the parent was previously pruned.
-			continue
-		}
-		for _, parent := range node.Parents {
-			if !visited[parent] {
-				queue.PushBack(parent)
-				visited[parent] = true
-			}
-		}
-		if err = cb(oid, version, node); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// hasDeletedDescendant returns true if the node (oid, version) exists in the
-// DAG DB and one of its descendants is a deleted node (i.e. has its "Deleted"
-// flag set true).  This means that at some object mutation after this version,
-// the object was deleted.
-func (d *dag) hasDeletedDescendant(oid ObjId, version Version) bool {
-	if d.store == nil {
-		return false
-	}
-	if !d.hasNode(oid, version) {
-		return false
-	}
-
-	// Do a breadth-first traversal from the object's head node back to
-	// the given version.  Along the way, track whether a deleted node is
-	// traversed.  Return true only if a traversal reaches the given version
-	// and had seen a deleted node along the way.
-
-	// nodeStep tracks a step along a traversal.  It stores the node to visit
-	// when taking that step and a boolean tracking whether a deleted node
-	// was seen so far along that trajectory.
-	head, err := d.getHead(oid)
-	if err != nil {
-		return false
-	}
-
-	type nodeStep struct {
-		node    Version
-		deleted bool
-	}
-
-	visited := make(map[nodeStep]struct{})
-	queue := list.New()
-
-	step := nodeStep{node: head, deleted: false}
-	queue.PushBack(&step)
-	visited[step] = struct{}{}
-
-	for queue.Len() > 0 {
-		step := queue.Remove(queue.Front()).(*nodeStep)
-		if step.node == version {
-			if step.deleted {
-				return true
-			}
-			continue
-		}
-		node, err := d.getNode(oid, step.node)
-		if err != nil {
-			// Ignore it, the parent was previously pruned.
-			continue
-		}
-		nextDel := step.deleted || node.Deleted
-
-		for _, parent := range node.Parents {
-			nextStep := nodeStep{node: parent, deleted: nextDel}
-			if _, ok := visited[nextStep]; !ok {
-				queue.PushBack(&nextStep)
-				visited[nextStep] = struct{}{}
-			}
-		}
-	}
-
-	return false
-}
-
-// prune trims the DAG of an object at a given version (node) by deleting
-// all its ancestor nodes, making it the new root node.  For each deleted
-// node it calls the given callback function to delete its log record.
-// This function should only be called when Sync determines that all devices
-// that know about the object have gotten past this version.
-// Also track any transaction sets affected by deleting DAG objects that
-// have transaction IDs.  This is later used to do garbage collection
-// on transaction sets when pruneDone() is called.
-func (d *dag) prune(oid ObjId, version Version, delLogRec func(logrec string) error) error {
-	if d.store == nil {
-		return errBadDAG
-	}
-
-	// Get the node at the pruning point and set its parents to nil.
-	// It will become the oldest DAG node (root) for the object.
-	node, err := d.getNode(oid, version)
-	if err != nil {
-		return err
-	}
-	if node.Parents == nil {
-		// Nothing to do, this node is already the root.
-		return nil
-	}
-
-	iterVersions := node.Parents
-
-	node.Parents = nil
-	if err = d.setNode(oid, version, node); err != nil {
-		return err
-	}
-
-	// Delete all ancestor nodes and their log records.
-	// Delete as many as possible and track the error counts.
-	// Keep track of objects deleted from transaction in order
-	// to cleanup transaction sets when pruneDone() is called.
-	numNodeErrs, numLogErrs := 0, 0
-	err = d.ancestorIter(oid, iterVersions, func(oid ObjId, v Version, node *dagNode) error {
-		nodeErrs, logErrs, err := d.removeNode(oid, v, node, delLogRec)
-		numNodeErrs += nodeErrs
-		numLogErrs += logErrs
-		return err
-	})
-	if err != nil {
-		return err
-	}
-	if numNodeErrs != 0 || numLogErrs != 0 {
-		return fmt.Errorf("prune failed to delete %d nodes and %d log records", numNodeErrs, numLogErrs)
-	}
-	return nil
-}
-
-// removeNode removes the state associated with a DAG node.
-func (d *dag) removeNode(oid ObjId, v Version, node *dagNode, delLogRec func(logrec string) error) (int, int, error) {
-	numNodeErrs, numLogErrs := 0, 0
-	if tid := node.TxId; tid != NoTxId {
-		if d.txGC[tid] == nil {
-			d.txGC[tid] = make(dagTxMap)
-		}
-		d.txGC[tid][oid] = v
-	}
-
-	if err := delLogRec(node.Logrec); err != nil {
-		numLogErrs++
-	}
-	if err := d.delNode(oid, v); err != nil {
-		numNodeErrs++
-	}
-	d.numNode.Incr(-1)
-	return numNodeErrs, numLogErrs, nil
-}
-
-// pruneAll prunes the entire DAG state corresponding to an object,
-// including the head.
-func (d *dag) pruneAll(oid ObjId, delLogRec func(logrec string) error) error {
-	vers, err := d.getHead(oid)
-	if err != nil {
-		return err
-	}
-	node, err := d.getNode(oid, vers)
-	if err != nil {
-		return err
-	}
-
-	if err := d.prune(oid, vers, delLogRec); err != nil {
-		return err
-	}
-
-	// Clean up the head.
-	numNodeErrs, numLogErrs, err := d.removeNode(oid, vers, node, delLogRec)
-	if err != nil {
-		return err
-	}
-	if numNodeErrs != 0 || numLogErrs != 0 {
-		return fmt.Errorf("pruneAll failed to delete %d nodes and %d log records", numNodeErrs, numLogErrs)
-	}
-
-	return d.delHead(oid)
-}
-
-// pruneDone is called when object pruning is finished within a single pass
-// of the Sync garbage collector.  It updates the transaction sets affected
-// by the objects deleted by the prune() calls.
-func (d *dag) pruneDone() error {
-	if d.store == nil {
-		return errBadDAG
-	}
-
-	// Update transaction sets by removing from them the objects that
-	// were pruned.  If the resulting set is empty, delete it.
-	for tid, txMapGC := range d.txGC {
-		txSt, err := d.getTransaction(tid)
-		if err != nil {
-			return err
-		}
-
-		for oid := range txMapGC {
-			delete(txSt.TxMap, oid)
-		}
-
-		if len(txSt.TxMap) > 0 {
-			err = d.setTransaction(tid, txSt)
-		} else {
-			err = d.delTransaction(tid)
-		}
-		if err != nil {
-			return err
-		}
-	}
-
-	d.clearTxGC()
-	return nil
-}
-
-// getLogrec returns the log record information for a given object version.
-func (d *dag) getLogrec(oid ObjId, version Version) (string, error) {
-	node, err := d.getNode(oid, version)
-	if err != nil {
-		return "", err
-	}
-	return node.Logrec, nil
-}
-
-// objNodeKey returns the key used to access the object node (oid, version)
-// in the DAG DB.
-func objNodeKey(oid ObjId, version Version) string {
-	return fmt.Sprintf("%v:%d", oid, version)
-}
-
-// setNode stores the dagNode structure for the object node (oid, version)
-// in the DAG DB.
-func (d *dag) setNode(oid ObjId, version Version, node *dagNode) error {
-	if d.store == nil {
-		return errBadDAG
-	}
-	key := objNodeKey(oid, version)
-	return d.nodes.set(key, node)
-}
-
-// getNode retrieves the dagNode structure for the object node (oid, version)
-// from the DAG DB.
-func (d *dag) getNode(oid ObjId, version Version) (*dagNode, error) {
-	if d.store == nil {
-		return nil, errBadDAG
-	}
-	var node dagNode
-	key := objNodeKey(oid, version)
-	if err := d.nodes.get(key, &node); err != nil {
-		return nil, err
-	}
-	return &node, nil
-}
-
-// delNode deletes the object node (oid, version) from the DAG DB.
-func (d *dag) delNode(oid ObjId, version Version) error {
-	if d.store == nil {
-		return errBadDAG
-	}
-	key := objNodeKey(oid, version)
-	return d.nodes.del(key)
-}
-
-// objHeadKey returns the key used to access the object head in the DAG DB.
-func objHeadKey(oid ObjId) string {
-	return oid.String()
-}
-
-// setHead stores version as the object head in the DAG DB.
-func (d *dag) setHead(oid ObjId, version Version) error {
-	if d.store == nil {
-		return errBadDAG
-	}
-	key := objHeadKey(oid)
-	return d.heads.set(key, version)
-}
-
-// getHead retrieves the object head from the DAG DB.
-func (d *dag) getHead(oid ObjId) (Version, error) {
-	var version Version
-	if d.store == nil {
-		return version, errBadDAG
-	}
-	key := objHeadKey(oid)
-	err := d.heads.get(key, &version)
-	if err != nil {
-		version = NoVersion
-	}
-	return version, err
-}
-
-// delHead deletes the object head from the DAG DB.
-func (d *dag) delHead(oid ObjId) error {
-	if d.store == nil {
-		return errBadDAG
-	}
-	key := objHeadKey(oid)
-	if err := d.heads.del(key); err != nil {
-		return err
-	}
-	d.numObj.Incr(-1)
-	return nil
-}
-
-// dagTransactionKey returns the key used to access the transaction in the DAG DB.
-func dagTransactionKey(tid TxId) string {
-	return fmt.Sprintf("%v", tid)
-}
-
-// setTransaction stores the transaction object/version map in the DAG DB.
-func (d *dag) setTransaction(tid TxId, txSt *dagTxState) error {
-	if d.store == nil {
-		return errBadDAG
-	}
-	if tid == NoTxId {
-		return fmt.Errorf("invalid TxId: %v", tid)
-	}
-	key := dagTransactionKey(tid)
-	exists := d.trans.hasKey(key)
-
-	if err := d.trans.set(key, txSt); err != nil {
-		return err
-	}
-
-	if !exists {
-		d.numTx.Incr(1)
-	}
-	return nil
-}
-
-// getTransaction retrieves the transaction object/version map from the DAG DB.
-func (d *dag) getTransaction(tid TxId) (*dagTxState, error) {
-	if d.store == nil {
-		return nil, errBadDAG
-	}
-	if tid == NoTxId {
-		return nil, fmt.Errorf("invalid TxId: %v", tid)
-	}
-	var txSt dagTxState
-	key := dagTransactionKey(tid)
-	if err := d.trans.get(key, &txSt); err != nil {
-		return nil, err
-	}
-	return &txSt, nil
-}
-
-// delTransaction deletes the transation object/version map from the DAG DB.
-func (d *dag) delTransaction(tid TxId) error {
-	if d.store == nil {
-		return errBadDAG
-	}
-	if tid == NoTxId {
-		return fmt.Errorf("invalid TxId: %v", tid)
-	}
-	key := dagTransactionKey(tid)
-	if err := d.trans.del(key); err != nil {
-		return err
-	}
-	d.numTx.Incr(-1)
-	return nil
-}
-
-// objPrivNodeKey returns the key used to access a private (unshared) node in the DAG DB.
-func objPrivNodeKey(oid ObjId) string {
-	return oid.String()
-}
-
-// setPrivNode stores the privNode structure for a private (unshared) object in the DAG DB.
-func (d *dag) setPrivNode(oid ObjId, priv *privNode) error {
-	if d.store == nil {
-		return errBadDAG
-	}
-	key := objPrivNodeKey(oid)
-	exists := d.priv.hasKey(key)
-
-	if err := d.priv.set(key, priv); err != nil {
-		return err
-	}
-
-	if !exists {
-		d.numPriv.Incr(1)
-	}
-	return nil
-}
-
-// getPrivNode retrieves the privNode structure for a private (unshared) object from the DAG DB.
-func (d *dag) getPrivNode(oid ObjId) (*privNode, error) {
-	if d.store == nil {
-		return nil, errBadDAG
-	}
-	var priv privNode
-	key := objPrivNodeKey(oid)
-	if err := d.priv.get(key, &priv); err != nil {
-		return nil, err
-	}
-	return &priv, nil
-}
-
-// delPrivNode deletes a private (unshared) object from the DAG DB.
-func (d *dag) delPrivNode(oid ObjId) error {
-	if d.store == nil {
-		return errBadDAG
-	}
-	key := objPrivNodeKey(oid)
-	if err := d.priv.del(key); err != nil {
-		return err
-	}
-	d.numPriv.Incr(-1)
-	return nil
-}
-
-// getParentMap is a testing and debug helper function that returns for
-// an object a map of all the object version in the DAG and their parents.
-// The map represents the graph of the object version history.
-func (d *dag) getParentMap(oid ObjId) map[Version][]Version {
-	parentMap := make(map[Version][]Version)
-	var iterVersions []Version
-
-	if head, err := d.getHead(oid); err == nil {
-		iterVersions = append(iterVersions, head)
-	}
-	if graft := d.graft[oid]; graft != nil {
-		for k := range graft.newHeads {
-			iterVersions = append(iterVersions, k)
-		}
-	}
-
-	// Breadth-first traversal starting from the object head.
-	d.ancestorIter(oid, iterVersions, func(oid ObjId, v Version, node *dagNode) error {
-		parentMap[v] = node.Parents
-		return nil
-	})
-
-	return parentMap
-}
-
-// getGraftNodes is a testing and debug helper function that returns for
-// an object the graft information built and used during a sync operation.
-// The newHeads map identifies the candidate head nodes based on the data
-// reported by the other device during a sync operation.  The graftNodes map
-// identifies the set of old nodes where the new DAG fragments were attached
-// and their depth level in the DAG.
-func (d *dag) getGraftNodes(oid ObjId) (map[Version]struct{}, map[Version]uint64) {
-	if d.store != nil {
-		if ginfo := d.graft[oid]; ginfo != nil {
-			return ginfo.newHeads, ginfo.graftNodes
-		}
-	}
-	return nil, nil
-}
-
-// strToTxId converts from a string to a transaction ID.
-func strToTxId(txStr string) (TxId, error) {
-	tx, err := strconv.ParseUint(txStr, 10, 64)
-	if err != nil {
-		return NoTxId, err
-	}
-	return TxId(tx), nil
-}
-
-// dump writes to the log file information on all DAG entries.
-func (d *dag) dump() {
-	if d.store == nil {
-		return
-	}
-
-	// Dump the head and ancestor information for DAG objects.
-	d.heads.keyIter(func(oidStr string) {
-		oid, err := strToObjId(oidStr)
-		if err != nil {
-			return
-		}
-
-		head, err := d.getHead(oid)
-		if err != nil {
-			return
-		}
-
-		vlog.VI(1).Infof("DUMP: DAG oid %v: head %v", oid, head)
-		start := []Version{head}
-		d.ancestorIter(oid, start, func(oid ObjId, v Version, node *dagNode) error {
-			vlog.VI(1).Infof("DUMP: DAG node %v:%v: tx %v, del %t, logrec %s --> %v",
-				oid, v, node.TxId, node.Deleted, node.Logrec, node.Parents)
-			return nil
-		})
-	})
-
-	// Dump the transactions.
-	d.trans.keyIter(func(tidStr string) {
-		tid, err := strToTxId(tidStr)
-		if err != nil {
-			return
-		}
-
-		txSt, err := d.getTransaction(tid)
-		if err != nil {
-			return
-		}
-
-		vlog.VI(1).Infof("DUMP: DAG tx %v: count %d, elem %v", tid, txSt.TxCount, txSt.TxMap)
-	})
-}
diff --git a/x/ref/services/syncbase/sync/dag_test.go b/x/ref/services/syncbase/sync/dag_test.go
deleted file mode 100644
index 3ea5865..0000000
--- a/x/ref/services/syncbase/sync/dag_test.go
+++ /dev/null
@@ -1,2128 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-// Tests for the Veyron Sync DAG component.
-
-import (
-	"errors"
-	"fmt"
-	"os"
-	"reflect"
-	"testing"
-	"time"
-
-	"v.io/x/ref/lib/stats"
-)
-
-// dagFilename generates a filename for a temporary (per unit test) DAG file.
-// Do not replace this function with TempFile because TempFile creates the new
-// file and the tests must verify that the DAG can create a non-existing file.
-func dagFilename() string {
-	return fmt.Sprintf("%s/sync_dag_test_%d_%d", os.TempDir(), os.Getpid(), time.Now().UnixNano())
-}
-
-// TestDAGOpen tests the creation of a DAG, closing and re-opening it.  It also
-// verifies that its backing file is created and that a 2nd close is safe.
-func TestDAGOpen(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	fsize := getFileSize(dagfile)
-	if fsize < 0 {
-		//t.Fatalf("DAG file %s not created", dagfile)
-	}
-
-	dag.flush()
-	oldfsize := fsize
-	fsize = getFileSize(dagfile)
-	if fsize <= oldfsize {
-		//t.Fatalf("DAG file %s not flushed", dagfile)
-	}
-
-	dag.close()
-
-	dag, err = openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot re-open existing DAG file %s", dagfile)
-	}
-
-	oldfsize = fsize
-	fsize = getFileSize(dagfile)
-	if fsize != oldfsize {
-		t.Fatalf("DAG file %s size changed across re-open", dagfile)
-	}
-
-	dag.close()
-	dag.close() // multiple closes should be a safe NOP
-
-	fsize = getFileSize(dagfile)
-	if fsize != oldfsize {
-		t.Fatalf("DAG file %s size changed across close", dagfile)
-	}
-
-	// Fail opening a DAG in a non-existent directory.
-	_, err = openDAG("/not/really/there/junk.dag")
-	if err == nil {
-		//t.Fatalf("openDAG() did not fail when using a bad pathname")
-	}
-}
-
-// TestInvalidDAG tests using DAG methods on an invalid (closed) DAG.
-func TestInvalidDAG(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	dag.close()
-
-	oid, err := strToObjId("6789")
-	if err != nil {
-		t.Error(err)
-	}
-
-	validateError := func(err error, funcName string) {
-		if err == nil || err.Error() != "invalid DAG" {
-			t.Errorf("%s() did not fail on a closed DAG: %v", funcName, err)
-		}
-	}
-
-	err = dag.addNode(oid, 4, false, false, []Version{2, 3}, "foobar", NoTxId)
-	validateError(err, "addNode")
-
-	err = dag.moveHead(oid, 4)
-	validateError(err, "moveHead")
-
-	_, _, _, _, err = dag.hasConflict(oid)
-	validateError(err, "hasConflict")
-
-	_, err = dag.getLogrec(oid, 4)
-	validateError(err, "getLogrec")
-
-	err = dag.prune(oid, 4, func(lr string) error {
-		return nil
-	})
-	validateError(err, "prune")
-
-	err = dag.pruneAll(oid, func(lr string) error {
-		return nil
-	})
-	validateError(err, "pruneAll")
-
-	err = dag.pruneDone()
-	validateError(err, "pruneDone")
-
-	node := &dagNode{Level: 15, Parents: []Version{444, 555}, Logrec: "logrec-23"}
-	err = dag.setNode(oid, 4, node)
-	validateError(err, "setNode")
-
-	_, err = dag.getNode(oid, 4)
-	validateError(err, "getNode")
-
-	err = dag.delNode(oid, 4)
-	validateError(err, "delNode")
-
-	err = dag.addParent(oid, 4, 2, true)
-	validateError(err, "addParent")
-
-	err = dag.setHead(oid, 4)
-	validateError(err, "setHead")
-
-	_, err = dag.getHead(oid)
-	validateError(err, "getHead")
-
-	err = dag.delHead(oid)
-	validateError(err, "delHead")
-
-	if tid := dag.addNodeTxStart(NoTxId); tid != NoTxId {
-		t.Errorf("addNodeTxStart() did not fail on a closed DAG: TxId %v", tid)
-	}
-
-	err = dag.addNodeTxEnd(1, 1)
-	validateError(err, "addNodeTxEnd")
-
-	err = dag.setTransaction(1, nil)
-	validateError(err, "setTransaction")
-
-	_, err = dag.getTransaction(1)
-	validateError(err, "getTransaction")
-
-	err = dag.delTransaction(1)
-	validateError(err, "delTransaction")
-
-	err = dag.setPrivNode(oid, nil)
-	validateError(err, "setPrivNode")
-
-	_, err = dag.getPrivNode(oid)
-	validateError(err, "getPrivNode")
-
-	err = dag.delPrivNode(oid)
-	validateError(err, "delPrivNode")
-
-	// These calls should be harmless NOPs.
-	dag.clearGraft()
-	dag.clearTxGC()
-	dag.dump()
-	dag.flush()
-	dag.close()
-	if dag.hasNode(oid, 4) {
-		t.Errorf("hasNode() found an object on a closed DAG")
-	}
-	if dag.hasDeletedDescendant(oid, 3) {
-		t.Errorf("hasDeletedDescendant() returned true on a closed DAG")
-	}
-	if pmap := dag.getParentMap(oid); len(pmap) != 0 {
-		t.Errorf("getParentMap() found data on a closed DAG: %v", pmap)
-	}
-	if hmap, gmap := dag.getGraftNodes(oid); hmap != nil || gmap != nil {
-		t.Errorf("getGraftNodes() found data on a closed DAG: head map: %v, graft map: %v", hmap, gmap)
-	}
-}
-
-// TestSetNode tests setting and getting a DAG node across DAG open/close/reopen.
-func TestSetNode(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	version := Version(0)
-	oid, err := strToObjId("1111")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	node, err := dag.getNode(oid, version)
-	if err == nil || node != nil {
-		t.Errorf("Found non-existent object %v:%d in DAG file %s: %v", oid, version, dagfile, node)
-	}
-
-	if dag.hasNode(oid, version) {
-		t.Errorf("hasNode() found non-existent object %v:%d in DAG file %s", oid, version, dagfile)
-	}
-
-	if logrec, err := dag.getLogrec(oid, version); err == nil || logrec != "" {
-		t.Errorf("Non-existent object %v:%d has a logrec in DAG file %s: %v", oid, version, dagfile, logrec)
-	}
-
-	node = &dagNode{Level: 15, Parents: []Version{444, 555}, Logrec: "logrec-23"}
-	if err = dag.setNode(oid, version, node); err != nil {
-		t.Fatalf("Cannot set object %v:%d (%v) in DAG file %s", oid, version, node, dagfile)
-	}
-
-	for i := 0; i < 2; i++ {
-		node2, err := dag.getNode(oid, version)
-		if err != nil || node2 == nil {
-			t.Errorf("Cannot find stored object %v:%d (i=%d) in DAG file %s", oid, version, i, dagfile)
-		}
-
-		if !dag.hasNode(oid, version) {
-			t.Errorf("hasNode() did not find object %v:%d (i=%d) in DAG file %s", oid, version, i, dagfile)
-		}
-
-		if !reflect.DeepEqual(node, node2) {
-			t.Errorf("Object %v:%d has wrong data (i=%d) in DAG file %s: %v instead of %v",
-				oid, version, i, dagfile, node2, node)
-		}
-
-		if logrec, err := dag.getLogrec(oid, version); err != nil || logrec != "logrec-23" {
-			t.Errorf("Object %v:%d has wrong logrec (i=%d) in DAG file %s: %v",
-				oid, version, i, dagfile, logrec)
-		}
-
-		if i == 0 {
-			dag.flush()
-			dag.close()
-			dag, err = openDAG(dagfile)
-			if err != nil {
-				t.Fatalf("Cannot re-open DAG file %s", dagfile)
-			}
-		}
-	}
-
-	dag.close()
-}
-
-// TestDelNode tests deleting a DAG node across DAG open/close/reopen.
-func TestDelNode(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	version := Version(1)
-	oid, err := strToObjId("2222")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	node := &dagNode{Level: 123, Parents: []Version{333}, Logrec: "logrec-789"}
-	if err = dag.setNode(oid, version, node); err != nil {
-		t.Fatalf("Cannot set object %v:%d (%v) in DAG file %s", oid, version, node, dagfile)
-	}
-
-	dag.flush()
-
-	err = dag.delNode(oid, version)
-	if err != nil {
-		t.Fatalf("Cannot delete object %v:%d in DAG file %s", oid, version, dagfile)
-	}
-
-	dag.flush()
-
-	for i := 0; i < 2; i++ {
-		node2, err := dag.getNode(oid, version)
-		if err == nil || node2 != nil {
-			t.Errorf("Found deleted object %v:%d (%v) (i=%d) in DAG file %s", oid, version, node2, i, dagfile)
-		}
-
-		if dag.hasNode(oid, version) {
-			t.Errorf("hasNode() found deleted object %v:%d (i=%d) in DAG file %s", oid, version, i, dagfile)
-		}
-
-		if logrec, err := dag.getLogrec(oid, version); err == nil || logrec != "" {
-			t.Errorf("Deleted object %v:%d (i=%d) has logrec in DAG file %s: %v", oid, version, i, dagfile, logrec)
-		}
-
-		if i == 0 {
-			dag.close()
-			dag, err = openDAG(dagfile)
-			if err != nil {
-				t.Fatalf("Cannot re-open DAG file %s", dagfile)
-			}
-		}
-	}
-
-	dag.close()
-}
-
-// TestAddParent tests adding parents to a DAG node.
-func TestAddParent(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	version := Version(7)
-	oid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if err = dag.addParent(oid, version, 1, true); err == nil {
-		t.Errorf("addParent() did not fail for an unknown object %v:%d in DAG file %s", oid, version, dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-00.log.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	node := &dagNode{Level: 15, Logrec: "logrec-22"}
-	if err = dag.setNode(oid, version, node); err != nil {
-		t.Fatalf("Cannot set object %v:%d (%v) in DAG file %s", oid, version, node, dagfile)
-	}
-
-	if err = dag.addParent(oid, version, version, true); err == nil {
-		t.Errorf("addParent() did not fail on a self-parent for object %v:%d in DAG file %s", oid, version, dagfile)
-	}
-
-	for _, parent := range []Version{4, 5, 6} {
-		if err = dag.addParent(oid, version, parent, true); err == nil {
-			t.Errorf("addParent() did not reject invalid parent %d for object %v:%d in DAG file %s",
-				parent, oid, version, dagfile)
-		}
-
-		pnode := &dagNode{Level: 11, Logrec: fmt.Sprintf("logrec-%d", parent), Parents: []Version{3}}
-		if err = dag.setNode(oid, parent, pnode); err != nil {
-			t.Fatalf("Cannot set parent object %v:%d (%v) in DAG file %s", oid, parent, pnode, dagfile)
-		}
-
-		remote := parent%2 == 0
-		for i := 0; i < 2; i++ {
-			if err = dag.addParent(oid, version, parent, remote); err != nil {
-				t.Errorf("addParent() failed on parent %d, remote %t (i=%d) for object %v:%d in DAG file %s: %v",
-					parent, remote, i, oid, version, dagfile, err)
-			}
-		}
-	}
-
-	node2, err := dag.getNode(oid, version)
-	if err != nil || node2 == nil {
-		t.Errorf("Cannot find stored object %v:%d in DAG file %s", oid, version, dagfile)
-	}
-
-	expParents := []Version{4, 5, 6}
-	if !reflect.DeepEqual(node2.Parents, expParents) {
-		t.Errorf("invalid parents for object %v:%d in DAG file %s: %v instead of %v",
-			oid, version, dagfile, node2.Parents, expParents)
-	}
-
-	// Creating cycles should fail.
-	for v := Version(1); v < version; v++ {
-		if err = dag.addParent(oid, v, version, false); err == nil {
-			t.Errorf("addParent() failed to reject a cycle for object %v: from ancestor %d to node %d in DAG file %s",
-				oid, v, version, dagfile)
-		}
-	}
-
-	dag.close()
-}
-
-// TestSetHead tests setting and getting a DAG head node across DAG open/close/reopen.
-func TestSetHead(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	oid, err := strToObjId("3333")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	version, err := dag.getHead(oid)
-	if err == nil {
-		t.Errorf("Found non-existent object head %v in DAG file %s: %d", oid, dagfile, version)
-	}
-
-	version = 555
-	if err = dag.setHead(oid, version); err != nil {
-		t.Fatalf("Cannot set object head %v (%d) in DAG file %s", oid, version, dagfile)
-	}
-
-	dag.flush()
-
-	for i := 0; i < 3; i++ {
-		version2, err := dag.getHead(oid)
-		if err != nil {
-			t.Errorf("Cannot find stored object head %v (i=%d) in DAG file %s", oid, i, dagfile)
-		}
-		if version != version2 {
-			t.Errorf("Object %v has wrong head data (i=%d) in DAG file %s: %d instead of %d",
-				oid, i, dagfile, version2, version)
-		}
-
-		if i == 0 {
-			dag.close()
-			dag, err = openDAG(dagfile)
-			if err != nil {
-				t.Fatalf("Cannot re-open DAG file %s", dagfile)
-			}
-		} else if i == 1 {
-			version = 888
-			if err = dag.setHead(oid, version); err != nil {
-				t.Fatalf("Cannot set new object head %v (%d) in DAG file %s", oid, version, dagfile)
-			}
-			dag.flush()
-		}
-	}
-
-	dag.close()
-}
-
-// checkEndOfSync simulates and check the end-of-sync operations: clear the
-// node grafting metadata and verify that it is empty and that HasConflict()
-// detects this case and fails, then close the DAG.
-func checkEndOfSync(d *dag, oid ObjId) error {
-	// Clear grafting info; this happens at the end of a sync log replay.
-	d.clearGraft()
-
-	// There should be no grafting or transaction info, and hasConflict() should fail.
-	newHeads, grafts := d.getGraftNodes(oid)
-	if newHeads != nil || grafts != nil {
-		return fmt.Errorf("Object %v: graft info not cleared: newHeads (%v), grafts (%v)", oid, newHeads, grafts)
-	}
-
-	if n := len(d.txSet); n != 0 {
-		return fmt.Errorf("transaction set not empty: %d entries found", n)
-	}
-
-	isConflict, newHead, oldHead, ancestor, errConflict := d.hasConflict(oid)
-	if errConflict == nil {
-		return fmt.Errorf("Object %v: conflict did not fail: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	d.dump()
-	d.close()
-	return nil
-}
-
-// checkDAGStats verifies the DAG stats counters.
-func checkDAGStats(t *testing.T, which string, numObj, numNode, numTx, numPriv int64) {
-	if num, err := stats.Value(statsNumDagObj); err != nil || num != numObj {
-		t.Errorf("num-dag-objects (%s): got %v (err: %v) instead of %v", which, num, err, numObj)
-	}
-	if num, err := stats.Value(statsNumDagNode); err != nil || num != numNode {
-		t.Errorf("num-dag-nodes (%s): got %v (err: %v) instead of %v", which, num, err, numNode)
-	}
-	if num, err := stats.Value(statsNumDagTx); err != nil || num != numTx {
-		t.Errorf("num-dag-tx (%s): got %v (err: %v) instead of %v", which, num, err, numTx)
-	}
-	if num, err := stats.Value(statsNumDagPrivNode); err != nil || num != numPriv {
-		t.Errorf("num-dag-privnodes (%s): got %v (err: %v) instead of %v", which, num, err, numPriv)
-	}
-}
-
-// TestLocalUpdates tests the sync handling of initial local updates: an object
-// is created (v0) and updated twice (v1, v2) on this device.  The DAG should
-// show: v0 -> v1 -> v2 and the head should point to v2.
-func TestLocalUpdates(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-00.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	// The head must have moved to "v2" and the parent map shows the updated DAG.
-	oid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if head, e := dag.getHead(oid); e != nil || head != 2 {
-		t.Errorf("Invalid object %v head in DAG file %s: %d", oid, dagfile, head)
-	}
-
-	pmap := dag.getParentMap(oid)
-
-	exp := map[Version][]Version{0: nil, 1: {0}, 2: {1}}
-
-	if !reflect.DeepEqual(pmap, exp) {
-		t.Errorf("Invalid object %v parent map in DAG file %s: (%v) instead of (%v)", oid, dagfile, pmap, exp)
-	}
-
-	// Make sure an existing node cannot be added again.
-	if err = dag.addNode(oid, 1, false, false, []Version{0, 2}, "foobar", NoTxId); err == nil {
-		t.Errorf("addNode() did not fail when given an existing node")
-	}
-
-	// Make sure a new node cannot have more than 2 parents.
-	if err = dag.addNode(oid, 3, false, false, []Version{0, 1, 2}, "foobar", NoTxId); err == nil {
-		t.Errorf("addNode() did not fail when given 3 parents")
-	}
-
-	// Make sure a new node cannot have an invalid parent.
-	if err = dag.addNode(oid, 3, false, false, []Version{0, 555}, "foobar", NoTxId); err == nil {
-		t.Errorf("addNode() did not fail when using an invalid parent")
-	}
-
-	// Make sure a new root node (no parents) cannot be added once a root exists.
-	// For the parents array, check both the "nil" and the empty array as input.
-	if err = dag.addNode(oid, 6789, false, false, nil, "foobar", NoTxId); err == nil {
-		t.Errorf("Adding a 2nd root node (nil parents) for object %v in DAG file %s did not fail", oid, dagfile)
-	}
-	if err = dag.addNode(oid, 6789, false, false, []Version{}, "foobar", NoTxId); err == nil {
-		t.Errorf("Adding a 2nd root node (empty parents) for object %v in DAG file %s did not fail", oid, dagfile)
-	}
-
-	checkDAGStats(t, "local-update", 1, 3, 0, 0)
-
-	if err := checkEndOfSync(dag, oid); err != nil {
-		t.Fatal(err)
-	}
-}
-
-// TestRemoteUpdates tests the sync handling of initial remote updates:
-// an object is created (v0) and updated twice (v1, v2) on another device and
-// we learn about it during sync.  The updated DAG should show: v0 -> v1 -> v2
-// and report no conflicts with the new head pointing at v2.
-func TestRemoteUpdates(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "remote-init-00.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	// The head must not have moved (i.e. still undefined) and the parent
-	// map shows the newly grafted DAG fragment.
-	oid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if head, e := dag.getHead(oid); e == nil {
-		t.Errorf("Object %v head found in DAG file %s: %d", oid, dagfile, head)
-	}
-
-	pmap := dag.getParentMap(oid)
-
-	exp := map[Version][]Version{0: nil, 1: {0}, 2: {1}}
-
-	if !reflect.DeepEqual(pmap, exp) {
-		t.Errorf("Invalid object %v parent map in DAG file %s: (%v) instead of (%v)", oid, dagfile, pmap, exp)
-	}
-
-	// Verify the grafting of remote nodes.
-	newHeads, grafts := dag.getGraftNodes(oid)
-
-	expNewHeads := map[Version]struct{}{2: struct{}{}}
-	if !reflect.DeepEqual(newHeads, expNewHeads) {
-		t.Errorf("Object %v has invalid newHeads in DAG file %s: (%v) instead of (%v)", oid, dagfile, newHeads, expNewHeads)
-	}
-
-	expgrafts := map[Version]uint64{}
-	if !reflect.DeepEqual(grafts, expgrafts) {
-		t.Errorf("Invalid object %v graft in DAG file %s: (%v) instead of (%v)", oid, dagfile, grafts, expgrafts)
-	}
-
-	// There should be no conflict.
-	isConflict, newHead, oldHead, ancestor, errConflict := dag.hasConflict(oid)
-	if !(!isConflict && newHead == 2 && oldHead == 0 && ancestor == 0 && errConflict == nil) {
-		t.Errorf("Object %v wrong conflict info: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	if logrec, e := dag.getLogrec(oid, newHead); e != nil || logrec != "logrec-02" {
-		t.Errorf("Invalid logrec for newhead object %v:%d in DAG file %s: %v", oid, newHead, dagfile, logrec)
-	}
-
-	// Make sure an unknown node cannot become the new head.
-	if err = dag.moveHead(oid, 55); err == nil {
-		t.Errorf("moveHead() did not fail on an invalid node")
-	}
-
-	// Then we can move the head and clear the grafting data.
-	if err = dag.moveHead(oid, newHead); err != nil {
-		t.Errorf("Object %v cannot move head to %d in DAG file %s: %v", oid, newHead, dagfile, err)
-	}
-
-	checkDAGStats(t, "remote-update", 1, 3, 0, 0)
-
-	if err := checkEndOfSync(dag, oid); err != nil {
-		t.Fatal(err)
-	}
-}
-
-// TestRemoteNoConflict tests sync of remote updates on top of a local initial
-// state without conflict.  An object is created locally and updated twice
-// (v0 -> v1 -> v2).  Another device, having gotten this info, makes 3 updates
-// on top of that (v2 -> v3 -> v4 -> v5) and sends this info in a later sync.
-// The updated DAG should show (v0 -> v1 -> v2 -> v3 -> v4 -> v5) and report
-// no conflicts with the new head pointing at v5.  It should also report v2 as
-// the graft point on which the new fragment (v3 -> v4 -> v5) gets attached.
-func TestRemoteNoConflict(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-00.sync"); err != nil {
-		t.Fatal(err)
-	}
-	if err = dagReplayCommands(dag, "remote-noconf-00.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	// The head must not have moved (i.e. still at v2) and the parent map
-	// shows the newly grafted DAG fragment on top of the prior DAG.
-	oid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if head, e := dag.getHead(oid); e != nil || head != 2 {
-		t.Errorf("Object %v has wrong head in DAG file %s: %d", oid, dagfile, head)
-	}
-
-	pmap := dag.getParentMap(oid)
-
-	exp := map[Version][]Version{0: nil, 1: {0}, 2: {1}, 3: {2}, 4: {3}, 5: {4}}
-
-	if !reflect.DeepEqual(pmap, exp) {
-		t.Errorf("Invalid object %v parent map in DAG file %s: (%v) instead of (%v)", oid, dagfile, pmap, exp)
-	}
-
-	// Verify the grafting of remote nodes.
-	newHeads, grafts := dag.getGraftNodes(oid)
-
-	expNewHeads := map[Version]struct{}{5: struct{}{}}
-	if !reflect.DeepEqual(newHeads, expNewHeads) {
-		t.Errorf("Object %v has invalid newHeads in DAG file %s: (%v) instead of (%v)", oid, dagfile, newHeads, expNewHeads)
-	}
-
-	expgrafts := map[Version]uint64{2: 2}
-	if !reflect.DeepEqual(grafts, expgrafts) {
-		t.Errorf("Invalid object %v graft in DAG file %s: (%v) instead of (%v)", oid, dagfile, grafts, expgrafts)
-	}
-
-	// There should be no conflict.
-	isConflict, newHead, oldHead, ancestor, errConflict := dag.hasConflict(oid)
-	if !(!isConflict && newHead == 5 && oldHead == 2 && ancestor == 0 && errConflict == nil) {
-		t.Errorf("Object %v wrong conflict info: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	if logrec, e := dag.getLogrec(oid, oldHead); e != nil || logrec != "logrec-02" {
-		t.Errorf("Invalid logrec for oldhead object %v:%d in DAG file %s: %v", oid, oldHead, dagfile, logrec)
-	}
-	if logrec, e := dag.getLogrec(oid, newHead); e != nil || logrec != "logrec-05" {
-		t.Errorf("Invalid logrec for newhead object %v:%d in DAG file %s: %v", oid, newHead, dagfile, logrec)
-	}
-
-	// Then we can move the head and clear the grafting data.
-	if err = dag.moveHead(oid, newHead); err != nil {
-		t.Errorf("Object %v cannot move head to %d in DAG file %s: %v", oid, newHead, dagfile, err)
-	}
-
-	// Clear the grafting data and verify that hasConflict() fails without it.
-	dag.clearGraft()
-	isConflict, newHead, oldHead, ancestor, errConflict = dag.hasConflict(oid)
-	if errConflict == nil {
-		t.Errorf("hasConflict() on %v did not fail w/o graft info: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	checkDAGStats(t, "remote-noconf", 1, 6, 0, 0)
-
-	if err := checkEndOfSync(dag, oid); err != nil {
-		t.Fatal(err)
-	}
-}
-
-// TestRemoteConflict tests sync handling remote updates that build on the
-// local initial state and trigger a conflict.  An object is created locally
-// and updated twice (v0 -> v1 -> v2).  Another device, having only gotten
-// the v0 -> v1 history, makes 3 updates on top of v1 (v1 -> v3 -> v4 -> v5)
-// and sends this info during a later sync.  Separately, the local device
-// makes a conflicting (concurrent) update v1 -> v2.  The updated DAG should
-// show the branches: (v0 -> v1 -> v2) and (v0 -> v1 -> v3 -> v4 -> v5) and
-// report the conflict between v2 and v5 (current and new heads).  It should
-// also report v1 as the graft point and the common ancestor in the conflict.
-// The conflict is resolved locally by creating v6 that is derived from both
-// v2 and v5 and it becomes the new head.
-func TestRemoteConflict(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-00.sync"); err != nil {
-		t.Fatal(err)
-	}
-	if err = dagReplayCommands(dag, "remote-conf-00.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	// The head must not have moved (i.e. still at v2) and the parent map
-	// shows the newly grafted DAG fragment on top of the prior DAG.
-	oid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if head, e := dag.getHead(oid); e != nil || head != 2 {
-		t.Errorf("Object %v has wrong head in DAG file %s: %d", oid, dagfile, head)
-	}
-
-	pmap := dag.getParentMap(oid)
-
-	exp := map[Version][]Version{0: nil, 1: {0}, 2: {1}, 3: {1}, 4: {3}, 5: {4}}
-
-	if !reflect.DeepEqual(pmap, exp) {
-		t.Errorf("Invalid object %v parent map in DAG file %s: (%v) instead of (%v)", oid, dagfile, pmap, exp)
-	}
-
-	// Verify the grafting of remote nodes.
-	newHeads, grafts := dag.getGraftNodes(oid)
-
-	expNewHeads := map[Version]struct{}{2: struct{}{}, 5: struct{}{}}
-	if !reflect.DeepEqual(newHeads, expNewHeads) {
-		t.Errorf("Object %v has invalid newHeads in DAG file %s: (%v) instead of (%v)", oid, dagfile, newHeads, expNewHeads)
-	}
-
-	expgrafts := map[Version]uint64{1: 1}
-	if !reflect.DeepEqual(grafts, expgrafts) {
-		t.Errorf("Invalid object %v graft in DAG file %s: (%v) instead of (%v)", oid, dagfile, grafts, expgrafts)
-	}
-
-	// There should be a conflict between v2 and v5 with v1 as ancestor.
-	isConflict, newHead, oldHead, ancestor, errConflict := dag.hasConflict(oid)
-	if !(isConflict && newHead == 5 && oldHead == 2 && ancestor == 1 && errConflict == nil) {
-		t.Errorf("Object %v wrong conflict info: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	if logrec, e := dag.getLogrec(oid, oldHead); e != nil || logrec != "logrec-02" {
-		t.Errorf("Invalid logrec for oldhead object %v:%d in DAG file %s: %v", oid, oldHead, dagfile, logrec)
-	}
-	if logrec, e := dag.getLogrec(oid, newHead); e != nil || logrec != "logrec-05" {
-		t.Errorf("Invalid logrec for newhead object %v:%d in DAG file %s: %v", oid, newHead, dagfile, logrec)
-	}
-	if logrec, e := dag.getLogrec(oid, ancestor); e != nil || logrec != "logrec-01" {
-		t.Errorf("Invalid logrec for ancestor object %v:%d in DAG file %s: %v", oid, ancestor, dagfile, logrec)
-	}
-
-	checkDAGStats(t, "remote-conf-pre", 1, 6, 0, 0)
-
-	// Resolve the conflict by adding a new local v6 derived from v2 and v5 (this replay moves the head).
-	if err = dagReplayCommands(dag, "local-resolve-00.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	// Verify that the head moved to v6 and the parent map shows the resolution.
-	if head, e := dag.getHead(oid); e != nil || head != 6 {
-		t.Errorf("Object %v has wrong head after conflict resolution in DAG file %s: %d", oid, dagfile, head)
-	}
-
-	exp[6] = []Version{2, 5}
-	pmap = dag.getParentMap(oid)
-	if !reflect.DeepEqual(pmap, exp) {
-		t.Errorf("Invalid object %v parent map after conflict resolution in DAG file %s: (%v) instead of (%v)",
-			oid, dagfile, pmap, exp)
-	}
-
-	checkDAGStats(t, "remote-conf-post", 1, 7, 0, 0)
-
-	if err := checkEndOfSync(dag, oid); err != nil {
-		t.Fatal(err)
-	}
-}
-
-// TestRemoteConflictTwoGrafts tests sync handling remote updates that build
-// on the local initial state and trigger a conflict with 2 graft points.
-// An object is created locally and updated twice (v0 -> v1 -> v2).  Another
-// device, first learns about v0 and makes it own conflicting update v0 -> v3.
-// That remote device later learns about v1 and resolves the v1/v3 confict by
-// creating v4.  Then it makes a last v4 -> v5 update -- which will conflict
-// with v2 but it doesn't know that.
-// Now the sync order is reversed and the local device learns all of what
-// happened on the remote device.  The local DAG should get be augmented by
-// a subtree with 2 graft points: v0 and v1.  It receives this new branch:
-// v0 -> v3 -> v4 -> v5.  Note that v4 is also derived from v1 as a remote
-// conflict resolution.  This should report a conflict between v2 and v5
-// (current and new heads), with v0 and v1 as graft points, and v1 as the
-// most-recent common ancestor for that conflict.  The conflict is resolved
-// locally by creating v6, derived from both v2 and v5, becoming the new head.
-func TestRemoteConflictTwoGrafts(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-00.sync"); err != nil {
-		t.Fatal(err)
-	}
-	if err = dagReplayCommands(dag, "remote-conf-01.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	// The head must not have moved (i.e. still at v2) and the parent map
-	// shows the newly grafted DAG fragment on top of the prior DAG.
-	oid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if head, e := dag.getHead(oid); e != nil || head != 2 {
-		t.Errorf("Object %v has wrong head in DAG file %s: %d", oid, dagfile, head)
-	}
-
-	pmap := dag.getParentMap(oid)
-
-	exp := map[Version][]Version{0: nil, 1: {0}, 2: {1}, 3: {0}, 4: {1, 3}, 5: {4}}
-
-	if !reflect.DeepEqual(pmap, exp) {
-		t.Errorf("Invalid object %v parent map in DAG file %s: (%v) instead of (%v)", oid, dagfile, pmap, exp)
-	}
-
-	// Verify the grafting of remote nodes.
-	newHeads, grafts := dag.getGraftNodes(oid)
-
-	expNewHeads := map[Version]struct{}{2: struct{}{}, 5: struct{}{}}
-	if !reflect.DeepEqual(newHeads, expNewHeads) {
-		t.Errorf("Object %v has invalid newHeads in DAG file %s: (%v) instead of (%v)", oid, dagfile, newHeads, expNewHeads)
-	}
-
-	expgrafts := map[Version]uint64{0: 0, 1: 1}
-	if !reflect.DeepEqual(grafts, expgrafts) {
-		t.Errorf("Invalid object %v graft in DAG file %s: (%v) instead of (%v)", oid, dagfile, grafts, expgrafts)
-	}
-
-	// There should be a conflict between v2 and v5 with v1 as ancestor.
-	isConflict, newHead, oldHead, ancestor, errConflict := dag.hasConflict(oid)
-	if !(isConflict && newHead == 5 && oldHead == 2 && ancestor == 1 && errConflict == nil) {
-		t.Errorf("Object %v wrong conflict info: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	if logrec, e := dag.getLogrec(oid, oldHead); e != nil || logrec != "logrec-02" {
-		t.Errorf("Invalid logrec for oldhead object %v:%d in DAG file %s: %v", oid, oldHead, dagfile, logrec)
-	}
-	if logrec, e := dag.getLogrec(oid, newHead); e != nil || logrec != "logrec-05" {
-		t.Errorf("Invalid logrec for newhead object %v:%d in DAG file %s: %v", oid, newHead, dagfile, logrec)
-	}
-	if logrec, e := dag.getLogrec(oid, ancestor); e != nil || logrec != "logrec-01" {
-		t.Errorf("Invalid logrec for ancestor object %v:%d in DAG file %s: %v", oid, ancestor, dagfile, logrec)
-	}
-
-	checkDAGStats(t, "remote-conf2-pre", 1, 6, 0, 0)
-
-	// Resolve the conflict by adding a new local v6 derived from v2 and v5 (this replay moves the head).
-	if err = dagReplayCommands(dag, "local-resolve-00.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	// Verify that the head moved to v6 and the parent map shows the resolution.
-	if head, e := dag.getHead(oid); e != nil || head != 6 {
-		t.Errorf("Object %v has wrong head after conflict resolution in DAG file %s: %d", oid, dagfile, head)
-	}
-
-	exp[6] = []Version{2, 5}
-	pmap = dag.getParentMap(oid)
-	if !reflect.DeepEqual(pmap, exp) {
-		t.Errorf("Invalid object %v parent map after conflict resolution in DAG file %s: (%v) instead of (%v)",
-			oid, dagfile, pmap, exp)
-	}
-
-	checkDAGStats(t, "remote-conf2-post", 1, 7, 0, 0)
-
-	if err := checkEndOfSync(dag, oid); err != nil {
-		t.Fatal(err)
-	}
-}
-
-// TestAncestorIterator checks that the iterator goes over the correct set
-// of ancestor nodes for an object given a starting node.  It should traverse
-// reconvergent DAG branches only visiting each ancestor once:
-// v0 -> v1 -> v2 -> v4 -> v5 -> v7 -> v8
-//        |--> v3 ---|           |
-//        +--> v6 ---------------+
-// - Starting at v0 it should only cover v0.
-// - Starting at v2 it should only cover v0-v2.
-// - Starting at v5 it should only cover v0-v5.
-// - Starting at v8 it should cover all nodes (v0-v8).
-func TestAncestorIterator(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-01.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	oid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	// Loop checking the iteration behavior for different starting nodes.
-	for _, start := range []Version{0, 2, 5, 8} {
-		visitCount := make(map[Version]int)
-		err = dag.ancestorIter(oid, []Version{start},
-			func(oid ObjId, v Version, node *dagNode) error {
-				visitCount[v]++
-				return nil
-			})
-
-		// Check that all prior nodes are visited only once.
-		for i := Version(0); i < (start + 1); i++ {
-			if visitCount[i] != 1 {
-				t.Errorf("wrong visit count for iter on object %v node %d starting from node %d: %d instead of 1",
-					oid, i, start, visitCount[i])
-			}
-		}
-	}
-
-	// Make sure an error in the callback is returned through the iterator.
-	cbErr := errors.New("callback error")
-	err = dag.ancestorIter(oid, []Version{8}, func(oid ObjId, v Version, node *dagNode) error {
-		if v == 0 {
-			return cbErr
-		}
-		return nil
-	})
-	if err != cbErr {
-		t.Errorf("wrong error returned from callback: %v instead of %v", err, cbErr)
-	}
-
-	checkDAGStats(t, "ancestor-iter", 1, 9, 0, 0)
-
-	if err = checkEndOfSync(dag, oid); err != nil {
-		t.Fatal(err)
-	}
-}
-
-// TestPruning tests sync pruning of the DAG for an object with 3 concurrent
-// updates (i.e. 2 conflict resolution convergent points).  The pruning must
-// get rid of the DAG branches across the reconvergence points:
-// v0 -> v1 -> v2 -> v4 -> v5 -> v7 -> v8
-//        |--> v3 ---|           |
-//        +--> v6 ---------------+
-// By pruning at v0, nothing is deleted.
-// Then by pruning at v1, only v0 is deleted.
-// Then by pruning at v5, v1-v4 are deleted leaving v5 and "v6 -> v7 -> v8".
-// Then by pruning at v7, v5-v6 are deleted leaving "v7 -> v8".
-// Then by pruning at v8, v7 is deleted leaving v8 as the head.
-// Then by pruning again at v8 nothing changes.
-func TestPruning(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-01.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	checkDAGStats(t, "prune-init", 1, 9, 0, 0)
-
-	oid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	exp := map[Version][]Version{0: nil, 1: {0}, 2: {1}, 3: {1}, 4: {2, 3}, 5: {4}, 6: {1}, 7: {5, 6}, 8: {7}}
-
-	// Loop pruning at an invalid version (333) then at v0, v5, v8 and again at v8.
-	testVersions := []Version{333, 0, 1, 5, 7, 8, 8}
-	delCounts := []int{0, 0, 1, 4, 2, 1, 0}
-	which := "prune-snip-"
-	remain := 9
-
-	for i, version := range testVersions {
-		del := 0
-		err = dag.prune(oid, version, func(lr string) error {
-			del++
-			return nil
-		})
-
-		if i == 0 && err == nil {
-			t.Errorf("pruning non-existent object %v:%d did not fail in DAG file %s", oid, version, dagfile)
-		} else if i > 0 && err != nil {
-			t.Errorf("pruning object %v:%d failed in DAG file %s: %v", oid, version, dagfile, err)
-		}
-
-		if del != delCounts[i] {
-			t.Errorf("pruning object %v:%d deleted %d log records instead of %d", oid, version, del, delCounts[i])
-		}
-
-		which += "*"
-		remain -= del
-		checkDAGStats(t, which, 1, int64(remain), 0, 0)
-
-		if head, err := dag.getHead(oid); err != nil || head != 8 {
-			t.Errorf("Object %v has wrong head in DAG file %s: %d", oid, dagfile, head)
-		}
-
-		err = dag.pruneDone()
-		if err != nil {
-			t.Errorf("pruneDone() failed in DAG file %s: %v", dagfile, err)
-		}
-
-		// Remove pruned nodes from the expected parent map used to validate
-		// and set the parents of the pruned node to nil.
-		if version < 10 {
-			for j := Version(0); j < version; j++ {
-				delete(exp, j)
-			}
-			exp[version] = nil
-		}
-
-		pmap := dag.getParentMap(oid)
-		if !reflect.DeepEqual(pmap, exp) {
-			t.Errorf("Invalid object %v parent map in DAG file %s: (%v) instead of (%v)", oid, dagfile, pmap, exp)
-		}
-	}
-
-	checkDAGStats(t, "prune-end", 1, 1, 0, 0)
-
-	err = dag.pruneAll(oid, func(lr string) error {
-		return nil
-	})
-	if err != nil {
-		t.Errorf("pruneAll() for object %v failed in DAG file %s: %v", oid, dagfile, err)
-	}
-
-	if err = checkEndOfSync(dag, oid); err != nil {
-		t.Fatal(err)
-	}
-}
-
-// TestPruningCallbackError tests sync pruning of the DAG when the callback
-// function returns an error.  The pruning must try to delete as many nodes
-// and log records as possible and properly adjust the parent pointers of
-// the pruning node.  The object DAG is:
-// v0 -> v1 -> v2 -> v4 -> v5 -> v7 -> v8
-//        |--> v3 ---|           |
-//        +--> v6 ---------------+
-// By pruning at v8 and having the callback function fail for v3, all other
-// nodes must be deleted and only v8 remains as the head.
-func TestPruningCallbackError(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-01.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	checkDAGStats(t, "prune-cb-init", 1, 9, 0, 0)
-
-	oid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	exp := map[Version][]Version{8: nil}
-
-	// Prune at v8 with a callback function that fails for v3.
-	del, expDel := 0, 8
-	version := Version(8)
-	err = dag.prune(oid, version, func(lr string) error {
-		del++
-		if lr == "logrec-03" {
-			return fmt.Errorf("refuse to delete %s", lr)
-		}
-		return nil
-	})
-
-	if err == nil {
-		t.Errorf("pruning object %v:%d did not fail in DAG file %s", oid, version, dagfile)
-	}
-	if del != expDel {
-		t.Errorf("pruning object %v:%d deleted %d log records instead of %d", oid, version, del, expDel)
-	}
-
-	err = dag.pruneDone()
-	if err != nil {
-		t.Errorf("pruneDone() failed in DAG file %s: %v", dagfile, err)
-	}
-
-	if head, err := dag.getHead(oid); err != nil || head != 8 {
-		t.Errorf("Object %v has wrong head in DAG file %s: %d", oid, dagfile, head)
-	}
-
-	pmap := dag.getParentMap(oid)
-	if !reflect.DeepEqual(pmap, exp) {
-		t.Errorf("Invalid object %v parent map in DAG file %s: (%v) instead of (%v)", oid, dagfile, pmap, exp)
-	}
-
-	checkDAGStats(t, "prune-cb-end", 1, 1, 0, 0)
-
-	if err = checkEndOfSync(dag, oid); err != nil {
-		t.Fatal(err)
-	}
-}
-
-// TestRemoteLinkedNoConflictSameHead tests sync of remote updates that contain
-// linked nodes (conflict resolution by selecting an existing version) on top of
-// a local initial state without conflict.  An object is created locally and
-// updated twice (v1 -> v2 -> v3).  Another device has learned about v1, created
-// (v1 -> v4), then learned about (v1 -> v2) and resolved that conflict by selecting
-// v2 over v4.  Now it sends that new info (v4 and the v2/v4 link) back to the
-// original (local) device.  Instead of a v3/v4 conflict, the device sees that
-// v2 was chosen over v4 and resolves it as a no-conflict case.
-func TestRemoteLinkedNoConflictSameHead(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-00.log.sync"); err != nil {
-		t.Fatal(err)
-	}
-	if err = dagReplayCommands(dag, "remote-noconf-link-00.log.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	checkDAGStats(t, "linked-noconf", 1, 4, 0, 0)
-
-	// The head must not have moved (i.e. still at v3) and the parent map
-	// shows the newly grafted DAG fragment on top of the prior DAG.
-	oid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if head, e := dag.getHead(oid); e != nil || head != 3 {
-		t.Errorf("Object %v has wrong head in DAG file %s: %d", oid, dagfile, head)
-	}
-
-	pmap := dag.getParentMap(oid)
-
-	exp := map[Version][]Version{1: nil, 2: {1, 4}, 3: {2}, 4: {1}}
-
-	if !reflect.DeepEqual(pmap, exp) {
-		t.Errorf("Invalid object %v parent map in DAG file %s: (%v) instead of (%v)", oid, dagfile, pmap, exp)
-	}
-
-	// Verify the grafting of remote nodes.
-	newHeads, grafts := dag.getGraftNodes(oid)
-
-	expNewHeads := map[Version]struct{}{3: struct{}{}}
-	if !reflect.DeepEqual(newHeads, expNewHeads) {
-		t.Errorf("Object %v has invalid newHeads in DAG file %s: (%v) instead of (%v)", oid, dagfile, newHeads, expNewHeads)
-	}
-
-	expgrafts := map[Version]uint64{1: 0, 4: 1}
-	if !reflect.DeepEqual(grafts, expgrafts) {
-		t.Errorf("Invalid object %v graft in DAG file %s: (%v) instead of (%v)", oid, dagfile, grafts, expgrafts)
-	}
-
-	// There should be no conflict.
-	isConflict, newHead, oldHead, ancestor, errConflict := dag.hasConflict(oid)
-	if !(!isConflict && newHead == 3 && oldHead == 3 && ancestor == NoVersion && errConflict == nil) {
-		t.Errorf("Object %v wrong conflict info: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	// Clear the grafting data and verify that hasConflict() fails without it.
-	dag.clearGraft()
-	isConflict, newHead, oldHead, ancestor, errConflict = dag.hasConflict(oid)
-	if errConflict == nil {
-		t.Errorf("hasConflict() on %v did not fail w/o graft info: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	if err := checkEndOfSync(dag, oid); err != nil {
-		t.Fatal(err)
-	}
-}
-
-// TestRemoteLinkedConflict tests sync of remote updates that contain linked
-// nodes (conflict resolution by selecting an existing version) on top of a local
-// initial state triggering a local conflict.  An object is created locally and
-// updated twice (v1 -> v2 -> v3).  Another device has along the way learned about v1,
-// created (v1 -> v4), then learned about (v1 -> v2) and resolved that conflict by
-// selecting v4 over v2.  Now it sends that new info (v4 and the v4/v2 link) back
-// to the original (local) device.  The device sees a v3/v4 conflict.
-func TestRemoteLinkedConflict(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-00.log.sync"); err != nil {
-		t.Fatal(err)
-	}
-	if err = dagReplayCommands(dag, "remote-conf-link.log.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	checkDAGStats(t, "linked-conf", 1, 4, 0, 0)
-
-	// The head must not have moved (i.e. still at v2) and the parent map
-	// shows the newly grafted DAG fragment on top of the prior DAG.
-	oid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if head, e := dag.getHead(oid); e != nil || head != 3 {
-		t.Errorf("Object %v has wrong head in DAG file %s: %d", oid, dagfile, head)
-	}
-
-	pmap := dag.getParentMap(oid)
-
-	exp := map[Version][]Version{1: nil, 2: {1}, 3: {2}, 4: {1, 2}}
-
-	if !reflect.DeepEqual(pmap, exp) {
-		t.Errorf("Invalid object %v parent map in DAG file %s: (%v) instead of (%v)", oid, dagfile, pmap, exp)
-	}
-
-	// Verify the grafting of remote nodes.
-	newHeads, grafts := dag.getGraftNodes(oid)
-
-	expNewHeads := map[Version]struct{}{3: struct{}{}, 4: struct{}{}}
-	if !reflect.DeepEqual(newHeads, expNewHeads) {
-		t.Errorf("Object %v has invalid newHeads in DAG file %s: (%v) instead of (%v)", oid, dagfile, newHeads, expNewHeads)
-	}
-
-	expgrafts := map[Version]uint64{1: 0, 2: 1}
-	if !reflect.DeepEqual(grafts, expgrafts) {
-		t.Errorf("Invalid object %v graft in DAG file %s: (%v) instead of (%v)", oid, dagfile, grafts, expgrafts)
-	}
-
-	// There should be a conflict.
-	isConflict, newHead, oldHead, ancestor, errConflict := dag.hasConflict(oid)
-	if !(isConflict && newHead == 4 && oldHead == 3 && ancestor == 2 && errConflict == nil) {
-		t.Errorf("Object %v wrong conflict info: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	// Clear the grafting data and verify that hasConflict() fails without it.
-	dag.clearGraft()
-	isConflict, newHead, oldHead, ancestor, errConflict = dag.hasConflict(oid)
-	if errConflict == nil {
-		t.Errorf("hasConflict() on %v did not fail w/o graft info: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	if err := checkEndOfSync(dag, oid); err != nil {
-		t.Fatal(err)
-	}
-}
-
-// TestRemoteLinkedNoConflictNewHead tests sync of remote updates that contain
-// linked nodes (conflict resolution by selecting an existing version) on top of
-// a local initial state without conflict, but moves the head node to a new one.
-// An object is created locally and updated twice (v1 -> v2 -> v3).  Another device
-// has along the way learned about v1, created (v1 -> v4), then learned about
-// (v1 -> v2 -> v3) and resolved that conflict by selecting v4 over v3.  Now it
-// sends that new info (v4 and the v4/v3 link) back to the original (local) device.
-// The device sees that the new head v4 is "derived" from v3 thus no conflict.
-func TestRemoteLinkedConflictNewHead(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-00.log.sync"); err != nil {
-		t.Fatal(err)
-	}
-	if err = dagReplayCommands(dag, "remote-noconf-link-01.log.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	checkDAGStats(t, "linked-conf2", 1, 4, 0, 0)
-
-	// The head must not have moved (i.e. still at v2) and the parent map
-	// shows the newly grafted DAG fragment on top of the prior DAG.
-	oid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if head, e := dag.getHead(oid); e != nil || head != 3 {
-		t.Errorf("Object %v has wrong head in DAG file %s: %d", oid, dagfile, head)
-	}
-
-	pmap := dag.getParentMap(oid)
-
-	exp := map[Version][]Version{1: nil, 2: {1}, 3: {2}, 4: {1, 3}}
-
-	if !reflect.DeepEqual(pmap, exp) {
-		t.Errorf("Invalid object %v parent map in DAG file %s: (%v) instead of (%v)", oid, dagfile, pmap, exp)
-	}
-
-	// Verify the grafting of remote nodes.
-	newHeads, grafts := dag.getGraftNodes(oid)
-
-	expNewHeads := map[Version]struct{}{4: struct{}{}}
-	if !reflect.DeepEqual(newHeads, expNewHeads) {
-		t.Errorf("Object %v has invalid newHeads in DAG file %s: (%v) instead of (%v)", oid, dagfile, newHeads, expNewHeads)
-	}
-
-	expgrafts := map[Version]uint64{1: 0, 3: 2}
-	if !reflect.DeepEqual(grafts, expgrafts) {
-		t.Errorf("Invalid object %v graft in DAG file %s: (%v) instead of (%v)", oid, dagfile, grafts, expgrafts)
-	}
-
-	// There should be no conflict.
-	isConflict, newHead, oldHead, ancestor, errConflict := dag.hasConflict(oid)
-	if !(!isConflict && newHead == 4 && oldHead == 3 && ancestor == NoVersion && errConflict == nil) {
-		t.Errorf("Object %v wrong conflict info: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	// Clear the grafting data and verify that hasConflict() fails without it.
-	dag.clearGraft()
-	isConflict, newHead, oldHead, ancestor, errConflict = dag.hasConflict(oid)
-	if errConflict == nil {
-		t.Errorf("hasConflict() on %v did not fail w/o graft info: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	if err := checkEndOfSync(dag, oid); err != nil {
-		t.Fatal(err)
-	}
-}
-
-// TestRemoteLinkedNoConflictNewHeadOvertake tests sync of remote updates that
-// contain linked nodes (conflict resolution by selecting an existing version)
-// on top of a local initial state without conflict, but moves the head node
-// to a new one that overtook the linked node.
-// An object is created locally and updated twice (v1 -> v2 -> v3).  Another
-// device has along the way learned about v1, created (v1 -> v4), then learned
-// about (v1 -> v2 -> v3) and resolved that conflict by selecting v3 over v4.
-// Then it creates a new update v5 from v3 (v3 -> v5).  Now it sends that new
-// info (v4, the v3/v4 link, and v5) back to the original (local) device.
-// The device sees that the new head v5 is "derived" from v3 thus no conflict.
-func TestRemoteLinkedConflictNewHeadOvertake(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-00.log.sync"); err != nil {
-		t.Fatal(err)
-	}
-	if err = dagReplayCommands(dag, "remote-noconf-link-02.log.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	checkDAGStats(t, "linked-conf3-pre", 1, 5, 0, 0)
-
-	// The head must not have moved (i.e. still at v2) and the parent map
-	// shows the newly grafted DAG fragment on top of the prior DAG.
-	oid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if head, e := dag.getHead(oid); e != nil || head != 3 {
-		t.Errorf("Object %v has wrong head in DAG file %s: %d", oid, dagfile, head)
-	}
-
-	pmap := dag.getParentMap(oid)
-
-	exp := map[Version][]Version{1: nil, 2: {1}, 3: {2, 4}, 4: {1}, 5: {3}}
-
-	if !reflect.DeepEqual(pmap, exp) {
-		t.Errorf("Invalid object %v parent map in DAG file %s: (%v) instead of (%v)", oid, dagfile, pmap, exp)
-	}
-
-	// Verify the grafting of remote nodes.
-	newHeads, grafts := dag.getGraftNodes(oid)
-
-	expNewHeads := map[Version]struct{}{5: struct{}{}}
-	if !reflect.DeepEqual(newHeads, expNewHeads) {
-		t.Errorf("Object %v has invalid newHeads in DAG file %s: (%v) instead of (%v)", oid, dagfile, newHeads, expNewHeads)
-	}
-
-	expgrafts := map[Version]uint64{1: 0, 3: 2, 4: 1}
-	if !reflect.DeepEqual(grafts, expgrafts) {
-		t.Errorf("Invalid object %v graft in DAG file %s: (%v) instead of (%v)", oid, dagfile, grafts, expgrafts)
-	}
-
-	// There should be no conflict.
-	isConflict, newHead, oldHead, ancestor, errConflict := dag.hasConflict(oid)
-	if !(!isConflict && newHead == 5 && oldHead == 3 && ancestor == NoVersion && errConflict == nil) {
-		t.Errorf("Object %v wrong conflict info: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	// Then we can move the head and clear the grafting data.
-	if err = dag.moveHead(oid, newHead); err != nil {
-		t.Errorf("Object %v cannot move head to %d in DAG file %s: %v", oid, newHead, dagfile, err)
-	}
-
-	// Clear the grafting data and verify that hasConflict() fails without it.
-	dag.clearGraft()
-	isConflict, newHead, oldHead, ancestor, errConflict = dag.hasConflict(oid)
-	if errConflict == nil {
-		t.Errorf("hasConflict() on %v did not fail w/o graft info: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	// Now new info comes from another device repeating the v2/v3 link.
-	// Verify that it is a NOP (no changes).
-	if err = dagReplayCommands(dag, "remote-noconf-link-repeat.log.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	if head, e := dag.getHead(oid); e != nil || head != 5 {
-		t.Errorf("Object %v has wrong head in DAG file %s: %d", oid, dagfile, head)
-	}
-
-	newHeads, grafts = dag.getGraftNodes(oid)
-	if !reflect.DeepEqual(newHeads, expNewHeads) {
-		t.Errorf("Object %v has invalid newHeads in DAG file %s: (%v) instead of (%v)", oid, dagfile, newHeads, expNewHeads)
-	}
-
-	expgrafts = map[Version]uint64{}
-	if !reflect.DeepEqual(grafts, expgrafts) {
-		t.Errorf("Invalid object %v graft in DAG file %s: (%v) instead of (%v)", oid, dagfile, grafts, expgrafts)
-	}
-
-	isConflict, newHead, oldHead, ancestor, errConflict = dag.hasConflict(oid)
-	if !(!isConflict && newHead == 5 && oldHead == 5 && ancestor == NoVersion && errConflict == nil) {
-		t.Errorf("Object %v wrong conflict info: flag %t, newHead %d, oldHead %d, ancestor %d, err %v",
-			oid, isConflict, newHead, oldHead, ancestor, errConflict)
-	}
-
-	checkDAGStats(t, "linked-conf3-post", 1, 5, 0, 0)
-
-	if err := checkEndOfSync(dag, oid); err != nil {
-		t.Fatal(err)
-	}
-}
-
-// TestAddNodeTransactional tests adding multiple DAG nodes grouped within a transaction.
-func TestAddNodeTransactional(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-02.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	checkDAGStats(t, "add-tx-init", 3, 5, 0, 0)
-
-	oid_a, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-	oid_b, err := strToObjId("6789")
-	if err != nil {
-		t.Fatal(err)
-	}
-	oid_c, err := strToObjId("2222")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	// Verify NoTxId is reported as an error.
-	if err := dag.addNodeTxEnd(NoTxId, 0); err == nil {
-		t.Errorf("addNodeTxEnd() did not fail for invalid 'NoTxId' value")
-	}
-	if _, err := dag.getTransaction(NoTxId); err == nil {
-		t.Errorf("getTransaction() did not fail for invalid 'NoTxId' value")
-	}
-	if err := dag.setTransaction(NoTxId, nil); err == nil {
-		t.Errorf("setTransaction() did not fail for invalid 'NoTxId' value")
-	}
-	if err := dag.delTransaction(NoTxId); err == nil {
-		t.Errorf("delTransaction() did not fail for invalid 'NoTxId' value")
-	}
-
-	// Mutate 2 objects within a transaction.
-	tid_1 := dag.addNodeTxStart(NoTxId)
-	if tid_1 == NoTxId {
-		t.Fatal("Cannot start 1st DAG addNode() transaction")
-	}
-	if err := dag.addNodeTxEnd(tid_1, 0); err == nil {
-		t.Errorf("addNodeTxEnd() did not fail for a zero-count transaction")
-	}
-
-	txSt, ok := dag.txSet[tid_1]
-	if !ok {
-		t.Errorf("Transactions state for Tx ID %v not found in DAG file %s", tid_1, dagfile)
-	}
-	if n := len(txSt.TxMap); n != 0 {
-		t.Errorf("Transactions map for Tx ID %v has length %d instead of 0 in DAG file %s", tid_1, n, dagfile)
-	}
-
-	if err := dag.addNode(oid_a, 3, false, false, []Version{2}, "logrec-a-03", tid_1); err != nil {
-		t.Errorf("Cannot addNode() on object %v and Tx ID %v in DAG file %s: %v", oid_a, tid_1, dagfile, err)
-	}
-
-	if tTmp := dag.addNodeTxStart(tid_1); tTmp != tid_1 {
-		t.Fatal("restarting transaction failed")
-	}
-
-	if err := dag.addNode(oid_b, 3, false, false, []Version{2}, "logrec-b-03", tid_1); err != nil {
-		t.Errorf("Cannot addNode() on object %v and Tx ID %v in DAG file %s: %v", oid_b, tid_1, dagfile, err)
-	}
-
-	// At the same time mutate the 3rd object in another transaction.
-	tid_2 := dag.addNodeTxStart(NoTxId)
-	if tid_2 == NoTxId {
-		t.Fatal("Cannot start 2nd DAG addNode() transaction")
-	}
-
-	txSt, ok = dag.txSet[tid_2]
-	if !ok {
-		t.Errorf("Transactions state for Tx ID %v not found in DAG file %s", tid_2, dagfile)
-	}
-	if n := len(txSt.TxMap); n != 0 {
-		t.Errorf("Transactions map for Tx ID %v has length %d instead of 0 in DAG file %s", tid_2, n, dagfile)
-	}
-
-	if err := dag.addNode(oid_c, 2, false, false, []Version{1}, "logrec-c-02", tid_2); err != nil {
-		t.Errorf("Cannot addNode() on object %v and Tx ID %v in DAG file %s: %v", oid_c, tid_2, dagfile, err)
-	}
-
-	// Verify the in-memory transaction sets constructed.
-	txSt, ok = dag.txSet[tid_1]
-	if !ok {
-		t.Errorf("Transactions state for Tx ID %v not found in DAG file %s", tid_1, dagfile)
-	}
-	expTxSt := &dagTxState{dagTxMap{oid_a: 3, oid_b: 3}, 0}
-	if !reflect.DeepEqual(txSt, expTxSt) {
-		t.Errorf("Invalid transaction state for Tx ID %v in DAG file %s: %v instead of %v", tid_1, dagfile, txSt, expTxSt)
-	}
-
-	txSt, ok = dag.txSet[tid_2]
-	if !ok {
-		t.Errorf("Transactions state for Tx ID %v not found in DAG file %s", tid_2, dagfile)
-	}
-	expTxSt = &dagTxState{dagTxMap{oid_c: 2}, 0}
-	if !reflect.DeepEqual(txSt, expTxSt) {
-		t.Errorf("Invalid transaction state for Tx ID %v in DAG file %s: %v instead of %v", tid_2, dagfile, txSt, expTxSt)
-	}
-
-	// Verify failing to use a Tx ID not returned by addNodeTxStart().
-	bad_tid := tid_1 + 1
-	for bad_tid == NoTxId || bad_tid == tid_2 {
-		bad_tid++
-	}
-
-	if err := dag.addNode(oid_c, 3, false, false, []Version{2}, "logrec-c-03", bad_tid); err == nil {
-		t.Errorf("addNode() did not fail on object %v for a bad Tx ID %v in DAG file %s", oid_c, bad_tid, dagfile)
-	}
-	if err := dag.addNodeTxEnd(bad_tid, 1); err == nil {
-		t.Errorf("addNodeTxEnd() did not fail for a bad Tx ID %v in DAG file %s", bad_tid, dagfile)
-	}
-
-	// End the 1st transaction and verify the in-memory and in-DAG data.
-	if err := dag.addNodeTxEnd(tid_1, 2); err != nil {
-		t.Errorf("Cannot addNodeTxEnd() for Tx ID %v in DAG file %s: %v", tid_1, dagfile, err)
-	}
-
-	checkDAGStats(t, "add-tx-1", 3, 8, 1, 0)
-
-	if _, ok = dag.txSet[tid_1]; ok {
-		t.Errorf("Transactions state for Tx ID %v still exists in DAG file %s", tid_1, dagfile)
-	}
-
-	txSt, err = dag.getTransaction(tid_1)
-	if err != nil {
-		t.Errorf("Cannot getTransaction() for Tx ID %v in DAG file %s: %v", tid_1, dagfile, err)
-	}
-
-	expTxSt = &dagTxState{dagTxMap{oid_a: 3, oid_b: 3}, 2}
-	if !reflect.DeepEqual(txSt, expTxSt) {
-		t.Errorf("Invalid transaction state from DAG storage for Tx ID %v in DAG file %s: %v instead of %v",
-			tid_1, dagfile, txSt, expTxSt)
-	}
-
-	txSt, ok = dag.txSet[tid_2]
-	if !ok {
-		t.Errorf("Transactions state for Tx ID %v not found in DAG file %s", tid_2, dagfile)
-	}
-
-	expTxSt = &dagTxState{dagTxMap{oid_c: 2}, 0}
-	if !reflect.DeepEqual(txSt, expTxSt) {
-		t.Errorf("Invalid transaction state for Tx ID %v in DAG file %s: %v instead of %v", tid_2, dagfile, txSt, expTxSt)
-	}
-
-	// End the 2nd transaction and re-verify the in-memory and in-DAG data.
-	if err := dag.addNodeTxEnd(tid_2, 1); err != nil {
-		t.Errorf("Cannot addNodeTxEnd() for Tx ID %v in DAG file %s: %v", tid_2, dagfile, err)
-	}
-
-	checkDAGStats(t, "add-tx-2", 3, 8, 2, 0)
-
-	if _, ok = dag.txSet[tid_2]; ok {
-		t.Errorf("Transactions state for Tx ID %v still exists in DAG file %s", tid_2, dagfile)
-	}
-
-	txSt, err = dag.getTransaction(tid_2)
-	if err != nil {
-		t.Errorf("Cannot getTransaction() for Tx ID %v in DAG file %s: %v", tid_2, dagfile, err)
-	}
-
-	expTxSt = &dagTxState{dagTxMap{oid_c: 2}, 1}
-	if !reflect.DeepEqual(txSt, expTxSt) {
-		t.Errorf("Invalid transaction state for Tx ID %v in DAG file %s: %v instead of %v", tid_2, dagfile, txSt, expTxSt)
-	}
-
-	if n := len(dag.txSet); n != 0 {
-		t.Errorf("Transaction sets in-memory: %d entries found, should be empty in DAG file %s", n, dagfile)
-	}
-
-	// Test incrementally filling up a transaction.
-	tid_3 := TxId(100)
-	if _, ok = dag.txSet[tid_3]; ok {
-		t.Errorf("Transactions state for Tx ID %v found in DAG file %s", tid_3, dagfile)
-	}
-
-	if tTmp := dag.addNodeTxStart(tid_3); tTmp != tid_3 {
-		t.Fatalf("Cannot start transaction %v", tid_3)
-	}
-
-	txSt, ok = dag.txSet[tid_3]
-	if !ok {
-		t.Errorf("Transactions state for Tx ID %v not found in DAG file %s", tid_3, dagfile)
-	}
-	if n := len(txSt.TxMap); n != 0 {
-		t.Errorf("Transactions map for Tx ID %v has length %d instead of 0 in DAG file %s", tid_3, n, dagfile)
-	}
-
-	if err := dag.addNode(oid_a, 4, false, false, []Version{3}, "logrec-a-04", tid_3); err != nil {
-		t.Errorf("Cannot addNode() on object %v and Tx ID %v in DAG file %s: %v", oid_a, tid_3, dagfile, err)
-	}
-
-	if err := dag.addNodeTxEnd(tid_3, 2); err != nil {
-		t.Errorf("Cannot addNodeTxEnd() for Tx ID %v in DAG file %s: %v", tid_3, dagfile, err)
-	}
-
-	checkDAGStats(t, "add-tx-3", 3, 9, 3, 0)
-
-	if _, ok = dag.txSet[tid_3]; ok {
-		t.Errorf("Transactions state for Tx ID %v still exists in DAG file %s", tid_3, dagfile)
-	}
-
-	txSt, err = dag.getTransaction(tid_3)
-	if err != nil {
-		t.Errorf("Cannot getTransaction() for Tx ID %v in DAG file %s: %v", tid_3, dagfile, err)
-	}
-
-	expTxSt = &dagTxState{dagTxMap{oid_a: 4}, 2}
-	if !reflect.DeepEqual(txSt, expTxSt) {
-		t.Errorf("Invalid transaction state from DAG storage for Tx ID %v in DAG file %s: %v instead of %v",
-			tid_3, dagfile, txSt, expTxSt)
-	}
-
-	if tTmp := dag.addNodeTxStart(tid_3); tTmp != tid_3 {
-		t.Fatalf("Cannot start transaction %v", tid_3)
-	}
-
-	txSt, ok = dag.txSet[tid_3]
-	if !ok {
-		t.Errorf("Transactions state for Tx ID %v not found in DAG file %s", tid_3, dagfile)
-	}
-
-	if !reflect.DeepEqual(txSt, expTxSt) {
-		t.Errorf("Invalid transaction state from DAG storage for Tx ID %v in DAG file %s: %v instead of %v",
-			tid_3, dagfile, txSt, expTxSt)
-	}
-
-	if err := dag.addNode(oid_b, 4, false, false, []Version{3}, "logrec-b-04", tid_3); err != nil {
-		t.Errorf("Cannot addNode() on object %v and Tx ID %v in DAG file %s: %v", oid_b, tid_3, dagfile, err)
-	}
-
-	if err := dag.addNodeTxEnd(tid_3, 3); err == nil {
-		t.Errorf("addNodeTxEnd() didn't fail for Tx ID %v in DAG file %s: %v", tid_3, dagfile, err)
-	}
-
-	if err := dag.addNodeTxEnd(tid_3, 2); err != nil {
-		t.Errorf("Cannot addNodeTxEnd() for Tx ID %v in DAG file %s: %v", tid_3, dagfile, err)
-	}
-
-	checkDAGStats(t, "add-tx-4", 3, 10, 3, 0)
-
-	txSt, err = dag.getTransaction(tid_3)
-	if err != nil {
-		t.Errorf("Cannot getTransaction() for Tx ID %v in DAG file %s: %v", tid_3, dagfile, err)
-	}
-
-	expTxSt = &dagTxState{dagTxMap{oid_a: 4, oid_b: 4}, 2}
-	if !reflect.DeepEqual(txSt, expTxSt) {
-		t.Errorf("Invalid transaction state from DAG storage for Tx ID %v in DAG file %s: %v instead of %v",
-			tid_3, dagfile, txSt, expTxSt)
-	}
-
-	// Get the 3 new nodes from the DAG and verify their Tx IDs.
-	node, err := dag.getNode(oid_a, 3)
-	if err != nil {
-		t.Errorf("Cannot find object %v:3 in DAG file %s: %v", oid_a, dagfile, err)
-	}
-	if node.TxId != tid_1 {
-		t.Errorf("Invalid TxId for object %v:3 in DAG file %s: %v instead of %v", oid_a, dagfile, node.TxId, tid_1)
-	}
-	node, err = dag.getNode(oid_a, 4)
-	if err != nil {
-		t.Errorf("Cannot find object %v:4 in DAG file %s: %v", oid_a, dagfile, err)
-	}
-	if node.TxId != tid_3 {
-		t.Errorf("Invalid TxId for object %v:4 in DAG file %s: %v instead of %v", oid_a, dagfile, node.TxId, tid_3)
-	}
-	node, err = dag.getNode(oid_b, 3)
-	if err != nil {
-		t.Errorf("Cannot find object %v:3 in DAG file %s: %v", oid_b, dagfile, err)
-	}
-	if node.TxId != tid_1 {
-		t.Errorf("Invalid TxId for object %v:3 in DAG file %s: %v instead of %v", oid_b, dagfile, node.TxId, tid_1)
-	}
-	node, err = dag.getNode(oid_b, 4)
-	if err != nil {
-		t.Errorf("Cannot find object %v:4 in DAG file %s: %v", oid_b, dagfile, err)
-	}
-	if node.TxId != tid_3 {
-		t.Errorf("Invalid TxId for object %v:4 in DAG file %s: %v instead of %v", oid_b, dagfile, node.TxId, tid_3)
-	}
-	node, err = dag.getNode(oid_c, 2)
-	if err != nil {
-		t.Errorf("Cannot find object %v:2 in DAG file %s: %v", oid_c, dagfile, err)
-	}
-	if node.TxId != tid_2 {
-		t.Errorf("Invalid TxId for object %v:2 in DAG file %s: %v instead of %v", oid_c, dagfile, node.TxId, tid_2)
-	}
-
-	for _, oid := range []ObjId{oid_a, oid_b, oid_c} {
-		if err := checkEndOfSync(dag, oid); err != nil {
-			t.Fatal(err)
-		}
-	}
-}
-
-// TestPruningTransactions tests pruning DAG nodes grouped within transactions.
-func TestPruningTransactions(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-02.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	checkDAGStats(t, "prune-tx-init", 3, 5, 0, 0)
-
-	oid_a, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-	oid_b, err := strToObjId("6789")
-	if err != nil {
-		t.Fatal(err)
-	}
-	oid_c, err := strToObjId("2222")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	// Mutate objects in 2 transactions then add non-transactional mutations
-	// to act as the pruning points.  Before pruning the DAG is:
-	// a1 -- a2 -- (a3) --- a4
-	// b1 -- b2 -- (b3) -- (b4) -- b5
-	// c1 ---------------- (c2)
-	// Now by pruning at (a4, b5, c2), the new DAG should be:
-	// a4
-	// b5
-	// (c2)
-	// Transaction 1 (a3, b3) gets deleted, but transaction 2 (b4, c2) still
-	// has (c2) dangling waiting for a future pruning.
-	tid_1 := dag.addNodeTxStart(NoTxId)
-	if tid_1 == NoTxId {
-		t.Fatal("Cannot start 1st DAG addNode() transaction")
-	}
-	if err := dag.addNode(oid_a, 3, false, false, []Version{2}, "logrec-a-03", tid_1); err != nil {
-		t.Errorf("Cannot addNode() on object %v and Tx ID %v in DAG file %s: %v", oid_a, tid_1, dagfile, err)
-	}
-	if err := dag.addNode(oid_b, 3, false, false, []Version{2}, "logrec-b-03", tid_1); err != nil {
-		t.Errorf("Cannot addNode() on object %v and Tx ID %v in DAG file %s: %v", oid_b, tid_1, dagfile, err)
-	}
-	if err := dag.addNodeTxEnd(tid_1, 2); err != nil {
-		t.Errorf("Cannot addNodeTxEnd() for Tx ID %v in DAG file %s: %v", tid_1, dagfile, err)
-	}
-
-	checkDAGStats(t, "prune-tx-1", 3, 7, 1, 0)
-
-	tid_2 := dag.addNodeTxStart(NoTxId)
-	if tid_2 == NoTxId {
-		t.Fatal("Cannot start 2nd DAG addNode() transaction")
-	}
-	if err := dag.addNode(oid_b, 4, false, false, []Version{3}, "logrec-b-04", tid_2); err != nil {
-		t.Errorf("Cannot addNode() on object %v and Tx ID %v in DAG file %s: %v", oid_b, tid_2, dagfile, err)
-	}
-	if err := dag.addNode(oid_c, 2, false, false, []Version{1}, "logrec-c-02", tid_2); err != nil {
-		t.Errorf("Cannot addNode() on object %v and Tx ID %v in DAG file %s: %v", oid_c, tid_2, dagfile, err)
-	}
-	if err := dag.addNodeTxEnd(tid_2, 2); err != nil {
-		t.Errorf("Cannot addNodeTxEnd() for Tx ID %v in DAG file %s: %v", tid_2, dagfile, err)
-	}
-
-	checkDAGStats(t, "prune-tx-2", 3, 9, 2, 0)
-
-	if err := dag.addNode(oid_a, 4, false, false, []Version{3}, "logrec-a-04", NoTxId); err != nil {
-		t.Errorf("Cannot addNode() on object %v and Tx ID %v in DAG file %s: %v", oid_a, tid_1, dagfile, err)
-	}
-	if err := dag.addNode(oid_b, 5, false, false, []Version{4}, "logrec-b-05", NoTxId); err != nil {
-		t.Errorf("Cannot addNode() on object %v and Tx ID %v in DAG file %s: %v", oid_b, tid_2, dagfile, err)
-	}
-
-	if err = dag.moveHead(oid_a, 4); err != nil {
-		t.Errorf("Object %v cannot move head in DAG file %s: %v", oid_a, dagfile, err)
-	}
-	if err = dag.moveHead(oid_b, 5); err != nil {
-		t.Errorf("Object %v cannot move head in DAG file %s: %v", oid_b, dagfile, err)
-	}
-	if err = dag.moveHead(oid_c, 2); err != nil {
-		t.Errorf("Object %v cannot move head in DAG file %s: %v", oid_c, dagfile, err)
-	}
-
-	checkDAGStats(t, "prune-tx-3", 3, 11, 2, 0)
-
-	// Verify the transaction sets.
-	txSt, err := dag.getTransaction(tid_1)
-	if err != nil {
-		t.Errorf("Cannot getTransaction() for Tx ID %v in DAG file %s: %v", tid_1, dagfile, err)
-	}
-
-	expTxSt := &dagTxState{dagTxMap{oid_a: 3, oid_b: 3}, 2}
-	if !reflect.DeepEqual(txSt, expTxSt) {
-		t.Errorf("Invalid transaction state from DAG storage for Tx ID %v in DAG file %s: %v instead of %v",
-			tid_1, dagfile, txSt, expTxSt)
-	}
-
-	txSt, err = dag.getTransaction(tid_2)
-	if err != nil {
-		t.Errorf("Cannot getTransaction() for Tx ID %v in DAG file %s: %v", tid_2, dagfile, err)
-	}
-
-	expTxSt = &dagTxState{dagTxMap{oid_b: 4, oid_c: 2}, 2}
-	if !reflect.DeepEqual(txSt, expTxSt) {
-		t.Errorf("Invalid transaction state for Tx ID %v in DAG file %s: %v instead of %v", tid_2, dagfile, txSt, expTxSt)
-	}
-
-	// Prune the 3 objects at their head nodes.
-	for _, oid := range []ObjId{oid_a, oid_b, oid_c} {
-		head, err := dag.getHead(oid)
-		if err != nil {
-			t.Errorf("Cannot getHead() on object %v in DAG file %s: %v", oid, dagfile, err)
-		}
-		err = dag.prune(oid, head, func(lr string) error {
-			return nil
-		})
-		if err != nil {
-			t.Errorf("Cannot prune() on object %v in DAG file %s: %v", oid, dagfile, err)
-		}
-	}
-
-	if err = dag.pruneDone(); err != nil {
-		t.Errorf("pruneDone() failed in DAG file %s: %v", dagfile, err)
-	}
-
-	if n := len(dag.txGC); n != 0 {
-		t.Errorf("Transaction GC map not empty after pruneDone() in DAG file %s: %d", dagfile, n)
-	}
-
-	// Verify that Tx-1 was deleted and Tx-2 still has c2 in it.
-	checkDAGStats(t, "prune-tx-4", 3, 3, 1, 0)
-
-	txSt, err = dag.getTransaction(tid_1)
-	if err == nil {
-		t.Errorf("getTransaction() did not fail for Tx ID %v in DAG file %s: %v", tid_1, dagfile, txSt)
-	}
-
-	txSt, err = dag.getTransaction(tid_2)
-	if err != nil {
-		t.Errorf("Cannot getTransaction() for Tx ID %v in DAG file %s: %v", tid_2, dagfile, err)
-	}
-
-	expTxSt = &dagTxState{dagTxMap{oid_c: 2}, 2}
-	if !reflect.DeepEqual(txSt, expTxSt) {
-		t.Errorf("Invalid transaction state for Tx ID %v in DAG file %s: %v instead of %v", tid_2, dagfile, txSt, expTxSt)
-	}
-
-	// Add c3 as a new head and prune at that point.  This should GC Tx-2.
-	if err := dag.addNode(oid_c, 3, false, false, []Version{2}, "logrec-c-03", NoTxId); err != nil {
-		t.Errorf("Cannot addNode() on object %v in DAG file %s: %v", oid_c, dagfile, err)
-	}
-	if err = dag.moveHead(oid_c, 3); err != nil {
-		t.Errorf("Object %v cannot move head in DAG file %s: %v", oid_c, dagfile, err)
-	}
-
-	checkDAGStats(t, "prune-tx-5", 3, 4, 1, 0)
-
-	err = dag.prune(oid_c, 3, func(lr string) error {
-		return nil
-	})
-	if err != nil {
-		t.Errorf("Cannot prune() on object %v in DAG file %s: %v", oid_c, dagfile, err)
-	}
-	if err = dag.pruneDone(); err != nil {
-		t.Errorf("pruneDone() #2 failed in DAG file %s: %v", dagfile, err)
-	}
-	if n := len(dag.txGC); n != 0 {
-		t.Errorf("Transaction GC map not empty after pruneDone() in DAG file %s: %d", dagfile, n)
-	}
-
-	checkDAGStats(t, "prune-tx-6", 3, 3, 0, 0)
-
-	txSt, err = dag.getTransaction(tid_2)
-	if err == nil {
-		t.Errorf("getTransaction() did not fail for Tx ID %v in DAG file %s: %v", tid_2, dagfile, txSt)
-	}
-
-	for _, oid := range []ObjId{oid_a, oid_b, oid_c} {
-		if err := checkEndOfSync(dag, oid); err != nil {
-			t.Fatal(err)
-		}
-	}
-}
-
-// TestHasDeletedDescendant tests lookup of DAG deleted nodes descending from a given node.
-func TestHasDeletedDescendant(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	if err = dagReplayCommands(dag, "local-init-03.sync"); err != nil {
-		t.Fatal(err)
-	}
-
-	oid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	// Delete node v3 to create a dangling parent link from v7 (increase code coverage).
-	if err = dag.delNode(oid, 3); err != nil {
-		t.Errorf("cannot delete node %v:3 in DAG file %s: %v", oid, dagfile, err)
-	}
-
-	type hasDelDescTest struct {
-		node   Version
-		result bool
-	}
-	tests := []hasDelDescTest{
-		{NoVersion, false},
-		{999, false},
-		{1, true},
-		{2, true},
-		{3, false},
-		{4, false},
-		{5, false},
-		{6, false},
-		{7, false},
-		{8, false},
-	}
-
-	for _, test := range tests {
-		result := dag.hasDeletedDescendant(oid, test.node)
-		if result != test.result {
-			t.Errorf("hasDeletedDescendant() for node %d in DAG file %s: %v instead of %v",
-				test.node, dagfile, result, test.result)
-		}
-	}
-
-	dag.close()
-}
-
-// TestPrivNode tests access to the private nodes table in a DAG.
-func TestPrivNode(t *testing.T) {
-	dagfile := dagFilename()
-	defer os.Remove(dagfile)
-
-	dag, err := openDAG(dagfile)
-	if err != nil {
-		t.Fatalf("Cannot open new DAG file %s", dagfile)
-	}
-
-	oid, err := strToObjId("2222")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	priv, err := dag.getPrivNode(oid)
-	if err == nil || priv != nil {
-		t.Errorf("Found non-existing private object %v in DAG file %s: %v, err %v", oid, dagfile, priv, err)
-	}
-
-	priv = &privNode{
-		//Mutation: &raw.Mutation{ID: oid, PriorVersion: 0x0, Version: 0x55104dc76695721d, Value: "value-foobar"},
-		PathIDs: []ObjId{oid, ObjId("haha"), ObjId("foobar")},
-		TxId:    56789,
-	}
-
-	if err = dag.setPrivNode(oid, priv); err != nil {
-		t.Fatalf("Cannot set private object %v (%v) in DAG file %s: %v", oid, priv, dagfile, err)
-	}
-
-	checkDAGStats(t, "priv-1", 0, 0, 0, 1)
-
-	priv2, err := dag.getPrivNode(oid)
-	if err != nil {
-		t.Fatalf("Cannot get private object %v from DAG file %s: %v", oid, dagfile, err)
-	}
-	if !reflect.DeepEqual(priv2, priv) {
-		t.Errorf("Private object %v has wrong data in DAG file %s: %v instead of %v", oid, dagfile, priv2, priv)
-	}
-
-	//priv.Mutation.PriorVersion = priv.Mutation.Version
-	//priv.Mutation.Version = 0x55555ddddd345abc
-	//priv.Mutation.Value = "value-new"
-	priv.TxId = 98765
-
-	if err = dag.setPrivNode(oid, priv); err != nil {
-		t.Fatalf("Cannot overwrite private object %v (%v) in DAG file %s: %v", oid, priv, dagfile, err)
-	}
-
-	checkDAGStats(t, "priv-1", 0, 0, 0, 1)
-
-	priv2, err = dag.getPrivNode(oid)
-	if err != nil {
-		t.Fatalf("Cannot get updated private object %v from DAG file %s: %v", oid, dagfile, err)
-	}
-	if !reflect.DeepEqual(priv2, priv) {
-		t.Errorf("Private object %v has wrong data post-update in DAG file %s: %v instead of %v", oid, dagfile, priv2, priv)
-	}
-
-	err = dag.delPrivNode(oid)
-	if err != nil {
-		t.Fatalf("Cannot delete private object %v in DAG file %s: %v", oid, dagfile, err)
-	}
-
-	checkDAGStats(t, "priv-1", 0, 0, 0, 0)
-
-	priv3, err := dag.getPrivNode(oid)
-	if err == nil || priv3 != nil {
-		t.Errorf("Found deleted private object %v in DAG file %s: %v, err %v", oid, dagfile, priv3, err)
-	}
-
-	dag.close()
-}
diff --git a/x/ref/services/syncbase/sync/devtable.go b/x/ref/services/syncbase/sync/devtable.go
deleted file mode 100644
index 9a463a1..0000000
--- a/x/ref/services/syncbase/sync/devtable.go
+++ /dev/null
@@ -1,546 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-// Package vsync provides veyron sync DevTable utility functions.
-// DevTable is indexed by the device id and stores device level
-// information needed by sync.  Main component of a device's info are
-// a set of generation vectors: one per SyncRoot. Generation vector
-// is the version vector for a device's view of a SyncRoot,
-// representing all the different generations (from different devices)
-// seen by that device for that SyncRoot. A generation represents a
-// collection of updates that originated on a device during an
-// interval of time. It serves as a checkpoint when communicating with
-// other devices. Generations do not overlap and all updates belong to
-// a generation.
-//
-// Synchronization for a given SyncRoot between two devices A and B
-// uses generation vectors as follows:
-//                 A                              B
-//                                     <== B's generation vector
-// diff(A's generation vector, B's generation vector)
-// log records of missing generations ==>
-// cache B's generation vector (for space reclamation)
-//
-// Implementation notes: DevTable is stored in a persistent K/V
-// database in the current implementation.  Generation vector is
-// implemented as a map of (Device ID -> Generation ID), one entry for
-// every known device.  If the generation vector contains an entry
-// (Device ID -> Generation ID), it implies that the device has
-// learned of all the generations until and including Generation
-// ID. Generation IDs start from 1.  A generation ID of 0 is a
-// reserved bootstrap value, and indicates the device has no updates.
-import (
-	"errors"
-	"fmt"
-	"sort"
-	"time"
-
-	"v.io/x/lib/vlog"
-)
-
-var (
-	errInvalidDTab = errors.New("invalid devtable db")
-)
-
-// devInfo is the information stored per device.
-type devInfo struct {
-	Vectors map[ObjId]GenVector // device generation vectors.
-	Ts      time.Time           // last communication time stamp.
-}
-
-// devTableHeader contains the header metadata.
-type devTableHeader struct {
-	Resmark []byte // resume marker for watch.
-	// Generation vector for space reclamation. All generations
-	// less than this generation vector are deleted from storage.
-	ReclaimVec GenVector
-}
-
-// devTable contains the metadata for the device table db.
-type devTable struct {
-	fname   string   // file pathname.
-	db      *kvdb    // underlying K/V DB.
-	devices *kvtable // pointer to the "devices" table in the kvdb. Contains device info.
-
-	// Key:"Head" Value:devTableHeader
-	header *kvtable        // pointer to the "header" table in the kvdb. Contains device table header.
-	head   *devTableHeader // devTable head cached in memory.
-
-	s *syncd // pointer to the sync daemon object.
-}
-
-// genOrder represents a generation along with its position in the log.
-type genOrder struct {
-	devID DeviceId
-	srID  ObjId
-	genID GenId
-	order uint32
-}
-
-// byOrder is used to sort the genOrder array.
-type byOrder []*genOrder
-
-func (a byOrder) Len() int {
-	return len(a)
-}
-
-func (a byOrder) Swap(i, j int) {
-	a[i], a[j] = a[j], a[i]
-}
-
-func (a byOrder) Less(i, j int) bool {
-	return a[i].order < a[j].order
-}
-
-// openDevTable opens or creates a devTable for the given filename.
-func openDevTable(filename string, sin *syncd) (*devTable, error) {
-	dtab := &devTable{
-		fname: filename,
-		s:     sin,
-	}
-	// Open the file and create it if it does not exist.
-	// Also initialize the kvdb and its collection.
-	db, tbls, err := kvdbOpen(filename, []string{"devices", "header"})
-	if err != nil {
-		return nil, err
-	}
-
-	dtab.db = db
-	dtab.devices = tbls[0]
-	dtab.header = tbls[1]
-
-	// Initialize the devTable header.
-	dtab.head = &devTableHeader{
-		ReclaimVec: GenVector{
-			dtab.s.id: 0,
-		},
-	}
-	// If header already exists in db, read it back from db.
-	if dtab.hasHead() {
-		if err := dtab.getHead(); err != nil {
-			dtab.db.close() // this also closes the tables.
-			return nil, err
-		}
-	}
-
-	return dtab, nil
-}
-
-// close closes the devTable and invalidates its struct.
-func (dt *devTable) close() error {
-	if dt.db == nil {
-		return errInvalidDTab
-	}
-	// Flush the dirty data.
-	if err := dt.flush(); err != nil {
-		return err
-	}
-	dt.db.close() // this also closes the tables.
-
-	*dt = devTable{} // zero out the devTable struct.
-	return nil
-}
-
-// flush flushes the devTable db to storage.
-func (dt *devTable) flush() error {
-	if dt.db == nil {
-		return errInvalidDTab
-	}
-	// Set the head from memory before flushing.
-	if err := dt.putHead(); err != nil {
-		return err
-	}
-	dt.db.flush()
-	return nil
-}
-
-// initSyncRoot initializes the local generation vector for this SyncRoot.
-func (dt *devTable) initSyncRoot(srid ObjId) error {
-	if dt.db == nil {
-		return errInvalidDTab
-	}
-	if _, err := dt.getGenVec(dt.s.id, srid); err == nil {
-		return fmt.Errorf("syncroot already exists %v", srid)
-	}
-	return dt.putGenVec(dt.s.id, srid, GenVector{dt.s.id: 0})
-}
-
-// delSyncRoot deletes the generation vector for this SyncRoot.
-func (dt *devTable) delSyncRoot(srid ObjId) error {
-	if dt.db == nil {
-		return errInvalidDTab
-	}
-	info, err := dt.getDevInfo(dt.s.id)
-	if err != nil {
-		return fmt.Errorf("dev doesn't exists %v", dt.s.id)
-	}
-	if _, ok := info.Vectors[srid]; !ok {
-		return fmt.Errorf("syncroot doesn't exist %v", srid)
-	}
-	delete(info.Vectors, srid)
-	if len(info.Vectors) == 0 {
-		return dt.delDevInfo(dt.s.id)
-	}
-	return dt.putDevInfo(dt.s.id, info)
-}
-
-// putHead puts the devTable head into the devTable db.
-func (dt *devTable) putHead() error {
-	return dt.header.set("Head", dt.head)
-}
-
-// getHead gets the devTable head from the devTable db.
-func (dt *devTable) getHead() error {
-	if dt.head == nil {
-		return errors.New("nil devTable header")
-	}
-	return dt.header.get("Head", dt.head)
-}
-
-// hasHead returns true if the devTable db has a devTable head.
-func (dt *devTable) hasHead() bool {
-	return dt.header.hasKey("Head")
-}
-
-// putDevInfo puts a devInfo struct in the devTable db.
-func (dt *devTable) putDevInfo(devid DeviceId, info *devInfo) error {
-	if dt.db == nil {
-		return errInvalidDTab
-	}
-	return dt.devices.set(string(devid), info)
-}
-
-// getDevInfo gets a devInfo struct from the devTable db.
-func (dt *devTable) getDevInfo(devid DeviceId) (*devInfo, error) {
-	if dt.db == nil {
-		return nil, errInvalidDTab
-	}
-	var info devInfo
-	if err := dt.devices.get(string(devid), &info); err != nil {
-		return nil, err
-	}
-	if info.Vectors == nil {
-		return nil, errors.New("nil genvectors")
-	}
-	return &info, nil
-}
-
-// hasDevInfo returns true if the device (devid) has any devInfo in the devTable db.
-func (dt *devTable) hasDevInfo(devid DeviceId) bool {
-	if dt.db == nil {
-		return false
-	}
-	return dt.devices.hasKey(string(devid))
-}
-
-// delDevInfo deletes devInfo struct in the devTable db.
-func (dt *devTable) delDevInfo(devid DeviceId) error {
-	if dt.db == nil {
-		return errInvalidDTab
-	}
-	return dt.devices.del(string(devid))
-}
-
-// putGenVec puts a generation vector in the devTable db.
-func (dt *devTable) putGenVec(devid DeviceId, srid ObjId, v GenVector) error {
-	if dt.db == nil {
-		return errInvalidDTab
-	}
-	var info *devInfo
-	if dt.hasDevInfo(devid) {
-		var err error
-		if info, err = dt.getDevInfo(devid); err != nil {
-			return err
-		}
-	} else {
-		info = &devInfo{
-			Vectors: make(map[ObjId]GenVector),
-		}
-	}
-	info.Vectors[srid] = v
-	info.Ts = time.Now().UTC()
-	return dt.putDevInfo(devid, info)
-}
-
-// getGenVec gets a generation vector from the devTable db.
-func (dt *devTable) getGenVec(devid DeviceId, srid ObjId) (GenVector, error) {
-	if dt.db == nil {
-		return nil, errInvalidDTab
-	}
-	info, err := dt.getDevInfo(devid)
-	if err != nil {
-		return nil, err
-	}
-	v, ok := info.Vectors[srid]
-	if !ok {
-		return nil, fmt.Errorf("srid %s doesn't exist", srid.String())
-	}
-	return v, nil
-}
-
-// populateGenOrderEntry populates a genOrder entry.
-func (dt *devTable) populateGenOrderEntry(e *genOrder, id DeviceId, srid ObjId, gnum GenId) error {
-	e.devID = id
-	e.srID = srid
-	e.genID = gnum
-
-	o, err := dt.s.log.getGenMetadata(id, srid, gnum)
-	if err != nil {
-		return err
-	}
-	e.order = o.Pos
-	return nil
-}
-
-// updateGeneration updates a single generation (upID, upGen) in a device's generation vector for SyncRoot srID.
-func (dt *devTable) updateGeneration(key DeviceId, srID ObjId, upID DeviceId, upGen GenId) error {
-	if dt.db == nil {
-		return errInvalidDTab
-	}
-	info, err := dt.getDevInfo(key)
-	if err != nil {
-		return err
-	}
-
-	v, ok := info.Vectors[srID]
-	if !ok {
-		return fmt.Errorf("srid %s doesn't exist", srID.String())
-	}
-	v[upID] = upGen
-	return dt.putDevInfo(key, info)
-}
-
-// updateLocalGenVector updates local generation vector based on the remote generation vector.
-func (dt *devTable) updateLocalGenVector(local, remote GenVector) error {
-	if dt.db == nil {
-		return errInvalidDTab
-	}
-	if local == nil || remote == nil {
-		return errors.New("invalid input args to function")
-	}
-	for rid, rgen := range remote {
-		lgen, ok := local[rid]
-		if !ok || lgen < rgen {
-			local[rid] = rgen
-		}
-	}
-	return nil
-}
-
-// diffGenVectors diffs generation vectors for a given SyncRoot belonging
-// to src and dest and returns the generations known to src and not known to
-// dest. In addition, sync needs to maintain the order in which device
-// generations are created/received. Hence, when two generation
-// vectors are diffed, the differing generations are returned in a
-// sorted order based on their position in the src's log.  genOrder
-// array consists of every generation that is missing between src and
-// dest sorted using its position in the src's log.
-// Example: Generation vector for device A (src) AVec = {A:10, B:5, C:1}
-//          Generation vector for device B (dest) BVec = {A:5, B:10, D:2}
-// Missing generations in unsorted order: {A:6, A:7, A:8, A:9, A:10, C:1}
-//
-// TODO(hpucha): Revisit for the case of a lot of generations to
-// send back (say during bootstrap).
-func (dt *devTable) diffGenVectors(srcVec, destVec GenVector, srid ObjId) ([]*genOrder, error) {
-	if dt.db == nil {
-		return nil, errInvalidDTab
-	}
-
-	// Create an array for the generations that need to be returned.
-	var gens []*genOrder
-
-	// Compute missing generations for devices that are in destination and source vector.
-	for devid, genid := range destVec {
-		srcGenId, ok := srcVec[devid]
-		// Skip since src doesn't know of this device.
-		if !ok {
-			continue
-		}
-		// Need to include all generations in the interval [genid+1, srcGenId],
-		// genid+1 and srcGenId inclusive.
-		// Check against reclaimVec to see if required generations are already GCed.
-		// Starting gen is then max(oldGen, genid+1)
-		startGen := genid + 1
-		oldGen := dt.getOldestGen(devid) + 1
-		if startGen < oldGen {
-			vlog.VI(1).Infof("diffGenVectors:: Adjusting starting generations from %d to %d",
-				startGen, oldGen)
-			startGen = oldGen
-		}
-		for i := startGen; i <= srcGenId; i++ {
-			// Populate the genorder entry.
-			var entry genOrder
-			if err := dt.populateGenOrderEntry(&entry, devid, srid, i); err != nil {
-				return nil, err
-			}
-			gens = append(gens, &entry)
-		}
-	}
-	// Compute missing generations for devices not in destination vector but in source vector.
-	for devid, genid := range srcVec {
-		// Add devices destination does not know about.
-		if _, ok := destVec[devid]; !ok {
-			// Bootstrap generation to oldest available.
-			destGenId := dt.getOldestGen(devid) + 1
-			// Need to include all generations in the interval [destGenId, genid],
-			// destGenId and genid inclusive.
-			for i := destGenId; i <= genid; i++ {
-				// Populate the genorder entry.
-				var entry genOrder
-				if err := dt.populateGenOrderEntry(&entry, devid, srid, i); err != nil {
-					return nil, err
-				}
-				gens = append(gens, &entry)
-			}
-		}
-	}
-
-	// Sort generations in log order.
-	sort.Sort(byOrder(gens))
-	return gens, nil
-}
-
-// getOldestGen returns the most recent gc'ed generation for the device "dev".
-func (dt *devTable) getOldestGen(dev DeviceId) GenId {
-	return dt.head.ReclaimVec[dev]
-}
-
-// // computeReclaimVector computes a generation vector such that the
-// // generations less than or equal to those in the vector can be
-// // garbage collected. Caller holds a lock on s.lock.
-// //
-// // Approach: For each device in the system, we compute its maximum
-// // generation known to all the other devices in the system. This is a
-// // O(N^2) algorithm where N is the number of devices in the system. N
-// // is assumed to be small, of the order of hundreds of devices.
-// func (dt *devTable) computeReclaimVector() (GenVector, error) {
-// 	// Get local generation vector to create the set of devices in
-// 	// the system. Local generation vector is a good bootstrap
-// 	// device set since it contains all the devices whose log
-// 	// records were ever stored locally.
-// 	devSet, err := dt.getGenVec(dt.s.id)
-// 	if err != nil {
-// 		return nil, err
-// 	}
-//
-// 	newReclaimVec := GenVector{}
-// 	for devid := range devSet {
-// 		if !dt.hasDevInfo(devid) {
-// 			// This node knows of devid, but hasn't yet
-// 			// contacted the device. Do not garbage
-// 			// collect any further. For instance, when
-// 			// node A learns of node C's generations from
-// 			// node B, node A may not have an entry for
-// 			// node C yet, but node C will be part of its
-// 			// devSet.
-// 			for dev := range devSet {
-// 				newReclaimVec[dev] = dt.getOldestGen(dev)
-// 			}
-// 			return newReclaimVec, nil
-// 		}
-//
-// 		vec, err := dt.getGenVec(devid)
-// 		if err != nil {
-// 			return nil, err
-// 		}
-// 		for dev := range devSet {
-// 			gen1, ok := vec[dev]
-// 			// Device "devid" does not know about device "dev".
-// 			if !ok {
-// 				newReclaimVec[dev] = dt.getOldestGen(dev)
-// 				continue
-// 			}
-// 			gen2, ok := newReclaimVec[dev]
-// 			if !ok || (gen1 < gen2) {
-// 				newReclaimVec[dev] = gen1
-// 			}
-// 		}
-// 	}
-// 	return newReclaimVec, nil
-// }
-//
-// // addDevice adds a newly learned device to the devTable state.
-// func (dt *devTable) addDevice(newDev DeviceId) error {
-// 	// Create an entry in the device table for the new device.
-// 	vector := GenVector{
-// 		newDev: 0,
-// 	}
-// 	if err := dt.putGenVec(newDev, vector); err != nil {
-// 		return err
-// 	}
-//
-// 	// Update local generation vector with the new device.
-// 	local, err := dt.getDevInfo(dt.s.id)
-// 	if err != nil {
-// 		return err
-// 	}
-// 	if err := dt.updateLocalGenVector(local.Vector, vector); err != nil {
-// 		return err
-// 	}
-// 	if err := dt.putDevInfo(dt.s.id, local); err != nil {
-// 		return err
-// 	}
-// 	return nil
-// }
-//
-// // updateReclaimVec updates the reclaim vector to track gc'ed generations.
-// func (dt *devTable) updateReclaimVec(minGens GenVector) error {
-// 	for dev, min := range minGens {
-// 		gen, ok := dt.head.ReclaimVec[dev]
-// 		if !ok {
-// 			if min < 1 {
-// 				vlog.Errorf("updateReclaimVec:: Received bad generation %s %d",
-// 					dev, min)
-// 				dt.head.ReclaimVec[dev] = 0
-// 			} else {
-// 				dt.head.ReclaimVec[dev] = min - 1
-// 			}
-// 			continue
-// 		}
-//
-// 		// We obtained a generation that is already reclaimed.
-// 		if min <= gen {
-// 			return errors.New("requested gen smaller than GC'ed gen")
-// 		}
-// 	}
-// 	return nil
-// }
-
-// getDeviceIds returns the IDs of all devices that are involved in synchronization.
-func (dt *devTable) getDeviceIds() ([]DeviceId, error) {
-	if dt.db == nil {
-		return nil, errInvalidDTab
-	}
-
-	devIDs := make([]DeviceId, 0)
-	dt.devices.keyIter(func(devStr string) {
-		devIDs = append(devIDs, DeviceId(devStr))
-	})
-
-	return devIDs, nil
-}
-
-// dump writes to the log file information on all device table entries.
-func (dt *devTable) dump() {
-	if dt.db == nil {
-		return
-	}
-
-	vlog.VI(1).Infof("DUMP: Dev: self %v: resmark %v, reclaim %v",
-		dt.s.id, dt.head.Resmark, dt.head.ReclaimVec)
-
-	dt.devices.keyIter(func(devStr string) {
-		info, err := dt.getDevInfo(DeviceId(devStr))
-		if err != nil {
-			return
-		}
-
-		vlog.VI(1).Infof("DUMP: Dev: %s: #SR %d, time %v", devStr, len(info.Vectors), info.Ts)
-		for sr, vec := range info.Vectors {
-			vlog.VI(1).Infof("DUMP: Dev: %s: SR %v, vec %v", devStr, sr, vec)
-		}
-	})
-}
diff --git a/x/ref/services/syncbase/sync/devtable_test.go b/x/ref/services/syncbase/sync/devtable_test.go
deleted file mode 100644
index 8cf5307..0000000
--- a/x/ref/services/syncbase/sync/devtable_test.go
+++ /dev/null
@@ -1,1227 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-// Tests for the Veyron Sync devTable component.
-import (
-	"os"
-	"reflect"
-	"runtime"
-	"testing"
-	"time"
-)
-
-// TestDevTabStore tests creating a backing file for devTable.
-func TestDevTabStore(t *testing.T) {
-	devfile := getFileName()
-	defer os.Remove(devfile)
-
-	s := &syncd{id: "VeyronPhone"}
-	dtab, err := openDevTable(devfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-	}
-
-	fsize := getFileSize(devfile)
-	if fsize < 0 {
-		//t.Errorf("DevTable file %s not created", devfile)
-	}
-
-	if err := dtab.flush(); err != nil {
-		t.Errorf("Cannot flush devTable file %s, err %v", devfile, err)
-	}
-
-	oldfsize := fsize
-	fsize = getFileSize(devfile)
-	if fsize <= oldfsize {
-		//t.Errorf("DevTable file %s not flushed", devfile)
-	}
-
-	if err := dtab.close(); err != nil {
-		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-	}
-
-	oldfsize = getFileSize(devfile)
-
-	dtab, err = openDevTable(devfile, s)
-	if err != nil {
-		t.Fatalf("Cannot re-open existing devTable file %s, err %v", devfile, err)
-	}
-
-	fsize = getFileSize(devfile)
-	if fsize != oldfsize {
-		t.Errorf("DevTable file %s size changed across re-open (%d %d)", devfile, fsize, oldfsize)
-	}
-
-	if err := dtab.flush(); err != nil {
-		t.Errorf("Cannot flush devTable file %s, err %v", devfile, err)
-	}
-
-	if err := dtab.close(); err != nil {
-		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-	}
-}
-
-// TestInvalidDTab tests devTable methods on an invalid (closed) devTable ptr.
-func TestInvalidDTab(t *testing.T) {
-	devfile := getFileName()
-	defer os.Remove(devfile)
-
-	s := &syncd{id: "VeyronPhone"}
-	dtab, err := openDevTable(devfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-	}
-
-	if err := dtab.close(); err != nil {
-		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-	}
-
-	validateError := func(err error, funcName string) {
-		_, file, line, _ := runtime.Caller(1)
-		if err == nil || err != errInvalidDTab {
-			t.Errorf("%s:%d %s() did not fail on a closed devTable: %v", file, line, funcName, err)
-		}
-	}
-
-	err = dtab.close()
-	validateError(err, "close")
-
-	err = dtab.flush()
-	validateError(err, "flush")
-
-	srid := ObjId("foo")
-
-	err = dtab.initSyncRoot(srid)
-	validateError(err, "initSyncRoot")
-
-	err = dtab.putDevInfo(s.id, &devInfo{})
-	validateError(err, "putDevInfo")
-
-	_, err = dtab.getDevInfo(s.id)
-	validateError(err, "getDevInfo")
-
-	err = dtab.delDevInfo(s.id)
-	validateError(err, "delDevInfo")
-
-	if dtab.hasDevInfo(s.id) {
-		t.Errorf("hasDevInfo() did not fail on a closed devTable: %v", err)
-	}
-
-	err = dtab.putGenVec(s.id, srid, GenVector{})
-	validateError(err, "putGenVec")
-
-	_, err = dtab.getGenVec(s.id, srid)
-	validateError(err, "getGenVec")
-
-	err = dtab.updateGeneration(s.id, srid, s.id, 0)
-	validateError(err, "updateGeneration")
-
-	err = dtab.updateLocalGenVector(GenVector{}, GenVector{})
-	validateError(err, "updateLocalGenVector")
-
-	_, err = dtab.diffGenVectors(GenVector{}, GenVector{}, srid)
-	validateError(err, "diffGenVectors")
-
-	_, err = dtab.getDeviceIds()
-	validateError(err, "getDeviceIds")
-
-	// Harmless NOP.
-	dtab.dump()
-}
-
-// TestPutGetDevTableHeader tests setting and getting devTable header across devTable open/close/reopen.
-func TestPutGetDevTableHeader(t *testing.T) {
-	devfile := getFileName()
-	defer os.Remove(devfile)
-
-	s := &syncd{id: "VeyronPhone"}
-	dtab, err := openDevTable(devfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-	}
-
-	// In memory head should be initialized.
-	if dtab.head.Resmark != nil {
-		t.Errorf("First time log create should reset header: %v", dtab.head.Resmark)
-	}
-	expVec := GenVector{dtab.s.id: 0}
-	if !reflect.DeepEqual(dtab.head.ReclaimVec, expVec) {
-		t.Errorf("Data mismatch for reclaimVec in devTable file %s: %v instead of %v",
-			devfile, dtab.head.ReclaimVec, expVec)
-	}
-
-	// No head should be there in db.
-	if err = dtab.getHead(); err == nil {
-		t.Errorf("getHead() found non-existent head in devTable file %s, err %v", devfile, err)
-	}
-
-	if dtab.hasHead() {
-		t.Errorf("hasHead() found non-existent head in devTable file %s", devfile)
-	}
-
-	expMark := []byte{1, 2, 3}
-	expVec = GenVector{
-		"VeyronTab":   30,
-		"VeyronPhone": 10,
-	}
-	dtab.head = &devTableHeader{
-		Resmark:    expMark,
-		ReclaimVec: expVec,
-	}
-
-	if err := dtab.putHead(); err != nil {
-		t.Errorf("Cannot put head %v in devTable file %s, err %v", dtab.head, devfile, err)
-	}
-
-	// Reset values.
-	dtab.head.Resmark = nil
-	dtab.head.ReclaimVec = GenVector{}
-
-	for i := 0; i < 2; i++ {
-		if err := dtab.getHead(); err != nil {
-			t.Fatalf("getHead() can not find head (i=%d) in devTable file %s, err %v", i, devfile, err)
-		}
-
-		if !dtab.hasHead() {
-			t.Errorf("hasHead() can not find head (i=%d) in devTable file %s", i, devfile)
-		}
-
-		if !reflect.DeepEqual(dtab.head.Resmark, expMark) {
-			t.Errorf("Data mismatch for resmark (i=%d) in devTable file %s: %v instead of %v",
-				i, devfile, dtab.head.Resmark, expMark)
-		}
-		if !reflect.DeepEqual(dtab.head.ReclaimVec, expVec) {
-			t.Errorf("Data mismatch for reclaimVec (i=%d) in devTable file %s: %v instead of %v",
-				i, devfile, dtab.head.ReclaimVec, expVec)
-		}
-
-		if i == 0 {
-			if err := dtab.close(); err != nil {
-				t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-			}
-			dtab, err = openDevTable(devfile, s)
-			if err != nil {
-				t.Fatalf("Cannot re-open devTable file %s, err %v", devfile, err)
-			}
-		}
-	}
-
-	dtab.dump()
-
-	if err := dtab.close(); err != nil {
-		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-	}
-}
-
-// TestPersistDevTableHeader tests that devTable header is
-// automatically persisted across devTable open/close/reopen.
-func TestPersistDevTableHeader(t *testing.T) {
-	devfile := getFileName()
-	defer os.Remove(devfile)
-
-	s := &syncd{id: "VeyronPhone"}
-	dtab, err := openDevTable(devfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-	}
-
-	// In memory head should be initialized.
-	if dtab.head.Resmark != nil {
-		t.Errorf("First time log create should reset header: %v", dtab.head.Resmark)
-	}
-	expVec := GenVector{dtab.s.id: 0}
-	if !reflect.DeepEqual(dtab.head.ReclaimVec, expVec) {
-		t.Errorf("Data mismatch for reclaimVec in devTable file %s: %v instead of %v",
-			devfile, dtab.head.ReclaimVec, expVec)
-	}
-
-	expMark := []byte{0, 2, 255}
-	expVec = GenVector{
-		"VeyronTab":   100,
-		"VeyronPhone": 10000,
-	}
-	dtab.head = &devTableHeader{
-		Resmark:    expMark,
-		ReclaimVec: expVec,
-	}
-
-	if err := dtab.close(); err != nil {
-		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-	}
-
-	dtab, err = openDevTable(devfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-	}
-
-	// In memory head should be initialized from db.
-	if !reflect.DeepEqual(dtab.head.Resmark, expMark) {
-		t.Errorf("Data mismatch for resmark in devTable file %s: %v instead of %v",
-			devfile, dtab.head.Resmark, expMark)
-	}
-	if !reflect.DeepEqual(dtab.head.ReclaimVec, expVec) {
-		t.Errorf("Data mismatch for reclaimVec in devTable file %s: %v instead of %v",
-			devfile, dtab.head.ReclaimVec, expVec)
-	}
-
-	expMark = []byte{60, 180, 7}
-	expVec = GenVector{
-		"VeyronTab":   1,
-		"VeyronPhone": 1987,
-	}
-	dtab.head = &devTableHeader{
-		Resmark:    expMark,
-		ReclaimVec: expVec,
-	}
-
-	if err := dtab.flush(); err != nil {
-		t.Errorf("Cannot flush devTable file %s, err %v", devfile, err)
-	}
-
-	// Reset values.
-	dtab.head.Resmark = nil
-	dtab.head.ReclaimVec = GenVector{}
-
-	if err := dtab.getHead(); err != nil {
-		t.Fatalf("getHead() can not find head in devTable file %s, err %v", devfile, err)
-	}
-
-	// In memory head should be initialized from db.
-	if !reflect.DeepEqual(dtab.head.Resmark, expMark) {
-		t.Errorf("Data mismatch for resmark in devTable file %s: %v instead of %v",
-			devfile, dtab.head.Resmark, expMark)
-	}
-	if !reflect.DeepEqual(dtab.head.ReclaimVec, expVec) {
-		t.Errorf("Data mismatch for reclaimVec in devTable file %s: %v instead of %v",
-			devfile, dtab.head.ReclaimVec, expVec)
-	}
-
-	dtab.dump()
-
-	if err := dtab.close(); err != nil {
-		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-	}
-}
-
-// TestDTabInitDelSyncRoot tests initing and deleting a new SyncRoot.
-func TestDTabInitDelSyncRoot(t *testing.T) {
-	devfile := getFileName()
-	defer os.Remove(devfile)
-
-	s := &syncd{id: "VeyronPhone"}
-	dtab, err := openDevTable(devfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-	}
-	srid1 := ObjId("foo")
-	if err := dtab.initSyncRoot(srid1); err != nil {
-		t.Fatalf("Cannot create new SyncRoot %s, err %v", srid1.String(), err)
-	}
-	srid2 := ObjId("bar")
-	if err := dtab.initSyncRoot(srid2); err != nil {
-		t.Fatalf("Cannot create new SyncRoot %s, err %v", srid2.String(), err)
-	}
-	if err := dtab.initSyncRoot(srid2); err == nil {
-		t.Fatalf("Creating existing SyncRoot didn't fail %s", srid2.String())
-	}
-
-	info, err := dtab.getDevInfo(s.id)
-	if err != nil {
-		t.Errorf("GetDevInfo() can not find device %s in devTable file %s err %v",
-			s.id, devfile, err)
-	}
-	expVec := map[ObjId]GenVector{
-		srid1: {s.id: 0},
-		srid2: {s.id: 0},
-	}
-	if !reflect.DeepEqual(info.Vectors, expVec) {
-		t.Errorf("Data mismatch for device %s %v instead of %v",
-			s.id, info.Vectors, expVec)
-	}
-	if err := dtab.delSyncRoot(srid1); err != nil {
-		t.Fatalf("Cannot delete SyncRoot %s, err %v", srid1.String(), err)
-	}
-	if err := dtab.delSyncRoot(srid1); err == nil {
-		t.Fatalf("Deleting non-existent SyncRoot didn't fail %s", srid1.String())
-	}
-	if err := dtab.delSyncRoot(srid2); err != nil {
-		t.Fatalf("Cannot delete SyncRoot %s, err %v", srid2.String(), err)
-	}
-
-	if _, err = dtab.getDevInfo(s.id); err == nil {
-		t.Errorf("GetDevInfo() found device %s in devTable file %s err %v",
-			s.id, devfile, err)
-	}
-}
-
-// TestPutGetDevInfo tests setting and getting devInfo across devTable open/close/reopen.
-func TestPutGetDevInfo(t *testing.T) {
-	devfile := getFileName()
-	defer os.Remove(devfile)
-
-	s := &syncd{id: "VeyronPhone"}
-	dtab, err := openDevTable(devfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-	}
-
-	var devid DeviceId = "VeyronTab"
-
-	info, err := dtab.getDevInfo(devid)
-	if err == nil || info != nil {
-		t.Errorf("GetDevInfo() found non-existent device %s in devTable file %s: %v, err %v",
-			devid, devfile, info, err)
-	}
-
-	if dtab.hasDevInfo(devid) {
-		t.Errorf("HasDevInfo() found non-existent device %s in devTable file %s",
-			devid, devfile)
-	}
-	info = &devInfo{
-		Vectors: map[ObjId]GenVector{ObjId("haha"): GenVector{"VeyronTab": 0, "VeyronPhone": 10},
-			ObjId("hello"): GenVector{"VeyronLaptop": 20, "VeyronPhone": 30, "VeyronTab": 80}},
-		Ts: time.Now().UTC(),
-	}
-
-	if err := dtab.putDevInfo(devid, info); err != nil {
-		t.Errorf("Cannot put device %s (%v) in devTable file %s, err %v", devid, info, devfile, err)
-	}
-
-	for i := 0; i < 2; i++ {
-		curInfo, err := dtab.getDevInfo(devid)
-		if err != nil || curInfo == nil {
-			t.Fatalf("GetDevInfo() can not find device %s (i=%d) in devTable file %s: %v, err: %v",
-				devid, i, devfile, curInfo, err)
-		}
-
-		if !dtab.hasDevInfo(devid) {
-			t.Errorf("HasDevInfo() can not find device %s (i=%d) in devTable file %s",
-				devid, i, devfile)
-		}
-
-		if !reflect.DeepEqual(curInfo, info) {
-			t.Errorf("Data mismatch for device %s (i=%d) in devTable file %s: %v instead of %v",
-				devid, i, devfile, curInfo, info)
-		}
-
-		if i == 0 {
-			if err := dtab.close(); err != nil {
-				t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-			}
-			dtab, err = openDevTable(devfile, s)
-			if err != nil {
-				t.Fatalf("Cannot re-open devTable file %s, err %v", devfile, err)
-			}
-		}
-	}
-
-	dtab.dump()
-
-	if err := dtab.close(); err != nil {
-		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-	}
-}
-
-// TestPutGetGenVec tests setting and getting generation vector across dtab open/close/reopen.
-func TestPutGetGenVec(t *testing.T) {
-	devfile := getFileName()
-	defer os.Remove(devfile)
-
-	s := &syncd{id: "VeyronPhone"}
-	dtab, err := openDevTable(devfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-	}
-
-	var devid DeviceId = "VeyronTab"
-	srids := []ObjId{"foo", "bar"}
-	vecs := []GenVector{GenVector{"VeyronTab": 0, "VeyronPhone": 10, "VeyronDesktop": 20, "VeyronLaptop": 2},
-		GenVector{"VeyronTab": 400, "VeyronPhone": 100, "VeyronDesktop": 200, "VeyronLaptop": 20}}
-
-	for i, sr := range srids {
-		vec, err := dtab.getGenVec(devid, sr)
-		if err == nil || vec != nil {
-			t.Errorf("GetGenVec() found non-existent device %s in devTable file %s: %v, err %v",
-				devid, devfile, vec, err)
-		}
-
-		if err := dtab.putGenVec(devid, sr, vecs[i]); err != nil {
-			t.Errorf("Cannot put device %s (%s %v) in devTable file %s, err %v", devid, sr.String(),
-				vecs[i], devfile, err)
-		}
-	}
-
-	for k, sr := range srids {
-		for i := 0; i < 2; i++ {
-			// Check for devid.
-			curVec, err := dtab.getGenVec(devid, sr)
-			if err != nil || curVec == nil {
-				t.Fatalf("GetGenVec() can not find device %s (i=%d) in devTable file %s, err %v",
-					devid, i, devfile, err)
-			}
-
-			if !reflect.DeepEqual(curVec, vecs[k]) {
-				t.Errorf("Data mismatch for device %s srid %s (i=%d) in devTable file %s: %v instead of %v",
-					devid, sr.String(), i, devfile, curVec, vecs[k])
-			}
-
-			if i == 0 {
-				if err := dtab.close(); err != nil {
-					t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-				}
-				dtab, err = openDevTable(devfile, s)
-				if err != nil {
-					t.Fatalf("Cannot re-open devTable file %s, err %v", devfile, err)
-				}
-			}
-		}
-	}
-
-	dtab.dump()
-
-	if err := dtab.close(); err != nil {
-		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-	}
-}
-
-// TestUpdateGeneration tests updating a generation.
-func TestUpdateGeneration(t *testing.T) {
-	devfile := getFileName()
-	defer os.Remove(devfile)
-
-	s := &syncd{id: "VeyronPhone"}
-	dtab, err := openDevTable(devfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-	}
-
-	var devid DeviceId = "VeyronTab"
-	srid := ObjId("foo")
-
-	err = dtab.updateGeneration(devid, srid, devid, 10)
-	if err == nil {
-		t.Errorf("UpdateGeneration() found non-existent device %s in devTable file %s, err %v",
-			devid, devfile, err)
-	}
-	v := GenVector{
-		"VeyronTab":     0,
-		"VeyronPhone":   10,
-		"VeyronDesktop": 20,
-		"VeyronLaptop":  2,
-	}
-
-	if err := dtab.putGenVec(devid, srid, v); err != nil {
-		t.Errorf("Cannot put device %s (%s %v) in devTable file %s, err %v", devid,
-			srid.String(), v, devfile, err)
-	}
-
-	// Try a non-existent SyncRoot ID.
-	if err = dtab.updateGeneration(devid, ObjId("haha"), devid, 10); err == nil {
-		t.Errorf("UpdateGeneration() did not fail on a wrong SyncRoot for %s in devTable file %s", devid, devfile)
-	}
-
-	err = dtab.updateGeneration(devid, srid, devid, 10)
-	if err != nil {
-		t.Errorf("UpdateGeneration() failed for %s in devTable file %s with error %v",
-			devid, devfile, err)
-	}
-	err = dtab.updateGeneration(devid, srid, "VeyronLaptop", 18)
-	if err != nil {
-		t.Errorf("UpdateGeneration() failed for %s in devTable file %s with error %v",
-			devid, devfile, err)
-	}
-	curVec, err := dtab.getGenVec(devid, srid)
-	if err != nil || curVec == nil {
-		t.Fatalf("GetGenVec() can not find device %s in devTable file %s, err %v",
-			devid, devfile, err)
-	}
-	vExp := GenVector{
-		"VeyronTab":     10,
-		"VeyronPhone":   10,
-		"VeyronDesktop": 20,
-		"VeyronLaptop":  18,
-	}
-
-	if !reflect.DeepEqual(curVec, vExp) {
-		t.Errorf("Data mismatch for device %s srid %s in devTable file %s: %v instead of %v",
-			devid, srid.String(), devfile, v, vExp)
-	}
-
-	dtab.dump()
-
-	if err := dtab.close(); err != nil {
-		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-	}
-}
-
-// TestUpdateLocalGenVector tests updating a gen vector.
-func TestUpdateLocalGenVector(t *testing.T) {
-	devfile := getFileName()
-	defer os.Remove(devfile)
-
-	s := &syncd{id: "VeyronPhone"}
-	dtab, err := openDevTable(devfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-	}
-
-	// Test nil args.
-	if err := dtab.updateLocalGenVector(nil, nil); err == nil {
-		t.Errorf("UpdateLocalGenVector() failed in devTable file %s with error %v",
-			devfile, err)
-	}
-
-	// Nothing to update.
-	local := GenVector{
-		"VeyronTab":   0,
-		"VeyronPhone": 1,
-	}
-	remote := GenVector{
-		"VeyronTab":   0,
-		"VeyronPhone": 1,
-	}
-	if err := dtab.updateLocalGenVector(local, remote); err != nil {
-		t.Errorf("UpdateLocalGenVector() failed in devTable file %s with error %v",
-			devfile, err)
-	}
-
-	if !reflect.DeepEqual(local, remote) {
-		t.Errorf("Data mismatch for object %v instead of %v",
-			local, remote)
-	}
-
-	// local is missing a generation.
-	local = GenVector{
-		"VeyronPhone": 1,
-	}
-	if err := dtab.updateLocalGenVector(local, remote); err != nil {
-		t.Errorf("UpdateLocalGenVector() failed in devTable file %s with error %v",
-			devfile, err)
-	}
-	if !reflect.DeepEqual(local, remote) {
-		t.Errorf("Data mismatch for object %v instead of %v",
-			local, remote)
-	}
-
-	// local is stale compared to remote.
-	local = GenVector{
-		"VeyronTab":   0,
-		"VeyronPhone": 0,
-	}
-	remote = GenVector{
-		"VeyronTab":    1,
-		"VeyronPhone":  0,
-		"VeyronLaptop": 2,
-	}
-	if err := dtab.updateLocalGenVector(local, remote); err != nil {
-		t.Errorf("UpdateLocalGenVector() failed in devTable file %s with error %v",
-			devfile, err)
-	}
-	if !reflect.DeepEqual(local, remote) {
-		t.Errorf("Data mismatch for object %v instead of %v",
-			local, remote)
-	}
-
-	// local is partially stale.
-	local = GenVector{
-		"VeyronTab":     0,
-		"VeyronPhone":   0,
-		"VeyronDesktop": 20,
-	}
-	remote = GenVector{
-		"VeyronTab":    1,
-		"VeyronPhone":  10,
-		"VeyronLaptop": 2,
-	}
-	localExp := GenVector{
-		"VeyronTab":     1,
-		"VeyronPhone":   10,
-		"VeyronDesktop": 20,
-		"VeyronLaptop":  2,
-	}
-	if err := dtab.updateLocalGenVector(local, remote); err != nil {
-		t.Errorf("UpdateLocalGenVector() failed in devTable file %s with error %v",
-			devfile, err)
-	}
-	if !reflect.DeepEqual(local, localExp) {
-		t.Errorf("Data mismatch for object %v instead of %v",
-			local, localExp)
-	}
-
-	if err := dtab.close(); err != nil {
-		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-	}
-}
-
-// TestDiffGenVectors tests diffing gen vectors.
-func TestDiffGenVectors(t *testing.T) {
-	logOrder := []DeviceId{"VeyronTab", "VeyronPhone", "VeyronDesktop", "VeyronLaptop"}
-	var expGens []*genOrder
-	srid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	// set reclaimVec such that it doesn't affect diffs.
-	reclaimVec := GenVector{
-		"VeyronTab":     0,
-		"VeyronPhone":   0,
-		"VeyronDesktop": 0,
-		"VeyronLaptop":  0,
-	}
-
-	// src and dest are identical vectors.
-	vec := GenVector{
-		"VeyronTab":     1,
-		"VeyronPhone":   10,
-		"VeyronDesktop": 20,
-		"VeyronLaptop":  2,
-	}
-	setupAndTestDiff(t, vec, vec, reclaimVec, logOrder, expGens)
-
-	// src has no updates.
-	srcVec := GenVector{
-		"VeyronTab": 0,
-	}
-	remoteVec := GenVector{
-		"VeyronTab":     5,
-		"VeyronPhone":   10,
-		"VeyronDesktop": 20,
-		"VeyronLaptop":  8,
-	}
-	setupAndTestDiff(t, srcVec, remoteVec, reclaimVec, []DeviceId{}, expGens)
-
-	// src and remote have no updates.
-	srcVec = GenVector{
-		"VeyronTab": 0,
-	}
-	remoteVec = GenVector{
-		"VeyronTab": 0,
-	}
-	setupAndTestDiff(t, srcVec, remoteVec, reclaimVec, []DeviceId{}, expGens)
-
-	// set reclaimVec such that it doesn't affect diffs.
-	reclaimVec = GenVector{
-		"VeyronTab": 0,
-	}
-
-	// src is staler than remote.
-	srcVec = GenVector{
-		"VeyronTab":     1,
-		"VeyronPhone":   10,
-		"VeyronDesktop": 20,
-		"VeyronLaptop":  2,
-	}
-	remoteVec = GenVector{
-		"VeyronTab":     5,
-		"VeyronPhone":   10,
-		"VeyronDesktop": 20,
-		"VeyronLaptop":  8,
-	}
-	setupAndTestDiff(t, srcVec, remoteVec, reclaimVec, logOrder, expGens)
-
-	// src is fresher than remote.
-	srcVec = GenVector{
-		"VeyronTab":     5,
-		"VeyronPhone":   10,
-		"VeyronDesktop": 20,
-		"VeyronLaptop":  2,
-	}
-	remoteVec = GenVector{
-		"VeyronTab":     1,
-		"VeyronPhone":   10,
-		"VeyronDesktop": 20,
-		"VeyronLaptop":  2,
-	}
-	expGens = make([]*genOrder, 4)
-	for i := 0; i < 4; i++ {
-		expGens[i] = &genOrder{
-			devID: "VeyronTab",
-			srID:  srid,
-			genID: GenId(i + 2),
-			order: uint32(i + 1),
-		}
-	}
-	setupAndTestDiff(t, srcVec, remoteVec, reclaimVec, logOrder, expGens)
-
-	// src is fresher than remote in all but one device.
-	srcVec = GenVector{
-		"VeyronTab":     5,
-		"VeyronPhone":   10,
-		"VeyronDesktop": 22,
-		"VeyronLaptop":  2,
-	}
-	remoteVec = GenVector{
-		"VeyronTab":     1,
-		"VeyronPhone":   10,
-		"VeyronDesktop": 20,
-		"VeyronLaptop":  2,
-		"VeyronCloud":   40,
-	}
-	expGens = make([]*genOrder, 6)
-	for i := 0; i < 6; i++ {
-		switch {
-		case i < 4:
-			expGens[i] = &genOrder{
-				devID: "VeyronTab",
-				srID:  srid,
-				genID: GenId(i + 2),
-				order: uint32(i + 1),
-			}
-		default:
-			expGens[i] = &genOrder{
-				devID: "VeyronDesktop",
-				srID:  srid,
-				genID: GenId(i - 4 + 21),
-				order: uint32(i - 4 + 35),
-			}
-		}
-	}
-	setupAndTestDiff(t, srcVec, remoteVec, reclaimVec, logOrder, expGens)
-
-	// src is fresher than dest, scramble log order.
-	o := []DeviceId{"VeyronTab", "VeyronLaptop", "VeyronPhone", "VeyronDesktop"}
-	srcVec = GenVector{
-		"VeyronTab":     1,
-		"VeyronPhone":   2,
-		"VeyronDesktop": 3,
-		"VeyronLaptop":  4,
-	}
-	remoteVec = GenVector{
-		"VeyronTab":     0,
-		"VeyronPhone":   2,
-		"VeyronDesktop": 0,
-	}
-	expGens = make([]*genOrder, 8)
-	for i := 0; i < 8; i++ {
-		switch {
-		case i < 1:
-			expGens[i] = &genOrder{
-				devID: "VeyronTab",
-				srID:  srid,
-				genID: GenId(i + 1),
-				order: uint32(i),
-			}
-		case i >= 1 && i < 5:
-			expGens[i] = &genOrder{
-				devID: "VeyronLaptop",
-				srID:  srid,
-				genID: GenId(i),
-				order: uint32(i),
-			}
-		default:
-			expGens[i] = &genOrder{
-				devID: "VeyronDesktop",
-				srID:  srid,
-				genID: GenId(i - 4),
-				order: uint32(i - 5 + 7),
-			}
-		}
-	}
-	setupAndTestDiff(t, srcVec, remoteVec, reclaimVec, o, expGens)
-
-	// remote has no updates.
-	srcVec = GenVector{
-		"VeyronTab":     1,
-		"VeyronPhone":   2,
-		"VeyronDesktop": 3,
-		"VeyronLaptop":  4,
-	}
-	remoteVec = GenVector{
-		"VeyronPhone": 0,
-	}
-	expGens = make([]*genOrder, 10)
-	for i := 0; i < 10; i++ {
-		switch {
-		case i < 1:
-			expGens[i] = &genOrder{
-				devID: "VeyronTab",
-				srID:  srid,
-				genID: GenId(i + 1),
-				order: uint32(i),
-			}
-		case i >= 1 && i < 3:
-			expGens[i] = &genOrder{
-				devID: "VeyronPhone",
-				srID:  srid,
-				genID: GenId(i),
-				order: uint32(i),
-			}
-		case i >= 3 && i < 6:
-			expGens[i] = &genOrder{
-				devID: "VeyronDesktop",
-				srID:  srid,
-				genID: GenId(i - 2),
-				order: uint32(i),
-			}
-		default:
-			expGens[i] = &genOrder{
-				devID: "VeyronLaptop",
-				srID:  srid,
-				genID: GenId(i - 5),
-				order: uint32(i),
-			}
-		}
-	}
-	setupAndTestDiff(t, srcVec, remoteVec, reclaimVec, logOrder, expGens)
-
-	// Test with reclaimVec fast-fwded.
-	reclaimVec = GenVector{
-		"VeyronPhone":  1,
-		"VeyronLaptop": 2,
-	}
-	srcVec = GenVector{
-		"VeyronTab":     1,
-		"VeyronPhone":   2,
-		"VeyronDesktop": 3,
-		"VeyronLaptop":  4,
-	}
-	remoteVec = GenVector{
-		"VeyronPhone": 0,
-	}
-	expGens = make([]*genOrder, 7)
-	for i := 0; i < 7; i++ {
-		switch {
-		case i < 1:
-			expGens[i] = &genOrder{
-				devID: "VeyronTab",
-				srID:  srid,
-				genID: GenId(i + 1),
-				order: uint32(i),
-			}
-		case i == 1:
-			expGens[i] = &genOrder{
-				devID: "VeyronPhone",
-				srID:  srid,
-				genID: GenId(i + 1),
-				order: uint32(i + 1),
-			}
-		case i >= 2 && i < 5:
-			expGens[i] = &genOrder{
-				devID: "VeyronDesktop",
-				srID:  srid,
-				genID: GenId(i - 1),
-				order: uint32(i + 1),
-			}
-		default:
-			expGens[i] = &genOrder{
-				devID: "VeyronLaptop",
-				srID:  srid,
-				genID: GenId(i - 2),
-				order: uint32(i + 3),
-			}
-		}
-	}
-	setupAndTestDiff(t, srcVec, remoteVec, reclaimVec, logOrder, expGens)
-}
-
-// setupAndTestDiff is an utility function to test diffing generation vectors.
-func setupAndTestDiff(t *testing.T, srcVec, remoteVec, reclaimVec GenVector, logOrder []DeviceId, expGens []*genOrder) {
-	devfile := getFileName()
-	defer os.Remove(devfile)
-
-	logfile := getFileName()
-	defer os.Remove(logfile)
-
-	var srcid DeviceId = "VeyronTab"
-	var destid DeviceId = "VeyronPhone"
-
-	var err error
-	s := &syncd{id: srcid}
-	s.log, err = openILog(logfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
-	}
-	dtab, err := openDevTable(devfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-	}
-	dtab.head.ReclaimVec = reclaimVec
-
-	srid, err := strToObjId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-	// Populate generations in log order.
-	var order uint32
-	for _, k := range logOrder {
-		v, ok := (srcVec)[k]
-		if !ok {
-			t.Errorf("Cannot find key %s in srcVec %v", k, srcVec)
-		}
-		for i := GenId(1); i <= v; i++ {
-			val := &genMetadata{Pos: order}
-			if err := s.log.putGenMetadata(k, srid, i, val); err != nil {
-				t.Errorf("Cannot put object %s:%d in log file %s, err %v", k, v, logfile, err)
-			}
-			order++
-		}
-	}
-	gens, err := dtab.diffGenVectors(srcVec, remoteVec, srid)
-	if err != nil {
-		t.Fatalf("DiffGenVectors() failed src: %s %v dest: %s %v in devTable file %s, err %v",
-			srcid, srcVec, destid, remoteVec, devfile, err)
-	}
-
-	if !reflect.DeepEqual(gens, expGens) {
-		t.Fatalf("Data mismatch for genorder %v instead of %v, src %v dest %v reclaim %v",
-			gens, expGens, srcVec, remoteVec, reclaimVec)
-	}
-
-	if err := dtab.close(); err != nil {
-		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-	}
-}
-
-// TestGetOldestGen tests obtaining generations from reclaimVec.
-func TestGetOldestGen(t *testing.T) {
-	devfile := getFileName()
-	defer os.Remove(devfile)
-
-	var srcid DeviceId = "VeyronTab"
-	s := &syncd{id: srcid}
-	var err error
-	s.devtab, err = openDevTable(devfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-	}
-
-	if s.devtab.getOldestGen(srcid) != 0 {
-		t.Errorf("Cannot get generation for device %s in devTable file %s",
-			srcid, devfile)
-	}
-
-	var destid DeviceId = "VeyronPhone"
-	if s.devtab.getOldestGen(destid) != 0 {
-		t.Errorf("Cannot get generation for device %s in devTable file %s",
-			destid, devfile)
-	}
-
-	s.devtab.head.ReclaimVec[srcid] = 10
-	if s.devtab.getOldestGen(srcid) != 10 {
-		t.Errorf("Cannot get generation for device %s in devTable file %s",
-			srcid, devfile)
-	}
-	if s.devtab.getOldestGen(destid) != 0 {
-		t.Errorf("Cannot get generation for device %s in devTable file %s",
-			destid, devfile)
-	}
-
-	if err := s.devtab.close(); err != nil {
-		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-	}
-}
-
-// // TestComputeReclaimVector tests reclaim vector computation.
-// func TestComputeReclaimVector(t *testing.T) {
-// 	devArr := []DeviceId{"VeyronTab", "VeyronPhone", "VeyronDesktop", "VeyronLaptop"}
-// 	genVecArr := make([]GenVector, 4)
-//
-// 	// All devices are up-to-date.
-// 	genVecArr[0] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 3, "VeyronLaptop": 4}
-// 	genVecArr[1] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 3, "VeyronLaptop": 4}
-// 	genVecArr[2] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 3, "VeyronLaptop": 4}
-// 	genVecArr[3] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 3, "VeyronLaptop": 4}
-// 	setupAndTestReclaimVector(t, devArr, genVecArr, nil, genVecArr[0])
-//
-// 	// Every device is missing at least one other device. Not possible to gc.
-// 	genVecArr[0] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 3, "VeyronLaptop": 4}
-// 	genVecArr[1] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronLaptop": 4}
-// 	genVecArr[2] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 3}
-// 	genVecArr[3] = GenVector{"VeyronDesktop": 3, "VeyronLaptop": 4}
-// 	expReclaimVec := GenVector{"VeyronTab": 0, "VeyronPhone": 0, "VeyronDesktop": 0, "VeyronLaptop": 0}
-// 	setupAndTestReclaimVector(t, devArr, genVecArr, nil, expReclaimVec)
-//
-// 	// All devices know at least one generation from other devices.
-// 	genVecArr[0] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 3, "VeyronLaptop": 4}
-// 	genVecArr[1] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 2, "VeyronLaptop": 2}
-// 	genVecArr[2] = GenVector{"VeyronTab": 1, "VeyronPhone": 1, "VeyronDesktop": 3, "VeyronLaptop": 1}
-// 	genVecArr[3] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 1, "VeyronLaptop": 4}
-// 	expReclaimVec = GenVector{"VeyronTab": 1, "VeyronPhone": 1, "VeyronDesktop": 1, "VeyronLaptop": 1}
-// 	setupAndTestReclaimVector(t, devArr, genVecArr, nil, expReclaimVec)
-//
-// 	// One device is missing from one other device.
-// 	genVecArr[0] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 3, "VeyronLaptop": 4}
-// 	genVecArr[1] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 2}
-// 	genVecArr[2] = GenVector{"VeyronTab": 1, "VeyronPhone": 1, "VeyronDesktop": 3, "VeyronLaptop": 1}
-// 	genVecArr[3] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 1, "VeyronLaptop": 4}
-// 	expReclaimVec = GenVector{"VeyronTab": 1, "VeyronPhone": 1, "VeyronDesktop": 1, "VeyronLaptop": 0}
-// 	setupAndTestReclaimVector(t, devArr, genVecArr, nil, expReclaimVec)
-//
-// 	// All devices know at least "n" generations from other devices.
-// 	var n GenId = 10
-// 	genVecArr[0] = GenVector{"VeyronTab": n + 10, "VeyronPhone": n,
-// 		"VeyronDesktop": n + 8, "VeyronLaptop": n + 4}
-// 	genVecArr[1] = GenVector{"VeyronTab": n + 6, "VeyronPhone": n + 10,
-// 		"VeyronDesktop": n, "VeyronLaptop": n + 3}
-// 	genVecArr[2] = GenVector{"VeyronTab": n, "VeyronPhone": n + 2,
-// 		"VeyronDesktop": n + 10, "VeyronLaptop": n}
-// 	genVecArr[3] = GenVector{"VeyronTab": n + 7, "VeyronPhone": n + 1,
-// 		"VeyronDesktop": n + 5, "VeyronLaptop": n + 10}
-// 	expReclaimVec = GenVector{"VeyronTab": n, "VeyronPhone": n, "VeyronDesktop": n, "VeyronLaptop": n}
-// 	setupAndTestReclaimVector(t, devArr, genVecArr, nil, expReclaimVec)
-//
-// 	// Never contacted a device.
-// 	devArr = []DeviceId{"VeyronTab", "VeyronDesktop", "VeyronLaptop"}
-// 	genVecArr[0] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 3, "VeyronLaptop": 4}
-// 	genVecArr[1] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 3, "VeyronLaptop": 4}
-// 	genVecArr[2] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 3, "VeyronLaptop": 4}
-// 	expReclaimVec = GenVector{"VeyronTab": 0, "VeyronPhone": 0, "VeyronDesktop": 0, "VeyronLaptop": 0}
-// 	setupAndTestReclaimVector(t, devArr, genVecArr, nil, expReclaimVec)
-//
-// 	// Start from existing reclaim vector.
-// 	devArr = []DeviceId{"VeyronTab", "VeyronPhone", "VeyronDesktop", "VeyronLaptop"}
-// 	reclaimVec := GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 3, "VeyronLaptop": 4}
-// 	genVecArr[0] = GenVector{"VeyronTab": 6, "VeyronPhone": 6, "VeyronDesktop": 6, "VeyronLaptop": 6}
-// 	genVecArr[1] = GenVector{"VeyronTab": 6, "VeyronPhone": 6, "VeyronDesktop": 3, "VeyronLaptop": 6}
-// 	genVecArr[2] = GenVector{"VeyronTab": 6, "VeyronPhone": 6, "VeyronDesktop": 6, "VeyronLaptop": 4}
-// 	genVecArr[3] = GenVector{"VeyronTab": 1, "VeyronPhone": 2, "VeyronDesktop": 6, "VeyronLaptop": 6}
-//
-// 	setupAndTestReclaimVector(t, devArr, genVecArr, reclaimVec, reclaimVec)
-// }
-//
-// // setupAndTestReclaimVector is an utility function to test reclaim vector computation.
-// func setupAndTestReclaimVector(t *testing.T, devArr []DeviceId, genVecArr []GenVector, reclaimStart, expReclaimVec GenVector) {
-// 	devfile := getFileName()
-// 	defer os.Remove(devfile)
-//
-// 	s := &syncd{id: "VeyronTab"}
-// 	dtab, err := openDevTable(devfile, s)
-// 	if err != nil {
-// 		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-// 	}
-// 	if reclaimStart != nil {
-// 		dtab.head.ReclaimVec = reclaimStart
-// 	}
-//
-// 	for i := range devArr {
-// 		if err := dtab.putGenVec(devArr[i], genVecArr[i]); err != nil {
-// 			t.Errorf("Cannot put object %s (%v) in devTable file %s, err %v",
-// 				devArr[i], genVecArr[i], devfile, err)
-// 		}
-// 	}
-//
-// 	reclaimVec, err := dtab.computeReclaimVector()
-// 	if err != nil {
-// 		t.Fatalf("computeReclaimVector() failed devices: %v, vectors: %v in devTable file %s, err %v",
-// 			devArr, genVecArr, devfile, err)
-// 	}
-//
-// 	if !reflect.DeepEqual(reclaimVec, expReclaimVec) {
-// 		t.Fatalf("Data mismatch for reclaimVec %v instead of %v",
-// 			reclaimVec, expReclaimVec)
-// 	}
-//
-// 	if err := dtab.close(); err != nil {
-// 		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-// 	}
-// }
-//
-// // TestAddDevice tests adding a device to the devTable.
-// func TestAddDevice(t *testing.T) {
-// 	devfile := getFileName()
-// 	defer os.Remove(devfile)
-//
-// 	s := &syncd{id: "VeyronPhone"}
-// 	dtab, err := openDevTable(devfile, s)
-// 	if err != nil {
-// 		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-// 	}
-//
-// 	var dev DeviceId = "VeyronLaptop"
-// 	if err := dtab.addDevice(dev); err != nil {
-// 		t.Fatalf("Cannot add new device in devTable file %s, err %v", devfile, err)
-// 	}
-//
-// 	vec, err := dtab.getGenVec(dev)
-// 	if err != nil || vec == nil {
-// 		t.Fatalf("GetGenVec() can not find object %s in devTable file %s, err %v",
-// 			dev, devfile, err)
-// 	}
-// 	expVec := GenVector{dev: 0}
-// 	if !reflect.DeepEqual(vec, expVec) {
-// 		t.Errorf("Data mismatch for object %s in devTable file %s: %v instead of %v",
-// 			dev, devfile, vec, expVec)
-// 	}
-//
-// 	vec, err = dtab.getGenVec(dtab.s.id)
-// 	if err != nil || vec == nil {
-// 		t.Fatalf("GetGenVec() can not find object %s in devTable file %s, err %v",
-// 			dtab.s.id, devfile, err)
-// 	}
-// 	expVec = GenVector{dtab.s.id: 0, dev: 0}
-// 	if !reflect.DeepEqual(vec, expVec) {
-// 		t.Errorf("Data mismatch for object %s in devTable file %s: %v instead of %v",
-// 			dtab.s.id, devfile, vec, expVec)
-// 	}
-//
-// 	expVec = GenVector{dtab.s.id: 10, "VeyronDesktop": 40, dev: 80}
-// 	if err := dtab.putGenVec(dtab.s.id, expVec); err != nil {
-// 		t.Fatalf("PutGenVec() can not put object %s in devTable file %s, err %v",
-// 			dtab.s.id, devfile, err)
-// 	}
-// 	dev = "VeyronTab"
-// 	if err := dtab.addDevice(dev); err != nil {
-// 		t.Fatalf("Cannot add new device in devTable file %s, err %v", devfile, err)
-// 	}
-// 	expVec[dev] = 0
-//
-// 	vec, err = dtab.getGenVec(dtab.s.id)
-// 	if err != nil || vec == nil {
-// 		t.Fatalf("GetGenVec() can not find object %s in devTable file %s, err %v",
-// 			dtab.s.id, devfile, err)
-// 	}
-// 	if !reflect.DeepEqual(vec, expVec) {
-// 		t.Errorf("Data mismatch for object %s in devTable file %s: %v instead of %v",
-// 			dtab.s.id, devfile, vec, expVec)
-// 	}
-//
-// 	if err := dtab.close(); err != nil {
-// 		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-// 	}
-// }
-//
-// // TestUpdateReclaimVec tests updating the reclaim vector.
-// func TestUpdateReclaimVec(t *testing.T) {
-// 	devfile := getFileName()
-// 	defer os.Remove(devfile)
-//
-// 	s := &syncd{id: "VeyronPhone"}
-// 	dtab, err := openDevTable(devfile, s)
-// 	if err != nil {
-// 		t.Fatalf("Cannot open new devTable file %s, err %v", devfile, err)
-// 	}
-//
-// 	minGens := GenVector{"VeyronTab": 1, "VeyronDesktop": 3, "VeyronLaptop": 4}
-// 	if err := dtab.updateReclaimVec(minGens); err != nil {
-// 		t.Fatalf("Cannot update reclaimvec in devTable file %s, err %v", devfile, err)
-// 	}
-// 	expVec := GenVector{dtab.s.id: 0, "VeyronTab": 0, "VeyronDesktop": 2, "VeyronLaptop": 3}
-// 	if !reflect.DeepEqual(dtab.head.ReclaimVec, expVec) {
-// 		t.Errorf("Data mismatch for reclaimVec in devTable file %s: %v instead of %v",
-// 			devfile, dtab.head.ReclaimVec, expVec)
-// 	}
-//
-// 	dtab.head.ReclaimVec[DeviceId("VeyronTab")] = 4
-// 	minGens = GenVector{"VeyronTab": 1}
-// 	if err := dtab.updateReclaimVec(minGens); err == nil {
-// 		t.Fatalf("Update reclaimvec didn't fail in devTable file %s", devfile)
-// 	}
-//
-// 	minGens = GenVector{"VeyronTab": 5}
-// 	if err := dtab.updateReclaimVec(minGens); err != nil {
-// 		t.Fatalf("Cannot update reclaimvec in devTable file %s, err %v", devfile, err)
-// 	}
-// 	expVec = GenVector{dtab.s.id: 0, "VeyronTab": 4, "VeyronDesktop": 2, "VeyronLaptop": 3}
-// 	if !reflect.DeepEqual(dtab.head.ReclaimVec, expVec) {
-// 		t.Errorf("Data mismatch for reclaimVec in devTable file %s: %v instead of %v",
-// 			devfile, dtab.head.ReclaimVec, expVec)
-// 	}
-//
-// 	if err := dtab.close(); err != nil {
-// 		t.Errorf("Cannot close devTable file %s, err %v", devfile, err)
-// 	}
-// }
diff --git a/x/ref/services/syncbase/sync/ilog.go b/x/ref/services/syncbase/sync/ilog.go
deleted file mode 100644
index 9e35c24..0000000
--- a/x/ref/services/syncbase/sync/ilog.go
+++ /dev/null
@@ -1,627 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-// Package vsync provides veyron sync ILog utility functions.  ILog
-// (Indexed Log) provides log functionality with indexing support.
-// ILog stores log records that are locally generated or obtained over
-// the network.  Indexing is needed since sync needs to selectively
-// retrieve log records that belong to a particular device, syncroot
-// and generation during synchronization.
-//
-// When a device receives a request to send log records, it first
-// computes the missing generations between itself and the incoming
-// request on a per-syncroot basis. It then sends all the log records
-// belonging to each missing generation.  A device that receives log
-// records over the network replays all the records received from
-// another device in a single batch. Each replayed log record adds a
-// new version to the dag of the object contained in the log
-// record. At the end of replaying all the log records, conflict
-// detection and resolution is carried out for all the objects learned
-// during this iteration. Conflict detection and resolution is carried
-// out after a batch of log records are replayed, instead of
-// incrementally after each record is replayed, to avoid repeating
-// conflict resolution already performed by other devices.
-//
-// New log records are created when objects in the local store are
-// created/updated. Local log records are also replayed to keep the
-// per-object dags consistent with the local store state.
-//
-// Note that syncgroups are mainly tracked between syncd/store and the
-// client. Sync-related metadata (log records, generations and
-// generation vectors) is unaware of syncgroups, and uses syncroots
-// instead. Each syncgroup contains a root object and a unique root
-// object ID. In case of peer syncgroups, all the peer syncgroups
-// contain the same root object ID.  Sync translates any given
-// syncgroup to a syncroot rooted at this root object ID and syncs all
-// the syncroots the device is part of.  Thus, although a device may
-// be part of more than 1 peer syncgroup, at the sync level, a single
-// syncroot is synced on behalf of all the peer syncgroups.
-//
-// Implementation notes: ILog records are stored in a persistent K/V
-// database in the current implementation.  ILog db consists of 3
-// tables:
-// ** records: table consists of all the log records indexed
-// by deviceid:srid:genid:lsn referring to
-//         the device that creates the log record
-//         the root oid of the syncroot to which the log record belongs
-//         the generation on the syncroot the log record is part of
-//         the sequence number in that generation
-// Note that lsn in each generation starts from 0 and genid starts from 1.
-// ** gens: table consists of the generation metadata for each
-// generation, and is indexed by deviceid:srid:genid.
-// ** head: table consists of the log header.
-import (
-	"errors"
-	"fmt"
-	"strconv"
-	"strings"
-
-	"v.io/x/lib/vlog"
-)
-
-var (
-	errNoUpdates  = errors.New("no new local updates")
-	errInvalidLog = errors.New("invalid log db")
-)
-
-// curLocalGen contains metadata re. the current local generation for a SyncRoot.
-type curLocalGen struct {
-	CurGenNum GenId  // generation id for a SyncRoot's current generation.
-	CurSeqNum SeqNum // log sequence number for a SyncRoot's current generation.
-}
-
-// iLogHeader contains the log header metadata.
-type iLogHeader struct {
-	CurSRGens map[ObjId]*curLocalGen // local generation info per SyncRoot.
-	Curorder  uint32                 // position in log for the next generation.
-}
-
-// genMetadata contains the metadata for a generation.
-type genMetadata struct {
-	// All generations stored in the log are ordered wrt each
-	// other and this order needs to be preserved.
-	// Position of this generation in the log.
-	Pos uint32
-
-	// Number of log records in this generation still stored in the log.
-	// This count is used during garbage collection.
-	Count uint64
-
-	// Maximum SeqNum that was part of this generation.
-	// This is useful during garbage collection to track any unclaimed log records.
-	MaxSeqNum SeqNum
-}
-
-// iLog contains the metadata for the ILog db.
-type iLog struct {
-	fname string // file pathname.
-	db    *kvdb  // underlying k/v db.
-
-	// Key:deviceid:srid:genid:lsn Value:LogRecord. Pointer to
-	// the "records" table in the kvdb. Contains log records.
-	records *kvtable
-
-	// Key:deviceid:srid:genid Value:genMetadata. Pointer to the
-	// "gens" table in the kvdb. Contains generation metadata for
-	// each generation.
-	gens *kvtable
-
-	// Key:"Head" Value:iLogHeader. Pointer to the "header" table
-	// in the kvdb. Contains logheader.
-	header *kvtable
-
-	head *iLogHeader // log head cached in memory.
-
-	s *syncd // pointer to the sync daemon object.
-}
-
-// openILog opens or creates a ILog for the given filename.
-func openILog(filename string, sin *syncd) (*iLog, error) {
-	ilog := &iLog{
-		fname: filename,
-		head:  &iLogHeader{},
-		s:     sin,
-	}
-	// Open the file and create it if it does not exist.
-	// Also initialize the kvdb and its three collections.
-	db, tbls, err := kvdbOpen(filename, []string{"records", "gens", "header"})
-	if err != nil {
-		return nil, err
-	}
-
-	ilog.db = db
-	ilog.records = tbls[0]
-	ilog.gens = tbls[1]
-	ilog.header = tbls[2]
-
-	// If header already exists in db, read it back from db.
-	if ilog.hasHead() {
-		if err := ilog.getHead(); err != nil {
-			ilog.db.close() // this also closes the tables.
-			return nil, err
-		}
-	} else {
-		ilog.head.CurSRGens = make(map[ObjId]*curLocalGen)
-	}
-
-	return ilog, nil
-}
-
-// close closes the ILog and invalidate its struct.
-func (l *iLog) close() error {
-	if l.db == nil {
-		return errInvalidLog
-	}
-	// Flush the dirty data.
-	if err := l.flush(); err != nil {
-		return err
-	}
-
-	l.db.close() // this also closes the tables.
-
-	*l = iLog{} // zero out the ILog struct.
-	return nil
-}
-
-// flush flushes the ILog db to disk.
-func (l *iLog) flush() error {
-	if l.db == nil {
-		return errInvalidLog
-	}
-	// Set the head from memory before flushing.
-	if err := l.putHead(); err != nil {
-		return err
-	}
-
-	l.db.flush()
-
-	return nil
-}
-
-// initSyncRoot initializes the log header for this SyncRoot.
-func (l *iLog) initSyncRoot(srid ObjId) error {
-	if l.db == nil {
-		return errInvalidLog
-	}
-	if _, ok := l.head.CurSRGens[srid]; ok {
-		return fmt.Errorf("syncroot already exists %v", srid)
-	}
-	// First generation to be created is generation 1. A
-	// generation of 0 represents no updates on the device.
-	l.head.CurSRGens[srid] = &curLocalGen{CurGenNum: 1}
-	return nil
-}
-
-// delSyncRoot deletes the log header for this SyncRoot.
-func (l *iLog) delSyncRoot(srid ObjId) error {
-	if l.db == nil {
-		return errInvalidLog
-	}
-	if _, ok := l.head.CurSRGens[srid]; !ok {
-		return fmt.Errorf("syncroot doesn't exist %v", srid)
-	}
-
-	delete(l.head.CurSRGens, srid)
-	return nil
-}
-
-// putHead puts the log head into the ILog db.
-func (l *iLog) putHead() error {
-	return l.header.set("Head", l.head)
-}
-
-// getHead gets the log head from the ILog db.
-func (l *iLog) getHead() error {
-	if l.head == nil {
-		return errors.New("nil log header")
-	}
-	if err := l.header.get("Head", l.head); err != nil {
-		return err
-	}
-	// When we put an empty map in kvdb, we get back a "nil" map
-	// (due to VOM encoding/decoding). To keep putHead/getHead
-	// symmetrical, we add this additional initialization.
-	if l.head.CurSRGens == nil {
-		l.head.CurSRGens = make(map[ObjId]*curLocalGen)
-	}
-	return nil
-}
-
-// hasHead returns true if the ILog db has a log head.
-func (l *iLog) hasHead() bool {
-	return l.header.hasKey("Head")
-}
-
-// logRecKey creates a key for a log record.
-func logRecKey(devid DeviceId, srid ObjId, gnum GenId, lsn SeqNum) string {
-	return fmt.Sprintf("%s:%s:%d:%d", devid, srid.String(), uint64(gnum), uint64(lsn))
-}
-
-// strToGenId converts a string to GenId.
-func strToGenId(genIDStr string) (GenId, error) {
-	id, err := strconv.ParseUint(genIDStr, 10, 64)
-	if err != nil {
-		return 0, err
-	}
-	return GenId(id), nil
-}
-
-// strToObjId converts a string to ObjId.
-func strToObjId(objIDStr string) (ObjId, error) {
-	return ObjId(objIDStr), nil
-}
-
-// splitLogRecKey splits a : separated logrec key into its components.
-func splitLogRecKey(key string) (DeviceId, ObjId, GenId, SeqNum, error) {
-	args := strings.Split(key, ":")
-	if len(args) != 4 {
-		return "", NoObjId, 0, 0, fmt.Errorf("bad logrec key %s", key)
-	}
-	srid, err := strToObjId(args[1])
-	if err != nil {
-		return "", NoObjId, 0, 0, err
-	}
-	gnum, err := strToGenId(args[2])
-	if err != nil {
-		return "", NoObjId, 0, 0, err
-	}
-	lsn, err := strconv.ParseUint(args[3], 10, 64)
-	if err != nil {
-		return "", NoObjId, 0, 0, err
-	}
-	return DeviceId(args[0]), srid, gnum, SeqNum(lsn), nil
-}
-
-// putLogRec puts the log record into the ILog db.
-func (l *iLog) putLogRec(rec *LogRec) (string, error) {
-	if l.db == nil {
-		return "", errInvalidLog
-	}
-	key := logRecKey(rec.DevId, rec.SyncRootId, rec.GenNum, rec.SeqNum)
-	return key, l.records.set(key, rec)
-}
-
-// getLogRec gets the log record from the ILog db.
-func (l *iLog) getLogRec(devid DeviceId, srid ObjId, gnum GenId, lsn SeqNum) (*LogRec, error) {
-	if l.db == nil {
-		return nil, errInvalidLog
-	}
-	key := logRecKey(devid, srid, gnum, lsn)
-	var rec LogRec
-	if err := l.records.get(key, &rec); err != nil {
-		return nil, err
-	}
-	return &rec, nil
-}
-
-// hasLogRec returns true if the ILog db has a log record matching (devid, srid, gnum, lsn).
-func (l *iLog) hasLogRec(devid DeviceId, srid ObjId, gnum GenId, lsn SeqNum) bool {
-	if l.db == nil {
-		return false
-	}
-	key := logRecKey(devid, srid, gnum, lsn)
-	return l.records.hasKey(key)
-}
-
-// delLogRec deletes the log record matching (devid, srid, gnum, lsn) from the ILog db.
-func (l *iLog) delLogRec(devid DeviceId, srid ObjId, gnum GenId, lsn SeqNum) error {
-	if l.db == nil {
-		return errInvalidLog
-	}
-	key := logRecKey(devid, srid, gnum, lsn)
-	return l.records.del(key)
-}
-
-// generationKey creates a key for a generation.
-func generationKey(devid DeviceId, srid ObjId, gnum GenId) string {
-	return fmt.Sprintf("%s:%s:%d", devid, srid.String(), gnum)
-}
-
-// splitGenerationKey splits a : separated generation key into its components.
-func splitGenerationKey(key string) (DeviceId, ObjId, GenId, error) {
-	args := strings.Split(key, ":")
-	if len(args) != 3 {
-		return "", NoObjId, 0, fmt.Errorf("bad generation key %s", key)
-	}
-	srid, err := strToObjId(args[1])
-	if err != nil {
-		return "", NoObjId, 0, err
-	}
-	gnum, err := strToGenId(args[2])
-	if err != nil {
-		return "", NoObjId, 0, err
-	}
-	return DeviceId(args[0]), srid, gnum, nil
-}
-
-// putGenMetadata puts the metadata of the generation (devid, srid, gnum) into the ILog db.
-func (l *iLog) putGenMetadata(devid DeviceId, srid ObjId, gnum GenId, val *genMetadata) error {
-	key := generationKey(devid, srid, gnum)
-	return l.gens.set(key, val)
-}
-
-// getGenMetadata gets the metadata of the generation (devid, srid, gnum) from the ILog db.
-func (l *iLog) getGenMetadata(devid DeviceId, srid ObjId, gnum GenId) (*genMetadata, error) {
-	if l.db == nil {
-		return nil, errInvalidLog
-	}
-	key := generationKey(devid, srid, gnum)
-	var val genMetadata
-	if err := l.gens.get(key, &val); err != nil {
-		return nil, err
-	}
-	return &val, nil
-}
-
-// hasGenMetadata returns true if the ILog db has the generation (devid, srid, gnum).
-func (l *iLog) hasGenMetadata(devid DeviceId, srid ObjId, gnum GenId) bool {
-	key := generationKey(devid, srid, gnum)
-	return l.gens.hasKey(key)
-}
-
-// delGenMetadata deletes the generation (devid, srid, gnum) metadata from the ILog db.
-func (l *iLog) delGenMetadata(devid DeviceId, srid ObjId, gnum GenId) error {
-	if l.db == nil {
-		return errInvalidLog
-	}
-	key := generationKey(devid, srid, gnum)
-	return l.gens.del(key)
-}
-
-// getLocalGenInfo gets the local generation info for the given syncroot.
-func (l *iLog) getLocalGenInfo(srid ObjId) (*curLocalGen, error) {
-	gen, ok := l.head.CurSRGens[srid]
-	if !ok {
-		return nil, fmt.Errorf("no gen info found for srid %s", srid.String())
-	}
-	return gen, nil
-}
-
-// createLocalLogRec creates a new local log record of type NodeRec.
-func (l *iLog) createLocalLogRec(obj ObjId, vers Version,
-	par []Version, val *LogValue, srid ObjId) (*LogRec, error) {
-	gen, err := l.getLocalGenInfo(srid)
-	if err != nil {
-		return nil, err
-	}
-	rec := &LogRec{
-		DevId:      l.s.id,
-		SyncRootId: srid,
-		GenNum:     gen.CurGenNum,
-		SeqNum:     gen.CurSeqNum,
-		RecType:    NodeRec,
-
-		ObjId:   obj,
-		CurVers: vers,
-		Parents: par,
-		Value:   *val,
-	}
-
-	// Increment the SeqNum for the local log.
-	gen.CurSeqNum++
-
-	return rec, nil
-}
-
-// createLocalLinkLogRec creates a new local log record of type LinkRec.
-func (l *iLog) createLocalLinkLogRec(obj ObjId, vers, par Version, srid ObjId) (*LogRec, error) {
-	gen, err := l.getLocalGenInfo(srid)
-	if err != nil {
-		return nil, err
-	}
-	rec := &LogRec{
-		DevId:      l.s.id,
-		SyncRootId: srid,
-		GenNum:     gen.CurGenNum,
-		SeqNum:     gen.CurSeqNum,
-		RecType:    LinkRec,
-
-		ObjId:   obj,
-		CurVers: vers,
-		Parents: []Version{par},
-		Value:   LogValue{},
-	}
-
-	// Increment the SeqNum for the local log.
-	gen.CurSeqNum++
-
-	return rec, nil
-}
-
-// createRemoteGeneration adds a new remote generation.
-func (l *iLog) createRemoteGeneration(dev DeviceId, srid ObjId, gnum GenId, gen *genMetadata) error {
-	if l.db == nil {
-		return errInvalidLog
-	}
-
-	if gen.Count != uint64(gen.MaxSeqNum+1) {
-		return errors.New("mismatch in count and lsn")
-	}
-
-	vlog.VI(2).Infof("createRemoteGeneration:: dev %s srid %s gen %d %v", dev, srid.String(), gnum, gen)
-
-	gen.Pos = l.head.Curorder
-	l.head.Curorder++
-
-	return l.putGenMetadata(dev, srid, gnum, gen)
-}
-
-// createLocalGeneration creates a new local generation.
-func (l *iLog) createLocalGeneration(srid ObjId) (GenId, error) {
-	if l.db == nil {
-		return 0, errInvalidLog
-	}
-
-	g, err := l.getLocalGenInfo(srid)
-	if err != nil {
-		return 0, err
-	}
-
-	gnum := g.CurGenNum
-
-	// If there are no updates, there will be no new generation.
-	if g.CurSeqNum == 0 {
-		return gnum - 1, errNoUpdates
-	}
-
-	// Add the current generation to the db.
-	val := &genMetadata{
-		Pos:       l.head.Curorder,
-		Count:     uint64(g.CurSeqNum),
-		MaxSeqNum: g.CurSeqNum - 1,
-	}
-
-	err = l.putGenMetadata(l.s.id, srid, gnum, val)
-
-	vlog.VI(2).Infof("createLocalGeneration:: created srid %s gen %d %v", srid.String(), gnum, val)
-	// Move to the next generation irrespective of err.
-	l.head.Curorder++
-	g.CurGenNum++
-	g.CurSeqNum = 0
-
-	return gnum, err
-}
-
-// processWatchRecord processes new object versions obtained from the local store.
-func (l *iLog) processWatchRecord(objID ObjId, vers, parent Version, val *LogValue, srid ObjId) error {
-	if l.db == nil {
-		return errInvalidLog
-	}
-
-	vlog.VI(2).Infof("processWatchRecord:: adding for object %v %v (srid %v)", objID, vers, srid)
-
-	if !srid.IsValid() {
-		return errors.New("invalid syncroot id")
-	}
-
-	// Filter out the echo from the watcher. When syncd puts mutations into store,
-	// it hears back these mutations once again on the watch stream. We need to
-	// filter out these echoes and only process brand new watch changes.
-	if vers != NoVersion {
-		// Check if the object's vers already exists in the DAG.
-		if l.s.dag.hasNode(objID, vers) {
-			return nil
-		}
-
-		// When we successfully join a SyncGroup, Store
-		// creates an empty directory with object ID as the
-		// rootObjId of the SyncGroup joined.  We do not care
-		// about this version of the directory. We will start
-		// accepting local mutations on this object only after
-		// we hear about it remotely first.
-		//
-		// NOTE: Since this new directory is protected from
-		// any local updates via an Permissions until after the first
-		// sync, waiting to hear remotely first works without
-		// dropping any updates. However, we cache in the
-		// "priv" table its version in the Store so that we
-		// can correctly specify prior version when we put
-		// mutations into the store.
-		if _, err := l.s.dag.getHead(objID); err != nil && objID == srid {
-			priv := &privNode{ /*Mutation: &val.Mutation, */ SyncTime: val.SyncTime, TxId: val.TxId, TxCount: val.TxCount}
-			return l.s.dag.setPrivNode(objID, priv)
-		}
-	} else {
-		// Check if the parent version has a deleted
-		// descendant already in the DAG.
-		if l.s.dag.hasDeletedDescendant(objID, parent) {
-			return nil
-		}
-	}
-
-	var pars []Version
-	if parent != NoVersion {
-		pars = []Version{parent}
-	}
-
-	// If the current version is a deletion, generate a new version number.
-	if val.Delete {
-		if vers != NoVersion {
-			return fmt.Errorf("deleted vers is %v", vers)
-		}
-		vers = NewVersion()
-		//val.Mutation.Version = vers
-	}
-
-	// Create a log record from Watch's Change Record.
-	rec, err := l.createLocalLogRec(objID, vers, pars, val, srid)
-	if err != nil {
-		return err
-	}
-
-	// Insert the new log record into the log.
-	logKey, err := l.putLogRec(rec)
-	if err != nil {
-		return err
-	}
-
-	// Insert the new log record into dag.
-	if err = l.s.dag.addNode(rec.ObjId, rec.CurVers, false, val.Delete, rec.Parents, logKey, val.TxId); err != nil {
-		return err
-	}
-
-	// Move the head.
-	return l.s.dag.moveHead(rec.ObjId, rec.CurVers)
-}
-
-// dump writes to the log file information on ILog internals.
-func (l *iLog) dump() {
-	if l.db == nil {
-		return
-	}
-
-	vlog.VI(1).Infof("DUMP: ILog: #SR %d, cur %d", len(l.head.CurSRGens), l.head.Curorder)
-	for sr, gen := range l.head.CurSRGens {
-		vlog.VI(1).Infof("DUMP: ILog: SR %v: gen %v, lsn %v", sr, gen.CurGenNum, gen.CurSeqNum)
-	}
-
-	// Find for each (devid, srid) pair its lowest and highest generation numbers.
-	type genInfo struct{ min, max GenId }
-	devs := make(map[DeviceId]map[ObjId]*genInfo)
-
-	l.gens.keyIter(func(genKey string) {
-		devid, srid, gnum, err := splitGenerationKey(genKey)
-		if err != nil {
-			return
-		}
-
-		srids, ok := devs[devid]
-		if !ok {
-			srids = make(map[ObjId]*genInfo)
-			devs[devid] = srids
-		}
-
-		info, ok := srids[srid]
-		if ok {
-			if gnum > info.max {
-				info.max = gnum
-			}
-			if gnum < info.min {
-				info.min = gnum
-			}
-		} else {
-			srids[srid] = &genInfo{min: gnum, max: gnum}
-		}
-	})
-
-	// For each (devid, srid) pair dump its generation info in order from
-	// min to max generation numbers, inclusive.
-	for devid, srids := range devs {
-		for srid, info := range srids {
-			for gnum := info.min; gnum <= info.max; gnum++ {
-				meta, err := l.getGenMetadata(devid, srid, gnum)
-				if err == nil {
-					vlog.VI(1).Infof(
-						"DUMP: ILog: gen (%v, %v, %v): pos %v, count %v, maxlsn %v",
-						devid, srid, gnum, meta.Pos, meta.Count, meta.MaxSeqNum)
-				} else {
-					vlog.VI(1).Infof("DUMP: ILog: gen (%v, %v, %v): missing generation",
-						devid, srid, gnum)
-				}
-			}
-		}
-	}
-}
diff --git a/x/ref/services/syncbase/sync/ilog_test.go b/x/ref/services/syncbase/sync/ilog_test.go
deleted file mode 100644
index ba12bf0..0000000
--- a/x/ref/services/syncbase/sync/ilog_test.go
+++ /dev/null
@@ -1,1024 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-// Tests for the Veyron Sync ILog component.
-import (
-	"fmt"
-	"math/rand"
-	"os"
-	"reflect"
-	"runtime"
-	"testing"
-)
-
-// TestILogStore tests creating a backing file for ILog.
-func TestILogStore(t *testing.T) {
-	logfile := getFileName()
-	defer os.Remove(logfile)
-
-	log, err := openILog(logfile, nil)
-	if err != nil {
-		t.Fatalf("Cannot open new ILog file %s, err %v", logfile, err)
-	}
-
-	fsize := getFileSize(logfile)
-	if fsize < 0 {
-		//t.Errorf("Log file %s not created", logfile)
-	}
-
-	if err := log.flush(); err != nil {
-		t.Errorf("Cannot flush ILog file %s, err %v", logfile, err)
-	}
-
-	oldfsize := fsize
-	fsize = getFileSize(logfile)
-	if fsize <= oldfsize {
-		//t.Errorf("Log file %s not flushed", logfile)
-	}
-
-	if err := log.close(); err != nil {
-		t.Errorf("Cannot close ILog file %s, err %v", logfile, err)
-	}
-
-	oldfsize = getFileSize(logfile)
-
-	log, err = openILog(logfile, nil)
-	if err != nil {
-		t.Fatalf("Cannot re-open existing log file %s, err %v", logfile, err)
-	}
-
-	fsize = getFileSize(logfile)
-	if fsize != oldfsize {
-		t.Errorf("Log file %s size changed across re-open (%d %d)", logfile, fsize, oldfsize)
-	}
-
-	if err := log.flush(); err != nil {
-		t.Errorf("Cannot flush ILog file %s, err %v", logfile, err)
-	}
-
-	if err := log.close(); err != nil {
-		t.Errorf("Cannot close ILog file %s, err %v", logfile, err)
-	}
-}
-
-// TestInvalidLog tests log methods on an invalid (closed) log ptr.
-func TestInvalidLog(t *testing.T) {
-	logfile := getFileName()
-	defer os.Remove(logfile)
-
-	log, err := openILog(logfile, nil)
-	if err != nil {
-		t.Fatalf("Cannot open new ILog file %s, err %v", logfile, err)
-	}
-
-	if err := log.close(); err != nil {
-		t.Errorf("Cannot close ILog file %s, err %v", logfile, err)
-	}
-
-	validateError := func(err error, funcName string) {
-		_, file, line, _ := runtime.Caller(1)
-		if err == nil || err != errInvalidLog {
-			t.Errorf("%s:%d %s() did not fail on a closed log: %v", file, line, funcName, err)
-		}
-	}
-
-	err = log.close()
-	validateError(err, "close")
-
-	err = log.flush()
-	validateError(err, "flush")
-
-	srid := ObjId("haha")
-
-	err = log.initSyncRoot(srid)
-	validateError(err, "initSyncRoot")
-
-	_, err = log.putLogRec(&LogRec{})
-	validateError(err, "putLogRec")
-
-	var devid DeviceId = "VeyronPhone"
-	var gnum GenId = 1
-	var lsn SeqNum
-
-	_, err = log.getLogRec(devid, srid, gnum, lsn)
-	validateError(err, "getLogRec")
-
-	if log.hasLogRec(devid, srid, gnum, lsn) {
-		t.Errorf("hasLogRec() did not fail on a closed log: %v", err)
-	}
-
-	err = log.delLogRec(devid, srid, gnum, lsn)
-	validateError(err, "delLogRec")
-
-	_, err = log.getGenMetadata(devid, srid, gnum)
-	validateError(err, "getGenMetadata")
-
-	err = log.delGenMetadata(devid, srid, gnum)
-	validateError(err, "delGenMetadata")
-
-	err = log.createRemoteGeneration(devid, srid, gnum, &genMetadata{})
-	validateError(err, "createRemoteGeneration")
-
-	_, err = log.createLocalGeneration(srid)
-	validateError(err, "createLocalGeneration")
-
-	err = log.processWatchRecord(ObjId("foobar"), 2, Version(999), &LogValue{}, srid)
-	validateError(err, "processWatchRecord")
-
-	// Harmless NOP.
-	log.dump()
-}
-
-// TestPutGetLogHeader tests setting and getting log header across log open/close/reopen.
-func TestPutGetLogHeader(t *testing.T) {
-	logfile := getFileName()
-	defer os.Remove(logfile)
-
-	log, err := openILog(logfile, nil)
-	if err != nil {
-		t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
-	}
-
-	// In memory head should be initialized.
-	if len(log.head.CurSRGens) != 0 || log.head.Curorder != 0 {
-		t.Errorf("First time log create should reset header")
-	}
-
-	// No head should be there in db.
-	if err = log.getHead(); err == nil {
-		t.Errorf("getHead() found non-existent head in log file %s, err %v", logfile, err)
-	}
-
-	if log.hasHead() {
-		t.Errorf("hasHead() found non-existent head in log file %s", logfile)
-	}
-
-	expVal := map[ObjId]*curLocalGen{ObjId("bla"): &curLocalGen{CurGenNum: 10, CurSeqNum: 100},
-		ObjId("qwerty"): &curLocalGen{CurGenNum: 40, CurSeqNum: 80}}
-
-	log.head = &iLogHeader{
-		CurSRGens: expVal,
-		Curorder:  1000,
-	}
-
-	if err := log.putHead(); err != nil {
-		t.Errorf("Cannot put head %v in log file %s, err %v", log.head, logfile, err)
-	}
-
-	// Reset values.
-	log.head.CurSRGens = nil
-	log.head.Curorder = 0
-
-	for i := 0; i < 2; i++ {
-		if err := log.getHead(); err != nil {
-			t.Fatalf("getHead() can not find head (i=%d) in log file %s, err %v", i, logfile, err)
-		}
-
-		if !log.hasHead() {
-			t.Errorf("hasHead() can not find head (i=%d) in log file %s", i, logfile)
-		}
-
-		if !reflect.DeepEqual(log.head.CurSRGens, expVal) || log.head.Curorder != 1000 {
-			t.Errorf("Data mismatch for head (i=%d) in log file %s: %v",
-				i, logfile, log.head)
-		}
-
-		if i == 0 {
-			if err := log.close(); err != nil {
-				t.Errorf("Cannot close log file %s, err %v", logfile, err)
-			}
-			log, err = openILog(logfile, nil)
-			if err != nil {
-				t.Fatalf("Cannot re-open log file %s, err %v", logfile, err)
-			}
-		}
-	}
-
-	if err := log.close(); err != nil {
-		t.Errorf("Cannot close log file %s, err %v", logfile, err)
-	}
-}
-
-// TestPersistLogHeader tests that log header is automatically persisted across log open/close/reopen.
-func TestPersistLogHeader(t *testing.T) {
-	logfile := getFileName()
-	defer os.Remove(logfile)
-
-	log, err := openILog(logfile, nil)
-	if err != nil {
-		t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
-	}
-
-	// In memory head should be initialized.
-	if len(log.head.CurSRGens) != 0 || log.head.Curorder != 0 {
-		t.Errorf("First time log create should reset header")
-	}
-
-	expVal := map[ObjId]*curLocalGen{ObjId("blabla"): &curLocalGen{CurGenNum: 10, CurSeqNum: 100},
-		ObjId("xyz"): &curLocalGen{CurGenNum: 40, CurSeqNum: 80}}
-	log.head = &iLogHeader{
-		CurSRGens: expVal,
-		Curorder:  1000,
-	}
-
-	if err = log.close(); err != nil {
-		t.Errorf("Cannot close log file %s, err %v", logfile, err)
-	}
-
-	log, err = openILog(logfile, nil)
-	if err != nil {
-		t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
-	}
-
-	// In memory head should be initialized from db.
-	if !reflect.DeepEqual(log.head.CurSRGens, expVal) || log.head.Curorder != 1000 {
-		t.Errorf("Data mismatch for head in log file %s: %v", logfile, log.head)
-	}
-
-	for sr := range expVal {
-		expVal[sr] = &curLocalGen{CurGenNum: 1000, CurSeqNum: 10}
-		break
-	}
-	log.head = &iLogHeader{
-		CurSRGens: expVal,
-		Curorder:  100,
-	}
-
-	if err := log.flush(); err != nil {
-		t.Errorf("Cannot flush ILog file %s, err %v", logfile, err)
-	}
-
-	// Reset values.
-	log.head.CurSRGens = nil
-	log.head.Curorder = 0
-
-	if err := log.getHead(); err != nil {
-		t.Fatalf("getHead() can not find head in log file %s, err %v", logfile, err)
-	}
-
-	// In memory head should be initialized from db.
-	if !reflect.DeepEqual(log.head.CurSRGens, expVal) || log.head.Curorder != 100 {
-		t.Errorf("Data mismatch for head in log file %s: %v", logfile, log.head)
-	}
-
-	if err = log.close(); err != nil {
-		t.Errorf("Cannot close log file %s, err %v", logfile, err)
-	}
-}
-
-// TestLogInitDelSyncRoot tests initing and deleting a new SyncRoot.
-func TestLogInitDelSyncRoot(t *testing.T) {
-	logfile := getFileName()
-	defer os.Remove(logfile)
-
-	log, err := openILog(logfile, nil)
-	if err != nil {
-		t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
-	}
-
-	srid1 := ObjId("haha")
-	if err := log.initSyncRoot(srid1); err != nil {
-		t.Fatalf("Cannot create new SyncRoot %s, err %v", srid1.String(), err)
-	}
-	srid2 := ObjId("asdf")
-	if err := log.initSyncRoot(srid2); err != nil {
-		t.Fatalf("Cannot create new SyncRoot %s, err %v", srid2.String(), err)
-	}
-	if err := log.initSyncRoot(srid2); err == nil {
-		t.Fatalf("Creating existing SyncRoot didn't fail %s, err %v", srid2.String(), err)
-	}
-	expGen := &curLocalGen{CurGenNum: 1, CurSeqNum: 0}
-	expSRGens := map[ObjId]*curLocalGen{
-		srid1: expGen,
-		srid2: expGen,
-	}
-	if !reflect.DeepEqual(log.head.CurSRGens, expSRGens) {
-		t.Errorf("Data mismatch for head in log file %s: %v instead of %v",
-			logfile, log.head.CurSRGens, expSRGens)
-	}
-
-	if err := log.delSyncRoot(srid1); err != nil {
-		t.Fatalf("Cannot delete SyncRoot %s, err %v", srid1.String(), err)
-	}
-	if err := log.delSyncRoot(srid2); err != nil {
-		t.Fatalf("Cannot delete SyncRoot %s, err %v", srid1.String(), err)
-	}
-	if err := log.delSyncRoot(srid1); err == nil {
-		t.Fatalf("Deleting non-existent SyncRoot didn't fail %s", srid1.String())
-	}
-	expSRGens = map[ObjId]*curLocalGen{}
-	if !reflect.DeepEqual(log.head.CurSRGens, expSRGens) {
-		t.Errorf("Data mismatch for head in log file %s: %v instead of %v",
-			logfile, log.head.CurSRGens, expSRGens)
-	}
-
-	if err := log.putHead(); err != nil {
-		t.Errorf("Cannot put head %v in log file %s, err %v", log.head, logfile, err)
-	}
-
-	// Reset values.
-	log.head.CurSRGens = nil
-	log.head.Curorder = 0
-
-	if err := log.getHead(); err != nil {
-		t.Fatalf("getHead() can not find head in log file %s, err %v", logfile, err)
-	}
-
-	if !reflect.DeepEqual(log.head.CurSRGens, expSRGens) {
-		t.Errorf("Data mismatch for head in log file %s: %v instead of %v",
-			logfile, log.head.CurSRGens, expSRGens)
-	}
-}
-
-// TestStringAndParseKeys tests conversion to/from string for log record and generation keys.
-func TestStringAndParseKeys(t *testing.T) {
-	var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
-	randString := func(n int) string {
-		b := make([]rune, n)
-		for i := range b {
-			b[i] = letters[rand.Intn(len(letters))]
-		}
-		return string(b)
-	}
-
-	for i := 0; i < 10; i++ {
-		devid := DeviceId(randString(rand.Intn(20) + 1))
-		srid := ObjId(fmt.Sprintf("haha-%d", i))
-		gnum := GenId(rand.Int63())
-		lsn := SeqNum(rand.Int63())
-
-		devid1, srid1, gnum1, lsn1, err := splitLogRecKey(logRecKey(devid, srid, gnum, lsn))
-		if err != nil || devid1 != devid || srid1 != srid || gnum1 != gnum || lsn1 != lsn {
-			t.Fatalf("LogRec conversion failed in iter %d: got %s %v %v %v want %s %v %v %v, err %v",
-				i, devid1, srid1, gnum1, lsn1, devid, srid, gnum, lsn, err)
-		}
-
-		devid2, srid2, gnum2, err := splitGenerationKey(generationKey(devid, srid, gnum))
-		if err != nil || devid2 != devid || srid2 != srid || gnum2 != gnum {
-			t.Fatalf("Gen conversion failed in iter %d: got %s %v %v want %s %v %v, err %v",
-				i, devid2, srid2, gnum2, devid, srid, gnum, err)
-		}
-	}
-
-	//badStrs := []string{"abc:3:4", "abc:123:4:5", "abc:1234:-1:10", "ijk:7890:1000:-1", "abc:3:4:5:6", "abc::3:4:5"}
-	badStrs := []string{"abc:3:4", "abc:1234:-1:10", "ijk:7890:1000:-1", "abc:3:4:5:6", "abc::3:4:5"}
-	for _, s := range badStrs {
-		if _, _, _, _, err := splitLogRecKey(s); err == nil {
-			t.Fatalf("LogRec conversion didn't fail for str %s", s)
-		}
-	}
-
-	//badStrs = []string{"abc:3", "abc:123:4", "abc:1234:-1", "abc:3:4:5", "abc:4::5"}
-	badStrs = []string{"abc:3", "abc:1234:-1", "abc:3:4:5", "abc:4::5"}
-	for _, s := range badStrs {
-		if _, _, _, err := splitGenerationKey(s); err == nil {
-			t.Fatalf("Gen conversion didn't fail for str %s", s)
-		}
-	}
-}
-
-// TestPutGetLogRec tests setting and getting a log record across log open/close/reopen.
-func TestPutGetLogRec(t *testing.T) {
-	logfile := getFileName()
-	defer os.Remove(logfile)
-
-	log, err := openILog(logfile, nil)
-	if err != nil {
-		t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
-	}
-
-	var devid DeviceId = "VeyronTab"
-	srid := ObjId("haha")
-	var gnum GenId = 100
-	var lsn SeqNum = 1000
-
-	rec, err := log.getLogRec(devid, srid, gnum, lsn)
-	if err == nil || rec != nil {
-		t.Errorf("GetLogRec() found non-existent object %s:%s:%d:%d in log file %s: %v, err %v",
-			devid, srid.String(), gnum, lsn, logfile, rec, err)
-	}
-
-	if log.hasLogRec(devid, srid, gnum, lsn) {
-		t.Errorf("HasLogRec() found non-existent object %s:%s:%d:%d in log file %s",
-			devid, srid.String(), gnum, lsn, logfile)
-	}
-
-	rec = &LogRec{
-		DevId:      devid,
-		SyncRootId: srid,
-		GenNum:     gnum,
-		SeqNum:     lsn,
-		ObjId:      ObjId("bla"),
-		CurVers:    2,
-		Parents:    []Version{0, 1},
-		Value:      LogValue{},
-	}
-
-	if _, err := log.putLogRec(rec); err != nil {
-		t.Errorf("Cannot put object %s:%s:%d:%d (%v) in log file %s, err %v", devid, srid.String(), gnum, lsn, rec, logfile, err)
-	}
-
-	for i := 0; i < 2; i++ {
-		curRec, err := log.getLogRec(devid, srid, gnum, lsn)
-		if err != nil || curRec == nil {
-			t.Fatalf("GetLogRec() can not find object %s:%s:%d:%d (i=%d) in log file %s, err %v",
-				devid, srid.String(), gnum, lsn, i, logfile, err)
-		}
-
-		if !log.hasLogRec(devid, srid, gnum, lsn) {
-			t.Errorf("HasLogRec() can not find object %s:%s:%d:%d (i=%d) in log file %s",
-				devid, srid.String(), gnum, lsn, i, logfile)
-		}
-
-		if !reflect.DeepEqual(curRec, rec) {
-			t.Errorf("Data mismatch for object %s:%d:%d (i=%d) in log file %s: %v instead of %v",
-				devid, gnum, lsn, i, logfile, curRec, rec)
-		}
-
-		if i == 0 {
-			if err := log.close(); err != nil {
-				t.Errorf("Cannot close log file %s, err %v", logfile, err)
-			}
-			log, err = openILog(logfile, nil)
-			if err != nil {
-				t.Fatalf("Cannot re-open log file %s, err %v", logfile, err)
-			}
-		}
-	}
-
-	log.dump()
-	if err := log.close(); err != nil {
-		t.Errorf("Cannot close log file %s, err %v", logfile, err)
-	}
-}
-
-// TestDelLogRec tests deleting a log record across log open/close/reopen.
-func TestDelLogRec(t *testing.T) {
-	logfile := getFileName()
-	defer os.Remove(logfile)
-
-	log, err := openILog(logfile, nil)
-	if err != nil {
-		t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
-	}
-
-	var devid DeviceId = "VeyronTab"
-	srid := ObjId("haha")
-	var gnum GenId = 100
-	var lsn SeqNum = 1000
-
-	rec := &LogRec{
-		DevId:      devid,
-		SyncRootId: srid,
-		GenNum:     gnum,
-		SeqNum:     lsn,
-		ObjId:      ObjId("bla"),
-		CurVers:    2,
-		Parents:    []Version{0, 1},
-		Value:      LogValue{},
-	}
-
-	if _, err := log.putLogRec(rec); err != nil {
-		t.Errorf("Cannot put object %s:%s:%d:%d (%v) in log file %s, err %v", devid, srid.String(), gnum, lsn, rec, logfile, err)
-	}
-
-	curRec, err := log.getLogRec(devid, srid, gnum, lsn)
-	if err != nil || curRec == nil {
-		t.Fatalf("GetLogRec() can not find object %s:%s:%d:%d in log file %s, err %v",
-			devid, srid.String(), gnum, lsn, logfile, err)
-	}
-
-	if err := log.delLogRec(devid, srid, gnum, lsn); err != nil {
-		t.Fatalf("DelLogRec() can not delete object %s:%s:%d:%d in log file %s, err %v",
-			devid, srid.String(), gnum, lsn, logfile, err)
-	}
-
-	for i := 0; i < 2; i++ {
-		curRec, err = log.getLogRec(devid, srid, gnum, lsn)
-		if err == nil || curRec != nil {
-			t.Fatalf("GetLogRec() finds deleted object %s:%s:%d:%d (i=%d) in log file %s, err %v",
-				devid, srid.String(), gnum, lsn, i, logfile, err)
-		}
-
-		if log.hasLogRec(devid, srid, gnum, lsn) {
-			t.Errorf("HasLogRec() finds deleted object %s:%s:%d:%d (i=%d) in log file %s",
-				devid, srid.String(), gnum, lsn, i, logfile)
-		}
-
-		if i == 0 {
-			if err := log.close(); err != nil {
-				t.Errorf("Cannot close log file %s, err %v", logfile, err)
-			}
-			log, err = openILog(logfile, nil)
-			if err != nil {
-				t.Fatalf("Cannot re-open log file %s, err %v", logfile, err)
-			}
-		}
-	}
-
-	log.dump()
-	if err := log.close(); err != nil {
-		t.Errorf("Cannot close log file %s, err %v", logfile, err)
-	}
-
-}
-
-// TestPutGetGenMetadata tests setting and getting generation metadata across log open/close/reopen.
-func TestPutGetGenMetadata(t *testing.T) {
-	logfile := getFileName()
-	defer os.Remove(logfile)
-
-	log, err := openILog(logfile, nil)
-	if err != nil {
-		t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
-	}
-
-	var devid DeviceId = "VeyronTab"
-	srid := ObjId("haha")
-	var gnum GenId = 100
-
-	val, err := log.getGenMetadata(devid, srid, gnum)
-	if err == nil || val != nil {
-		t.Errorf("GetGenMetadata() found non-existent object %s:%s:%d in log file %s: %v, err %v",
-			devid, srid.String(), gnum, logfile, val, err)
-	}
-
-	if log.hasGenMetadata(devid, srid, gnum) {
-		t.Errorf("hasGenMetadata() found non-existent object %s:%s:%d in log file %s",
-			devid, srid.String(), gnum, logfile)
-	}
-
-	val = &genMetadata{Pos: 40, Count: 100, MaxSeqNum: 99}
-	if err := log.putGenMetadata(devid, srid, gnum, val); err != nil {
-		t.Errorf("Cannot put object %s:%s:%d in log file %s, err %v", devid, srid.String(), gnum, logfile, err)
-	}
-
-	for i := 0; i < 2; i++ {
-		curVal, err := log.getGenMetadata(devid, srid, gnum)
-		if err != nil || curVal == nil {
-			t.Fatalf("GetGenMetadata() can not find object %s:%s:%d (i=%d) in log file %s, err %v",
-				devid, srid.String(), gnum, i, logfile, err)
-		}
-
-		if !log.hasGenMetadata(devid, srid, gnum) {
-			t.Errorf("hasGenMetadata() can not find object %s:%s:%d (i=%d) in log file %s",
-				devid, srid.String(), gnum, i, logfile)
-		}
-
-		if !reflect.DeepEqual(curVal, val) {
-			t.Errorf("Data mismatch for object %s:%s:%d (i=%d) in log file %s: %v instead of %v",
-				devid, srid.String(), gnum, i, logfile, curVal, val)
-		}
-
-		if i == 0 {
-			if err := log.close(); err != nil {
-				t.Errorf("Cannot close log file %s, err %v", logfile, err)
-			}
-			log, err = openILog(logfile, nil)
-			if err != nil {
-				t.Fatalf("Cannot re-open log file %s, err %v", logfile, err)
-			}
-		}
-	}
-
-	log.dump()
-	if err := log.close(); err != nil {
-		t.Errorf("Cannot close log file %s, err %v", logfile, err)
-	}
-}
-
-// TestDelGenMetadata tests deleting generation metadata across log open/close/reopen.
-func TestDelGenMetadata(t *testing.T) {
-	logfile := getFileName()
-	defer os.Remove(logfile)
-
-	log, err := openILog(logfile, nil)
-	if err != nil {
-		t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
-	}
-
-	var devid DeviceId = "VeyronTab"
-	srid := ObjId("haha")
-	var gnum GenId = 100
-
-	val := &genMetadata{Pos: 40, Count: 100, MaxSeqNum: 99}
-	if err := log.putGenMetadata(devid, srid, gnum, val); err != nil {
-		t.Errorf("Cannot put object %s:%s:%d in log file %s, err %v", devid, srid.String(), gnum, logfile, err)
-	}
-
-	curVal, err := log.getGenMetadata(devid, srid, gnum)
-	if err != nil || curVal == nil {
-		t.Fatalf("GetGenMetadata() can not find object %s:%s:%d in log file %s, err %v",
-			devid, srid.String(), gnum, logfile, err)
-	}
-
-	if err := log.delGenMetadata(devid, srid, gnum); err != nil {
-		t.Fatalf("DelGenMetadata() can not delete object %s:%s:%d in log file %s, err %v",
-			devid, srid.String(), gnum, logfile, err)
-	}
-
-	for i := 0; i < 2; i++ {
-		curVal, err := log.getGenMetadata(devid, srid, gnum)
-		if err == nil || curVal != nil {
-			t.Fatalf("GetGenMetadata() finds deleted object %s:%s:%d (i=%d) in log file %s, err %v",
-				devid, srid.String(), gnum, i, logfile, err)
-		}
-
-		if log.hasGenMetadata(devid, srid, gnum) {
-			t.Errorf("hasGenMetadata() finds deleted object %s:%s:%d (i=%d) in log file %s",
-				devid, srid.String(), gnum, i, logfile)
-		}
-
-		if i == 0 {
-			if err := log.close(); err != nil {
-				t.Errorf("Cannot close log file %s, err %v", logfile, err)
-			}
-			log, err = openILog(logfile, nil)
-			if err != nil {
-				t.Fatalf("Cannot re-open log file %s, err %v", logfile, err)
-			}
-		}
-	}
-
-	log.dump()
-	if err := log.close(); err != nil {
-		t.Errorf("Cannot close log file %s, err %v", logfile, err)
-	}
-}
-
-// TestPersistLogState tests that generation metadata and record state
-// is persisted across log open/close/reopen.
-func TestPersistLogState(t *testing.T) {
-	logfile := getFileName()
-	defer os.Remove(logfile)
-
-	log, err := openILog(logfile, nil)
-	if err != nil {
-		t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
-	}
-
-	var devid DeviceId = "VeyronTab"
-	srid := ObjId("haha")
-
-	// Add several generations.
-	for i := uint32(0); i < 10; i++ {
-		val := &genMetadata{Pos: i}
-		if err := log.putGenMetadata(devid, srid, GenId(i+10), val); err != nil {
-			t.Errorf("Cannot put object %s:%s:%d in log file %s, err %v", devid, srid.String(),
-				i, logfile, err)
-		}
-	}
-	if err := log.close(); err != nil {
-		t.Errorf("Cannot close log file %s, err %v", logfile, err)
-	}
-	log, err = openILog(logfile, nil)
-	if err != nil {
-		t.Fatalf("Cannot re-open log file %s, err %v", logfile, err)
-	}
-	for i := uint32(0); i < 10; i++ {
-		curVal, err := log.getGenMetadata(devid, srid, GenId(i+10))
-		if err != nil || curVal == nil {
-			t.Fatalf("GetGenMetadata() can not find object %s:%s:%d in log file %s, err %v",
-				devid, srid.String(), i, logfile, err)
-		}
-		if curVal.Pos != i {
-			t.Errorf("Data mismatch for object %s:%s:%d in log file %s: %v",
-				devid, srid.String(), i, logfile, curVal)
-		}
-		// Should safely overwrite the same keys.
-		curVal.Pos = i + 10
-		if err := log.putGenMetadata(devid, srid, GenId(i+10), curVal); err != nil {
-			t.Errorf("Cannot put object %s:%s:%d in log file %s, err %v", devid, srid.String(),
-				i, logfile, err)
-		}
-	}
-	for i := uint32(0); i < 10; i++ {
-		curVal, err := log.getGenMetadata(devid, srid, GenId(i+10))
-		if err != nil || curVal == nil {
-			t.Fatalf("GetGenMetadata() can not find object %s:%s:%d in log file %s, err %v",
-				devid, srid.String(), i, logfile, err)
-		}
-		if curVal.Pos != (i + 10) {
-			t.Errorf("Data mismatch for object %s:%s:%d in log file %s: %v, err %v",
-				devid, srid.String(), i, logfile, curVal, err)
-		}
-	}
-
-	log.dump()
-	if err := log.close(); err != nil {
-		t.Errorf("Cannot close log file %s, err %v", logfile, err)
-	}
-}
-
-// fillFakeLogRecords fills fake log records for testing purposes.
-func (l *iLog) fillFakeLogRecords(srid ObjId) {
-	const num = 10
-	var parvers []Version
-	id := ObjId("haha")
-	for i := int(0); i < num; i++ {
-		// Create a local log record.
-		curvers := Version(i)
-		rec, err := l.createLocalLogRec(id, curvers, parvers, &LogValue{}, srid)
-		if err != nil {
-			return
-		}
-		// Insert the new log record into the log.
-		_, err = l.putLogRec(rec)
-		if err != nil {
-			return
-		}
-		parvers = []Version{curvers}
-	}
-}
-
-// TestCreateGeneration tests that local log records and local
-// generations are created uniquely and remote generations are
-// correctly inserted in log order.
-func TestCreateGeneration(t *testing.T) {
-	logfile := getFileName()
-	defer os.Remove(logfile)
-
-	s := &syncd{id: "VeyronTab"}
-	log, err := openILog(logfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
-	}
-
-	// Try using a wrong SyncRoot ID.
-	bad_srid := ObjId("xyz")
-	if _, err := log.createLocalLogRec(ObjId("asdf"), NoVersion, nil, &LogValue{}, bad_srid); err == nil {
-		t.Errorf("createLocalLogRec did not fail when using an invalid SyncRoot ID %v", bad_srid)
-	}
-	if _, err := log.createLocalLinkLogRec(ObjId("qwerty"), NoVersion, NoVersion, bad_srid); err == nil {
-		t.Errorf("createLocalLinkLogRec did not fail when using an invalid SyncRoot ID %v", bad_srid)
-	}
-	if _, err := log.createLocalGeneration(bad_srid); err == nil {
-		t.Errorf("createLocalGeneration did not fail when using an invalid SyncRoot ID %v", bad_srid)
-	}
-
-	srids := []ObjId{ObjId("foo"), ObjId("bar")}
-	num := []uint64{10, 20}
-	for pos, sr := range srids {
-		if err := log.initSyncRoot(sr); err != nil {
-			t.Fatalf("Cannot create new SyncRoot %s, err %v", sr.String(), err)
-		}
-		if g, err := log.createLocalGeneration(sr); err != errNoUpdates {
-			t.Errorf("Should not find local updates gen %d with error %v", g, err)
-		}
-
-		var parvers []Version
-		id := ObjId(fmt.Sprintf("haha-%d", pos))
-		for i := uint64(0); i < num[pos]; i++ {
-			// Create a local log record.
-			curvers := Version(i)
-			rec, err := log.createLocalLogRec(id, curvers, parvers, &LogValue{}, sr)
-			if err != nil {
-				t.Fatalf("Cannot create local log rec ObjId: %v Current: %v Parents: %v Error: %v",
-					id, curvers, parvers, err)
-			}
-
-			temprec := &LogRec{
-				DevId:      log.s.id,
-				SyncRootId: sr,
-				GenNum:     GenId(1),
-				SeqNum:     SeqNum(i),
-				ObjId:      id,
-				CurVers:    curvers,
-				Parents:    parvers,
-				Value:      LogValue{},
-			}
-			// Verify that the log record has the right values.
-			if !reflect.DeepEqual(rec, temprec) {
-				t.Errorf("Data mismtach in log record %v instead of %v", rec, temprec)
-			}
-
-			// Insert the new log record into the log.
-			_, err = log.putLogRec(rec)
-			if err != nil {
-				t.Errorf("Cannot put log record:: failed with err %v", err)
-			}
-
-			parvers = []Version{curvers}
-		}
-	}
-	if err = log.close(); err != nil {
-		t.Errorf("Cannot close log file %s, err %v", logfile, err)
-	}
-
-	log, err = openILog(logfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
-	}
-
-	for pos, sr := range srids {
-		if log.head.CurSRGens[sr].CurGenNum != 1 || log.head.CurSRGens[sr].CurSeqNum != SeqNum(num[pos]) {
-			t.Errorf("Data mismatch in log header %v (pos=%d)", log.head, pos)
-		}
-
-		g, err := log.createLocalGeneration(sr)
-		if g != 1 || err != nil {
-			t.Errorf("Could not create local generation srid %s gen %d (pos=%d) with error %v",
-				sr.String(), g, pos, err)
-		}
-		curVal, err := log.getGenMetadata(log.s.id, sr, g)
-		if err != nil || curVal == nil {
-			t.Fatalf("GetGenMetadata() can not find object %s:%s:%d (pos=%d) in log file %s, err %v",
-				log.s.id, sr.String(), g, pos, logfile, err)
-		}
-		expVal := &genMetadata{Pos: uint32(pos), Count: num[pos], MaxSeqNum: SeqNum(num[pos] - 1)}
-		if !reflect.DeepEqual(curVal, expVal) {
-			t.Errorf("Data mismatch for object %s:%s:%d (pos=%d) in log file %s: %v instead of %v",
-				log.s.id, sr.String(), g, pos, logfile, curVal, expVal)
-		}
-		if log.head.CurSRGens[sr].CurGenNum != 2 || log.head.CurSRGens[sr].CurSeqNum != 0 ||
-			log.head.Curorder != uint32(pos+1) {
-			t.Errorf("Data mismatch in log header (pos=%d) %v %v",
-				pos, log.head.CurSRGens[sr], log.head.Curorder)
-		}
-
-		if g, err := log.createLocalGeneration(sr); err != errNoUpdates {
-			t.Errorf("Should not find local updates sr %s gen %d (pos=%d) with error %v",
-				sr.String(), g, pos, err)
-		}
-	}
-
-	// Populate one more generation for only one syncroot.
-	log.fillFakeLogRecords(srids[0])
-
-	if g, err := log.createLocalGeneration(srids[0]); g != 2 || err != nil {
-		t.Errorf("Could not create local generation sgid %s gen %d with error %v",
-			srids[0].String(), g, err)
-	}
-
-	if g, err := log.createLocalGeneration(srids[1]); err != errNoUpdates {
-		t.Errorf("Should not find local updates sr %s gen %d with error %v", srids[1].String(), g, err)
-	}
-
-	// Create a remote generation.
-	expGen := &genMetadata{Count: 1, MaxSeqNum: 50}
-	if err = log.createRemoteGeneration("VeyronPhone", srids[0], 1, expGen); err == nil {
-		t.Errorf("Remote generation create should have failed")
-	}
-	expGen.MaxSeqNum = 0
-	if err = log.createRemoteGeneration("VeyronPhone", srids[0], 1, expGen); err != nil {
-		t.Errorf("createRemoteGeneration failed with err %v", err)
-	}
-	if expGen.Pos != 3 {
-		t.Errorf("createRemoteGeneration created incorrect log order %d", expGen.Pos)
-	}
-
-	if err = log.close(); err != nil {
-		t.Errorf("Cannot close log file %s, err %v", logfile, err)
-	}
-
-	// Reopen the log and ensure that all log records for generations exist.
-	// Also ensure that generation metadata is accurate.
-	log, err = openILog(logfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
-	}
-
-	gens := []GenId{2, 1}
-	order := map[ObjId][]uint32{srids[0]: []uint32{0, 2},
-		srids[1]: []uint32{1}}
-	for pos, sr := range srids {
-		for g := GenId(1); g <= gens[pos]; g++ {
-			// Check all log records.
-			for i := SeqNum(0); i < SeqNum(num[pos]); i++ {
-				curRec, err := log.getLogRec(log.s.id, sr, g, i)
-				if err != nil || curRec == nil {
-					t.Fatalf("GetLogRec() can not find object %s:%s:%d:%d in log file %s, err %v",
-						log.s.id, sr.String(), g, i, logfile, err)
-				}
-			}
-			// Check generation metadata.
-			curVal, err := log.getGenMetadata(log.s.id, sr, g)
-			if err != nil || curVal == nil {
-				t.Fatalf("GetGenMetadata() can not find object %s:%s:%d in log file %s, err %v",
-					log.s.id, sr.String(), g, logfile, err)
-			}
-			expVal := &genMetadata{Count: num[pos], MaxSeqNum: SeqNum(num[pos] - 1), Pos: order[sr][g-1]}
-			if !reflect.DeepEqual(curVal, expVal) {
-				t.Errorf("Data mismatch for object %s:%s:%d in log file %s: %v instead of %v",
-					log.s.id, sr.String(), g, logfile, curVal, expVal)
-			}
-		}
-	}
-
-	// Check remote generation metadata.
-	curVal, err := log.getGenMetadata("VeyronPhone", srids[0], 1)
-	if err != nil || curVal == nil {
-		t.Fatalf("GetGenMetadata() can not find object in log file %s, err %v",
-			logfile, err)
-	}
-	if !reflect.DeepEqual(curVal, expGen) {
-		t.Errorf("Data mismatch for object in log file %s: %v instead of %v",
-			logfile, curVal, expGen)
-	}
-
-	expSRGens := map[ObjId]*curLocalGen{srids[0]: &curLocalGen{3, 0},
-		srids[1]: &curLocalGen{2, 0}}
-
-	if !reflect.DeepEqual(log.head.CurSRGens, expSRGens) || log.head.Curorder != 4 {
-		t.Errorf("Data mismatch in log header %v %v", log.head.CurSRGens, log.head.Curorder)
-	}
-
-	log.dump()
-	if err = log.close(); err != nil {
-		t.Errorf("Cannot close log file %s, err %v", logfile, err)
-	}
-}
-
-// TestProcessWatchRecord tests that local updates are correctly handled.
-// Commands are in file testdata/<local-init-00.log.sync, local-init-02.log.sync>.
-/*
-func TestProcessWatchRecord(t *testing.T) {
-	logfile := getFileName()
-	defer os.Remove(logfile)
-
-	dagfile := getFileName()
-	defer os.Remove(dagfile)
-
-	var err error
-	s := &syncd{id: "VeyronTab"}
-
-	if s.dag, err = openDAG(dagfile); err != nil {
-		t.Fatalf("Cannot open new dag file %s, err %v", logfile, err)
-	}
-	log, err := openILog(logfile, s)
-	if err != nil {
-		t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
-	}
-
-	srids := []ObjId{"foo", "bar"}
-	fnames := []string{"local-init-00.log.sync", "local-init-02.log.sync"}
-	num := []uint32{3, 5}
-
-	// Try using an invalid SyncRoot ID.
-	if err = log.processWatchRecord(ObjId("haha"), 2, Version(999), &LogValue{}, NoObjId); err == nil {
-		t.Errorf("processWatchRecord did not fail when using an invalid SyncRoot ID")
-	}
-
-	// Test echo suppression on JoinSyncGroup.
-	if err := log.processWatchRecord(srids[0], 5, NoVersion, &LogValue{}, srids[0]); err != nil {
-		t.Fatalf("Echo suppression on JoinSyncGroup failed with err %v", err)
-	}
-
-	for pos, sr := range srids {
-		if err := log.initSyncRoot(sr); err != nil {
-			t.Fatalf("Cannot create new SyncRoot %s, err %v", sr.String(), err)
-		}
-
-		if err := logReplayCommands(log, fnames[pos], sr); err != nil {
-			t.Error(err)
-		}
-	}
-
-	checkObject := func(obj string, expHead Version) {
-		_, file, line, _ := runtime.Caller(1)
-		objid, err := strToObjId(obj)
-		if err != nil {
-			t.Errorf("%s:%d Could not create objid %v", file, line, err)
-		}
-
-		// Verify DAG state.
-		if head, err := log.s.dag.getHead(objid); err != nil || head != expHead {
-			t.Errorf("%s:%d Invalid object %d head in DAG %v want %v, err %v", file, line,
-				objid, head, expHead, err)
-		}
-	}
-	for pos, sr := range srids {
-		if log.head.CurSRGens[sr].CurGenNum != 1 || log.head.CurSRGens[sr].CurSeqNum != SeqNum(num[pos]) ||
-			log.head.Curorder != 0 {
-			t.Errorf("Data mismatch in log header %v", log.head)
-		}
-
-		// Check all log records.
-		for i := SeqNum(0); i < SeqNum(num[pos]); i++ {
-			curRec, err := log.getLogRec(log.s.id, sr, GenId(1), i)
-			if err != nil || curRec == nil {
-				t.Fatalf("GetLogRec() can not find object %s:%s:1:%d in log file %s, err %v",
-					log.s.id, sr.String(), i, logfile, err)
-			}
-		}
-
-		if pos == 0 {
-			checkObject("1234", 3)
-		} else if pos == 1 {
-			checkObject("12", 3)
-			checkObject("45", 20)
-		}
-	}
-
-	s.dag.flush()
-	s.dag.close()
-
-	log.dump()
-	if err = log.close(); err != nil {
-		t.Errorf("Cannot close log file %s, err %v", logfile, err)
-	}
-}
-*/
diff --git a/x/ref/services/syncbase/sync/initiator.go b/x/ref/services/syncbase/sync/initiator.go
deleted file mode 100644
index 8d23976..0000000
--- a/x/ref/services/syncbase/sync/initiator.go
+++ /dev/null
@@ -1,943 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-import (
-	"errors"
-	"fmt"
-	"math/rand"
-	"os"
-	"time"
-
-	"v.io/v23/context"
-	"v.io/v23/naming"
-	"v.io/v23/vtrace"
-	"v.io/x/lib/vlog"
-)
-
-// Policies to pick a peer to sync with.
-const (
-	// Picks a peer at random from the available set.
-	selectRandom = iota
-
-	// TODO(hpucha): implement other policies.
-	// Picks a peer with most differing generations.
-	selectMostDiff
-
-	// Picks a peer that was synced with the furthest in the past.
-	selectOldest
-)
-
-// Policies for conflict resolution.
-const (
-	// Resolves conflicts by picking the mutation with the most recent timestamp.
-	useTime = iota
-
-	// TODO(hpucha): implement other policies.
-	// Resolves conflicts by using the app conflict resolver callbacks via store.
-	useCallback
-)
-
-var (
-	// peerSyncInterval is the duration between two consecutive
-	// sync events.  In every sync event, the initiator contacts
-	// one of its peers to obtain any pending updates.
-	peerSyncInterval = 50 * time.Millisecond
-
-	// peerSelectionPolicy is the policy used to select a peer when
-	// the initiator gets a chance to sync.
-	peerSelectionPolicy = selectRandom
-
-	// conflictResolutionPolicy is the policy used to resolve conflicts.
-	conflictResolutionPolicy = useTime
-
-	errNoUsefulPeer = errors.New("no useful peer to contact")
-)
-
-// syncInitiator contains the metadata and state for the initiator thread.
-type syncInitiator struct {
-	syncd *syncd
-
-	// State accumulated during an initiation round.
-	iState *initiationState
-}
-
-// initiationState accumulated during an initiation round.
-type initiationState struct {
-
-	// Veyron name of peer being synced with.
-	peer string
-
-	// Local generation vector.
-	local map[ObjId]GenVector
-
-	// SyncGroups being requested in the initiation round.
-	syncGroups map[ObjId]GroupIdSet
-
-	// Map to track new generations received in the RPC reply.
-	newGens map[string]*genMetadata
-
-	// Array to track order of arrival for the generations.
-	// We need to preserve this order for the replay.
-	orderGens []string
-
-	// Generation vector to track the oldest generation received
-	// in the RPC reply per device, for garbage collection.
-	minGens map[ObjId]GenVector
-
-	// Generation vector from the remote peer.
-	remote map[ObjId]GenVector
-
-	// Tmp kvdb state.
-	tmpFile string
-	tmpDB   *kvdb
-	tmpTbl  *kvtable
-
-	// State to track updated objects during a log replay.
-	updObjects map[ObjId]*objConflictState
-
-	// State to delete objects from the "priv" table.
-	delPrivObjs map[ObjId]struct{}
-}
-
-// objConflictState contains the conflict state for objects that are
-// updated during an initiator run.
-type objConflictState struct {
-	isConflict bool
-	newHead    Version
-	oldHead    Version
-	ancestor   Version
-	resolvVal  *LogValue
-	srID       ObjId
-}
-
-// newInitiator creates a new initiator instance attached to the given syncd instance.
-func newInitiator(syncd *syncd, syncTick time.Duration) *syncInitiator {
-	i := &syncInitiator{syncd: syncd}
-
-	// Override the default peerSyncInterval value if syncTick is specified.
-	if syncTick > 0 {
-		peerSyncInterval = syncTick
-	}
-
-	vlog.VI(1).Infof("newInitiator: My device ID: %s", i.syncd.id)
-	vlog.VI(1).Infof("newInitiator: Sync interval: %v", peerSyncInterval)
-
-	return i
-}
-
-// contactPeers wakes up every peerSyncInterval to contact peers and get deltas from them.
-func (i *syncInitiator) contactPeers() {
-	ticker := time.NewTicker(peerSyncInterval)
-	for {
-		select {
-		case <-i.syncd.closed:
-			ticker.Stop()
-			i.syncd.pending.Done()
-			return
-		case <-ticker.C:
-		}
-
-		peerRelName, err := i.pickPeer()
-		if err != nil {
-			continue
-		}
-
-		i.getDeltasFromPeer(peerRelName)
-	}
-}
-
-// pickPeer picks a sync endpoint to sync with.
-func (i *syncInitiator) pickPeer() (string, error) {
-	myID := string(i.syncd.relName)
-
-	switch peerSelectionPolicy {
-	case selectRandom:
-		// TODO(hpucha): Eliminate reaching into syncd's lock.
-		i.syncd.lock.RLock()
-		// neighbors, err := i.syncd.sgtab.getMembers()
-		neighbors := make(map[string]uint32)
-		var err error
-		i.syncd.lock.RUnlock()
-
-		if err != nil {
-			return "", err
-		}
-
-		// Remove myself from the set so only neighbors are counted.
-		delete(neighbors, myID)
-
-		if len(neighbors) == 0 {
-			return "", errNoUsefulPeer
-		}
-
-		// Pick a neighbor at random.
-		ind := rand.Intn(len(neighbors))
-		for k := range neighbors {
-			if ind == 0 {
-				return k, nil
-			}
-			ind--
-		}
-		return "", fmt.Errorf("random selection didn't succeed")
-	default:
-		return "", fmt.Errorf("unknown peer selection policy")
-	}
-}
-
-// getDeltasFromPeer performs an initiation round to the specified
-// peer. An initiation round consists of:
-// * Creating local generation for syncroots that are going to be requested in this round.
-// * Contacting the peer to receive all the deltas based on the local gen vector.
-// * Processing those deltas to discover objects which have been updated.
-// * Processing updated objects to detect and resolve any conflicts if needed.
-// * Communicating relevant object updates to the store.
-func (i *syncInitiator) getDeltasFromPeer(peerRelName string) {
-
-	vlog.VI(2).Infof("getDeltasFromPeer:: %s", peerRelName)
-	// Initialize initiation state.
-	i.newInitiationState()
-	defer i.clearInitiationState()
-
-	// Freeze the most recent batch of local changes
-	// before fetching remote changes from a peer.
-	//
-	// We only allow an initiator to create new local
-	// generations (not responders/watcher) in order to
-	// maintain a static baseline for the duration of a
-	// sync. This addresses the following race condition:
-	// If we allow responders to create new local
-	// generations while the initiator is in progress,
-	// they may beat the initiator and send these new
-	// generations to remote devices.  These remote
-	// devices in turn can send these generations back to
-	// the initiator in progress which was started with
-	// older generation information.
-	err := i.updateLocalGeneration(peerRelName)
-	if err != nil {
-		vlog.Fatalf("getDeltasFromPeer:: error updating local generation: err %v", err)
-	}
-
-	// Obtain deltas from the peer over the network. These deltas
-	// are stored in a tmp kvdb.
-	if err := i.getDeltas(); err != nil {
-		vlog.Errorf("getDeltasFromPeer:: error getting deltas: err %v", err)
-		return
-	}
-
-	i.syncd.sgOp.Lock()
-	defer i.syncd.sgOp.Unlock()
-
-	if err := i.processDeltas(); err != nil {
-		vlog.Fatalf("getDeltasFromPeer:: error processing logs: err %v", err)
-	}
-
-	if err := i.processUpdatedObjects(); err != nil {
-		vlog.Fatalf("getDeltasFromPeer:: error processing objects: err %v", err)
-	}
-}
-
-// newInitiationState creates new initiation state.
-func (i *syncInitiator) newInitiationState() {
-	st := &initiationState{}
-	st.local = make(map[ObjId]GenVector)
-	st.syncGroups = make(map[ObjId]GroupIdSet)
-	st.newGens = make(map[string]*genMetadata)
-	st.minGens = make(map[ObjId]GenVector)
-	st.remote = make(map[ObjId]GenVector)
-	st.updObjects = make(map[ObjId]*objConflictState)
-	st.delPrivObjs = make(map[ObjId]struct{})
-
-	i.iState = st
-}
-
-// clearInitiationState cleans up the state from the current initiation round.
-func (i *syncInitiator) clearInitiationState() {
-	if i.iState.tmpDB != nil {
-		i.iState.tmpDB.close()
-	}
-	if i.iState.tmpFile != "" {
-		os.Remove(i.iState.tmpFile)
-	}
-
-	for o := range i.iState.delPrivObjs {
-		i.syncd.dag.delPrivNode(o)
-	}
-
-	i.syncd.dag.clearGraft()
-
-	i.iState = nil
-}
-
-// updateLocalGeneration creates a new local generation if needed and
-// populates the newest local generation vector.
-func (i *syncInitiator) updateLocalGeneration(peerRelName string) error {
-	// TODO(hpucha): Eliminate reaching into syncd's lock.
-	i.syncd.lock.Lock()
-	defer i.syncd.lock.Unlock()
-
-	// peerInfo, err := i.syncd.sgtab.getMemberInfo(peerRelName)
-	// if err != nil {
-	// 	return err
-	// }
-
-	// Re-construct all mount table possibilities.
-	mtTables := make(map[string]struct{})
-
-	// for sg := range peerInfo.gids {
-	// 	sgData, err := i.syncd.sgtab.getSyncGroupByID(sg)
-	// 	if err != nil {
-	// 		return err
-	// 	}
-	// 	sr := ObjId(sgData.SrvInfo.RootObjId)
-	// 	for _, mt := range sgData.SrvInfo.Config.MountTables {
-	// 		mtTables[mt] = struct{}{}
-	// 	}
-	// 	i.iState.syncGroups[sr] = append(i.iState.syncGroups[sr], sg)
-	// }
-
-	// Create a new local generation if there are any local updates
-	// for every syncroot that is common with the "peer" to be
-	// contacted.
-	for sr := range i.iState.syncGroups {
-		gen, err := i.syncd.log.createLocalGeneration(sr)
-		if err != nil && err != errNoUpdates {
-			return err
-		}
-
-		if err == nil {
-			vlog.VI(2).Infof("updateLocalGeneration:: created gen for sr %s at %d", sr.String(), gen)
-
-			// Update local generation vector in devTable.
-			if err = i.syncd.devtab.updateGeneration(i.syncd.id, sr, i.syncd.id, gen); err != nil {
-				return err
-			}
-		}
-
-		v, err := i.syncd.devtab.getGenVec(i.syncd.id, sr)
-		if err != nil {
-			return err
-		}
-
-		i.iState.local[sr] = v
-	}
-
-	// Check if the name is absolute, and if so, use the name as-is.
-	if naming.Rooted(peerRelName) {
-		i.iState.peer = peerRelName
-		return nil
-	}
-
-	// Pick any global name to contact the peer.
-	for mt := range mtTables {
-		i.iState.peer = naming.Join(mt, peerRelName)
-		vlog.VI(2).Infof("updateLocalGeneration:: contacting peer %s", i.iState.peer)
-		return nil
-	}
-
-	return fmt.Errorf("no mounttables found")
-}
-
-// getDeltas obtains the deltas from the peer and stores them in a tmp kvdb.
-func (i *syncInitiator) getDeltas() error {
-	// As log records are streamed, they are saved
-	// in a tmp kvdb so that they can be processed in one batch.
-	if err := i.initTmpKVDB(); err != nil {
-		return err
-	}
-
-	ctx, _ := vtrace.WithNewTrace(i.syncd.ctx)
-	ctx, cancel := context.WithTimeout(ctx, time.Minute)
-	defer cancel()
-
-	vlog.VI(1).Infof("getDeltas:: From peer with DeviceId %s at %v", i.iState.peer, time.Now().UTC())
-
-	// Construct a new stub that binds to peer endpoint.
-	c := SyncClient(naming.JoinAddressName(i.iState.peer, SyncSuffix))
-
-	vlog.VI(1).Infof("GetDeltasFromPeer:: Sending local information: %v", i.iState.local)
-
-	// Issue a GetDeltas() rpc.
-	stream, err := c.GetDeltas(ctx, i.iState.local, i.iState.syncGroups, i.syncd.id)
-	if err != nil {
-		return err
-	}
-
-	return i.recvLogStream(stream)
-}
-
-// initTmpKVDB initializes the tmp kvdb to store the log record stream.
-func (i *syncInitiator) initTmpKVDB() error {
-	i.iState.tmpFile = fmt.Sprintf("%s/tmp_%d", i.syncd.kvdbPath, time.Now().UnixNano())
-	tmpDB, tbls, err := kvdbOpen(i.iState.tmpFile, []string{"tmprec"})
-	if err != nil {
-		return err
-	}
-	i.iState.tmpDB = tmpDB
-	i.iState.tmpTbl = tbls[0]
-	return nil
-}
-
-// recvLogStream receives the log records from the GetDeltas stream
-// and puts them in tmp kvdb for later processing.
-func (i *syncInitiator) recvLogStream(stream SyncGetDeltasClientCall) error {
-	rStream := stream.RecvStream()
-	for rStream.Advance() {
-		rec := rStream.Value()
-
-		// Insert log record in tmpTbl.
-		if err := i.iState.tmpTbl.set(logRecKey(rec.DevId, rec.SyncRootId, rec.GenNum, rec.SeqNum), &rec); err != nil {
-			// TODO(hpucha): do we need to cancel stream?
-			return err
-		}
-
-		// Populate the generation metadata.
-		genKey := generationKey(rec.DevId, rec.SyncRootId, rec.GenNum)
-		if gen, ok := i.iState.newGens[genKey]; !ok {
-			// New generation in the stream.
-			i.iState.orderGens = append(i.iState.orderGens, genKey)
-			i.iState.newGens[genKey] = &genMetadata{
-				Count:     1,
-				MaxSeqNum: rec.SeqNum,
-			}
-			if _, ok := i.iState.minGens[rec.SyncRootId]; !ok {
-				i.iState.minGens[rec.SyncRootId] = GenVector{}
-			}
-			g, ok := i.iState.minGens[rec.SyncRootId][rec.DevId]
-			if !ok || g > rec.GenNum {
-				i.iState.minGens[rec.SyncRootId][rec.DevId] = rec.GenNum
-			}
-		} else {
-			gen.Count++
-			if rec.SeqNum > gen.MaxSeqNum {
-				gen.MaxSeqNum = rec.SeqNum
-			}
-		}
-	}
-
-	if err := rStream.Err(); err != nil {
-		return err
-	}
-
-	var err error
-	if i.iState.remote, err = stream.Finish(); err != nil {
-		return err
-	}
-	vlog.VI(1).Infof("recvLogStream:: Local vector %v", i.iState.local)
-	vlog.VI(1).Infof("recvLogStream:: Remote vector %v", i.iState.remote)
-	vlog.VI(2).Infof("recvLogStream:: orderGens %v", i.iState.orderGens)
-	return nil
-}
-
-// processDeltas replays an entire log stream spanning multiple
-// generations from different devices received from a single GetDeltas
-// call. It does not perform any conflict resolution during replay.
-// This avoids resolving conflicts that have already been resolved by
-// other devices.
-func (i *syncInitiator) processDeltas() error {
-	// TODO(hpucha): Eliminate reaching into syncd's lock.
-	i.syncd.lock.Lock()
-	defer i.syncd.lock.Unlock()
-
-	// Track received transactions.
-	txMap := make(map[TxId]uint32)
-
-	// Loop through each received generation in order.
-	for _, key := range i.iState.orderGens {
-		gen := i.iState.newGens[key]
-		dev, sr, gnum, err := splitGenerationKey(key)
-		if err != nil {
-			return err
-		}
-
-		// If "sr" has been left since getDeltas, skip processing.
-		// if !i.syncd.sgtab.isSyncRoot(sr) {
-		// 	continue
-		// }
-
-		for l := SeqNum(0); l <= gen.MaxSeqNum; l++ {
-			var rec LogRec
-			if err := i.iState.tmpTbl.get(logRecKey(dev, sr, gnum, l), &rec); err != nil {
-				return err
-			}
-
-			// Begin a new transaction if needed.
-			curTx := rec.Value.TxId
-			if curTx != NoTxId {
-				if cnt, ok := txMap[curTx]; !ok {
-					if i.syncd.dag.addNodeTxStart(curTx) != curTx {
-						return fmt.Errorf("failed to create transaction")
-					}
-					txMap[curTx] = rec.Value.TxCount
-					vlog.VI(2).Infof("processDeltas:: Begin Tx %v, count %d", curTx, rec.Value.TxCount)
-				} else if cnt != rec.Value.TxCount {
-					return fmt.Errorf("inconsistent counts for tid %v %d, %d", curTx, cnt, rec.Value.TxCount)
-				}
-			}
-
-			if err := i.insertRecInLogAndDag(&rec, curTx); err != nil {
-				return err
-			}
-
-			// Mark object dirty.
-			i.iState.updObjects[rec.ObjId] = &objConflictState{
-				srID: rec.SyncRootId,
-			}
-		}
-		// Insert the generation metadata.
-		if err := i.syncd.log.createRemoteGeneration(dev, sr, gnum, gen); err != nil {
-			return err
-		}
-	}
-
-	// End the started transactions if any.
-	for t, cnt := range txMap {
-		if err := i.syncd.dag.addNodeTxEnd(t, cnt); err != nil {
-			return err
-		}
-		vlog.VI(2).Infof("processLogStream:: End Tx %v %v", t, cnt)
-	}
-
-	return nil
-}
-
-// insertRecInLogAndDag adds a new log record to log and dag data structures.
-func (i *syncInitiator) insertRecInLogAndDag(rec *LogRec, txID TxId) error {
-	logKey, err := i.syncd.log.putLogRec(rec)
-	if err != nil {
-		return err
-	}
-
-	vlog.VI(2).Infof("insertRecInLogAndDag:: Adding log record %v, Tx %v", rec, txID)
-	switch rec.RecType {
-	case NodeRec:
-		return i.syncd.dag.addNode(rec.ObjId, rec.CurVers, true, rec.Value.Delete, rec.Parents, logKey, txID)
-	case LinkRec:
-		return i.syncd.dag.addParent(rec.ObjId, rec.CurVers, rec.Parents[0], true)
-	default:
-		return fmt.Errorf("unknown log record type")
-	}
-}
-
-// processUpdatedObjects processes all the updates received by the
-// initiator, one object at a time. For each updated object, we first
-// check if the object has any conflicts, resulting in three
-// possibilities:
-//
-// * There is no conflict, and no updates are needed to the store
-// (isConflict=false, newHead == oldHead). All changes received convey
-// information that still keeps the local head as the most recent
-// version. This occurs when conflicts are resolved by blessings.
-//
-// * There is no conflict, but a remote version is discovered that
-// builds on the local head (isConflict=false, newHead != oldHead). In
-// this case, we generate a store mutation to simply update the store
-// to the latest value.
-//
-// * There is a conflict and we call into the app or the system to
-// resolve the conflict, resulting in three possibilties: (a) conflict
-// was resolved by blessing the local version. In this case, store
-// need not be updated, but a link is added to record the
-// blessing. (b) conflict was resolved by blessing the remote
-// version. In this case, store is updated with the remote version and
-// a link is added as well. (c) conflict was resolved by generating a
-// new store mutation. In this case, store is updated with the new
-// version.
-//
-// We then put all these mutations in the store. If the put succeeds,
-// we update the log and dag state suitably (move the head ptr of the
-// object in the dag to the latest version, and create a new log
-// record reflecting conflict resolution if any). Puts to store can
-// fail since preconditions on the objects may have been violated. In
-// this case, we wait to get the latest versions of objects from the
-// store, and recheck if the object has any conflicts and repeat the
-// above steps, until put to store succeeds.
-func (i *syncInitiator) processUpdatedObjects() error {
-	for {
-		if err := i.detectConflicts(); err != nil {
-			return err
-		}
-
-		if err := i.resolveConflicts(); err != nil {
-			return err
-		}
-
-		err := i.updateStoreAndSync()
-		if err == nil {
-			break
-		}
-
-		vlog.Errorf("PutMutations failed %v. Will retry", err)
-		// TODO(hpucha): Sleeping and retrying is a temporary
-		// solution. Next iteration will have coordination
-		// with watch thread to intelligently retry. Hence
-		// this value is not a config param.
-		time.Sleep(1 * time.Second)
-	}
-
-	return nil
-}
-
-// detectConflicts iterates through all the updated objects to detect
-// conflicts.
-func (i *syncInitiator) detectConflicts() error {
-	// TODO(hpucha): Eliminate reaching into syncd's lock.
-	i.syncd.lock.RLock()
-	defer i.syncd.lock.RUnlock()
-
-	for obj, st := range i.iState.updObjects {
-		// Check if object has a conflict.
-		var err error
-		st.isConflict, st.newHead, st.oldHead, st.ancestor, err = i.syncd.dag.hasConflict(obj)
-		vlog.VI(2).Infof("detectConflicts:: object %v state %v err %v",
-			obj, st, err)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-// resolveConflicts resolves conflicts for updated objects. Conflicts
-// may be resolved by adding new versions or blessing either the local
-// or the remote version.
-func (i *syncInitiator) resolveConflicts() error {
-	for obj, st := range i.iState.updObjects {
-		if !st.isConflict {
-			continue
-		}
-
-		res, err := i.resolveObjConflict(obj, st.oldHead, st.newHead, st.ancestor)
-		if err != nil {
-			return err
-		}
-
-		st.resolvVal = res
-	}
-
-	return nil
-}
-
-// resolveObjConflict resolves a conflict for an object given its ID and
-// the 3 versions that express the conflict: the object's local version, its
-// remote version (from the device contacted), and the common ancestor from
-// which both versions branched away.  The function returns the new object
-// value according to the conflict resolution policy.
-func (i *syncInitiator) resolveObjConflict(oid ObjId,
-	local, remote, ancestor Version) (*LogValue, error) {
-
-	// Fetch the log records of the 3 object versions.
-	versions := []Version{local, remote, ancestor}
-	lrecs, err := i.getLogRecsBatch(oid, versions)
-	if err != nil {
-		return nil, err
-	}
-
-	// Resolve the conflict according to the resolution policy.
-	var res *LogValue
-
-	switch conflictResolutionPolicy {
-	case useTime:
-		res, err = i.resolveObjConflictByTime(oid, lrecs[0], lrecs[1], lrecs[2])
-	default:
-		err = fmt.Errorf("unknown conflict resolution policy: %v", conflictResolutionPolicy)
-	}
-
-	if err != nil {
-		return nil, err
-	}
-
-	resCopy := *res
-	// resCopy.Mutation.Version = NewVersion()
-	// resCopy.Mutation.Dir = resDir
-	resCopy.SyncTime = time.Now().UnixNano()
-	resCopy.TxId = NoTxId
-	resCopy.TxCount = 0
-	return &resCopy, nil
-}
-
-// resolveObjConflictByTime resolves conflicts using the timestamps
-// of the conflicting mutations.  It picks a mutation with the larger
-// timestamp, i.e. the most recent update.  If the timestamps are equal,
-// it uses the mutation version numbers as a tie-breaker, picking the
-// mutation with the larger version.
-// Instead of creating a new version that resolves the conflict, we are
-// blessing an existing version as the conflict resolution.
-func (i *syncInitiator) resolveObjConflictByTime(oid ObjId,
-	local, remote, ancestor *LogRec) (*LogValue, error) {
-
-	var res *LogValue
-	switch {
-	case local.Value.SyncTime > remote.Value.SyncTime:
-		res = &local.Value
-	case local.Value.SyncTime < remote.Value.SyncTime:
-		res = &remote.Value
-		// case local.Value.Mutation.Version > remote.Value.Mutation.Version:
-		// 	res = &local.Value
-		// case local.Value.Mutation.Version < remote.Value.Mutation.Version:
-		// 	res = &remote.Value
-	}
-
-	return res, nil
-}
-
-// getLogRecsBatch gets the log records for an array of versions.
-func (i *syncInitiator) getLogRecsBatch(obj ObjId, versions []Version) ([]*LogRec, error) {
-	// TODO(hpucha): Eliminate reaching into syncd's lock.
-	i.syncd.lock.RLock()
-	defer i.syncd.lock.RUnlock()
-
-	lrecs := make([]*LogRec, len(versions))
-	var err error
-	for p, v := range versions {
-		lrecs[p], err = i.getLogRec(obj, v)
-		if err != nil {
-			return nil, err
-		}
-	}
-	return lrecs, nil
-}
-
-// updateStoreAndSync updates the store, and if that is successful,
-// updates log and dag data structures.
-func (i *syncInitiator) updateStoreAndSync() error {
-
-	// TODO(hpucha): Eliminate reaching into syncd's lock.
-	i.syncd.lock.Lock()
-	defer i.syncd.lock.Unlock()
-
-	// var m []raw.Mutation
-	// for obj, st := range i.iState.updObjects {
-	// 	if !st.isConflict {
-	// 		rec, err := i.getLogRec(obj, st.newHead)
-	// 		if err != nil {
-	// 			return err
-	// 		}
-	// 		st.resolvVal = &rec.Value
-	// 		// Sanity check.
-	// 		if st.resolvVal.Mutation.Version != st.newHead {
-	// 			return fmt.Errorf("bad mutation %d %d",
-	// 				st.resolvVal.Mutation.Version, st.newHead)
-	// 		}
-	// 	}
-
-	// 	// If the local version is picked, no further updates
-	// 	// to the store are needed. If the remote version is
-	// 	// picked, we put it in the store.
-	// 	if st.resolvVal.Mutation.Version != st.oldHead {
-	// 		st.resolvVal.Mutation.PriorVersion = st.oldHead
-
-	// 		// Convert resolvVal.Mutation into a mutation for the Store.
-	// 		stMutation, err := i.storeMutation(obj, st.resolvVal)
-	// 		if err != nil {
-	// 			return err
-	// 		}
-
-	// 		vlog.VI(2).Infof("updateStoreAndSync:: Try to append mutation %v (%v) for obj %v (nh %v, oh %v)",
-	// 			st.resolvVal.Mutation, stMutation, obj, st.newHead, st.oldHead)
-
-	// 		// Append to mutations, skipping a delete following a delete mutation.
-	// 		if stMutation.Version != NoVersion ||
-	// 			stMutation.PriorVersion != NoVersion {
-	// 			vlog.VI(2).Infof("updateStoreAndSync:: appending mutation %v for obj %v",
-	// 				stMutation, obj)
-	// 			m = append(m, stMutation)
-	// 		}
-	// 	}
-	// }
-
-	// TODO(hpucha): We will hold the lock across PutMutations rpc
-	// to prevent a race with watcher. The next iteration will
-	// clean up this coordination.
-	// if store := i.syncd.store; store != nil && len(m) > 0 {
-	// 	ctx, _ := vtrace.SetNewTrace(i.syncd.ctx)
-	// 	ctx, cancel := context.WithTimeout(ctx, time.Minute)
-	// 	defer cancel()
-
-	// 	stream, err := store.PutMutations(ctx)
-	// 	if err != nil {
-	// 		vlog.Errorf("updateStoreAndSync:: putmutations err %v", err)
-	// 		return err
-	// 	}
-	// 	sender := stream.SendStream()
-	// 	for i := range m {
-	// 		if err := sender.Send(m[i]); err != nil {
-	// 			vlog.Errorf("updateStoreAndSync:: send err %v", err)
-	// 			return err
-	// 		}
-	// 	}
-	// 	if err := sender.Close(); err != nil {
-	// 		vlog.Errorf("updateStoreAndSync:: closesend err %v", err)
-	// 		return err
-	// 	}
-	// 	if err := stream.Finish(); err != nil {
-	// 		vlog.Errorf("updateStoreAndSync:: finish err %v", err)
-	// 		return err
-	// 	}
-	// }
-
-	vlog.VI(2).Infof("updateStoreAndSync:: putmutations succeeded")
-	if err := i.updateLogAndDag(); err != nil {
-		return err
-	}
-
-	if err := i.updateGenVecs(); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// storeMutation converts a resolved mutation generated by syncd to
-// one that can be sent to the store. To send to the store, it
-// converts the version numbers corresponding to object deletions to
-// NoVersion when required. It also converts the version number
-// appropriately to handle SyncGroup join.
-// func (i *syncInitiator) storeMutation(obj ObjId, resolvVal *LogValue) (raw.Mutation, error) {
-// 	curDelete := resolvVal.Delete
-// 	priorDelete := false
-// 	if resolvVal.Mutation.PriorVersion != raw.NoVersion {
-// 		oldRec, err := i.getLogRec(obj, resolvVal.Mutation.PriorVersion)
-// 		if err != nil {
-// 			return raw.Mutation{}, err
-// 		}
-// 		priorDelete = oldRec.Value.Delete
-// 	}
-
-// 	// Handle the versioning of a SyncGroup's root ObjId during join.
-// 	if resolvVal.Mutation.PriorVersion == raw.NoVersion {
-// 		if i.syncd.sgtab.isSyncRoot(obj) {
-// 			node, err := i.syncd.dag.getPrivNode(obj)
-// 			if err != nil {
-// 				return raw.Mutation{}, err
-// 			}
-// 			resolvVal.Mutation.PriorVersion = node.Mutation.Version
-// 			i.iState.delPrivObjs[obj] = struct{}{}
-// 		}
-// 	}
-
-// 	// Current version and prior versions are not deletes.
-// 	if !curDelete && !priorDelete {
-// 		return resolvVal.Mutation, nil
-// 	}
-
-// 	// Creating a new copy of the mutation to adjust version
-// 	// numbers when handling deletions.
-// 	stMutation := resolvVal.Mutation
-// 	// Adjust the current version if this a deletion.
-// 	if curDelete {
-// 		stMutation.Version = NoVersion
-// 	}
-// 	// Adjust the prior version if it is a deletion.
-// 	if priorDelete {
-// 		stMutation.PriorVersion = NoVersion
-// 	}
-
-// 	return stMutation, nil
-// }
-
-// getLogRec returns the log record corresponding to a given object and its version.
-func (i *syncInitiator) getLogRec(obj ObjId, vers Version) (*LogRec, error) {
-	logKey, err := i.syncd.dag.getLogrec(obj, vers)
-	vlog.VI(2).Infof("getLogRec:: logkey from dag is %s", logKey)
-	if err != nil {
-		return nil, err
-	}
-	dev, sg, gen, lsn, err := splitLogRecKey(logKey)
-	if err != nil {
-		return nil, err
-	}
-	vlog.VI(2).Infof("getLogRec:: splitting logkey %s to %s %v %v %v", logKey, dev, sg, gen, lsn)
-	rec, err := i.syncd.log.getLogRec(dev, sg, gen, lsn)
-	if err != nil {
-		return nil, err
-	}
-	return rec, nil
-}
-
-// updateLogAndDag updates the log and dag data structures on a successful store put.
-func (i *syncInitiator) updateLogAndDag() error {
-	for obj, st := range i.iState.updObjects {
-		if st.isConflict {
-			// Object had a conflict, which was resolved successfully.
-			// Put is successful, create a log record.
-			var err error
-			var rec *LogRec
-
-			// switch {
-			// case st.resolvVal.Mutation.Version == st.oldHead:
-			// 	// Local version was blessed as the conflict resolution.
-			// 	rec, err = i.syncd.log.createLocalLinkLogRec(obj, st.oldHead, st.newHead, st.srID)
-			// case st.resolvVal.Mutation.Version == st.newHead:
-			// 	// Remote version was blessed as the conflict resolution.
-			// 	rec, err = i.syncd.log.createLocalLinkLogRec(obj, st.newHead, st.oldHead, st.srID)
-			// default:
-			// 	// New version was created to resolve the conflict.
-			// 	parents := []Version{st.newHead, st.oldHead}
-			// 	rec, err = i.syncd.log.createLocalLogRec(obj, st.resolvVal.Mutation.Version, parents, st.resolvVal, st.srID)
-			// }
-			if err != nil {
-				return err
-			}
-			logKey, err := i.syncd.log.putLogRec(rec)
-			if err != nil {
-				return err
-			}
-			// Add a new DAG node.
-			switch rec.RecType {
-			case NodeRec:
-				// TODO(hpucha): addNode operations arising out of conflict resolution
-				// may need to be part of a transaction when app-driven resolution
-				// is introduced.
-				err = i.syncd.dag.addNode(obj, rec.CurVers, false, rec.Value.Delete, rec.Parents, logKey, NoTxId)
-			case LinkRec:
-				err = i.syncd.dag.addParent(obj, rec.CurVers, rec.Parents[0], false)
-			default:
-				return fmt.Errorf("unknown log record type")
-			}
-			if err != nil {
-				return err
-			}
-		}
-
-		// Move the head. This should be idempotent. We may
-		// move head to the local head in some cases.
-		// if err := i.syncd.dag.moveHead(obj, st.resolvVal.Mutation.Version); err != nil {
-		// 	return err
-		// }
-	}
-	return nil
-}
-
-// updateGenVecs updates local, reclaim and remote vectors at the end of an initiator cycle.
-func (i *syncInitiator) updateGenVecs() error {
-	// Update the local gen vector and put it in kvdb only if we have new updates.
-	if len(i.iState.updObjects) > 0 {
-		// remote can be a subset of local.
-		for sr, rVec := range i.iState.remote {
-			lVec := i.iState.local[sr]
-
-			if err := i.syncd.devtab.updateLocalGenVector(lVec, rVec); err != nil {
-				return err
-			}
-
-			if err := i.syncd.devtab.putGenVec(i.syncd.id, sr, lVec); err != nil {
-				return err
-			}
-
-			// if err := i.syncd.devtab.updateReclaimVec(minGens); err != nil {
-			// 	return err
-			// }
-		}
-	}
-
-	for sr, rVec := range i.iState.remote {
-		// Cache the remote generation vector for space reclamation.
-		if err := i.syncd.devtab.putGenVec(i.syncd.nameToDevId(i.iState.peer), sr, rVec); err != nil {
-			return err
-		}
-	}
-	return nil
-}
diff --git a/x/ref/services/syncbase/sync/kvdb.go b/x/ref/services/syncbase/sync/kvdb.go
deleted file mode 100644
index 79b1101..0000000
--- a/x/ref/services/syncbase/sync/kvdb.go
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-// Helpful wrappers to a persistent key/value (K/V) DB used by Veyron Sync.
-// The current underlying DB is an in-memory map.
-
-import (
-	"bytes"
-	"fmt"
-	"sort"
-
-	"v.io/v23/vom"
-)
-
-var memStore map[string]*kvdb
-
-type kvdb struct {
-	tables map[string]*kvtable
-}
-
-type kvtable struct {
-	data map[string][]byte
-}
-
-// kvdbOpen opens or creates a K/V DB for the given filename and table names
-// within the DB.  It returns the DB handler and handlers for each table.
-func kvdbOpen(filename string, tables []string) (*kvdb, []*kvtable, error) {
-	if memStore == nil {
-		memStore = make(map[string]*kvdb)
-	}
-
-	db := memStore[filename]
-	if db == nil {
-		db = &kvdb{tables: make(map[string]*kvtable)}
-		memStore[filename] = db
-	}
-
-	tbls := make([]*kvtable, len(tables))
-
-	for i, table := range tables {
-		t := db.tables[table]
-		if t == nil {
-			t = &kvtable{data: make(map[string][]byte)}
-			db.tables[table] = t
-		}
-		tbls[i] = t
-	}
-
-	return db, tbls, nil
-}
-
-// close closes the given K/V DB.
-func (db *kvdb) close() {
-}
-
-// flush flushes the given K/V DB to disk.
-func (db *kvdb) flush() {
-}
-
-// set stores (or overwrites) the given key/value pair in the DB table.
-func (t *kvtable) set(key string, value interface{}) error {
-	var val bytes.Buffer
-	if err := vom.NewEncoder(&val).Encode(value); err != nil {
-		return err
-	}
-	t.data[key] = val.Bytes()
-	return nil
-}
-
-// create stores the given key/value pair in the DB table only if
-// the key does not already exist.  Otherwise it returns an error.
-func (t *kvtable) create(key string, value interface{}) error {
-	if t.hasKey(key) {
-		return fmt.Errorf("key %s exists", key)
-	}
-	return t.set(key, value)
-}
-
-// update stores the given key/value pair in the DB table only if
-// the key already exists.  Otherwise it returns an error.
-func (t *kvtable) update(key string, value interface{}) error {
-	if !t.hasKey(key) {
-		return fmt.Errorf("key %s does not exist", key)
-	}
-	return t.set(key, value)
-}
-
-// get retrieves the value of a key from the DB table.
-func (t *kvtable) get(key string, value interface{}) error {
-	val := t.data[key]
-	if val == nil {
-		return fmt.Errorf("entry %s not found in the K/V DB table", key)
-	}
-	return vom.NewDecoder(bytes.NewBuffer(val)).Decode(value)
-}
-
-// del deletes the entry in the DB table given its key.
-func (t *kvtable) del(key string) error {
-	delete(t.data, key)
-	return nil
-}
-
-// hasKey returns true if the given key exists in the DB table.
-func (t *kvtable) hasKey(key string) bool {
-	val, ok := t.data[key]
-	return ok && val != nil
-}
-
-// keyIter iterates over all keys in a DB table invoking the given callback
-// function for each one.  The key iterator callback is passed the item key.
-func (t *kvtable) keyIter(keyIterCB func(key string)) error {
-	keys := make([]string, 0, len(t.data))
-	for k := range t.data {
-		keys = append(keys, k)
-	}
-	sort.Strings(keys)
-	for _, k := range keys {
-		keyIterCB(k)
-	}
-	return nil
-}
-
-// getNumKeys returns the number of keys (entries) in the DB table.
-func (t *kvtable) getNumKeys() uint64 {
-	return uint64(len(t.data))
-}
diff --git a/x/ref/services/syncbase/sync/kvdb_test.go b/x/ref/services/syncbase/sync/kvdb_test.go
deleted file mode 100644
index 69116e3..0000000
--- a/x/ref/services/syncbase/sync/kvdb_test.go
+++ /dev/null
@@ -1,407 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-// Tests for the Veyron Sync K/V DB component.
-
-import (
-	"fmt"
-	"os"
-	"reflect"
-	"testing"
-	"time"
-)
-
-// A user structure stores info in the "users" table.
-type user struct {
-	Username string
-	Drinks   []string
-}
-
-// A drink structure stores info in the "drinks" table.
-type drink struct {
-	Name    string
-	Alcohol bool
-}
-
-var (
-	users = []user{
-		{Username: "lancelot", Drinks: []string{"beer", "coffee"}},
-		{Username: "arthur", Drinks: []string{"coke", "beer", "coffee"}},
-		{Username: "robin", Drinks: []string{"pepsi"}},
-		{Username: "galahad"},
-	}
-	drinks = []drink{
-		{Name: "coke", Alcohol: false},
-		{Name: "pepsi", Alcohol: false},
-		{Name: "beer", Alcohol: true},
-		{Name: "coffee", Alcohol: false},
-	}
-)
-
-// createTestDB creates a K/V DB with 2 tables.
-func createTestDB(t *testing.T) (fname string, db *kvdb, usersTbl, drinksTbl *kvtable) {
-	fname = fmt.Sprintf("%s/sync_kvdb_test_%d_%d", os.TempDir(), os.Getpid(), time.Now().UnixNano())
-	db, tbls, err := kvdbOpen(fname, []string{"users", "drinks"})
-	if err != nil {
-		os.Remove(fname)
-		t.Fatalf("cannot create new K/V DB file %s: %v", fname, err)
-	}
-
-	usersTbl, drinksTbl = tbls[0], tbls[1]
-	return
-}
-
-// initTestTables initializes the K/V tables used by the tests.
-func initTestTables(t *testing.T, usersTbl, drinksTbl *kvtable, useCreate bool) {
-	userPut, drinkPut, funcName := usersTbl.set, drinksTbl.set, "set()"
-	if useCreate {
-		userPut, drinkPut, funcName = usersTbl.create, drinksTbl.create, "create()"
-	}
-
-	for _, uu := range users {
-		if err := userPut(uu.Username, &uu); err != nil {
-			t.Fatalf("%s failed for user %s", funcName, uu.Username)
-		}
-	}
-
-	for _, dd := range drinks {
-		if err := drinkPut(dd.Name, &dd); err != nil {
-			t.Fatalf("%s failed for drink %s", funcName, dd.Name)
-		}
-	}
-
-	return
-}
-
-// checkTestTables verifies the contents of the K/V tables.
-func checkTestTables(t *testing.T, usersTbl, drinksTbl *kvtable) {
-	for _, uu := range users {
-		var u2 user
-		if err := usersTbl.get(uu.Username, &u2); err != nil {
-			t.Fatalf("get() failed for user %s", uu.Username)
-		}
-		if !reflect.DeepEqual(u2, uu) {
-			t.Fatalf("got wrong data for user %s: %#v instead of %#v", uu.Username, u2, uu)
-		}
-		if !usersTbl.hasKey(uu.Username) {
-			t.Fatalf("hasKey() did not find user %s", uu.Username)
-		}
-	}
-	for _, dd := range drinks {
-		var d2 drink
-		if err := drinksTbl.get(dd.Name, &d2); err != nil {
-			t.Fatalf("get() failed for drink %s", dd.Name)
-		}
-		if !reflect.DeepEqual(d2, dd) {
-			t.Fatalf("got wrong data for drink %s: %#v instead of %#v", dd.Name, d2, dd)
-		}
-		if !drinksTbl.hasKey(dd.Name) {
-			t.Fatalf("hasKey() did not find drink %s", dd.Name)
-		}
-	}
-
-	if num := usersTbl.getNumKeys(); num != uint64(len(users)) {
-		t.Fatalf("getNumKeys(): wrong user count: got %v instead of %v", num, len(users))
-	}
-	if num := drinksTbl.getNumKeys(); num != uint64(len(drinks)) {
-		t.Fatalf("getNumKeys(): wrong drink count: got %v instead of %v", num, len(drinks))
-	}
-}
-
-func TestKVDBSet(t *testing.T) {
-	kvdbfile, db, usersTbl, drinksTbl := createTestDB(t)
-	defer os.Remove(kvdbfile)
-	defer db.close()
-
-	initTestTables(t, usersTbl, drinksTbl, false)
-
-	db.flush()
-}
-
-func TestKVDBCreate(t *testing.T) {
-	kvdbfile, db, usersTbl, drinksTbl := createTestDB(t)
-	defer os.Remove(kvdbfile)
-	defer db.close()
-
-	initTestTables(t, usersTbl, drinksTbl, true)
-
-	db.flush()
-}
-
-func TestKVDBBadGet(t *testing.T) {
-	kvdbfile, db, usersTbl, drinksTbl := createTestDB(t)
-	defer os.Remove(kvdbfile)
-	defer db.close()
-
-	// The DB is empty, all gets must fail.
-	for _, uu := range users {
-		var u2 user
-		if err := usersTbl.get(uu.Username, &u2); err == nil {
-			t.Fatalf("get() found non-existent user %s in file %s: %v", uu.Username, kvdbfile, u2)
-		}
-	}
-	for _, dd := range drinks {
-		var d2 drink
-		if err := drinksTbl.get(dd.Name, &d2); err == nil {
-			t.Fatalf("get() found non-existent drink %s in file %s: %v", dd.Name, kvdbfile, d2)
-		}
-	}
-}
-
-func TestKVDBBadUpdate(t *testing.T) {
-	kvdbfile, db, usersTbl, drinksTbl := createTestDB(t)
-	defer os.Remove(kvdbfile)
-	defer db.close()
-
-	// The DB is empty, all updates must fail.
-	for _, uu := range users {
-		u2 := user{Username: uu.Username}
-		if err := usersTbl.update(uu.Username, &u2); err == nil {
-			t.Fatalf("update() worked for a non-existent user %s in file %s", uu.Username, kvdbfile)
-		}
-	}
-	for _, dd := range drinks {
-		d2 := drink{Name: dd.Name}
-		if err := drinksTbl.update(dd.Name, &d2); err == nil {
-			t.Fatalf("update() worked for a non-existent drink %s in file %s", dd.Name, kvdbfile)
-		}
-	}
-}
-
-func TestKVDBBadHasKey(t *testing.T) {
-	kvdbfile, db, usersTbl, drinksTbl := createTestDB(t)
-	defer os.Remove(kvdbfile)
-	defer db.close()
-
-	// The DB is empty, all key-checks must fail.
-	for _, uu := range users {
-		if usersTbl.hasKey(uu.Username) {
-			t.Fatalf("hasKey() found non-existent user %s in file %s", uu.Username, kvdbfile)
-		}
-	}
-	for _, dd := range drinks {
-		if drinksTbl.hasKey(dd.Name) {
-			t.Fatalf("hasKey() found non-existent drink %s in file %s", dd.Name, kvdbfile)
-		}
-	}
-}
-
-func TestKVDBGet(t *testing.T) {
-	kvdbfile, db, usersTbl, drinksTbl := createTestDB(t)
-	defer os.Remove(kvdbfile)
-	defer db.close()
-
-	initTestTables(t, usersTbl, drinksTbl, false)
-	checkTestTables(t, usersTbl, drinksTbl)
-
-	db.flush()
-	checkTestTables(t, usersTbl, drinksTbl)
-}
-
-func TestKVDBBadCreate(t *testing.T) {
-	kvdbfile, db, usersTbl, drinksTbl := createTestDB(t)
-	defer os.Remove(kvdbfile)
-	defer db.close()
-
-	initTestTables(t, usersTbl, drinksTbl, false)
-
-	// Must not be able to re-create the same entries.
-	for _, uu := range users {
-		u2 := user{Username: uu.Username}
-		if err := usersTbl.create(uu.Username, &u2); err == nil {
-			t.Fatalf("create() worked for an existing user %s in file %s", uu.Username, kvdbfile)
-		}
-	}
-	for _, dd := range drinks {
-		d2 := drink{Name: dd.Name}
-		if err := drinksTbl.create(dd.Name, &d2); err == nil {
-			t.Fatalf("create() worked for an existing drink %s in file %s", dd.Name, kvdbfile)
-		}
-	}
-
-	db.flush()
-}
-
-func TestKVDBReopen(t *testing.T) {
-	kvdbfile, db, usersTbl, drinksTbl := createTestDB(t)
-	defer os.Remove(kvdbfile)
-
-	initTestTables(t, usersTbl, drinksTbl, true)
-
-	// Close the re-open the file.
-	db.flush()
-	db.close()
-
-	db, tbls, err := kvdbOpen(kvdbfile, []string{"users", "drinks"})
-	if err != nil {
-		t.Fatalf("Cannot re-open existing K/V DB file %s", kvdbfile)
-	}
-	defer db.close()
-
-	usersTbl, drinksTbl = tbls[0], tbls[1]
-	checkTestTables(t, usersTbl, drinksTbl)
-}
-
-func TestKVDBKeyIter(t *testing.T) {
-	kvdbfile, db, usersTbl, drinksTbl := createTestDB(t)
-	defer os.Remove(kvdbfile)
-	defer db.close()
-
-	initTestTables(t, usersTbl, drinksTbl, false)
-
-	// Get the list of all entry keys in each table.
-	keylist := ""
-	err := usersTbl.keyIter(func(key string) {
-		keylist += key + ","
-	})
-	if err != nil || keylist != "arthur,galahad,lancelot,robin," {
-		t.Fatalf("keyIter() failed in file %s: err %v, user names: %v", kvdbfile, err, keylist)
-	}
-	keylist = ""
-	err = drinksTbl.keyIter(func(key string) {
-		keylist += key + ","
-	})
-	if err != nil || keylist != "beer,coffee,coke,pepsi," {
-		t.Fatalf("keyIter() failed in file %s: err %v, drink names: %v", kvdbfile, err, keylist)
-	}
-
-	db.flush()
-}
-
-func TestKVDBUpdate(t *testing.T) {
-	kvdbfile, db, usersTbl, drinksTbl := createTestDB(t)
-	defer os.Remove(kvdbfile)
-
-	initTestTables(t, usersTbl, drinksTbl, false)
-	db.flush()
-	db.close()
-
-	db, tbls, err := kvdbOpen(kvdbfile, []string{"users", "drinks"})
-	if err != nil {
-		t.Fatalf("Cannot re-open existing K/V DB file %s", kvdbfile)
-	}
-	defer db.close()
-
-	usersTbl, drinksTbl = tbls[0], tbls[1]
-
-	for _, uu := range users {
-		key := uu.Username
-		u2 := uu
-		u2.Username += "-New"
-
-		if err = usersTbl.update(key, &u2); err != nil {
-			t.Fatalf("update() failed for user %s in file %s", key, kvdbfile)
-		}
-
-		var u3 user
-		if err = usersTbl.get(key, &u3); err != nil {
-			t.Fatalf("get() failed for user %s in file %s", key, kvdbfile)
-		}
-		if !reflect.DeepEqual(u3, u2) {
-			t.Fatalf("got wrong new data for user %s in file %s: %#v instead of %#v", key, kvdbfile, u3, u2)
-		}
-	}
-
-	for _, dd := range drinks {
-		key := dd.Name
-		d2 := dd
-		d2.Alcohol = !d2.Alcohol
-
-		if err = drinksTbl.update(key, &d2); err != nil {
-			t.Fatalf("update() failed for drink %s in file %s", key, kvdbfile)
-		}
-
-		var d3 drink
-		if err = drinksTbl.get(key, &d3); err != nil {
-			t.Fatalf("get() failed for drink %s in file %s", key, kvdbfile)
-		}
-		if !reflect.DeepEqual(d3, d2) {
-			t.Fatalf("got wrong new data for drink %s in file %s: %#v instead of %#v", key, kvdbfile, d3, d2)
-		}
-	}
-
-	db.flush()
-}
-
-func TestKVDBSetAgain(t *testing.T) {
-	kvdbfile, db, usersTbl, drinksTbl := createTestDB(t)
-	defer os.Remove(kvdbfile)
-	defer db.close()
-
-	initTestTables(t, usersTbl, drinksTbl, false)
-
-	for _, uu := range users {
-		key := uu.Username
-		u2 := uu
-		u2.Username += "-New"
-
-		if err := usersTbl.set(key, &u2); err != nil {
-			t.Fatalf("set() again failed for user %s in file %s", key, kvdbfile)
-		}
-
-		var u3 user
-		if err := usersTbl.get(key, &u3); err != nil {
-			t.Fatalf("get() failed for user %s in file %s", key, kvdbfile)
-		}
-		if !reflect.DeepEqual(u3, u2) {
-			t.Fatalf("got wrong new data for user %s in file %s: %#v instead of %#v", key, kvdbfile, u3, u2)
-		}
-	}
-
-	for _, dd := range drinks {
-		key := dd.Name
-		d2 := dd
-		d2.Alcohol = !d2.Alcohol
-
-		if err := drinksTbl.update(key, &d2); err != nil {
-			t.Fatalf("set() again failed for drink %s in file %s", key, kvdbfile)
-		}
-
-		var d3 drink
-		if err := drinksTbl.get(key, &d3); err != nil {
-			t.Fatalf("get() failed for drink %s in file %s", key, kvdbfile)
-		}
-		if !reflect.DeepEqual(d3, d2) {
-			t.Fatalf("got wrong new data for drink %s in file %s: %#v instead of %#v", key, kvdbfile, d3, d2)
-		}
-	}
-
-	db.flush()
-}
-
-func TestKVDBDelete(t *testing.T) {
-	kvdbfile, db, usersTbl, drinksTbl := createTestDB(t)
-	defer os.Remove(kvdbfile)
-	defer db.close()
-
-	initTestTables(t, usersTbl, drinksTbl, false)
-
-	db.flush()
-
-	// Delete entries and verify that they no longer exist.
-
-	for _, uu := range users {
-		key := uu.Username
-		if err := usersTbl.del(key); err != nil {
-			t.Errorf("del() failed for user %s in file %s", key, kvdbfile)
-		}
-		if usersTbl.hasKey(key) {
-			t.Errorf("hasKey() still finds deleted user %s in file %s", key, kvdbfile)
-		}
-	}
-
-	for _, dd := range drinks {
-		key := dd.Name
-		if err := drinksTbl.del(key); err != nil {
-			t.Errorf("del() failed for drink %s in file %s", key, kvdbfile)
-		}
-		if drinksTbl.hasKey(key) {
-			t.Errorf("hasKey() still finds deleted drink %s in file %s", key, kvdbfile)
-		}
-	}
-
-	db.flush()
-}
diff --git a/x/ref/services/syncbase/sync/replay_test.go b/x/ref/services/syncbase/sync/replay_test.go
deleted file mode 100644
index 218abae..0000000
--- a/x/ref/services/syncbase/sync/replay_test.go
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-// Used to ease the setup of Veyron Sync test scenarios.
-// Parses a sync command file and returns a vector of commands to execute.
-//
-// Used by different test replay engines:
-// - dagReplayCommands() executes the parsed commands at the DAG API level.
-// - logReplayCommands() executes the parsed commands at the Log API level.
-
-import (
-	"bufio"
-	"fmt"
-	"os"
-	"strconv"
-	"strings"
-)
-
-const (
-	addLocal = iota
-	addRemote
-	setDevTable
-	linkLocal
-	linkRemote
-)
-
-type syncCommand struct {
-	cmd     int
-	objID   ObjId
-	version Version
-	parents []Version
-	logrec  string
-	devID   DeviceId
-	genVec  GenVector
-	txID    TxId
-	txCount uint32
-	deleted bool
-}
-
-func strToVersion(verStr string) (Version, error) {
-	ver, err := strconv.ParseUint(verStr, 10, 64)
-	if err != nil {
-		return 0, err
-	}
-	return Version(ver), nil
-}
-
-func parseSyncCommands(file string) ([]syncCommand, error) {
-	cmds := []syncCommand{}
-	sf, err := os.Open("testdata/" + file)
-	if err != nil {
-		return nil, err
-	}
-	defer sf.Close()
-
-	scanner := bufio.NewScanner(sf)
-	lineno := 0
-	for scanner.Scan() {
-		lineno++
-		line := strings.TrimSpace(scanner.Text())
-		if line == "" || line[0] == '#' {
-			continue
-		}
-
-		args := strings.Split(line, "|")
-		nargs := len(args)
-
-		switch args[0] {
-		case "addl", "addr":
-			expNargs := 9
-			if nargs != expNargs {
-				return nil, fmt.Errorf("%s:%d: need %d args instead of %d", file, lineno, expNargs, nargs)
-			}
-			version, err := strToVersion(args[2])
-			if err != nil {
-				return nil, fmt.Errorf("%s:%d: invalid version: %s", file, lineno, args[2])
-			}
-			var parents []Version
-			for i := 3; i <= 4; i++ {
-				if args[i] != "" {
-					pver, err := strToVersion(args[i])
-					if err != nil {
-						return nil, fmt.Errorf("%s:%d: invalid parent: %s", file, lineno, args[i])
-					}
-					parents = append(parents, pver)
-				}
-			}
-
-			txID, err := strToTxId(args[6])
-			if err != nil {
-				return nil, fmt.Errorf("%s:%d: invalid TxId: %s", file, lineno, args[6])
-			}
-			txCount, err := strconv.ParseUint(args[7], 10, 32)
-			if err != nil {
-				return nil, fmt.Errorf("%s:%d: invalid tx count: %s", file, lineno, args[7])
-			}
-			del, err := strconv.ParseBool(args[8])
-			if err != nil {
-				return nil, fmt.Errorf("%s:%d: invalid deleted bit: %s", file, lineno, args[8])
-			}
-			cmd := syncCommand{
-				version: version,
-				parents: parents,
-				logrec:  args[5],
-				txID:    txID,
-				txCount: uint32(txCount),
-				deleted: del,
-			}
-			if args[0] == "addl" {
-				cmd.cmd = addLocal
-			} else {
-				cmd.cmd = addRemote
-			}
-			if cmd.objID, err = strToObjId(args[1]); err != nil {
-				return nil, fmt.Errorf("%s:%d: invalid object ID: %s", file, lineno, args[1])
-			}
-			cmds = append(cmds, cmd)
-
-		case "setdev":
-			expNargs := 3
-			if nargs != expNargs {
-				return nil, fmt.Errorf("%s:%d: need %d args instead of %d", file, lineno, expNargs, nargs)
-			}
-
-			genVec := make(GenVector)
-			for _, elem := range strings.Split(args[2], ",") {
-				kv := strings.Split(elem, ":")
-				if len(kv) != 2 {
-					return nil, fmt.Errorf("%s:%d: invalid gen vector key/val: %s", file, lineno, elem)
-				}
-				genID, err := strToGenId(kv[1])
-				if err != nil {
-					return nil, fmt.Errorf("%s:%d: invalid gen ID: %s", file, lineno, kv[1])
-				}
-				genVec[DeviceId(kv[0])] = genID
-			}
-
-			cmd := syncCommand{cmd: setDevTable, devID: DeviceId(args[1]), genVec: genVec}
-			cmds = append(cmds, cmd)
-
-		case "linkl", "linkr":
-			expNargs := 6
-			if nargs != expNargs {
-				return nil, fmt.Errorf("%s:%d: need %d args instead of %d", file, lineno, expNargs, nargs)
-			}
-
-			version, err := strToVersion(args[2])
-			if err != nil {
-				return nil, fmt.Errorf("%s:%d: invalid version: %s", file, lineno, args[2])
-			}
-			if args[3] == "" {
-				return nil, fmt.Errorf("%s:%d: parent (to-node) version not specified", file, lineno)
-			}
-			if args[4] != "" {
-				return nil, fmt.Errorf("%s:%d: cannot specify a 2nd parent (to-node): %s", file, lineno, args[4])
-			}
-			parent, err := strToVersion(args[3])
-			if err != nil {
-				return nil, fmt.Errorf("%s:%d: invalid parent (to-node) version: %s", file, lineno, args[3])
-			}
-
-			cmd := syncCommand{version: version, parents: []Version{parent}, logrec: args[5]}
-			if args[0] == "linkl" {
-				cmd.cmd = linkLocal
-			} else {
-				cmd.cmd = linkRemote
-			}
-			if cmd.objID, err = strToObjId(args[1]); err != nil {
-				return nil, fmt.Errorf("%s:%d: invalid object ID: %s", file, lineno, args[1])
-			}
-			cmds = append(cmds, cmd)
-
-		default:
-			return nil, fmt.Errorf("%s:%d: invalid operation: %s", file, lineno, args[0])
-		}
-	}
-
-	err = scanner.Err()
-	return cmds, err
-}
-
-func dagReplayCommands(dag *dag, syncfile string) error {
-	cmds, err := parseSyncCommands(syncfile)
-	if err != nil {
-		return err
-	}
-
-	for _, cmd := range cmds {
-		switch cmd.cmd {
-		case addLocal:
-			err = dag.addNode(cmd.objID, cmd.version, false, cmd.deleted, cmd.parents, cmd.logrec, NoTxId)
-			if err != nil {
-				return fmt.Errorf("cannot add local node %v:%d to DAG: %v", cmd.objID, cmd.version, err)
-			}
-			if err := dag.moveHead(cmd.objID, cmd.version); err != nil {
-				return fmt.Errorf("cannot move head to %v:%d in DAG: %v", cmd.objID, cmd.version, err)
-			}
-			dag.flush()
-
-		case addRemote:
-			err = dag.addNode(cmd.objID, cmd.version, true, cmd.deleted, cmd.parents, cmd.logrec, NoTxId)
-			if err != nil {
-				return fmt.Errorf("cannot add remote node %v:%d to DAG: %v", cmd.objID, cmd.version, err)
-			}
-			dag.flush()
-
-		case linkLocal:
-			if err = dag.addParent(cmd.objID, cmd.version, cmd.parents[0], false); err != nil {
-				return fmt.Errorf("cannot add local parent %d to DAG node %v:%d: %v",
-					cmd.parents[0], cmd.objID, cmd.version, err)
-			}
-			dag.flush()
-
-		case linkRemote:
-			if err = dag.addParent(cmd.objID, cmd.version, cmd.parents[0], true); err != nil {
-				return fmt.Errorf("cannot add remote parent %d to DAG node %v:%d: %v",
-					cmd.parents[0], cmd.objID, cmd.version, err)
-			}
-			dag.flush()
-		}
-	}
-	return nil
-}
diff --git a/x/ref/services/syncbase/sync/sgtable.go b/x/ref/services/syncbase/sync/sgtable.go
deleted file mode 100644
index 41818c1..0000000
--- a/x/ref/services/syncbase/sync/sgtable.go
+++ /dev/null
@@ -1,500 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-// The SyncGroup Table stores the group information in a K/V DB.  It also
-// maintains an index to provide access by SyncGroup ID or name.
-//
-// The SyncGroup info is fetched from the SyncGroup server by the create or
-// join operations, and is regularly updated after that.
-//
-// The DB contains two tables persisted to disk (data, names) and one
-// in-memory (ephemeral) map (members):
-//   * data:    one entry per SyncGroup ID containing the SyncGroup data
-//   * names:   one entry per SyncGroup name pointing to its SyncGroup ID
-//   * members: an inverted index of SyncGroup members to SyncGroup IDs
-//              built from the list of SyncGroup joiners
-
-import (
-	"errors"
-	"fmt"
-	"path"
-	"strconv"
-
-	"v.io/x/lib/vlog"
-	"v.io/x/ref/lib/stats"
-)
-
-var (
-	errBadSGTable = errors.New("invalid SyncGroup Table")
-)
-
-type syncGroupTable struct {
-	fname   string                 // file pathname
-	store   *kvdb                  // underlying K/V store
-	sgData  *kvtable               // pointer to "data" table in the kvdb
-	sgNames *kvtable               // pointer to "names" table in the kvdb
-	members map[string]*memberInfo // in-memory tracking of SyncGroup member info
-
-	// SyncGroup Table stats
-	numSGs     *stats.Integer // number of SyncGroups
-	numMembers *stats.Integer // number of Sync members
-}
-
-type syncGroupData struct {
-	SrvInfo   SyncGroupInfo // SyncGroup info from SyncGroupServer
-	LocalPath string        // local path of the SyncGroup in the Store
-}
-
-type memberInfo struct {
-	gids map[GroupId]*memberMetaData // map of SyncGroup IDs joined and their metadata
-}
-
-type memberMetaData struct {
-	metaData JoinerMetaData // joiner metadata at the SyncGroup server
-}
-
-type sgSet map[GroupId]struct{} // a set of SyncGroups
-
-// strToGroupId converts a SyncGroup ID in string format to an GroupId.
-func strToGroupId(str string) (GroupId, error) {
-	id, err := strconv.ParseUint(str, 10, 64)
-	if err != nil {
-		return NoGroupId, err
-	}
-	return GroupId(id), nil
-}
-
-// openSyncGroupTable opens or creates a syncGroupTable for the given filename.
-func openSyncGroupTable(filename string) (*syncGroupTable, error) {
-	// Open the file and create it if it does not exist.
-	// Also initialize the store and its tables.
-	db, tbls, err := kvdbOpen(filename, []string{"data", "names"})
-	if err != nil {
-		return nil, err
-	}
-
-	s := &syncGroupTable{
-		fname:      filename,
-		store:      db,
-		sgData:     tbls[0],
-		sgNames:    tbls[1],
-		members:    make(map[string]*memberInfo),
-		numSGs:     stats.NewInteger(statsNumSyncGroup),
-		numMembers: stats.NewInteger(statsNumMember),
-	}
-
-	// Reconstruct the in-memory tracking maps by iterating over the SyncGroups.
-	// This is needed when an existing SyncGroup Table file is re-opened.
-	s.sgData.keyIter(func(gidStr string) {
-		// Get the SyncGroup data given the group ID in string format (as the data table key).
-		gid, err := strToGroupId(gidStr)
-		if err != nil {
-			return
-		}
-
-		data, err := s.getSyncGroupByID(gid)
-		if err != nil {
-			return
-		}
-
-		s.numSGs.Incr(1)
-
-		// Add all SyncGroup members to the members inverted index.
-		s.addAllMembers(data)
-	})
-
-	return s, nil
-}
-
-// close closes the syncGroupTable and invalidates its structure.
-func (s *syncGroupTable) close() {
-	if s.store != nil {
-		s.store.close() // this also closes the tables
-		stats.Delete(statsNumSyncGroup)
-		stats.Delete(statsNumMember)
-	}
-	*s = syncGroupTable{} // zero out the structure
-}
-
-// flush flushes the syncGroupTable store to disk.
-func (s *syncGroupTable) flush() {
-	if s.store != nil {
-		s.store.flush()
-	}
-}
-
-// addSyncGroup adds a new SyncGroup given its information.
-func (s *syncGroupTable) addSyncGroup(sgData *syncGroupData) error {
-	if s.store == nil {
-		return errBadSGTable
-	}
-	if sgData == nil {
-		return errors.New("group information not specified")
-	}
-	gid, name := sgData.SrvInfo.Id, path.Join(sgData.SrvInfo.ServerName, sgData.SrvInfo.GroupName)
-	if name == "" {
-		return errors.New("group name not specified")
-	}
-	if sgData.LocalPath == "" {
-		return errors.New("group local path not specified")
-	}
-	if len(sgData.SrvInfo.Joiners) == 0 {
-		return errors.New("group has no joiners")
-	}
-
-	if s.hasSGDataEntry(gid) {
-		return fmt.Errorf("group %d already exists", gid)
-	}
-	if s.hasSGNameEntry(name) {
-		return fmt.Errorf("group name %s already exists", name)
-	}
-
-	// Add the group name and data entries.
-	if err := s.setSGNameEntry(name, gid); err != nil {
-		return err
-	}
-
-	if err := s.setSGDataEntry(gid, sgData); err != nil {
-		s.delSGNameEntry(name)
-		return err
-	}
-
-	s.numSGs.Incr(1)
-	s.addAllMembers(sgData)
-	return nil
-}
-
-// getSyncGroupID retrieves the SyncGroup ID given its name.
-func (s *syncGroupTable) getSyncGroupID(name string) (GroupId, error) {
-	return s.getSGNameEntry(name)
-}
-
-// getSyncGroupName retrieves the SyncGroup name given its ID.
-func (s *syncGroupTable) getSyncGroupName(gid GroupId) (string, error) {
-	data, err := s.getSyncGroupByID(gid)
-	if err != nil {
-		return "", err
-	}
-
-	return path.Join(data.SrvInfo.ServerName, data.SrvInfo.GroupName), nil
-}
-
-// getSyncGroupByID retrieves the SyncGroup given its ID.
-func (s *syncGroupTable) getSyncGroupByID(gid GroupId) (*syncGroupData, error) {
-	return s.getSGDataEntry(gid)
-}
-
-// getSyncGroupByName retrieves the SyncGroup given its name.
-func (s *syncGroupTable) getSyncGroupByName(name string) (*syncGroupData, error) {
-	gid, err := s.getSyncGroupID(name)
-	if err != nil {
-		return nil, err
-	}
-	return s.getSyncGroupByID(gid)
-}
-
-// updateSyncGroup updates the SyncGroup data.
-func (s *syncGroupTable) updateSyncGroup(data *syncGroupData) error {
-	if s.store == nil {
-		return errBadSGTable
-	}
-	if data == nil {
-		return errors.New("SyncGroup data not specified")
-	}
-	if data.SrvInfo.GroupName == "" {
-		return errors.New("group name not specified")
-	}
-	if len(data.SrvInfo.Joiners) == 0 {
-		return errors.New("group has no joiners")
-	}
-
-	fullGroupName := path.Join(data.SrvInfo.ServerName, data.SrvInfo.GroupName)
-	oldData, err := s.getSyncGroupByName(fullGroupName)
-	if err != nil {
-		return err
-	}
-
-	if data.SrvInfo.Id != oldData.SrvInfo.Id {
-		return fmt.Errorf("cannot change ID of SyncGroup name %s", fullGroupName)
-	}
-	if data.LocalPath == "" {
-		data.LocalPath = oldData.LocalPath
-	} else if data.LocalPath != oldData.LocalPath {
-		return fmt.Errorf("cannot change local path of SyncGroup name %s", fullGroupName)
-	}
-
-	// Get the old set of SyncGroup joiners and diff it with the new set.
-	// Add all the current members because this inserts the new members and
-	// updates the metadata of the existing ones (addMember() is like a "put").
-	// Delete the members that are no longer part of the SyncGroup.
-	gid := oldData.SrvInfo.Id
-	newJoiners, oldJoiners := data.SrvInfo.Joiners, oldData.SrvInfo.Joiners
-
-	for member, memberData := range newJoiners {
-		s.addMember(member, gid, memberData)
-	}
-
-	for member := range oldJoiners {
-		if _, ok := newJoiners[member]; !ok {
-			s.delMember(member, gid)
-		}
-	}
-
-	return s.setSGDataEntry(gid, data)
-}
-
-// delSyncGroupByID deletes the SyncGroup given its ID.
-func (s *syncGroupTable) delSyncGroupByID(gid GroupId) error {
-	data, err := s.getSyncGroupByID(gid)
-	if err != nil {
-		return err
-	}
-	if err = s.delSGNameEntry(path.Join(data.SrvInfo.ServerName, data.SrvInfo.GroupName)); err != nil {
-		return err
-	}
-
-	s.numSGs.Incr(-1)
-	s.delAllMembers(data)
-	return s.delSGDataEntry(gid)
-}
-
-// delSyncGroupByName deletes the SyncGroup given its name.
-func (s *syncGroupTable) delSyncGroupByName(name string) error {
-	gid, err := s.getSyncGroupID(name)
-	if err != nil {
-		return err
-	}
-
-	return s.delSyncGroupByID(gid)
-}
-
-// getAllSyncGroupNames returns the names of all SyncGroups.
-func (s *syncGroupTable) getAllSyncGroupNames() ([]string, error) {
-	if s.store == nil {
-		return nil, errBadSGTable
-	}
-
-	names := make([]string, 0)
-
-	err := s.sgNames.keyIter(func(name string) {
-		names = append(names, name)
-	})
-
-	if err != nil {
-		return nil, err
-	}
-	return names, nil
-}
-
-// getMembers returns all SyncGroup members and the count of SyncGroups each one joined.
-func (s *syncGroupTable) getMembers() (map[string]uint32, error) {
-	if s.store == nil {
-		return nil, errBadSGTable
-	}
-
-	members := make(map[string]uint32)
-	for member, info := range s.members {
-		members[member] = uint32(len(info.gids))
-	}
-
-	return members, nil
-}
-
-// getMemberInfo returns SyncGroup information for a given member.
-func (s *syncGroupTable) getMemberInfo(member string) (*memberInfo, error) {
-	if s.store == nil {
-		return nil, errBadSGTable
-	}
-
-	info, ok := s.members[member]
-	if !ok {
-		return nil, fmt.Errorf("unknown member: %s", member)
-	}
-
-	return info, nil
-}
-
-// addMember inserts or updates a (member, group ID) entry in the in-memory
-// structure that indexes SyncGroup memberships based on member names and stores
-// in it the member's joiner metadata.
-func (s *syncGroupTable) addMember(member string, gid GroupId, metadata JoinerMetaData) {
-	if s.store == nil {
-		return
-	}
-
-	info, ok := s.members[member]
-	if !ok {
-		info = &memberInfo{gids: make(map[GroupId]*memberMetaData)}
-		s.members[member] = info
-		s.numMembers.Incr(1)
-	}
-
-	info.gids[gid] = &memberMetaData{metaData: metadata}
-}
-
-// delMember removes a (member, group ID) entry from the in-memory structure
-// that indexes SyncGroup memberships based on member names.
-func (s *syncGroupTable) delMember(member string, gid GroupId) {
-	if s.store == nil {
-		return
-	}
-
-	info, ok := s.members[member]
-	if !ok {
-		return
-	}
-
-	delete(info.gids, gid)
-	if len(info.gids) == 0 {
-		delete(s.members, member)
-		s.numMembers.Incr(-1)
-	}
-}
-
-// addAllMembers inserts all members of a SyncGroup in the in-memory structure
-// that indexes SyncGroup memberships based on member names.
-func (s *syncGroupTable) addAllMembers(data *syncGroupData) {
-	if s.store == nil || data == nil {
-		return
-	}
-
-	gid := data.SrvInfo.Id
-	for member, memberData := range data.SrvInfo.Joiners {
-		s.addMember(member, gid, memberData)
-	}
-}
-
-// delAllMembers removes all members of a SyncGroup from the in-memory structure
-// that indexes SyncGroup memberships based on member names.
-func (s *syncGroupTable) delAllMembers(data *syncGroupData) {
-	if s.store == nil || data == nil {
-		return
-	}
-
-	gid := data.SrvInfo.Id
-	for member := range data.SrvInfo.Joiners {
-		s.delMember(member, gid)
-	}
-}
-
-// Low-level functions to access the tables in the K/V DB.
-// They directly access the table entries without tracking their relationships.
-
-// sgDataKey returns the key used to access the SyncGroup data in the DB.
-func sgDataKey(gid GroupId) string {
-	return fmt.Sprintf("%d", gid)
-}
-
-// hasSGDataEntry returns true if the SyncGroup data entry exists in the DB.
-func (s *syncGroupTable) hasSGDataEntry(gid GroupId) bool {
-	if s.store == nil {
-		return false
-	}
-	key := sgDataKey(gid)
-	return s.sgData.hasKey(key)
-}
-
-// setSGDataEntry stores the SyncGroup data in the DB.
-func (s *syncGroupTable) setSGDataEntry(gid GroupId, data *syncGroupData) error {
-	if s.store == nil {
-		return errBadSGTable
-	}
-	key := sgDataKey(gid)
-	return s.sgData.set(key, data)
-}
-
-// getSGDataEntry retrieves from the DB the SyncGroup data for a given group ID.
-func (s *syncGroupTable) getSGDataEntry(gid GroupId) (*syncGroupData, error) {
-	if s.store == nil {
-		return nil, errBadSGTable
-	}
-	var data syncGroupData
-	key := sgDataKey(gid)
-	if err := s.sgData.get(key, &data); err != nil {
-		return nil, err
-	}
-	return &data, nil
-}
-
-// delSGDataEntry deletes the SyncGroup data from the DB.
-func (s *syncGroupTable) delSGDataEntry(gid GroupId) error {
-	if s.store == nil {
-		return errBadSGTable
-	}
-	key := sgDataKey(gid)
-	return s.sgData.del(key)
-}
-
-// sgNameKey returns the key used to access the SyncGroup name in the DB.
-func sgNameKey(name string) string {
-	return name
-}
-
-// hasSGNameEntry returns true if the SyncGroup name entry exists in the DB.
-func (s *syncGroupTable) hasSGNameEntry(name string) bool {
-	if s.store == nil {
-		return false
-	}
-	key := sgNameKey(name)
-	return s.sgNames.hasKey(key)
-}
-
-// setSGNameEntry stores the SyncGroup name to ID mapping in the DB.
-func (s *syncGroupTable) setSGNameEntry(name string, gid GroupId) error {
-	if s.store == nil {
-		return errBadSGTable
-	}
-	key := sgNameKey(name)
-	return s.sgNames.set(key, gid)
-}
-
-// getSGNameEntry retrieves the SyncGroup name to ID mapping from the DB.
-func (s *syncGroupTable) getSGNameEntry(name string) (GroupId, error) {
-	var gid GroupId
-	if s.store == nil {
-		return gid, errBadSGTable
-	}
-	key := sgNameKey(name)
-	err := s.sgNames.get(key, &gid)
-	return gid, err
-}
-
-// delSGNameEntry deletes the SyncGroup name to ID mapping from the DB.
-func (s *syncGroupTable) delSGNameEntry(name string) error {
-	if s.store == nil {
-		return errBadSGTable
-	}
-	key := sgNameKey(name)
-	return s.sgNames.del(key)
-}
-
-// dump writes to the log file information on all SyncGroups.
-func (s *syncGroupTable) dump() {
-	if s.store == nil {
-		return
-	}
-
-	s.sgData.keyIter(func(gidStr string) {
-		// Get the SyncGroup data given the group ID in string format (as the data table key).
-		gid, err := strToGroupId(gidStr)
-		if err != nil {
-			return
-		}
-
-		data, err := s.getSyncGroupByID(gid)
-		if err != nil {
-			return
-		}
-
-		members := make([]string, 0, len(data.SrvInfo.Joiners))
-		for joiner := range data.SrvInfo.Joiners {
-			members = append(members, joiner)
-		}
-		vlog.VI(1).Infof("DUMP: SyncGroup %s: id %v, path %s, members: %s",
-			path.Join(data.SrvInfo.ServerName, data.SrvInfo.GroupName),
-			gid, data.LocalPath, members)
-	})
-}
diff --git a/x/ref/services/syncbase/sync/sgtable_test.go b/x/ref/services/syncbase/sync/sgtable_test.go
deleted file mode 100644
index c80a2a9..0000000
--- a/x/ref/services/syncbase/sync/sgtable_test.go
+++ /dev/null
@@ -1,769 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-// Tests for the Veyron SyncGroup Table.
-
-import (
-	"os"
-	"reflect"
-	"testing"
-
-	"v.io/x/ref/lib/stats"
-)
-
-// TestSyncGroupTableOpen tests the creation of a SyncGroup Table, closing and re-opening it.
-// It also verifies that its backing file is created and that a 2nd close is safe.
-func TestSyncGroupTableOpen(t *testing.T) {
-	sgfile := getFileName()
-	defer os.Remove(sgfile)
-
-	sg, err := openSyncGroupTable(sgfile)
-	if err != nil {
-		t.Fatalf("cannot open new SyncGroup Table file %s", sgfile)
-	}
-
-	fsize := getFileSize(sgfile)
-	if fsize < 0 {
-		//t.Fatalf("SyncGroup Table file %s not created", sgfile)
-	}
-
-	sg.flush()
-	oldfsize := fsize
-	fsize = getFileSize(sgfile)
-	if fsize <= oldfsize {
-		//t.Fatalf("SyncGroup Table file %s not flushed", sgfile)
-	}
-
-	sg.close()
-
-	sg, err = openSyncGroupTable(sgfile)
-	if err != nil {
-		t.Fatalf("cannot re-open existing SyncGroup Table file %s", sgfile)
-	}
-
-	oldfsize = fsize
-	fsize = getFileSize(sgfile)
-	if fsize != oldfsize {
-		t.Fatalf("SyncGroup Table file %s size changed across re-open", sgfile)
-	}
-
-	sg.close()
-	sg.close() // multiple closes should be a safe NOP
-
-	fsize = getFileSize(sgfile)
-	if fsize != oldfsize {
-		t.Fatalf("SyncGroup Table file %s size changed across close", sgfile)
-	}
-
-	// Fail opening a SyncGroup Table in a non-existent directory.
-	_, err = openSyncGroupTable("/not/really/there/junk.sg")
-	if err == nil {
-		//t.Fatalf("openSyncGroupTable() did not fail when using a bad pathname")
-	}
-}
-
-// TestInvalidSyncGroupTable tests using methods on an invalid (closed) SyncGroup Table.
-func TestInvalidSyncGroupTable(t *testing.T) {
-	sgfile := getFileName()
-	defer os.Remove(sgfile)
-
-	sg, err := openSyncGroupTable(sgfile)
-	if err != nil {
-		t.Fatalf("cannot open new SyncGroup Table file %s", sgfile)
-	}
-
-	sg.close()
-
-	sgid, err := strToGroupId("1234")
-	if err != nil {
-		t.Error(err)
-	}
-
-	validateError := func(t *testing.T, err error, funcName string) {
-		if err == nil || err.Error() != "invalid SyncGroup Table" {
-			t.Errorf("%s() did not fail on a closed SyncGroup Table: %v", funcName, err)
-		}
-	}
-
-	err = sg.addSyncGroup(&syncGroupData{})
-	validateError(t, err, "addSyncGroup")
-
-	_, err = sg.getSyncGroupID("foobar")
-	validateError(t, err, "getSyncGroupID")
-
-	_, err = sg.getSyncGroupName(sgid)
-	validateError(t, err, "getSyncGroupName")
-
-	_, err = sg.getSyncGroupByID(sgid)
-	validateError(t, err, "getSyncGroupByID")
-
-	_, err = sg.getSyncGroupByName("foobar")
-	validateError(t, err, "getSyncGroupByName")
-
-	err = sg.updateSyncGroup(&syncGroupData{})
-	validateError(t, err, "updateSyncGroup")
-
-	err = sg.delSyncGroupByID(sgid)
-	validateError(t, err, "delSyncGroupByID")
-
-	err = sg.delSyncGroupByName("foobar")
-	validateError(t, err, "delSyncGroupByName")
-
-	_, err = sg.getAllSyncGroupNames()
-	validateError(t, err, "getAllSyncGroupNames")
-
-	_, err = sg.getMembers()
-	validateError(t, err, "getMembers")
-
-	_, err = sg.getMemberInfo("foobar")
-	validateError(t, err, "getMemberInfo")
-
-	err = sg.setSGDataEntry(sgid, &syncGroupData{})
-	validateError(t, err, "setSGDataEntry")
-
-	_, err = sg.getSGDataEntry(sgid)
-	validateError(t, err, "getSGDataEntry")
-
-	err = sg.delSGDataEntry(sgid)
-	validateError(t, err, "delSGDataEntry")
-
-	err = sg.setSGNameEntry("foobar", sgid)
-	validateError(t, err, "setSGNameEntry")
-
-	_, err = sg.getSGNameEntry("foobar")
-	validateError(t, err, "getSGNameEntry")
-
-	err = sg.delSGNameEntry("foobar")
-	validateError(t, err, "delSGNameEntry")
-
-	// These calls should be harmless NOPs.
-	sg.dump()
-	sg.flush()
-	sg.close()
-	sg.addMember("foobar", sgid, JoinerMetaData{})
-	sg.delMember("foobar", sgid)
-	sg.addAllMembers(&syncGroupData{})
-	sg.delAllMembers(&syncGroupData{})
-
-	if sg.hasSGDataEntry(sgid) {
-		t.Errorf("hasSGDataEntry() found an entry on a closed SyncGroup Table")
-	}
-	if sg.hasSGNameEntry("foobar") {
-		t.Errorf("hasSGNameEntry() found an entry on a closed SyncGroup Table")
-	}
-}
-
-// checkSGStats verifies the SyncGroup Table stats counters.
-func checkSGStats(t *testing.T, which string, numSG, numMembers int64) {
-	if num, err := stats.Value(statsNumSyncGroup); err != nil || num != numSG {
-		t.Errorf("num-syncgroups (%s): got %v (err: %v) instead of %v", which, num, err, numSG)
-	}
-	if num, err := stats.Value(statsNumMember); err != nil || num != numMembers {
-		t.Errorf("num-members (%s): got %v  (err: %v) instead of %v", which, num, err, numMembers)
-	}
-}
-
-// TestAddSyncGroup tests adding SyncGroups.
-func TestAddSyncGroup(t *testing.T) {
-	sgfile := getFileName()
-	defer os.Remove(sgfile)
-
-	sg, err := openSyncGroupTable(sgfile)
-	if err != nil {
-		t.Fatalf("cannot open new SyncGroup Table file %s", sgfile)
-	}
-
-	checkSGStats(t, "add-1", 0, 0)
-
-	sgname := "foobar"
-	sgid, err := strToGroupId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	sgData := &syncGroupData{
-		SrvInfo: SyncGroupInfo{
-			Id:        sgid,
-			GroupName: sgname,
-			Joiners: map[string]JoinerMetaData{
-				"phone":  JoinerMetaData{SyncPriority: 10},
-				"tablet": JoinerMetaData{SyncPriority: 25},
-				"cloud":  JoinerMetaData{SyncPriority: 1},
-			},
-		},
-		LocalPath: "/foo/bar",
-	}
-
-	err = sg.addSyncGroup(sgData)
-	if err != nil {
-		t.Errorf("adding SyncGroup ID %d failed in SyncGroup Table file %s: %v", sgid, sgfile, err)
-	}
-
-	// Verify SyncGroup ID, name, and data.
-	if id, err := sg.getSyncGroupID(sgname); err != nil || id != sgid {
-		t.Errorf("cannot get back ID of SyncGroup %s: got ID %d instead of %d; err: %v", sgname, id, sgid, err)
-	}
-	if name, err := sg.getSyncGroupName(sgid); err != nil || name != sgname {
-		t.Errorf("cannot get back name of SyncGroup ID %d: got %s instead of %s; err: %v", sgid, name, sgname, err)
-	}
-
-	data, err := sg.getSyncGroupByID(sgid)
-	if err != nil {
-		t.Errorf("cannot get SyncGroup by ID %d: %v", sgid, err)
-	}
-	if !reflect.DeepEqual(data, sgData) {
-		t.Errorf("invalid SyncGroup data for group ID %d: got %v instead of %v", sgid, data, sgData)
-	}
-
-	data, err = sg.getSyncGroupByName(sgname)
-	if err != nil {
-		t.Errorf("cannot get SyncGroup by Name %s: %v", sgname, err)
-	}
-	if !reflect.DeepEqual(data, sgData) {
-		t.Errorf("invalid SyncGroup data for group name %s: got %v instead of %v", sgname, data, sgData)
-	}
-
-	// Verify membership data.
-	members, err := sg.getMembers()
-	if err != nil {
-		t.Errorf("cannot get all SyncGroup members: %v", err)
-	}
-	expMembers := map[string]uint32{"phone": 1, "tablet": 1, "cloud": 1}
-	if !reflect.DeepEqual(members, expMembers) {
-		t.Errorf("invalid SyncGroup members: got %v instead of %v", members, expMembers)
-	}
-
-	expMetaData := map[string]*memberMetaData{
-		"phone":  &memberMetaData{metaData: JoinerMetaData{SyncPriority: 10}},
-		"tablet": &memberMetaData{metaData: JoinerMetaData{SyncPriority: 25}},
-		"cloud":  &memberMetaData{metaData: JoinerMetaData{SyncPriority: 1}},
-	}
-	for mm := range members {
-		info, err := sg.getMemberInfo(mm)
-		if err != nil || info == nil {
-			t.Errorf("cannot get info for SyncGroup member %s: info: %v, err: %v", mm, info, err)
-		}
-		if len(info.gids) != 1 {
-			t.Errorf("invalid info for SyncGroup member %s: %v", mm, info)
-		}
-		expJoinerMetaData := expMetaData[mm]
-		joinerMetaData := info.gids[sgid]
-		if !reflect.DeepEqual(joinerMetaData, expJoinerMetaData) {
-			t.Errorf("invalid joiner Data for SyncGroup member %s under group ID %d: got %v instead of %v",
-				mm, sgid, joinerMetaData, expJoinerMetaData)
-		}
-	}
-
-	checkSGStats(t, "add-2", 1, 3)
-
-	// Use a non-existent member.
-	if info, err := sg.getMemberInfo("should-not-be-there"); err == nil {
-		t.Errorf("found info for invalid SyncGroup member: %v", info)
-	}
-
-	// Adding a SyncGroup for a pre-existing group ID or name should fail.
-	err = sg.addSyncGroup(sgData)
-	if err == nil {
-		t.Errorf("re-adding SyncGroup %d did not fail", sgid)
-	}
-
-	sgData.SrvInfo.Id, err = strToGroupId("5555")
-	if err != nil {
-		t.Fatal(err)
-	}
-	err = sg.addSyncGroup(sgData)
-	if err == nil {
-		t.Errorf("adding SyncGroup %s with a different ID did not fail", sgname)
-	}
-
-	checkSGStats(t, "add-3", 1, 3)
-
-	sg.dump()
-	sg.close()
-}
-
-// TestInvalidAddSyncGroup tests adding SyncGroups.
-func TestInvalidAddSyncGroup(t *testing.T) {
-	sgfile := getFileName()
-	defer os.Remove(sgfile)
-
-	sg, err := openSyncGroupTable(sgfile)
-	if err != nil {
-		t.Fatalf("cannot open new SyncGroup Table file %s", sgfile)
-	}
-
-	sgname := "foobar"
-	sgid, err := strToGroupId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	err = sg.addSyncGroup(nil)
-	if err == nil {
-		t.Errorf("adding a nil SyncGroup did not fail in SyncGroup Table file %s", sgfile)
-	}
-
-	sgData := &syncGroupData{}
-	sgData.SrvInfo.Id = sgid
-
-	err = sg.addSyncGroup(sgData)
-	if err == nil {
-		t.Errorf("adding a SyncGroup with an empty name did not fail in SyncGroup Table file %s", sgfile)
-	}
-
-	sgData.SrvInfo.GroupName = sgname
-
-	err = sg.addSyncGroup(sgData)
-	if err == nil {
-		t.Errorf("adding a SyncGroup with no local path did not fail in SyncGroup Table file %s", sgfile)
-	}
-
-	sgData.LocalPath = "/foo/bar"
-
-	err = sg.addSyncGroup(sgData)
-	if err == nil {
-		t.Errorf("adding a SyncGroup with no joiners did not fail in SyncGroup Table file %s", sgfile)
-	}
-
-	sg.dump()
-	sg.close()
-}
-
-// TestUpdateSyncGroup tests updating a SyncGroup.
-func TestUpdateSyncGroup(t *testing.T) {
-	sgfile := getFileName()
-	defer os.Remove(sgfile)
-
-	sg, err := openSyncGroupTable(sgfile)
-	if err != nil {
-		t.Fatalf("cannot open new SyncGroup Table file %s", sgfile)
-	}
-
-	err = sg.updateSyncGroup(nil)
-	if err == nil {
-		t.Errorf("updating a nil SyncGroup did not fail in SyncGroup Table file %s", sgfile)
-	}
-
-	sgData := &syncGroupData{}
-	err = sg.updateSyncGroup(sgData)
-	if err == nil {
-		t.Errorf("updating a SyncGroup with an empty name did not fail in SyncGroup Table file %s", sgfile)
-	}
-
-	sgData.SrvInfo.GroupName = "blabla"
-	err = sg.updateSyncGroup(sgData)
-	if err == nil {
-		t.Errorf("updating a SyncGroup with no joiners did not fail in SyncGroup Table file %s", sgfile)
-	}
-
-	sgData.SrvInfo.Joiners = map[string]JoinerMetaData{
-		"phone": JoinerMetaData{SyncPriority: 10},
-	}
-	err = sg.updateSyncGroup(sgData)
-	if err == nil {
-		t.Errorf("updating a SyncGroup with a non-existing name did not fail in SyncGroup Table file %s", sgfile)
-	}
-
-	// Create the SyncGroup to update later.
-	sgname := "foobar"
-	sgid, err := strToGroupId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	sgData = &syncGroupData{
-		SrvInfo: SyncGroupInfo{
-			Id:        sgid,
-			GroupName: sgname,
-			Joiners: map[string]JoinerMetaData{
-				"phone":  JoinerMetaData{SyncPriority: 10},
-				"tablet": JoinerMetaData{SyncPriority: 25},
-				"cloud":  JoinerMetaData{SyncPriority: 1},
-			},
-		},
-		LocalPath: "/foo/bar",
-	}
-
-	err = sg.addSyncGroup(sgData)
-	if err != nil {
-		t.Errorf("creating SyncGroup ID %d failed in SyncGroup Table file %s: %v", sgid, sgfile, err)
-	}
-
-	checkSGStats(t, "up-1", 1, 3)
-
-	// Update it using different group or root IDs, which is not allowed.
-	xid, err := strToGroupId("9999")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	sgData.SrvInfo.Id = xid
-
-	err = sg.updateSyncGroup(sgData)
-	if err == nil {
-		t.Errorf("updating a SyncGroup with an ID mismatch did not fail in SyncGroup Table file %s", sgfile)
-	}
-
-	sgData.SrvInfo.Id = sgid
-	sgData.LocalPath = "hahahaha"
-	err = sg.updateSyncGroup(sgData)
-	if err == nil {
-		t.Errorf("updating a SyncGroup with a local path mismatch did not fail in SyncGroup Table file %s", sgfile)
-	}
-
-	checkSGStats(t, "up-2", 1, 3)
-
-	// Update it using a modified set of joiners.
-	// An empty string indicates no change to the local path.
-	sgData.LocalPath = ""
-	sgData.SrvInfo.Joiners["universe"] = JoinerMetaData{SyncPriority: 0}
-	delete(sgData.SrvInfo.Joiners, "cloud")
-
-	err = sg.updateSyncGroup(sgData)
-	if err != nil {
-		t.Errorf("updating SyncGroup ID %d failed in SyncGroup Table file %s: %v", sgid, sgfile, err)
-	}
-
-	// Do some NOP member deletions (bad member, bad group ID).
-	// SyncGroup verification (below) should see the expected info asserting these were NOPs.
-	sg.delMember("blablablablabla", sgid)
-	sg.delMember("phone", xid)
-
-	checkSGStats(t, "up-3", 1, 3)
-
-	// Verify updated SyncGroup.
-	if id, err := sg.getSyncGroupID(sgname); err != nil || id != sgid {
-		t.Errorf("cannot get back ID of updated SyncGroup %s: got ID %d instead of %d; err: %v", sgname, id, sgid, err)
-	}
-	if name, err := sg.getSyncGroupName(sgid); err != nil || name != sgname {
-		t.Errorf("cannot get back name of updated SyncGroup ID %d: got %s instead of %s; err: %v", sgid, name, sgname, err)
-	}
-
-	expData := &syncGroupData{
-		SrvInfo: SyncGroupInfo{
-			Id:        sgid,
-			GroupName: sgname,
-			Joiners: map[string]JoinerMetaData{
-				"phone":    JoinerMetaData{SyncPriority: 10},
-				"tablet":   JoinerMetaData{SyncPriority: 25},
-				"universe": JoinerMetaData{SyncPriority: 0},
-			},
-		},
-		LocalPath: "/foo/bar",
-	}
-
-	data, err := sg.getSyncGroupByID(sgid)
-	if err != nil {
-		t.Errorf("cannot get updated SyncGroup by ID %d: %v", sgid, err)
-	}
-	if !reflect.DeepEqual(data, expData) {
-		t.Errorf("invalid SyncGroup data for updated group ID %d: got %v instead of %v", sgid, data, expData)
-	}
-
-	data, err = sg.getSyncGroupByName(sgname)
-	if err != nil {
-		t.Errorf("cannot get updated SyncGroup by Name %s: %v", sgname, err)
-	}
-	if !reflect.DeepEqual(data, expData) {
-		t.Errorf("invalid SyncGroup data for updated group name %s: got %v instead of %v", sgname, data, expData)
-	}
-
-	// Verify membership data.
-	members, err := sg.getMembers()
-	if err != nil {
-		t.Errorf("cannot get all SyncGroup members after update: %v", err)
-	}
-	expMembers := map[string]uint32{"phone": 1, "tablet": 1, "universe": 1}
-	if !reflect.DeepEqual(members, expMembers) {
-		t.Errorf("invalid SyncGroup members after update: got %v instead of %v", members, expMembers)
-	}
-
-	expMetaData := map[string]*memberMetaData{
-		"phone":    &memberMetaData{metaData: JoinerMetaData{SyncPriority: 10}},
-		"tablet":   &memberMetaData{metaData: JoinerMetaData{SyncPriority: 25}},
-		"universe": &memberMetaData{metaData: JoinerMetaData{SyncPriority: 0}},
-	}
-	for mm := range members {
-		info, err := sg.getMemberInfo(mm)
-		if err != nil || info == nil {
-			t.Errorf("cannot get info for SyncGroup member %s: info: %v, err: %v", mm, info, err)
-		}
-		if len(info.gids) != 1 {
-			t.Errorf("invalid info for SyncGroup member %s: %v", mm, info)
-		}
-		expJoinerMetaData := expMetaData[mm]
-		joinerMetaData := info.gids[sgid]
-		if !reflect.DeepEqual(joinerMetaData, expJoinerMetaData) {
-			t.Errorf("invalid joiner Data for SyncGroup member %s under group ID %d: got %v instead of %v",
-				mm, sgid, joinerMetaData, expJoinerMetaData)
-		}
-	}
-
-	sg.dump()
-	sg.close()
-}
-
-// TestDeleteSyncGroup tests deleting a SyncGroup.
-func TestDeleteSyncGroup(t *testing.T) {
-	sgfile := getFileName()
-	defer os.Remove(sgfile)
-
-	sg, err := openSyncGroupTable(sgfile)
-	if err != nil {
-		t.Fatalf("cannot open new SyncGroup Table file %s", sgfile)
-	}
-
-	sgname := "foobar"
-	sgid, err := strToGroupId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	// Delete non-existing SyncGroups.
-	err = sg.delSyncGroupByID(sgid)
-	if err == nil {
-		t.Errorf("deleting a non-existing SyncGroup ID did not fail in SyncGroup Table file %s", sgfile)
-	}
-
-	err = sg.delSyncGroupByName(sgname)
-	if err == nil {
-		t.Errorf("deleting a non-existing SyncGroup name did not fail in SyncGroup Table file %s", sgfile)
-	}
-
-	checkSGStats(t, "del-1", 0, 0)
-
-	// Create the SyncGroup to delete later.
-	sgData := &syncGroupData{
-		SrvInfo: SyncGroupInfo{
-			Id:        sgid,
-			GroupName: sgname,
-			Joiners: map[string]JoinerMetaData{
-				"phone":  JoinerMetaData{SyncPriority: 10},
-				"tablet": JoinerMetaData{SyncPriority: 25},
-				"cloud":  JoinerMetaData{SyncPriority: 1},
-			},
-		},
-		LocalPath: "/foo/bar",
-	}
-
-	err = sg.addSyncGroup(sgData)
-	if err != nil {
-		t.Errorf("creating SyncGroup ID %d failed in SyncGroup Table file %s: %v", sgid, sgfile, err)
-	}
-
-	checkSGStats(t, "del-2", 1, 3)
-
-	// Delete it by ID.
-	err = sg.delSyncGroupByID(sgid)
-	if err != nil {
-		t.Errorf("deleting SyncGroup ID %d failed in SyncGroup Table file %s: %v", sgid, sgfile, err)
-	}
-
-	checkSGStats(t, "del-3", 0, 0)
-
-	// Create it again then delete it by name.
-	err = sg.addSyncGroup(sgData)
-	if err != nil {
-		t.Errorf("creating SyncGroup ID %d failed in SyncGroup Table file %s: %v", sgid, sgfile, err)
-	}
-
-	checkSGStats(t, "del-4", 1, 3)
-
-	err = sg.delSyncGroupByName(sgname)
-	if err != nil {
-		t.Errorf("deleting SyncGroup name %s failed in SyncGroup Table file %s: %v", sgname, sgfile, err)
-	}
-
-	checkSGStats(t, "del-5", 0, 0)
-
-	sg.dump()
-	sg.close()
-}
-
-// TestMultiSyncGroups tests creating multiple SyncGroups.
-func TestMultiSyncGroups(t *testing.T) {
-	sgfile := getFileName()
-	defer os.Remove(sgfile)
-
-	sg, err := openSyncGroupTable(sgfile)
-	if err != nil {
-		t.Fatalf("cannot open new SyncGroup Table file %s", sgfile)
-	}
-
-	sgname1, sgname2 := "foo", "bar"
-	sgid1, err := strToGroupId("1234")
-	if err != nil {
-		t.Fatal(err)
-	}
-	sgid2, err := strToGroupId("8888")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	// Add two SyncGroups.
-	sgData1 := &syncGroupData{
-		SrvInfo: SyncGroupInfo{
-			Id:        sgid1,
-			GroupName: sgname1,
-			Joiners: map[string]JoinerMetaData{
-				"phone":  JoinerMetaData{SyncPriority: 10},
-				"tablet": JoinerMetaData{SyncPriority: 25},
-				"cloud":  JoinerMetaData{SyncPriority: 1},
-			},
-		},
-		LocalPath: "/foo/bar",
-	}
-
-	sgData2 := &syncGroupData{
-		SrvInfo: SyncGroupInfo{
-			Id:        sgid2,
-			GroupName: sgname2,
-			Joiners: map[string]JoinerMetaData{
-				"tablet": JoinerMetaData{SyncPriority: 111},
-				"door":   JoinerMetaData{SyncPriority: 33},
-				"lamp":   JoinerMetaData{SyncPriority: 9},
-			},
-		},
-		LocalPath: "/foo/bar",
-	}
-
-	err = sg.addSyncGroup(sgData1)
-	if err != nil {
-		t.Errorf("creating SyncGroup ID %d failed in SyncGroup Table file %s: %v", sgid1, sgfile, err)
-	}
-
-	checkSGStats(t, "multi-1", 1, 3)
-
-	err = sg.addSyncGroup(sgData2)
-	if err != nil {
-		t.Errorf("creating SyncGroup ID %d failed in SyncGroup Table file %s: %v", sgid2, sgfile, err)
-	}
-
-	checkSGStats(t, "multi-2", 2, 5)
-
-	// Verify SyncGroup names.
-	sgNames, err := sg.getAllSyncGroupNames()
-	if err != nil {
-		t.Errorf("cannot get all SyncGroup names: %v", err)
-	}
-	if len(sgNames) != 2 {
-		t.Errorf("wrong number of SyncGroup names: %d instead of 2", len(sgNames))
-	}
-	expNames := map[string]struct{}{sgname1: struct{}{}, sgname2: struct{}{}}
-	for _, name := range sgNames {
-		if _, ok := expNames[name]; !ok {
-			t.Errorf("unknown SyncGroup name returned: %s", name)
-		} else {
-			delete(expNames, name)
-		}
-	}
-
-	if len(expNames) > 0 {
-		t.Errorf("SyncGroup names missing, not returned: %v", expNames)
-	}
-
-	// Verify SyncGroup membership data.
-	members, err := sg.getMembers()
-	if err != nil {
-		t.Errorf("cannot get all SyncGroup members: %v", err)
-	}
-
-	expMembers := map[string]uint32{"phone": 1, "tablet": 2, "cloud": 1, "door": 1, "lamp": 1}
-	if !reflect.DeepEqual(members, expMembers) {
-		t.Errorf("invalid SyncGroup members: got %v instead of %v", members, expMembers)
-	}
-
-	expMemberInfo := map[string]*memberInfo{
-		"phone": &memberInfo{
-			gids: map[GroupId]*memberMetaData{
-				sgid1: &memberMetaData{metaData: JoinerMetaData{SyncPriority: 10}},
-			},
-		},
-		"tablet": &memberInfo{
-			gids: map[GroupId]*memberMetaData{
-				sgid1: &memberMetaData{metaData: JoinerMetaData{SyncPriority: 25}},
-				sgid2: &memberMetaData{metaData: JoinerMetaData{SyncPriority: 111}},
-			},
-		},
-		"cloud": &memberInfo{
-			gids: map[GroupId]*memberMetaData{
-				sgid1: &memberMetaData{metaData: JoinerMetaData{SyncPriority: 1}},
-			},
-		},
-		"door": &memberInfo{
-			gids: map[GroupId]*memberMetaData{
-				sgid2: &memberMetaData{metaData: JoinerMetaData{SyncPriority: 33}},
-			},
-		},
-		"lamp": &memberInfo{
-			gids: map[GroupId]*memberMetaData{
-				sgid2: &memberMetaData{metaData: JoinerMetaData{SyncPriority: 9}},
-			},
-		},
-	}
-
-	for mm := range members {
-		info, err := sg.getMemberInfo(mm)
-		if err != nil || info == nil {
-			t.Errorf("cannot get info for SyncGroup member %s: info: %v, err: %v", mm, info, err)
-		}
-		expInfo := expMemberInfo[mm]
-		if !reflect.DeepEqual(info, expInfo) {
-			t.Errorf("invalid info for SyncGroup member %s: got %v instead of %v", mm, info, expInfo)
-		}
-	}
-
-	// Delete the 1st SyncGroup.
-	err = sg.delSyncGroupByID(sgid1)
-	if err != nil {
-		t.Errorf("deleting SyncGroup ID %d failed in SyncGroup Table file %s: %v", sgid1, sgfile, err)
-	}
-
-	checkSGStats(t, "multi-3", 1, 3)
-
-	// Verify SyncGroup membership data.
-	members, err = sg.getMembers()
-	if err != nil {
-		t.Errorf("cannot get all SyncGroup members: %v", err)
-	}
-
-	expMembers = map[string]uint32{"tablet": 1, "door": 1, "lamp": 1}
-	if !reflect.DeepEqual(members, expMembers) {
-		t.Errorf("invalid SyncGroup members: got %v instead of %v", members, expMembers)
-	}
-
-	expMemberInfo = map[string]*memberInfo{
-		"tablet": &memberInfo{
-			gids: map[GroupId]*memberMetaData{
-				sgid2: &memberMetaData{metaData: JoinerMetaData{SyncPriority: 111}},
-			},
-		},
-		"door": &memberInfo{
-			gids: map[GroupId]*memberMetaData{
-				sgid2: &memberMetaData{metaData: JoinerMetaData{SyncPriority: 33}},
-			},
-		},
-		"lamp": &memberInfo{
-			gids: map[GroupId]*memberMetaData{
-				sgid2: &memberMetaData{metaData: JoinerMetaData{SyncPriority: 9}},
-			},
-		},
-	}
-
-	for mm := range members {
-		info, err := sg.getMemberInfo(mm)
-		if err != nil || info == nil {
-			t.Errorf("cannot get info for SyncGroup member %s: info: %v, err: %v", mm, info, err)
-		}
-		expInfo := expMemberInfo[mm]
-		if !reflect.DeepEqual(info, expInfo) {
-			t.Errorf("invalid info for SyncGroup member %s: got %v instead of %v", mm, info, expInfo)
-		}
-	}
-
-	sg.dump()
-	sg.close()
-}
diff --git a/x/ref/services/syncbase/sync/testdata/local-init-00.log.sync b/x/ref/services/syncbase/sync/testdata/local-init-00.log.sync
deleted file mode 100644
index 46b3502..0000000
--- a/x/ref/services/syncbase/sync/testdata/local-init-00.log.sync
+++ /dev/null
@@ -1,6 +0,0 @@
-# Create an object locally and update it twice (linked-list).
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addl|1234|1|||logrec-00|0|1|false
-addl|1234|2|1||logrec-01|0|1|false
-addl|1234|3|2||logrec-02|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/local-init-00.sync b/x/ref/services/syncbase/sync/testdata/local-init-00.sync
deleted file mode 100644
index 9ab99dc..0000000
--- a/x/ref/services/syncbase/sync/testdata/local-init-00.sync
+++ /dev/null
@@ -1,6 +0,0 @@
-# Create an object locally and update it twice (linked-list).
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addl|1234|0|||logrec-00|0|1|false
-addl|1234|1|0||logrec-01|0|1|false
-addl|1234|2|1||logrec-02|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/local-init-01.log.sync b/x/ref/services/syncbase/sync/testdata/local-init-01.log.sync
deleted file mode 100644
index 2f5930c..0000000
--- a/x/ref/services/syncbase/sync/testdata/local-init-01.log.sync
+++ /dev/null
@@ -1,9 +0,0 @@
-# Create objects locally and update one and delete another.
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addl|12|1|||logrec-00|0|1|false
-addl|12|2|1||logrec-01|0|1|false
-addl|12|3|2||logrec-02|0|1|false
-
-addl|45|1|||logrec-00|0|1|false
-addl|45|0|1||logrec-00|0|1|true
\ No newline at end of file
diff --git a/x/ref/services/syncbase/sync/testdata/local-init-01.sync b/x/ref/services/syncbase/sync/testdata/local-init-01.sync
deleted file mode 100644
index 1178c62..0000000
--- a/x/ref/services/syncbase/sync/testdata/local-init-01.sync
+++ /dev/null
@@ -1,12 +0,0 @@
-# Create an object DAG locally with branches and resolved conflicts.
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addl|1234|0|||logrec-00|0|1|false
-addl|1234|1|0||logrec-01|0|1|false
-addl|1234|2|1||logrec-02|0|1|false
-addl|1234|3|1||logrec-03|0|1|false
-addl|1234|4|2|3|logrec-04|0|1|false
-addl|1234|5|4||logrec-05|0|1|false
-addl|1234|6|1||logrec-06|0|1|false
-addl|1234|7|5|6|logrec-07|0|1|false
-addl|1234|8|7||logrec-08|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/local-init-02.log.sync b/x/ref/services/syncbase/sync/testdata/local-init-02.log.sync
deleted file mode 100644
index 7f9f6a7..0000000
--- a/x/ref/services/syncbase/sync/testdata/local-init-02.log.sync
+++ /dev/null
@@ -1,9 +0,0 @@
-# Create objects locally and update one and delete another.
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addl|12|1|||logrec-00|0|1|false
-addl|12|2|1||logrec-01|0|1|false
-addl|12|3|2||logrec-02|0|1|false
-
-addl|45|10|||logrec-00|0|1|false
-addl|45|20|10||logrec-00|0|1|false
\ No newline at end of file
diff --git a/x/ref/services/syncbase/sync/testdata/local-init-02.sync b/x/ref/services/syncbase/sync/testdata/local-init-02.sync
deleted file mode 100644
index cb60a79..0000000
--- a/x/ref/services/syncbase/sync/testdata/local-init-02.sync
+++ /dev/null
@@ -1,10 +0,0 @@
-# Create DAGs for 3 objects locally.
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addl|1234|1|||logrec-a-01|0|1|false
-addl|1234|2|1||logrec-a-02|0|1|false
-
-addl|6789|1|||logrec-b-01|0|1|false
-addl|6789|2|1||logrec-b-02|0|1|false
-
-addl|2222|1|||logrec-c-01|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/local-init-03.sync b/x/ref/services/syncbase/sync/testdata/local-init-03.sync
deleted file mode 100644
index 202a752..0000000
--- a/x/ref/services/syncbase/sync/testdata/local-init-03.sync
+++ /dev/null
@@ -1,10 +0,0 @@
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addl|1234|1|||logrec-01|0|1|false
-addl|1234|2|1||logrec-02|0|1|false
-addl|1234|3|1||logrec-03|0|1|false
-addl|1234|4|2||logrec-04|0|1|false
-addl|1234|5|2||logrec-05|0|1|true
-addl|1234|6|4|5|logrec-06|0|1|false
-addl|1234|7|3|5|logrec-07|0|1|false
-addl|1234|8|6|7|logrec-08|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/local-init-watch.log.sync b/x/ref/services/syncbase/sync/testdata/local-init-watch.log.sync
deleted file mode 100644
index 3e895c8..0000000
--- a/x/ref/services/syncbase/sync/testdata/local-init-watch.log.sync
+++ /dev/null
@@ -1,3 +0,0 @@
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addl|082bc42e15af4fcf611d7f19a8d7831f|4|||logrec-00|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/local-resolve-00.sync b/x/ref/services/syncbase/sync/testdata/local-resolve-00.sync
deleted file mode 100644
index 7026060..0000000
--- a/x/ref/services/syncbase/sync/testdata/local-resolve-00.sync
+++ /dev/null
@@ -1,4 +0,0 @@
-# Create an object locally and update it twice (linked-list).
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addl|1234|6|2|5|logrec-06|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/remote-2obj-del.log.sync b/x/ref/services/syncbase/sync/testdata/remote-2obj-del.log.sync
deleted file mode 100644
index 5274ecf..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-2obj-del.log.sync
+++ /dev/null
@@ -1,7 +0,0 @@
-# Update one object and delete another object remotely.
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|12|4|3||VeyronPhone:10:1:0|0|1|false
-addr|12|5|4||VeyronPhone:10:1:1|0|1|false
-addr|12|6|5||VeyronPhone:10:1:2|0|1|true
-addr|45|2|1||VeyronPhone:10:1:3|0|1|false
\ No newline at end of file
diff --git a/x/ref/services/syncbase/sync/testdata/remote-conf-00.log.sync b/x/ref/services/syncbase/sync/testdata/remote-conf-00.log.sync
deleted file mode 100644
index 1f9bb5b..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-conf-00.log.sync
+++ /dev/null
@@ -1,8 +0,0 @@
-# Update an object remotely three times triggering one conflict after
-# it was created locally up to v3 (i.e. assume the remote sync received
-# it from the local sync at v2, then updated separately).
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|4|2||VeyronPhone:10:1:0|0|1|false
-addr|1234|5|4||VeyronPhone:10:1:1|0|1|false
-addr|1234|6|5||VeyronPhone:10:1:2|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/remote-conf-00.sync b/x/ref/services/syncbase/sync/testdata/remote-conf-00.sync
deleted file mode 100644
index 8fae794..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-conf-00.sync
+++ /dev/null
@@ -1,8 +0,0 @@
-# Update an object remotely three times triggering one conflict after
-# it was created locally up to v2 (i.e. assume the remote sync received
-# it from the local sync at v1, then updated separately).
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|3|1||logrec-03|0|1|false
-addr|1234|4|3||logrec-04|0|1|false
-addr|1234|5|4||logrec-05|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/remote-conf-01.log.sync b/x/ref/services/syncbase/sync/testdata/remote-conf-01.log.sync
deleted file mode 100644
index 9581f69..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-conf-01.log.sync
+++ /dev/null
@@ -1,10 +0,0 @@
-# Update an object remotely three times triggering a conflict with
-# 2 graft points: v1 and v4.  This assumes that the remote sync got
-# v1, made its own conflicting v4 that it resolved into v5 (against v2)
-# then made a v6 change.  When the local sync gets back this info it
-# sees 2 graft points: v1-v4 and v2-v5.
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|4|1||VeyronLaptop:10:1:0|0|1|false
-addr|1234|5|2|4|VeyronPhone:10:1:0|0|1|false
-addr|1234|6|5||VeyronPhone:10:1:1|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/remote-conf-01.sync b/x/ref/services/syncbase/sync/testdata/remote-conf-01.sync
deleted file mode 100644
index 7485aeb..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-conf-01.sync
+++ /dev/null
@@ -1,10 +0,0 @@
-# Update an object remotely three times triggering a conflict with
-# 2 graft points: v0 and v2.  This assumes that the remote sync got
-# v0, made its own conflicting v3 that it resolved into v4 (against v1)
-# then made a v5 change.  When the local sync gets back this info it
-# sees 2 graft points: v0-v3 and v1-v4.
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|3|0||logrec-03|0|1|false
-addr|1234|4|1|3|logrec-04|0|1|false
-addr|1234|5|4||logrec-05|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/remote-conf-02.log.sync b/x/ref/services/syncbase/sync/testdata/remote-conf-02.log.sync
deleted file mode 100644
index 3c36277..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-conf-02.log.sync
+++ /dev/null
@@ -1,15 +0,0 @@
-# Update an object remotely three times triggering one conflict after
-# it was created locally up to v3 (i.e. assume the remote sync received
-# it from the local sync at v2, then updated separately).
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|4|2||VeyronPhone:10:1:0|0|1|false
-addr|1234|5|4||VeyronPhone:10:1:1|0|1|false
-addr|1234|6|5||VeyronPhone:10:1:2|0|1|false
-
-addr|12|4|2||VeyronPhone:99:1:0|0|1|false
-addr|12|5|4||VeyronPhone:99:1:1|0|1|false
-addr|12|6|5||VeyronPhone:99:1:2|0|1|false
-
-addr|45|30|20||VeyronPhone:99:2:0|0|1|false
-addr|45|40|30||VeyronPhone:99:2:1|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/remote-conf-link.log.sync b/x/ref/services/syncbase/sync/testdata/remote-conf-link.log.sync
deleted file mode 100644
index a324e4f..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-conf-link.log.sync
+++ /dev/null
@@ -1,5 +0,0 @@
-# Update an object remotely, detect conflict, and bless the local version.
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|4|1||VeyronPhone:10:1:0|0|1|false
-linkr|1234|4|2||VeyronPhone:10:1:1
diff --git a/x/ref/services/syncbase/sync/testdata/remote-init-00.log.sync b/x/ref/services/syncbase/sync/testdata/remote-init-00.log.sync
deleted file mode 100644
index 9795d53..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-init-00.log.sync
+++ /dev/null
@@ -1,6 +0,0 @@
-# Create an object remotely and update it twice (linked-list).
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|1|||VeyronPhone:10:1:0|0|1|false
-addr|1234|2|1||VeyronPhone:10:1:1|0|1|false
-addr|1234|3|2||VeyronPhone:10:1:2|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/remote-init-00.sync b/x/ref/services/syncbase/sync/testdata/remote-init-00.sync
deleted file mode 100644
index 18d288e..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-init-00.sync
+++ /dev/null
@@ -1,6 +0,0 @@
-# Create an object remotely and update it twice (linked-list).
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|0|||logrec-00|0|1|false
-addr|1234|1|0||logrec-01|0|1|false
-addr|1234|2|1||logrec-02|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/remote-init-01.log.sync b/x/ref/services/syncbase/sync/testdata/remote-init-01.log.sync
deleted file mode 100644
index 1db4c50..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-init-01.log.sync
+++ /dev/null
@@ -1,6 +0,0 @@
-# Create an object remotely and update it twice (linked-list).
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|1|||VeyronPhone:10:5:0|0|1|false
-addr|1234|2|1||VeyronPhone:10:5:1|0|1|false
-addr|1234|3|2||VeyronPhone:10:5:2|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/remote-init-02.log.sync b/x/ref/services/syncbase/sync/testdata/remote-init-02.log.sync
deleted file mode 100644
index 1274b7d..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-init-02.log.sync
+++ /dev/null
@@ -1,17 +0,0 @@
-# Create objects and transactions remotely.
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|12|1|||VeyronPhone:10:1:0|0|1|false
-
-addr|12|2|1||VeyronPhone:10:1:1|100|3|false
-addr|45|1|||VeyronPhone:10:1:2|100|3|false
-addr|78|1|||VeyronPhone:10:1:3|100|3|false
-
-addr|78|2|1||VeyronPhone:10:1:4|0|1|false
-
-addr|78|3|1||VeyronLaptop:10:1:0|0|1|false
-
-addr|78|4|2|3|VeyronPhone:10:2:0|0|1|false
-
-addr|12|3|2||VeyronPhone:10:2:1|101|2|false
-addr|45|2|1||VeyronPhone:10:2:2|101|2|false
diff --git a/x/ref/services/syncbase/sync/testdata/remote-init-03.log.sync b/x/ref/services/syncbase/sync/testdata/remote-init-03.log.sync
deleted file mode 100644
index 1ccac35..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-init-03.log.sync
+++ /dev/null
@@ -1,6 +0,0 @@
-# Create an object remotely and delete it.
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|1|||VeyronPhone:10:1:0|0|1|false
-addr|1234|2|1||VeyronPhone:10:1:1|0|1|false
-addr|1234|3|2||VeyronPhone:10:1:2|0|1|true
diff --git a/x/ref/services/syncbase/sync/testdata/remote-noconf-00.log.sync b/x/ref/services/syncbase/sync/testdata/remote-noconf-00.log.sync
deleted file mode 100644
index e2e2afa..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-noconf-00.log.sync
+++ /dev/null
@@ -1,8 +0,0 @@
-# Update an object remotely three times without triggering a conflict
-# after it was created locally up to v3 (i.e. assume the remote sync
-# received it from the local sync first, then updated it).
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|4|3||VeyronPhone:10:1:0|0|1|false
-addr|1234|5|4||VeyronPhone:10:1:1|0|1|false
-addr|1234|6|5||VeyronPhone:10:1:2|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/remote-noconf-00.sync b/x/ref/services/syncbase/sync/testdata/remote-noconf-00.sync
deleted file mode 100644
index d44b6ac..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-noconf-00.sync
+++ /dev/null
@@ -1,8 +0,0 @@
-# Update an object remotely three times without triggering a conflict
-# after it was created locally up to v2 (i.e. assume the remote sync
-# received it from the local sync first, then updated it).
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|3|2||logrec-03|0|1|false
-addr|1234|4|3||logrec-04|0|1|false
-addr|1234|5|4||logrec-05|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/remote-noconf-link-00.log.sync b/x/ref/services/syncbase/sync/testdata/remote-noconf-link-00.log.sync
deleted file mode 100644
index 6945bb2..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-noconf-link-00.log.sync
+++ /dev/null
@@ -1,5 +0,0 @@
-# Update an object remotely, detect conflict, and bless the remote version.
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|4|1||VeyronPhone:10:1:0|0|1|false
-linkr|1234|2|4||VeyronPhone:10:1:1
diff --git a/x/ref/services/syncbase/sync/testdata/remote-noconf-link-01.log.sync b/x/ref/services/syncbase/sync/testdata/remote-noconf-link-01.log.sync
deleted file mode 100644
index 0c6969e..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-noconf-link-01.log.sync
+++ /dev/null
@@ -1,5 +0,0 @@
-# Update an object remotely, detect conflict, and bless the local version.
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|4|1||VeyronPhone:10:1:0|0|1|false
-linkr|1234|4|3||VeyronPhone:10:1:1
diff --git a/x/ref/services/syncbase/sync/testdata/remote-noconf-link-02.log.sync b/x/ref/services/syncbase/sync/testdata/remote-noconf-link-02.log.sync
deleted file mode 100644
index df9e128..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-noconf-link-02.log.sync
+++ /dev/null
@@ -1,6 +0,0 @@
-# Update an object remotely, detect conflict, and bless the remote version, and continue updating.
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-addr|1234|4|1||VeyronPhone:10:1:0|0|1|false
-linkr|1234|3|4||VeyronPhone:10:1:1
-addr|1234|5|3||VeyronPhone:10:2:0|0|1|false
diff --git a/x/ref/services/syncbase/sync/testdata/remote-noconf-link-repeat.log.sync b/x/ref/services/syncbase/sync/testdata/remote-noconf-link-repeat.log.sync
deleted file mode 100644
index 82e11c6..0000000
--- a/x/ref/services/syncbase/sync/testdata/remote-noconf-link-repeat.log.sync
+++ /dev/null
@@ -1,4 +0,0 @@
-# Resolve the same conflict on two different devices.
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-
-linkr|1234|3|4||VeyronLaptop:10:1:0
diff --git a/x/ref/services/syncbase/sync/testdata/test-1obj.gc.sync b/x/ref/services/syncbase/sync/testdata/test-1obj.gc.sync
deleted file mode 100644
index 4d1a8d0..0000000
--- a/x/ref/services/syncbase/sync/testdata/test-1obj.gc.sync
+++ /dev/null
@@ -1,14 +0,0 @@
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-# Local node is A. Remote nodes are B and C.
-### NOT UP-TO-DATE
-addr|12345|0|||C:1:0|false|false
-addr|12345|1|0||B:1:0|false|false
-addl|12345|2|0||A:1:0|false|false
-addl|12345|3|1|2|A:2:0|false|false
-addr|12345|4|3||C:2:0|false|false
-addr|12345|5|3||B:2:0|false|false
-addr|12345|6|4|5|B:3:0|false|false
-# Devtable state
-setdev|A|A:2,B:3,C:2
-setdev|B|A:2,B:3,C:2
-setdev|C|A:2,B:1,C:2
diff --git a/x/ref/services/syncbase/sync/testdata/test-3obj.gc.sync b/x/ref/services/syncbase/sync/testdata/test-3obj.gc.sync
deleted file mode 100644
index ce0656c..0000000
--- a/x/ref/services/syncbase/sync/testdata/test-3obj.gc.sync
+++ /dev/null
@@ -1,45 +0,0 @@
-# The format is: <cmd>|<objid>|<version>|<parent1>|<parent2>|<logrec>|<txid>|<txcount>|<deleted>
-# Local node is A. Remote nodes are B and C.
-### NOT UP-TO-DATE
-addl|123|1|||A:1:0|false|false
-
-addr|456|1|||B:1:0|false|false
-
-addr|456|2|1||B:2:0|false|false
-addr|123|2|1||B:2:1|false|false
-
-addl|456|3|2||A:2:0|false|false
-addl|123|4|2||A:2:1|false|false
-
-addr|789|1|||C:1:0|false|false
-
-addr|789|2|1||C:2:0|false|false
-
-addr|123|3|1||C:3:0|false|false
-addr|789|3|2||C:3:1|false|false
-
-addr|123|5|3|2|C:4:0|false|false
-
-addl|123|6|4|5|A:3:0|false|false
-addl|456|4|3||A:3:1|false|false
-addl|789|4|3||A:3:2|false|false
-
-addr|456|5|2||B:3:0|false|false
-
-addl|456|7|4|5|A:4:0|false|false
-
-addr|456|6|2||C:5:0|false|false
-addr|123|7|5||C:5:1|false|false
-addr|123|8|7||C:5:2|false|false
-addr|789|5|3||C:5:3|false|false
-
-addl|123|9|6|8|A:5:0|false|false
-addl|456|8|6|7|A:5:1|false|false
-addl|789|6|4|5|A:5:2|false|false
-
-addl|123|10|9||A:6:0|false|false
-
-# Devtable state
-setdev|A|A:6,B:3,C:5
-setdev|B|A:4,B:3,C:4
-setdev|C|A:4,B:3,C:4
diff --git a/x/ref/services/syncbase/sync/util_test.go b/x/ref/services/syncbase/sync/util_test.go
deleted file mode 100644
index 54ed906..0000000
--- a/x/ref/services/syncbase/sync/util_test.go
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-// Utilities for testing.
-import (
-	"container/list"
-	"fmt"
-	"os"
-	"time"
-)
-
-// getFileName generates a filename for a temporary (per unit test) kvdb file.
-func getFileName() string {
-	return fmt.Sprintf("%s/sync_test_%d_%d", os.TempDir(), os.Getpid(), time.Now().UnixNano())
-}
-
-// createTempDir creates a unique temporary directory to store kvdb files.
-func createTempDir() (string, error) {
-	dir := fmt.Sprintf("%s/sync_test_%d_%d/", os.TempDir(), os.Getpid(), time.Now().UnixNano())
-	if err := os.MkdirAll(dir, 0700); err != nil {
-		return "", err
-	}
-	return dir, nil
-}
-
-// getFileSize returns the size of a file.
-func getFileSize(fname string) int64 {
-	finfo, err := os.Stat(fname)
-	if err != nil {
-		return -1
-	}
-	return finfo.Size()
-}
-
-// dummyStream struct emulates stream of log records received from RPC.
-type dummyStream struct {
-	l     *list.List
-	value LogRec
-}
-
-func newStream() *dummyStream {
-	ds := &dummyStream{
-		l: list.New(),
-	}
-	return ds
-}
-
-func (ds *dummyStream) Advance() bool {
-	if ds.l.Len() > 0 {
-		ds.value = ds.l.Remove(ds.l.Front()).(LogRec)
-		return true
-	}
-	return false
-}
-
-func (ds *dummyStream) Value() LogRec {
-	return ds.value
-}
-
-func (ds *dummyStream) RecvStream() interface {
-	Advance() bool
-	Value() LogRec
-	Err() error
-} {
-	return ds
-}
-
-func (*dummyStream) Err() error { return nil }
-
-func (ds *dummyStream) Finish() (map[ObjId]GenVector, error) {
-	return nil, nil
-}
-
-func (ds *dummyStream) Cancel() {
-}
-
-func (ds *dummyStream) add(rec LogRec) {
-	ds.l.PushBack(rec)
-}
-
-// logReplayCommands replays local log records parsed from the input file.
-func logReplayCommands(log *iLog, syncfile string, srid ObjId) error {
-	cmds, err := parseSyncCommands(syncfile)
-	if err != nil {
-		return err
-	}
-
-	for _, cmd := range cmds {
-		switch cmd.cmd {
-		case addLocal:
-			parent := NoVersion
-			if cmd.parents != nil {
-				parent = cmd.parents[0]
-			}
-
-			val := &LogValue{
-				//Mutation: raw.Mutation{Version: cmd.version},
-				Delete:  cmd.deleted,
-				TxId:    cmd.txID,
-				TxCount: cmd.txCount,
-			}
-			err = log.processWatchRecord(cmd.objID, cmd.version, parent, val, srid)
-			if err != nil {
-				return fmt.Errorf("cannot replay local log records %v:%v err %v",
-					cmd.objID, cmd.version, err)
-			}
-		default:
-			return fmt.Errorf("unknown cmd %v", cmd.cmd)
-		}
-	}
-
-	return nil
-}
-
-// createReplayStream creates a dummy stream of log records parsed from the input file.
-func createReplayStream(syncfile string) (*dummyStream, error) {
-	cmds, err := parseSyncCommands(syncfile)
-	if err != nil {
-		return nil, err
-	}
-
-	stream := newStream()
-	for _, cmd := range cmds {
-		id, srid, gnum, lsn, err := splitLogRecKey(cmd.logrec)
-		if err != nil {
-			return nil, err
-		}
-		rec := LogRec{
-			DevId:      id,
-			SyncRootId: srid,
-			GenNum:     gnum,
-			SeqNum:     lsn,
-			ObjId:      cmd.objID,
-			CurVers:    cmd.version,
-			Parents:    cmd.parents,
-			Value: LogValue{
-				//Mutation: raw.Mutation{Version: cmd.version},
-				Delete:  cmd.deleted,
-				TxId:    cmd.txID,
-				TxCount: cmd.txCount,
-			},
-		}
-
-		switch cmd.cmd {
-		case addRemote:
-			rec.RecType = NodeRec
-		case linkRemote:
-			rec.RecType = LinkRec
-		default:
-			return nil, err
-		}
-		stream.add(rec)
-	}
-
-	return stream, nil
-}
-
-//
-// // populates the log and dag state as part of state initialization.
-// func populateLogAndDAG(s *syncd, rec *LogRec) error {
-// 	logKey, err := s.log.putLogRec(rec)
-// 	if err != nil {
-// 		return err
-// 	}
-//
-// 	if err := s.dag.addNode(rec.ObjId, rec.CurVers, false, rec.Value.Delete, rec.Parents, logKey, NoTxId); err != nil {
-// 		return err
-// 	}
-// 	if err := s.dag.moveHead(rec.ObjId, rec.CurVers); err != nil {
-// 		return err
-// 	}
-// 	return nil
-// }
-//
-//
-// // vsyncInitState initializes log, dag and devtable state obtained from an input trace-like file.
-// func vsyncInitState(s *syncd, syncfile string) error {
-// 	cmds, err := parseSyncCommands(syncfile)
-// 	if err != nil {
-// 		return err
-// 	}
-//
-// 	var curGen GenId
-// 	genMap := make(map[string]*genMetadata)
-//
-// 	for _, cmd := range cmds {
-// 		switch cmd.cmd {
-// 		case addLocal, addRemote:
-// 			id, sgid, gnum, lsn, err := splitLogRecKey(cmd.logrec)
-// 			if err != nil {
-// 				return err
-// 			}
-// 			rec := &LogRec{
-// 				DevId:   id,
-// 				SGrpID:  sgid,
-// 				GenNum:  gnum,
-// 				SeqNum:     lsn,
-// 				ObjId:   cmd.objID,
-// 				CurVers: cmd.version,
-// 				Parents: cmd.parents,
-// 				Value:   LogValue{Continued: cmd.continued, Delete: cmd.deleted},
-// 			}
-// 			if err := populateLogAndDAG(s, rec); err != nil {
-// 				return err
-// 			}
-// 			key := generationKey(id, sgid, gnum)
-// 			if m, ok := genMap[key]; !ok {
-// 				genMap[key] = &genMetadata{
-// 					Pos:    s.log.head.Curorder,
-// 					Count:  1,
-// 					MaxSeqNum: rec.SeqNum,
-// 				}
-// 				s.log.head.Curorder++
-// 			} else {
-// 				m.Count++
-// 				if rec.SeqNum > m.MaxSeqNum {
-// 					m.MaxSeqNum = rec.SeqNum
-// 				}
-// 			}
-// 			if cmd.cmd == addLocal {
-// 				curGen = gnum
-// 			}
-//
-// 		case setDevTable:
-// 			if err := s.devtab.putGenVec(cmd.devID, cmd.genVec); err != nil {
-// 				return err
-// 			}
-// 		}
-// 	}
-//
-// 	// Initializing genMetadata.
-// 	for key, gen := range genMap {
-// 		dev, gnum, err := splitGenerationKey(key)
-// 		if err != nil {
-// 			return err
-// 		}
-// 		if err := s.log.putGenMetadata(dev, gnum, gen); err != nil {
-// 			return err
-// 		}
-// 	}
-//
-// 	// Initializing generation in log header.
-// 	s.log.head.Curgen = curGen + 1
-//
-// 	return nil
-// }
-//
diff --git a/x/ref/services/syncbase/sync/vsync.vdl b/x/ref/services/syncbase/sync/vsync.vdl
deleted file mode 100644
index 8ad9d30..0000000
--- a/x/ref/services/syncbase/sync/vsync.vdl
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-import (
-  "v.io/v23/security/access"
-)
-
-// temporary types
-type ObjId string
-type Version uint64
-type GroupId uint64
-
-// DeviceId is the globally unique Id of a device.
-type DeviceId string
-// GenId is the unique Id per generation per device.
-type GenId uint64
-// SeqNum is the log sequence number.
-type SeqNum uint64
-// GenVector is the generation vector.
-type GenVector map[DeviceId]GenId
-// TxId is the unique Id per transaction.
-type TxId uint64
-// GroupIdSet is the list of SyncGroup Ids.
-type GroupIdSet []GroupId
-
-const (
-	// NodeRec type log record adds a new node in the dag.
-	NodeRec = byte(0)
-	// LinkRec type log record adds a new link in the dag.
-	LinkRec = byte(1)
-
-	// Sync interface has Object name "global/vsync/<devid>/sync".
-	SyncSuffix = "sync"
-
-	// temporary nil values
-	NoObjId = ObjId("")
-	NoVersion = Version(0)
-	NoGroupId = GroupId(0)
-)
-
-// LogRec represents a single log record that is exchanged between two
-// peers.
-//
-// It contains log related metadata: DevId is the id of the device
-// that created the log record, SyncRootId is the id of a SyncRoot this log
-// record was created under, GNum is the Id of the generation that the
-// log record is part of, SeqNum is the log sequence number of the log
-// record in the generation GNum, and RecType is the type of log
-// record.
-//
-// It also contains information relevant to the updates to an object
-// in the store: ObjId is the id of the object that was
-// updated. CurVers is the current version number of the
-// object. Parents can contain 0, 1 or 2 parent versions that the
-// current version is derived from, and Value is the actual value of
-// the object mutation.
-type LogRec struct {
-	// Log related information.
-	DevId      DeviceId
-	SyncRootId ObjId
-	GenNum     GenId
-	SeqNum     SeqNum
-	RecType    byte
-
-	// Object related information.
-	ObjId     ObjId
-	CurVers   Version
-	Parents   []Version
-	Value     LogValue
-}
-
-// LogValue represents an object mutation within a transaction.
-type LogValue struct {
-	// Mutation is the store mutation representing the change in the object.
-	//Mutation raw.Mutation
-	// SyncTime is the timestamp of the mutation when it arrives at the Sync server.
-	SyncTime int64
-	// Delete indicates whether the mutation resulted in the object being
-	// deleted from the store.
-	Delete bool
-	// TxId is the unique Id of the transaction this mutation belongs to.
-	TxId TxId
-	// TxCount is the number of mutations in the transaction TxId.
-	TxCount uint32
-}
-
-// DeviceStats contains high-level information on a device participating in
-// peer-to-peer synchronization.
-type DeviceStats struct {
-	DevId         DeviceId                  // Device Id.
-	LastSync      int64                     // Timestamp of last sync from the device.
-	GenVectors    map[ObjId]GenVector         // Generation vectors per SyncRoot.
-	IsSelf        bool                      // True if the responder is on this device.
-}
-
-// SyncGroupStats contains high-level information on a SyncGroup.
-type SyncGroupStats struct {
-	Name       string         // Global name of the SyncGroup.
-	Id         GroupId        // Global Id of the SyncGroup.
-	Path       string         // Local store path for the root of the SyncGroup.
-	RootObjId  ObjId          // Id of the store object at the root path.
-	NumJoiners uint32         // Number of members currently in the SyncGroup.
-}
-
-// SyncGroupMember contains information on a SyncGroup member.
-type SyncGroupMember struct {
-	Name       string         // Name of SyncGroup member.
-	Id         GroupId        // Global Id of the SyncGroup.
-	Metadata   JoinerMetaData // Extra member metadata.
-}
-
-// A SyncGroupInfo is the conceptual state of a SyncGroup object.
-type SyncGroupInfo struct {
-	Id         GroupId         // Globally unique SyncGroup Id.
-	ServerName string          // Global Vanadium name of SyncGroupServer.
-	GroupName  string          // Relative name of group; global name is ServerName/GroupName.
-	Config     SyncGroupConfig // Configuration parameters of this SyncGroup.
-	ETag       string          // Version Id for concurrency control.
-
-	// A map from joiner names to the associated metaData for devices that
-	// have called Join() or Create() and not subsequently called Leave()
-	// or had Eject() called on them.  The map returned by the calls below
-	// may contain only a subset of joiners if the number is large.
-	Joiners map[string]JoinerMetaData
-
-	// Blessings for joiners of this SyncGroup will be self-signed by the
-	// SyncGroupServer, and will have names matching
-	// JoinerBlessingPrefix/Name/...
-	JoinerBlessingPrefix string
-}
-
-// A SyncGroupConfig contains some fields of SyncGroupInfo that
-// are passed at create time, but which can be changed later.
-type SyncGroupConfig struct {
-	Desc         string               // Human readable description.
-	Options      map[string]any       // Options for future evolution.
-	Permissions  access.Permissions   // The object's Permissions.
-
-	// Mount tables used to advertise for synchronization.
-	// Typically, we will have only one entry.  However, an array allows
-	// mount tables to be changed over time.
-	MountTables []string
-
-	BlessingsDurationNanos int64   // Duration of blessings, in nanoseconds. 0 => use server default.
-}
-
-// A JoinerMetaData contains the non-name information stored per joiner.
-type JoinerMetaData struct {
-	// SyncPriority is a hint to bias the choice of syncing partners.
-	// Members of the SyncGroup should choose to synchronize more often
-	// with partners with lower values.
-	SyncPriority int32
-}
-
-// Sync allows a device to GetDeltas from another device.
-type Sync interface {
-	// GetDeltas returns a device's current generation vector and all
-	// the missing log records when compared to the incoming generation vector.
-	GetDeltas(in map[ObjId]GenVector, sgs map[ObjId]GroupIdSet, clientId DeviceId) stream<_, LogRec> (map[ObjId]GenVector | error) {access.Write}
-
-	// GetObjectHistory returns the mutation history of a store object.
-	GetObjectHistory(oid ObjId) stream<_, LogRec> (Version | error)
-
-	// GetDeviceStats returns information on devices participating in
-	// peer-to-peer synchronization.
-	GetDeviceStats() stream<_, DeviceStats> error {access.Admin}
-
-	// GetSyncGroupMembers returns information on SyncGroup members.
-	// If SyncGroup names are specified, only members of these SyncGroups
-	// are returned.  Otherwise, if the slice of names is nil or empty,
-	// members of all SyncGroups are returned.
-	GetSyncGroupMembers(sgNames []string) stream<_, SyncGroupMember> error {access.Admin}
-
-	// GetSyncGroupStats returns high-level information on all SyncGroups.
-	GetSyncGroupStats() stream<_, SyncGroupStats> error {access.Admin}
-
-	// Dump writes to the Sync log internal information used for debugging.
-	Dump() error {access.Admin}
-}
diff --git a/x/ref/services/syncbase/sync/vsync.vdl.go b/x/ref/services/syncbase/sync/vsync.vdl.go
deleted file mode 100644
index 8c7a6ae..0000000
--- a/x/ref/services/syncbase/sync/vsync.vdl.go
+++ /dev/null
@@ -1,1091 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file was auto-generated by the vanadium vdl tool.
-// Source: vsync.vdl
-
-package vsync
-
-import (
-	// VDL system imports
-	"io"
-	"v.io/v23"
-	"v.io/v23/context"
-	"v.io/v23/rpc"
-	"v.io/v23/vdl"
-
-	// VDL user imports
-	"v.io/v23/security/access"
-)
-
-// temporary types
-type ObjId string
-
-func (ObjId) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.ObjId"`
-}) {
-}
-
-type Version uint64
-
-func (Version) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.Version"`
-}) {
-}
-
-type GroupId uint64
-
-func (GroupId) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.GroupId"`
-}) {
-}
-
-// DeviceId is the globally unique Id of a device.
-type DeviceId string
-
-func (DeviceId) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.DeviceId"`
-}) {
-}
-
-// GenId is the unique Id per generation per device.
-type GenId uint64
-
-func (GenId) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.GenId"`
-}) {
-}
-
-// SeqNum is the log sequence number.
-type SeqNum uint64
-
-func (SeqNum) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.SeqNum"`
-}) {
-}
-
-// GenVector is the generation vector.
-type GenVector map[DeviceId]GenId
-
-func (GenVector) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.GenVector"`
-}) {
-}
-
-// TxId is the unique Id per transaction.
-type TxId uint64
-
-func (TxId) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.TxId"`
-}) {
-}
-
-// GroupIdSet is the list of SyncGroup Ids.
-type GroupIdSet []GroupId
-
-func (GroupIdSet) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.GroupIdSet"`
-}) {
-}
-
-// LogRec represents a single log record that is exchanged between two
-// peers.
-//
-// It contains log related metadata: DevId is the id of the device
-// that created the log record, SyncRootId is the id of a SyncRoot this log
-// record was created under, GNum is the Id of the generation that the
-// log record is part of, SeqNum is the log sequence number of the log
-// record in the generation GNum, and RecType is the type of log
-// record.
-//
-// It also contains information relevant to the updates to an object
-// in the store: ObjId is the id of the object that was
-// updated. CurVers is the current version number of the
-// object. Parents can contain 0, 1 or 2 parent versions that the
-// current version is derived from, and Value is the actual value of
-// the object mutation.
-type LogRec struct {
-	// Log related information.
-	DevId      DeviceId
-	SyncRootId ObjId
-	GenNum     GenId
-	SeqNum     SeqNum
-	RecType    byte
-	// Object related information.
-	ObjId   ObjId
-	CurVers Version
-	Parents []Version
-	Value   LogValue
-}
-
-func (LogRec) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.LogRec"`
-}) {
-}
-
-// LogValue represents an object mutation within a transaction.
-type LogValue struct {
-	// Mutation is the store mutation representing the change in the object.
-	//Mutation raw.Mutation
-	// SyncTime is the timestamp of the mutation when it arrives at the Sync server.
-	SyncTime int64
-	// Delete indicates whether the mutation resulted in the object being
-	// deleted from the store.
-	Delete bool
-	// TxId is the unique Id of the transaction this mutation belongs to.
-	TxId TxId
-	// TxCount is the number of mutations in the transaction TxId.
-	TxCount uint32
-}
-
-func (LogValue) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.LogValue"`
-}) {
-}
-
-// DeviceStats contains high-level information on a device participating in
-// peer-to-peer synchronization.
-type DeviceStats struct {
-	DevId      DeviceId            // Device Id.
-	LastSync   int64               // Timestamp of last sync from the device.
-	GenVectors map[ObjId]GenVector // Generation vectors per SyncRoot.
-	IsSelf     bool                // True if the responder is on this device.
-}
-
-func (DeviceStats) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.DeviceStats"`
-}) {
-}
-
-// SyncGroupStats contains high-level information on a SyncGroup.
-type SyncGroupStats struct {
-	Name       string  // Global name of the SyncGroup.
-	Id         GroupId // Global Id of the SyncGroup.
-	Path       string  // Local store path for the root of the SyncGroup.
-	RootObjId  ObjId   // Id of the store object at the root path.
-	NumJoiners uint32  // Number of members currently in the SyncGroup.
-}
-
-func (SyncGroupStats) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.SyncGroupStats"`
-}) {
-}
-
-// SyncGroupMember contains information on a SyncGroup member.
-type SyncGroupMember struct {
-	Name     string         // Name of SyncGroup member.
-	Id       GroupId        // Global Id of the SyncGroup.
-	Metadata JoinerMetaData // Extra member metadata.
-}
-
-func (SyncGroupMember) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.SyncGroupMember"`
-}) {
-}
-
-// A SyncGroupInfo is the conceptual state of a SyncGroup object.
-type SyncGroupInfo struct {
-	Id         GroupId         // Globally unique SyncGroup Id.
-	ServerName string          // Global Vanadium name of SyncGroupServer.
-	GroupName  string          // Relative name of group; global name is ServerName/GroupName.
-	Config     SyncGroupConfig // Configuration parameters of this SyncGroup.
-	ETag       string          // Version Id for concurrency control.
-	// A map from joiner names to the associated metaData for devices that
-	// have called Join() or Create() and not subsequently called Leave()
-	// or had Eject() called on them.  The map returned by the calls below
-	// may contain only a subset of joiners if the number is large.
-	Joiners map[string]JoinerMetaData
-	// Blessings for joiners of this SyncGroup will be self-signed by the
-	// SyncGroupServer, and will have names matching
-	// JoinerBlessingPrefix/Name/...
-	JoinerBlessingPrefix string
-}
-
-func (SyncGroupInfo) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.SyncGroupInfo"`
-}) {
-}
-
-// A SyncGroupConfig contains some fields of SyncGroupInfo that
-// are passed at create time, but which can be changed later.
-type SyncGroupConfig struct {
-	Desc        string                // Human readable description.
-	Options     map[string]*vdl.Value // Options for future evolution.
-	Permissions access.Permissions    // The object's Permissions.
-	// Mount tables used to advertise for synchronization.
-	// Typically, we will have only one entry.  However, an array allows
-	// mount tables to be changed over time.
-	MountTables            []string
-	BlessingsDurationNanos int64 // Duration of blessings, in nanoseconds. 0 => use server default.
-}
-
-func (SyncGroupConfig) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.SyncGroupConfig"`
-}) {
-}
-
-// A JoinerMetaData contains the non-name information stored per joiner.
-type JoinerMetaData struct {
-	// SyncPriority is a hint to bias the choice of syncing partners.
-	// Members of the SyncGroup should choose to synchronize more often
-	// with partners with lower values.
-	SyncPriority int32
-}
-
-func (JoinerMetaData) __VDLReflect(struct {
-	Name string `vdl:"v.io/syncbase/x/ref/services/syncbase/sync.JoinerMetaData"`
-}) {
-}
-
-func init() {
-	vdl.Register((*ObjId)(nil))
-	vdl.Register((*Version)(nil))
-	vdl.Register((*GroupId)(nil))
-	vdl.Register((*DeviceId)(nil))
-	vdl.Register((*GenId)(nil))
-	vdl.Register((*SeqNum)(nil))
-	vdl.Register((*GenVector)(nil))
-	vdl.Register((*TxId)(nil))
-	vdl.Register((*GroupIdSet)(nil))
-	vdl.Register((*LogRec)(nil))
-	vdl.Register((*LogValue)(nil))
-	vdl.Register((*DeviceStats)(nil))
-	vdl.Register((*SyncGroupStats)(nil))
-	vdl.Register((*SyncGroupMember)(nil))
-	vdl.Register((*SyncGroupInfo)(nil))
-	vdl.Register((*SyncGroupConfig)(nil))
-	vdl.Register((*JoinerMetaData)(nil))
-}
-
-// NodeRec type log record adds a new node in the dag.
-const NodeRec = byte(0)
-
-// LinkRec type log record adds a new link in the dag.
-const LinkRec = byte(1)
-
-// Sync interface has Object name "global/vsync/<devid>/sync".
-const SyncSuffix = "sync"
-
-// temporary nil values
-const NoObjId = ObjId("")
-
-const NoVersion = Version(0)
-
-const NoGroupId = GroupId(0)
-
-// SyncClientMethods is the client interface
-// containing Sync methods.
-//
-// Sync allows a device to GetDeltas from another device.
-type SyncClientMethods interface {
-	// GetDeltas returns a device's current generation vector and all
-	// the missing log records when compared to the incoming generation vector.
-	GetDeltas(ctx *context.T, in map[ObjId]GenVector, sgs map[ObjId]GroupIdSet, clientId DeviceId, opts ...rpc.CallOpt) (SyncGetDeltasClientCall, error)
-	// GetObjectHistory returns the mutation history of a store object.
-	GetObjectHistory(ctx *context.T, oid ObjId, opts ...rpc.CallOpt) (SyncGetObjectHistoryClientCall, error)
-	// GetDeviceStats returns information on devices participating in
-	// peer-to-peer synchronization.
-	GetDeviceStats(*context.T, ...rpc.CallOpt) (SyncGetDeviceStatsClientCall, error)
-	// GetSyncGroupMembers returns information on SyncGroup members.
-	// If SyncGroup names are specified, only members of these SyncGroups
-	// are returned.  Otherwise, if the slice of names is nil or empty,
-	// members of all SyncGroups are returned.
-	GetSyncGroupMembers(ctx *context.T, sgNames []string, opts ...rpc.CallOpt) (SyncGetSyncGroupMembersClientCall, error)
-	// GetSyncGroupStats returns high-level information on all SyncGroups.
-	GetSyncGroupStats(*context.T, ...rpc.CallOpt) (SyncGetSyncGroupStatsClientCall, error)
-	// Dump writes to the Sync log internal information used for debugging.
-	Dump(*context.T, ...rpc.CallOpt) error
-}
-
-// SyncClientStub adds universal methods to SyncClientMethods.
-type SyncClientStub interface {
-	SyncClientMethods
-	rpc.UniversalServiceMethods
-}
-
-// SyncClient returns a client stub for Sync.
-func SyncClient(name string) SyncClientStub {
-	return implSyncClientStub{name}
-}
-
-type implSyncClientStub struct {
-	name string
-}
-
-func (c implSyncClientStub) GetDeltas(ctx *context.T, i0 map[ObjId]GenVector, i1 map[ObjId]GroupIdSet, i2 DeviceId, opts ...rpc.CallOpt) (ocall SyncGetDeltasClientCall, err error) {
-	var call rpc.ClientCall
-	if call, err = v23.GetClient(ctx).StartCall(ctx, c.name, "GetDeltas", []interface{}{i0, i1, i2}, opts...); err != nil {
-		return
-	}
-	ocall = &implSyncGetDeltasClientCall{ClientCall: call}
-	return
-}
-
-func (c implSyncClientStub) GetObjectHistory(ctx *context.T, i0 ObjId, opts ...rpc.CallOpt) (ocall SyncGetObjectHistoryClientCall, err error) {
-	var call rpc.ClientCall
-	if call, err = v23.GetClient(ctx).StartCall(ctx, c.name, "GetObjectHistory", []interface{}{i0}, opts...); err != nil {
-		return
-	}
-	ocall = &implSyncGetObjectHistoryClientCall{ClientCall: call}
-	return
-}
-
-func (c implSyncClientStub) GetDeviceStats(ctx *context.T, opts ...rpc.CallOpt) (ocall SyncGetDeviceStatsClientCall, err error) {
-	var call rpc.ClientCall
-	if call, err = v23.GetClient(ctx).StartCall(ctx, c.name, "GetDeviceStats", nil, opts...); err != nil {
-		return
-	}
-	ocall = &implSyncGetDeviceStatsClientCall{ClientCall: call}
-	return
-}
-
-func (c implSyncClientStub) GetSyncGroupMembers(ctx *context.T, i0 []string, opts ...rpc.CallOpt) (ocall SyncGetSyncGroupMembersClientCall, err error) {
-	var call rpc.ClientCall
-	if call, err = v23.GetClient(ctx).StartCall(ctx, c.name, "GetSyncGroupMembers", []interface{}{i0}, opts...); err != nil {
-		return
-	}
-	ocall = &implSyncGetSyncGroupMembersClientCall{ClientCall: call}
-	return
-}
-
-func (c implSyncClientStub) GetSyncGroupStats(ctx *context.T, opts ...rpc.CallOpt) (ocall SyncGetSyncGroupStatsClientCall, err error) {
-	var call rpc.ClientCall
-	if call, err = v23.GetClient(ctx).StartCall(ctx, c.name, "GetSyncGroupStats", nil, opts...); err != nil {
-		return
-	}
-	ocall = &implSyncGetSyncGroupStatsClientCall{ClientCall: call}
-	return
-}
-
-func (c implSyncClientStub) Dump(ctx *context.T, opts ...rpc.CallOpt) (err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "Dump", nil, nil, opts...)
-	return
-}
-
-// SyncGetDeltasClientStream is the client stream for Sync.GetDeltas.
-type SyncGetDeltasClientStream interface {
-	// RecvStream returns the receiver side of the Sync.GetDeltas client stream.
-	RecvStream() interface {
-		// Advance stages an item so that it may be retrieved via Value.  Returns
-		// true iff there is an item to retrieve.  Advance must be called before
-		// Value is called.  May block if an item is not available.
-		Advance() bool
-		// Value returns the item that was staged by Advance.  May panic if Advance
-		// returned false or was not called.  Never blocks.
-		Value() LogRec
-		// Err returns any error encountered by Advance.  Never blocks.
-		Err() error
-	}
-}
-
-// SyncGetDeltasClientCall represents the call returned from Sync.GetDeltas.
-type SyncGetDeltasClientCall interface {
-	SyncGetDeltasClientStream
-	// Finish blocks until the server is done, and returns the positional return
-	// values for call.
-	//
-	// Finish returns immediately if the call has been canceled; depending on the
-	// timing the output could either be an error signaling cancelation, or the
-	// valid positional return values from the server.
-	//
-	// Calling Finish is mandatory for releasing stream resources, unless the call
-	// has been canceled or any of the other methods return an error.  Finish should
-	// be called at most once.
-	Finish() (map[ObjId]GenVector, error)
-}
-
-type implSyncGetDeltasClientCall struct {
-	rpc.ClientCall
-	valRecv LogRec
-	errRecv error
-}
-
-func (c *implSyncGetDeltasClientCall) RecvStream() interface {
-	Advance() bool
-	Value() LogRec
-	Err() error
-} {
-	return implSyncGetDeltasClientCallRecv{c}
-}
-
-type implSyncGetDeltasClientCallRecv struct {
-	c *implSyncGetDeltasClientCall
-}
-
-func (c implSyncGetDeltasClientCallRecv) Advance() bool {
-	c.c.valRecv = LogRec{}
-	c.c.errRecv = c.c.Recv(&c.c.valRecv)
-	return c.c.errRecv == nil
-}
-func (c implSyncGetDeltasClientCallRecv) Value() LogRec {
-	return c.c.valRecv
-}
-func (c implSyncGetDeltasClientCallRecv) Err() error {
-	if c.c.errRecv == io.EOF {
-		return nil
-	}
-	return c.c.errRecv
-}
-func (c *implSyncGetDeltasClientCall) Finish() (o0 map[ObjId]GenVector, err error) {
-	err = c.ClientCall.Finish(&o0)
-	return
-}
-
-// SyncGetObjectHistoryClientStream is the client stream for Sync.GetObjectHistory.
-type SyncGetObjectHistoryClientStream interface {
-	// RecvStream returns the receiver side of the Sync.GetObjectHistory client stream.
-	RecvStream() interface {
-		// Advance stages an item so that it may be retrieved via Value.  Returns
-		// true iff there is an item to retrieve.  Advance must be called before
-		// Value is called.  May block if an item is not available.
-		Advance() bool
-		// Value returns the item that was staged by Advance.  May panic if Advance
-		// returned false or was not called.  Never blocks.
-		Value() LogRec
-		// Err returns any error encountered by Advance.  Never blocks.
-		Err() error
-	}
-}
-
-// SyncGetObjectHistoryClientCall represents the call returned from Sync.GetObjectHistory.
-type SyncGetObjectHistoryClientCall interface {
-	SyncGetObjectHistoryClientStream
-	// Finish blocks until the server is done, and returns the positional return
-	// values for call.
-	//
-	// Finish returns immediately if the call has been canceled; depending on the
-	// timing the output could either be an error signaling cancelation, or the
-	// valid positional return values from the server.
-	//
-	// Calling Finish is mandatory for releasing stream resources, unless the call
-	// has been canceled or any of the other methods return an error.  Finish should
-	// be called at most once.
-	Finish() (Version, error)
-}
-
-type implSyncGetObjectHistoryClientCall struct {
-	rpc.ClientCall
-	valRecv LogRec
-	errRecv error
-}
-
-func (c *implSyncGetObjectHistoryClientCall) RecvStream() interface {
-	Advance() bool
-	Value() LogRec
-	Err() error
-} {
-	return implSyncGetObjectHistoryClientCallRecv{c}
-}
-
-type implSyncGetObjectHistoryClientCallRecv struct {
-	c *implSyncGetObjectHistoryClientCall
-}
-
-func (c implSyncGetObjectHistoryClientCallRecv) Advance() bool {
-	c.c.valRecv = LogRec{}
-	c.c.errRecv = c.c.Recv(&c.c.valRecv)
-	return c.c.errRecv == nil
-}
-func (c implSyncGetObjectHistoryClientCallRecv) Value() LogRec {
-	return c.c.valRecv
-}
-func (c implSyncGetObjectHistoryClientCallRecv) Err() error {
-	if c.c.errRecv == io.EOF {
-		return nil
-	}
-	return c.c.errRecv
-}
-func (c *implSyncGetObjectHistoryClientCall) Finish() (o0 Version, err error) {
-	err = c.ClientCall.Finish(&o0)
-	return
-}
-
-// SyncGetDeviceStatsClientStream is the client stream for Sync.GetDeviceStats.
-type SyncGetDeviceStatsClientStream interface {
-	// RecvStream returns the receiver side of the Sync.GetDeviceStats client stream.
-	RecvStream() interface {
-		// Advance stages an item so that it may be retrieved via Value.  Returns
-		// true iff there is an item to retrieve.  Advance must be called before
-		// Value is called.  May block if an item is not available.
-		Advance() bool
-		// Value returns the item that was staged by Advance.  May panic if Advance
-		// returned false or was not called.  Never blocks.
-		Value() DeviceStats
-		// Err returns any error encountered by Advance.  Never blocks.
-		Err() error
-	}
-}
-
-// SyncGetDeviceStatsClientCall represents the call returned from Sync.GetDeviceStats.
-type SyncGetDeviceStatsClientCall interface {
-	SyncGetDeviceStatsClientStream
-	// Finish blocks until the server is done, and returns the positional return
-	// values for call.
-	//
-	// Finish returns immediately if the call has been canceled; depending on the
-	// timing the output could either be an error signaling cancelation, or the
-	// valid positional return values from the server.
-	//
-	// Calling Finish is mandatory for releasing stream resources, unless the call
-	// has been canceled or any of the other methods return an error.  Finish should
-	// be called at most once.
-	Finish() error
-}
-
-type implSyncGetDeviceStatsClientCall struct {
-	rpc.ClientCall
-	valRecv DeviceStats
-	errRecv error
-}
-
-func (c *implSyncGetDeviceStatsClientCall) RecvStream() interface {
-	Advance() bool
-	Value() DeviceStats
-	Err() error
-} {
-	return implSyncGetDeviceStatsClientCallRecv{c}
-}
-
-type implSyncGetDeviceStatsClientCallRecv struct {
-	c *implSyncGetDeviceStatsClientCall
-}
-
-func (c implSyncGetDeviceStatsClientCallRecv) Advance() bool {
-	c.c.valRecv = DeviceStats{}
-	c.c.errRecv = c.c.Recv(&c.c.valRecv)
-	return c.c.errRecv == nil
-}
-func (c implSyncGetDeviceStatsClientCallRecv) Value() DeviceStats {
-	return c.c.valRecv
-}
-func (c implSyncGetDeviceStatsClientCallRecv) Err() error {
-	if c.c.errRecv == io.EOF {
-		return nil
-	}
-	return c.c.errRecv
-}
-func (c *implSyncGetDeviceStatsClientCall) Finish() (err error) {
-	err = c.ClientCall.Finish()
-	return
-}
-
-// SyncGetSyncGroupMembersClientStream is the client stream for Sync.GetSyncGroupMembers.
-type SyncGetSyncGroupMembersClientStream interface {
-	// RecvStream returns the receiver side of the Sync.GetSyncGroupMembers client stream.
-	RecvStream() interface {
-		// Advance stages an item so that it may be retrieved via Value.  Returns
-		// true iff there is an item to retrieve.  Advance must be called before
-		// Value is called.  May block if an item is not available.
-		Advance() bool
-		// Value returns the item that was staged by Advance.  May panic if Advance
-		// returned false or was not called.  Never blocks.
-		Value() SyncGroupMember
-		// Err returns any error encountered by Advance.  Never blocks.
-		Err() error
-	}
-}
-
-// SyncGetSyncGroupMembersClientCall represents the call returned from Sync.GetSyncGroupMembers.
-type SyncGetSyncGroupMembersClientCall interface {
-	SyncGetSyncGroupMembersClientStream
-	// Finish blocks until the server is done, and returns the positional return
-	// values for call.
-	//
-	// Finish returns immediately if the call has been canceled; depending on the
-	// timing the output could either be an error signaling cancelation, or the
-	// valid positional return values from the server.
-	//
-	// Calling Finish is mandatory for releasing stream resources, unless the call
-	// has been canceled or any of the other methods return an error.  Finish should
-	// be called at most once.
-	Finish() error
-}
-
-type implSyncGetSyncGroupMembersClientCall struct {
-	rpc.ClientCall
-	valRecv SyncGroupMember
-	errRecv error
-}
-
-func (c *implSyncGetSyncGroupMembersClientCall) RecvStream() interface {
-	Advance() bool
-	Value() SyncGroupMember
-	Err() error
-} {
-	return implSyncGetSyncGroupMembersClientCallRecv{c}
-}
-
-type implSyncGetSyncGroupMembersClientCallRecv struct {
-	c *implSyncGetSyncGroupMembersClientCall
-}
-
-func (c implSyncGetSyncGroupMembersClientCallRecv) Advance() bool {
-	c.c.valRecv = SyncGroupMember{}
-	c.c.errRecv = c.c.Recv(&c.c.valRecv)
-	return c.c.errRecv == nil
-}
-func (c implSyncGetSyncGroupMembersClientCallRecv) Value() SyncGroupMember {
-	return c.c.valRecv
-}
-func (c implSyncGetSyncGroupMembersClientCallRecv) Err() error {
-	if c.c.errRecv == io.EOF {
-		return nil
-	}
-	return c.c.errRecv
-}
-func (c *implSyncGetSyncGroupMembersClientCall) Finish() (err error) {
-	err = c.ClientCall.Finish()
-	return
-}
-
-// SyncGetSyncGroupStatsClientStream is the client stream for Sync.GetSyncGroupStats.
-type SyncGetSyncGroupStatsClientStream interface {
-	// RecvStream returns the receiver side of the Sync.GetSyncGroupStats client stream.
-	RecvStream() interface {
-		// Advance stages an item so that it may be retrieved via Value.  Returns
-		// true iff there is an item to retrieve.  Advance must be called before
-		// Value is called.  May block if an item is not available.
-		Advance() bool
-		// Value returns the item that was staged by Advance.  May panic if Advance
-		// returned false or was not called.  Never blocks.
-		Value() SyncGroupStats
-		// Err returns any error encountered by Advance.  Never blocks.
-		Err() error
-	}
-}
-
-// SyncGetSyncGroupStatsClientCall represents the call returned from Sync.GetSyncGroupStats.
-type SyncGetSyncGroupStatsClientCall interface {
-	SyncGetSyncGroupStatsClientStream
-	// Finish blocks until the server is done, and returns the positional return
-	// values for call.
-	//
-	// Finish returns immediately if the call has been canceled; depending on the
-	// timing the output could either be an error signaling cancelation, or the
-	// valid positional return values from the server.
-	//
-	// Calling Finish is mandatory for releasing stream resources, unless the call
-	// has been canceled or any of the other methods return an error.  Finish should
-	// be called at most once.
-	Finish() error
-}
-
-type implSyncGetSyncGroupStatsClientCall struct {
-	rpc.ClientCall
-	valRecv SyncGroupStats
-	errRecv error
-}
-
-func (c *implSyncGetSyncGroupStatsClientCall) RecvStream() interface {
-	Advance() bool
-	Value() SyncGroupStats
-	Err() error
-} {
-	return implSyncGetSyncGroupStatsClientCallRecv{c}
-}
-
-type implSyncGetSyncGroupStatsClientCallRecv struct {
-	c *implSyncGetSyncGroupStatsClientCall
-}
-
-func (c implSyncGetSyncGroupStatsClientCallRecv) Advance() bool {
-	c.c.valRecv = SyncGroupStats{}
-	c.c.errRecv = c.c.Recv(&c.c.valRecv)
-	return c.c.errRecv == nil
-}
-func (c implSyncGetSyncGroupStatsClientCallRecv) Value() SyncGroupStats {
-	return c.c.valRecv
-}
-func (c implSyncGetSyncGroupStatsClientCallRecv) Err() error {
-	if c.c.errRecv == io.EOF {
-		return nil
-	}
-	return c.c.errRecv
-}
-func (c *implSyncGetSyncGroupStatsClientCall) Finish() (err error) {
-	err = c.ClientCall.Finish()
-	return
-}
-
-// SyncServerMethods is the interface a server writer
-// implements for Sync.
-//
-// Sync allows a device to GetDeltas from another device.
-type SyncServerMethods interface {
-	// GetDeltas returns a device's current generation vector and all
-	// the missing log records when compared to the incoming generation vector.
-	GetDeltas(ctx *context.T, call SyncGetDeltasServerCall, in map[ObjId]GenVector, sgs map[ObjId]GroupIdSet, clientId DeviceId) (map[ObjId]GenVector, error)
-	// GetObjectHistory returns the mutation history of a store object.
-	GetObjectHistory(ctx *context.T, call SyncGetObjectHistoryServerCall, oid ObjId) (Version, error)
-	// GetDeviceStats returns information on devices participating in
-	// peer-to-peer synchronization.
-	GetDeviceStats(*context.T, SyncGetDeviceStatsServerCall) error
-	// GetSyncGroupMembers returns information on SyncGroup members.
-	// If SyncGroup names are specified, only members of these SyncGroups
-	// are returned.  Otherwise, if the slice of names is nil or empty,
-	// members of all SyncGroups are returned.
-	GetSyncGroupMembers(ctx *context.T, call SyncGetSyncGroupMembersServerCall, sgNames []string) error
-	// GetSyncGroupStats returns high-level information on all SyncGroups.
-	GetSyncGroupStats(*context.T, SyncGetSyncGroupStatsServerCall) error
-	// Dump writes to the Sync log internal information used for debugging.
-	Dump(*context.T, rpc.ServerCall) error
-}
-
-// SyncServerStubMethods is the server interface containing
-// Sync methods, as expected by rpc.Server.
-// The only difference between this interface and SyncServerMethods
-// is the streaming methods.
-type SyncServerStubMethods interface {
-	// GetDeltas returns a device's current generation vector and all
-	// the missing log records when compared to the incoming generation vector.
-	GetDeltas(ctx *context.T, call *SyncGetDeltasServerCallStub, in map[ObjId]GenVector, sgs map[ObjId]GroupIdSet, clientId DeviceId) (map[ObjId]GenVector, error)
-	// GetObjectHistory returns the mutation history of a store object.
-	GetObjectHistory(ctx *context.T, call *SyncGetObjectHistoryServerCallStub, oid ObjId) (Version, error)
-	// GetDeviceStats returns information on devices participating in
-	// peer-to-peer synchronization.
-	GetDeviceStats(*context.T, *SyncGetDeviceStatsServerCallStub) error
-	// GetSyncGroupMembers returns information on SyncGroup members.
-	// If SyncGroup names are specified, only members of these SyncGroups
-	// are returned.  Otherwise, if the slice of names is nil or empty,
-	// members of all SyncGroups are returned.
-	GetSyncGroupMembers(ctx *context.T, call *SyncGetSyncGroupMembersServerCallStub, sgNames []string) error
-	// GetSyncGroupStats returns high-level information on all SyncGroups.
-	GetSyncGroupStats(*context.T, *SyncGetSyncGroupStatsServerCallStub) error
-	// Dump writes to the Sync log internal information used for debugging.
-	Dump(*context.T, rpc.ServerCall) error
-}
-
-// SyncServerStub adds universal methods to SyncServerStubMethods.
-type SyncServerStub interface {
-	SyncServerStubMethods
-	// Describe the Sync interfaces.
-	Describe__() []rpc.InterfaceDesc
-}
-
-// SyncServer returns a server stub for Sync.
-// It converts an implementation of SyncServerMethods into
-// an object that may be used by rpc.Server.
-func SyncServer(impl SyncServerMethods) SyncServerStub {
-	stub := implSyncServerStub{
-		impl: impl,
-	}
-	// Initialize GlobState; always check the stub itself first, to handle the
-	// case where the user has the Glob method defined in their VDL source.
-	if gs := rpc.NewGlobState(stub); gs != nil {
-		stub.gs = gs
-	} else if gs := rpc.NewGlobState(impl); gs != nil {
-		stub.gs = gs
-	}
-	return stub
-}
-
-type implSyncServerStub struct {
-	impl SyncServerMethods
-	gs   *rpc.GlobState
-}
-
-func (s implSyncServerStub) GetDeltas(ctx *context.T, call *SyncGetDeltasServerCallStub, i0 map[ObjId]GenVector, i1 map[ObjId]GroupIdSet, i2 DeviceId) (map[ObjId]GenVector, error) {
-	return s.impl.GetDeltas(ctx, call, i0, i1, i2)
-}
-
-func (s implSyncServerStub) GetObjectHistory(ctx *context.T, call *SyncGetObjectHistoryServerCallStub, i0 ObjId) (Version, error) {
-	return s.impl.GetObjectHistory(ctx, call, i0)
-}
-
-func (s implSyncServerStub) GetDeviceStats(ctx *context.T, call *SyncGetDeviceStatsServerCallStub) error {
-	return s.impl.GetDeviceStats(ctx, call)
-}
-
-func (s implSyncServerStub) GetSyncGroupMembers(ctx *context.T, call *SyncGetSyncGroupMembersServerCallStub, i0 []string) error {
-	return s.impl.GetSyncGroupMembers(ctx, call, i0)
-}
-
-func (s implSyncServerStub) GetSyncGroupStats(ctx *context.T, call *SyncGetSyncGroupStatsServerCallStub) error {
-	return s.impl.GetSyncGroupStats(ctx, call)
-}
-
-func (s implSyncServerStub) Dump(ctx *context.T, call rpc.ServerCall) error {
-	return s.impl.Dump(ctx, call)
-}
-
-func (s implSyncServerStub) Globber() *rpc.GlobState {
-	return s.gs
-}
-
-func (s implSyncServerStub) Describe__() []rpc.InterfaceDesc {
-	return []rpc.InterfaceDesc{SyncDesc}
-}
-
-// SyncDesc describes the Sync interface.
-var SyncDesc rpc.InterfaceDesc = descSync
-
-// descSync hides the desc to keep godoc clean.
-var descSync = rpc.InterfaceDesc{
-	Name:    "Sync",
-	PkgPath: "v.io/syncbase/x/ref/services/syncbase/sync",
-	Doc:     "// Sync allows a device to GetDeltas from another device.",
-	Methods: []rpc.MethodDesc{
-		{
-			Name: "GetDeltas",
-			Doc:  "// GetDeltas returns a device's current generation vector and all\n// the missing log records when compared to the incoming generation vector.",
-			InArgs: []rpc.ArgDesc{
-				{"in", ``},       // map[ObjId]GenVector
-				{"sgs", ``},      // map[ObjId]GroupIdSet
-				{"clientId", ``}, // DeviceId
-			},
-			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // map[ObjId]GenVector
-			},
-			Tags: []*vdl.Value{vdl.ValueOf(access.Tag("Write"))},
-		},
-		{
-			Name: "GetObjectHistory",
-			Doc:  "// GetObjectHistory returns the mutation history of a store object.",
-			InArgs: []rpc.ArgDesc{
-				{"oid", ``}, // ObjId
-			},
-			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // Version
-			},
-		},
-		{
-			Name: "GetDeviceStats",
-			Doc:  "// GetDeviceStats returns information on devices participating in\n// peer-to-peer synchronization.",
-			Tags: []*vdl.Value{vdl.ValueOf(access.Tag("Admin"))},
-		},
-		{
-			Name: "GetSyncGroupMembers",
-			Doc:  "// GetSyncGroupMembers returns information on SyncGroup members.\n// If SyncGroup names are specified, only members of these SyncGroups\n// are returned.  Otherwise, if the slice of names is nil or empty,\n// members of all SyncGroups are returned.",
-			InArgs: []rpc.ArgDesc{
-				{"sgNames", ``}, // []string
-			},
-			Tags: []*vdl.Value{vdl.ValueOf(access.Tag("Admin"))},
-		},
-		{
-			Name: "GetSyncGroupStats",
-			Doc:  "// GetSyncGroupStats returns high-level information on all SyncGroups.",
-			Tags: []*vdl.Value{vdl.ValueOf(access.Tag("Admin"))},
-		},
-		{
-			Name: "Dump",
-			Doc:  "// Dump writes to the Sync log internal information used for debugging.",
-			Tags: []*vdl.Value{vdl.ValueOf(access.Tag("Admin"))},
-		},
-	},
-}
-
-// SyncGetDeltasServerStream is the server stream for Sync.GetDeltas.
-type SyncGetDeltasServerStream interface {
-	// SendStream returns the send side of the Sync.GetDeltas server stream.
-	SendStream() interface {
-		// Send places the item onto the output stream.  Returns errors encountered
-		// while sending.  Blocks if there is no buffer space; will unblock when
-		// buffer space is available.
-		Send(item LogRec) error
-	}
-}
-
-// SyncGetDeltasServerCall represents the context passed to Sync.GetDeltas.
-type SyncGetDeltasServerCall interface {
-	rpc.ServerCall
-	SyncGetDeltasServerStream
-}
-
-// SyncGetDeltasServerCallStub is a wrapper that converts rpc.StreamServerCall into
-// a typesafe stub that implements SyncGetDeltasServerCall.
-type SyncGetDeltasServerCallStub struct {
-	rpc.StreamServerCall
-}
-
-// Init initializes SyncGetDeltasServerCallStub from rpc.StreamServerCall.
-func (s *SyncGetDeltasServerCallStub) Init(call rpc.StreamServerCall) {
-	s.StreamServerCall = call
-}
-
-// SendStream returns the send side of the Sync.GetDeltas server stream.
-func (s *SyncGetDeltasServerCallStub) SendStream() interface {
-	Send(item LogRec) error
-} {
-	return implSyncGetDeltasServerCallSend{s}
-}
-
-type implSyncGetDeltasServerCallSend struct {
-	s *SyncGetDeltasServerCallStub
-}
-
-func (s implSyncGetDeltasServerCallSend) Send(item LogRec) error {
-	return s.s.Send(item)
-}
-
-// SyncGetObjectHistoryServerStream is the server stream for Sync.GetObjectHistory.
-type SyncGetObjectHistoryServerStream interface {
-	// SendStream returns the send side of the Sync.GetObjectHistory server stream.
-	SendStream() interface {
-		// Send places the item onto the output stream.  Returns errors encountered
-		// while sending.  Blocks if there is no buffer space; will unblock when
-		// buffer space is available.
-		Send(item LogRec) error
-	}
-}
-
-// SyncGetObjectHistoryServerCall represents the context passed to Sync.GetObjectHistory.
-type SyncGetObjectHistoryServerCall interface {
-	rpc.ServerCall
-	SyncGetObjectHistoryServerStream
-}
-
-// SyncGetObjectHistoryServerCallStub is a wrapper that converts rpc.StreamServerCall into
-// a typesafe stub that implements SyncGetObjectHistoryServerCall.
-type SyncGetObjectHistoryServerCallStub struct {
-	rpc.StreamServerCall
-}
-
-// Init initializes SyncGetObjectHistoryServerCallStub from rpc.StreamServerCall.
-func (s *SyncGetObjectHistoryServerCallStub) Init(call rpc.StreamServerCall) {
-	s.StreamServerCall = call
-}
-
-// SendStream returns the send side of the Sync.GetObjectHistory server stream.
-func (s *SyncGetObjectHistoryServerCallStub) SendStream() interface {
-	Send(item LogRec) error
-} {
-	return implSyncGetObjectHistoryServerCallSend{s}
-}
-
-type implSyncGetObjectHistoryServerCallSend struct {
-	s *SyncGetObjectHistoryServerCallStub
-}
-
-func (s implSyncGetObjectHistoryServerCallSend) Send(item LogRec) error {
-	return s.s.Send(item)
-}
-
-// SyncGetDeviceStatsServerStream is the server stream for Sync.GetDeviceStats.
-type SyncGetDeviceStatsServerStream interface {
-	// SendStream returns the send side of the Sync.GetDeviceStats server stream.
-	SendStream() interface {
-		// Send places the item onto the output stream.  Returns errors encountered
-		// while sending.  Blocks if there is no buffer space; will unblock when
-		// buffer space is available.
-		Send(item DeviceStats) error
-	}
-}
-
-// SyncGetDeviceStatsServerCall represents the context passed to Sync.GetDeviceStats.
-type SyncGetDeviceStatsServerCall interface {
-	rpc.ServerCall
-	SyncGetDeviceStatsServerStream
-}
-
-// SyncGetDeviceStatsServerCallStub is a wrapper that converts rpc.StreamServerCall into
-// a typesafe stub that implements SyncGetDeviceStatsServerCall.
-type SyncGetDeviceStatsServerCallStub struct {
-	rpc.StreamServerCall
-}
-
-// Init initializes SyncGetDeviceStatsServerCallStub from rpc.StreamServerCall.
-func (s *SyncGetDeviceStatsServerCallStub) Init(call rpc.StreamServerCall) {
-	s.StreamServerCall = call
-}
-
-// SendStream returns the send side of the Sync.GetDeviceStats server stream.
-func (s *SyncGetDeviceStatsServerCallStub) SendStream() interface {
-	Send(item DeviceStats) error
-} {
-	return implSyncGetDeviceStatsServerCallSend{s}
-}
-
-type implSyncGetDeviceStatsServerCallSend struct {
-	s *SyncGetDeviceStatsServerCallStub
-}
-
-func (s implSyncGetDeviceStatsServerCallSend) Send(item DeviceStats) error {
-	return s.s.Send(item)
-}
-
-// SyncGetSyncGroupMembersServerStream is the server stream for Sync.GetSyncGroupMembers.
-type SyncGetSyncGroupMembersServerStream interface {
-	// SendStream returns the send side of the Sync.GetSyncGroupMembers server stream.
-	SendStream() interface {
-		// Send places the item onto the output stream.  Returns errors encountered
-		// while sending.  Blocks if there is no buffer space; will unblock when
-		// buffer space is available.
-		Send(item SyncGroupMember) error
-	}
-}
-
-// SyncGetSyncGroupMembersServerCall represents the context passed to Sync.GetSyncGroupMembers.
-type SyncGetSyncGroupMembersServerCall interface {
-	rpc.ServerCall
-	SyncGetSyncGroupMembersServerStream
-}
-
-// SyncGetSyncGroupMembersServerCallStub is a wrapper that converts rpc.StreamServerCall into
-// a typesafe stub that implements SyncGetSyncGroupMembersServerCall.
-type SyncGetSyncGroupMembersServerCallStub struct {
-	rpc.StreamServerCall
-}
-
-// Init initializes SyncGetSyncGroupMembersServerCallStub from rpc.StreamServerCall.
-func (s *SyncGetSyncGroupMembersServerCallStub) Init(call rpc.StreamServerCall) {
-	s.StreamServerCall = call
-}
-
-// SendStream returns the send side of the Sync.GetSyncGroupMembers server stream.
-func (s *SyncGetSyncGroupMembersServerCallStub) SendStream() interface {
-	Send(item SyncGroupMember) error
-} {
-	return implSyncGetSyncGroupMembersServerCallSend{s}
-}
-
-type implSyncGetSyncGroupMembersServerCallSend struct {
-	s *SyncGetSyncGroupMembersServerCallStub
-}
-
-func (s implSyncGetSyncGroupMembersServerCallSend) Send(item SyncGroupMember) error {
-	return s.s.Send(item)
-}
-
-// SyncGetSyncGroupStatsServerStream is the server stream for Sync.GetSyncGroupStats.
-type SyncGetSyncGroupStatsServerStream interface {
-	// SendStream returns the send side of the Sync.GetSyncGroupStats server stream.
-	SendStream() interface {
-		// Send places the item onto the output stream.  Returns errors encountered
-		// while sending.  Blocks if there is no buffer space; will unblock when
-		// buffer space is available.
-		Send(item SyncGroupStats) error
-	}
-}
-
-// SyncGetSyncGroupStatsServerCall represents the context passed to Sync.GetSyncGroupStats.
-type SyncGetSyncGroupStatsServerCall interface {
-	rpc.ServerCall
-	SyncGetSyncGroupStatsServerStream
-}
-
-// SyncGetSyncGroupStatsServerCallStub is a wrapper that converts rpc.StreamServerCall into
-// a typesafe stub that implements SyncGetSyncGroupStatsServerCall.
-type SyncGetSyncGroupStatsServerCallStub struct {
-	rpc.StreamServerCall
-}
-
-// Init initializes SyncGetSyncGroupStatsServerCallStub from rpc.StreamServerCall.
-func (s *SyncGetSyncGroupStatsServerCallStub) Init(call rpc.StreamServerCall) {
-	s.StreamServerCall = call
-}
-
-// SendStream returns the send side of the Sync.GetSyncGroupStats server stream.
-func (s *SyncGetSyncGroupStatsServerCallStub) SendStream() interface {
-	Send(item SyncGroupStats) error
-} {
-	return implSyncGetSyncGroupStatsServerCallSend{s}
-}
-
-type implSyncGetSyncGroupStatsServerCallSend struct {
-	s *SyncGetSyncGroupStatsServerCallStub
-}
-
-func (s implSyncGetSyncGroupStatsServerCallSend) Send(item SyncGroupStats) error {
-	return s.s.Send(item)
-}
diff --git a/x/ref/services/syncbase/sync/vsyncd.go b/x/ref/services/syncbase/sync/vsyncd.go
deleted file mode 100644
index ac25c29..0000000
--- a/x/ref/services/syncbase/sync/vsyncd.go
+++ /dev/null
@@ -1,647 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-// Package vsync provides veyron sync daemon utility functions. Sync
-// daemon serves incoming GetDeltas requests and contacts other peers
-// to get deltas from them. When it receives a GetDeltas request, the
-// incoming generation vector is diffed with the local generation
-// vector, and missing generations are sent back. When it receives
-// log records in response to a GetDeltas request, it replays those
-// log records to get in sync with the sender.
-import (
-	"fmt"
-	"math/rand"
-	"strings"
-	"sync"
-	"time"
-
-	"v.io/v23/context"
-	"v.io/v23/naming"
-	"v.io/v23/rpc"
-	"v.io/v23/security"
-	"v.io/x/lib/vlog"
-	"v.io/x/ref/lib/stats"
-)
-
-var (
-	rng *rand.Rand
-)
-
-// Names of Sync stats entries.
-const (
-	statsKvdbPath       = "vsync/kvdb-path"
-	statsDevId          = "vsync/dev-id"
-	statsNumDagObj      = "vsync/num-dag-objects"
-	statsNumDagNode     = "vsync/num-dag-nodes"
-	statsNumDagTx       = "vsync/num-dag-tx"
-	statsNumDagPrivNode = "vsync/num-dag-privnodes"
-	statsNumSyncGroup   = "vsync/num-syncgroups"
-	statsNumMember      = "vsync/num-members"
-)
-
-// syncd contains the metadata for the sync daemon.
-type syncd struct {
-	// Pointers to metadata structures.
-	log    *iLog
-	devtab *devTable
-	dag    *dag
-	sgtab  *syncGroupTable
-
-	// Filesystem path to store metadata.
-	kvdbPath string
-
-	// Local device id.
-	id DeviceId
-	// Handle to the server to publish names to mount tables during syncgroup create/join.
-	server rpc.Server
-	// Map to track the set of names already published for this sync service.
-	names map[string]struct{}
-	// Relative name of the sync service to be advertised to the syncgroup server.
-	// This is built off the local device id.
-	relName string
-
-	// Authorizer for sync service.
-	auth security.Authorizer
-
-	// RWlock to concurrently access log and device table data structures.
-	lock sync.RWMutex
-
-	// Mutex to serialize syncgroup operations and an going
-	// initiation round. If both "lock" and "sgOp" need to be
-	// acquired, sgOp must be acquired first.
-	sgOp sync.Mutex
-
-	// Channel used by Sync responders to notify the SyncGroup refresher
-	// to fetch from SyncGroupServer updated info for some SyncGroups.
-	// sgRefresh chan *refreshRequest
-
-	// State to coordinate shutting down all spawned goroutines.
-	pending sync.WaitGroup
-	closed  chan struct{}
-
-	// Handlers for goroutine procedures.
-	// hdlGC        *syncGC
-	hdlWatcher   *syncWatcher
-	hdlInitiator *syncInitiator
-
-	// The context used for background activities.
-	ctx *context.T
-}
-
-func (o ObjId) String() string {
-	return string(o)
-}
-
-func (o ObjId) IsValid() bool {
-	return o != NoObjId
-}
-
-func init() {
-	rng = rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
-}
-
-func NewVersion() Version {
-	for {
-		if v := Version(rng.Int63()); v != NoVersion {
-			return v
-		}
-	}
-}
-
-// NewSyncd creates a new syncd instance.
-//
-// Syncd concurrency: syncd initializes three goroutines at
-// startup. The "watcher" thread is responsible for watching the store
-// for changes to its objects. The "initiator" thread is responsible
-// for periodically checking the neighborhood and contacting a peer to
-// obtain changes from that peer. The "gc" thread is responsible for
-// periodically checking if any log records and dag state can be
-// pruned. All these 3 threads perform write operations to the data
-// structures, and synchronize by acquiring a write lock on s.lock. In
-// addition, when syncd receives an incoming RPC, it responds to the
-// request by acquiring a read lock on s.lock. Thus, at any instant in
-// time, either one of the watcher, initiator or gc threads is active,
-// or any number of responders can be active, serving incoming
-// requests. Fairness between these threads follows from
-// sync.RWMutex. The spec says that the writers cannot be starved by
-// the readers but it does not guarantee FIFO. We may have to revisit
-// this in the future.
-func NewSyncd(devid string, syncTick time.Duration, server rpc.Server, ctx *context.T) *syncd {
-	return newSyncdCore(devid, syncTick, server, ctx)
-}
-
-// newSyncdCore is the internal function that creates the Syncd
-// structure and initilizes its thread (goroutines).  It takes a
-// Veyron Store parameter to separate the core of Syncd setup from the
-// external dependency on Veyron Store.
-func newSyncdCore(devid string, syncTick time.Duration,
-	server rpc.Server, ctx *context.T) *syncd {
-	// TODO(kash): Get this to compile
-	return nil
-	// s := &syncd{ctx: ctx}
-
-	// storePath := "/tmp"
-
-	// // If no authorizer is specified, allow access to all Principals.
-	// if s.auth = vflag.NewAuthorizerOrDie(); s.auth == nil {
-	// 	s.auth = allowEveryone{}
-	// }
-
-	// // Bootstrap my own DeviceId.
-	// s.id = DeviceId(devid)
-
-	// // Cache the server to publish names if required.
-	// s.server = server
-	// s.relName = devIDToName(s.id)
-	// s.names = make(map[string]struct{})
-
-	// var err error
-	// // Log init.
-	// if s.log, err = openILog(storePath+"/vsync_ilog.db", s); err != nil {
-	// 	vlog.Fatalf("newSyncd: openILog failed: err %v", err)
-	// }
-
-	// // DevTable init.
-	// if s.devtab, err = openDevTable(storePath+"/vsync_dtab.db", s); err != nil {
-	// 	vlog.Fatalf("newSyncd: openDevTable failed: err %v", err)
-	// }
-
-	// // Dag init.
-	// if s.dag, err = openDAG(storePath + "/vsync_dag.db"); err != nil {
-	// 	vlog.Fatalf("newSyncd: OpenDag failed: err %v", err)
-	// }
-
-	// // SyncGroupTable init.
-	// if s.sgtab, err = openSyncGroupTable(storePath + "/vsync_sgtab.db"); err != nil {
-	// 	vlog.Fatalf("newSyncd: openSyncGroupTable failed: err %v", err)
-	// }
-
-	// // Channel for responders to notify the SyncGroup refresher.
-	// // Give the channel some buffering to ease a burst of responders
-	// // while the refresher is still finishing a previous request.
-	// // s.sgRefresh = make(chan *refreshRequest, 4096)
-
-	// // Channel to propagate close event to all threads.
-	// s.closed = make(chan struct{})
-
-	// s.pending.Add(2)
-
-	// // Get deltas every peerSyncInterval.
-	// s.hdlInitiator = newInitiator(s, syncTick)
-	// go s.hdlInitiator.contactPeers()
-
-	// // // Garbage collect every garbageCollectInterval.
-	// // s.hdlGC = newGC(s)
-	// // go s.hdlGC.garbageCollect()
-
-	// // Start a watcher thread that will get updates from local store.
-	// s.hdlWatcher = newWatcher(s)
-	// go s.hdlWatcher.watchStore()
-
-	// // Start a thread to refresh SyncGroup info from SyncGroupServer.
-	// // go s.syncGroupRefresher()
-
-	// // Server stats.
-	// stats.NewString(statsKvdbPath).Set(s.kvdbPath)
-	// stats.NewString(statsDevId).Set(string(s.id))
-
-	// return s
-}
-
-// devIDToName converts a device ID to its relative name.
-func devIDToName(id DeviceId) string {
-	return naming.Join([]string{"global", "vsync", string(id)}...)
-}
-
-// nameToDevId extracts the DeviceId from a name.
-func (s *syncd) nameToDevId(name string) DeviceId {
-	parts := strings.Split(name, "/")
-	return DeviceId(parts[len(parts)-1])
-}
-
-// Close cleans up syncd state.
-func (s *syncd) Close() {
-	close(s.closed)
-	s.pending.Wait()
-
-	stats.Delete(statsKvdbPath)
-	stats.Delete(statsDevId)
-
-	// TODO(hpucha): close without flushing.
-}
-
-// isSyncClosing returns true if Close() was called i.e. the "closed" channel is closed.
-func (s *syncd) isSyncClosing() bool {
-	select {
-	case <-s.closed:
-		return true
-	default:
-		return false
-	}
-}
-
-// GetDeltas responds to the incoming request from a client by sending missing generations to the client.
-func (s *syncd) GetDeltas(call SyncGetDeltasServerCall, in map[ObjId]GenVector,
-	syncGroups map[ObjId]GroupIdSet, clientID DeviceId) (map[ObjId]GenVector, error) {
-
-	vlog.VI(1).Infof("GetDeltas:: Received vector %v from client %s", in, clientID)
-
-	// Handle misconfiguration: the client cannot have the same ID as this node.
-	if clientID == s.id {
-		vlog.VI(1).Infof("GetDeltas:: impostor alert: client ID %s is the same as mine %s", clientID, s.id)
-		return nil, fmt.Errorf("impostor: cannot be %s, for this node is %s", clientID, s.id)
-	}
-
-	out := make(map[ObjId]GenVector)
-	for sr, gvIn := range in {
-		gvOut, gens, gensInfo, err := s.prepareGensToReply(call, sr, syncGroups[sr], gvIn)
-		if err != nil {
-			vlog.Errorf("GetDeltas:: prepareGensToReply for sr %s failed with err %v",
-				sr.String(), err)
-			continue
-		}
-
-		for pos, v := range gens {
-			gen := gensInfo[pos]
-			var count uint64
-			for i := SeqNum(0); i <= gen.MaxSeqNum; i++ {
-				count++
-				rec, err := s.getLogRec(v.devID, v.srID, v.genID, i)
-				if err != nil {
-					vlog.Fatalf("GetDeltas:: Couldn't get log record %s %s %d %d, err %v",
-						v.devID, v.srID.String(), v.genID, i, err)
-				}
-				vlog.VI(1).Infof("Sending log record %v", rec)
-				if err := call.SendStream().Send(*rec); err != nil {
-					vlog.Errorf("GetDeltas:: Couldn't send stream err: %v", err)
-					return nil, err
-				}
-			}
-			if count != gen.Count {
-				vlog.Fatalf("GetDeltas:: GenMetadata has incorrect log records for generation %s %s %d %v",
-					v.devID, v.srID.String(), v.genID, gen)
-			}
-		}
-		out[sr] = gvOut
-	}
-
-	// Notify the SyncGroup Refresher about the SyncGroup IDs associated
-	// with the SyncRoots in the incoming request from the client.
-	// Note: the initial slice capacity is only a lower-bound.
-	// Note: by using the SyncRoots from the incoming "in" map and all
-	// all SyncGroup IDs for each SyncRoot, the refresh is eager because
-	// it does not filter out SyncRoots and SyncGroup IDs by the Permissions.
-	// sgIDs := make([]syncgroup.Id, 0, len(in))
-	// for sr := range in {
-	//	for _, id := range syncGroups[sr] {
-	//		sgIDs = append(sgIDs, id)
-	//	}
-	// }
-	// s.notifySyncGroupRefresher(devIDToName(clientID), sgIDs)
-
-	return out, nil
-}
-
-// // updateDeviceInfo updates the remote device's information based on
-// // the incoming GetDeltas request.
-// func (s *syncd) updateDeviceInfo(ClientID DeviceId, In GenVector) error {
-//	s.lock.Lock()
-//	defer s.lock.Unlock()
-
-// // Note that the incoming client generation vector cannot be
-// // used for garbage collection. We can only garbage collect
-// // based on the generations we receive from other
-// // devices. Receiving a set of generations assures that all
-// // updates branching from those generations are also received
-// // and hence generations present on all devices can be
-// // GC'ed. This function sends generations to other devices and
-// // hence does not use the generation vector for GC.
-// //
-// // TODO(hpucha): Cache the client's incoming generation vector
-// // to assist in tracking missing generations and hence next
-// // peer to contact.
-//	if !s.devtab.hasDevInfo(ClientID) {
-//		if err := s.devtab.addDevice(ClientID); err != nil {
-//			return err
-//		}
-//	}
-//	return nil
-// }
-
-// prepareGensToReply verifies if the requestor has access to the
-// requested syncroot. If allowed, it processes the incoming
-// generation vector and returns the metadata of all the missing
-// generations between the incoming and the local generation vector
-// for that syncroot.
-func (s *syncd) prepareGensToReply(call rpc.ServerCall, srid ObjId, sgVec GroupIdSet, In GenVector) (GenVector, []*genOrder, []*genMetadata, error) {
-	s.lock.RLock()
-	defer s.lock.RUnlock()
-
-	// Respond to the caller if any of the syncgroups
-	// corresponding to the syncroot allow access.
-	var allowedErr error
-	// for _, sg := range sgVec {
-	// 	// Check permissions for the syncgroup.
-	// 	sgData, err := s.sgtab.getSyncGroupByID(sg)
-	// 	if err != nil {
-	// 		return GenVector{}, nil, nil, err
-	// 	}
-	// 	if vlog.V(2) {
-	// 		b, _ := ctx.RemoteBlessings().ForContext(ctx)
-	// 		vlog.Infof("prepareGensToReply:: matching acl %v, remote names %v", sgData.SrvInfo.Config.Permissions, b)
-	// 	}
-	// 	// TODO(ashankar): Consider changing TaggedPermissionsAuthorizer so that it doesn't return an error -
-	// 	// instead of an error, it just locks down access completely. The error condition is really
-	// 	// really rare and doing so will make for shorter code?
-	// 	auth, err := access.TaggedPermissionsAuthorizer(sgData.SrvInfo.Config.Permissions, access.TypicalTagType())
-	// 	if err != nil {
-	// 		vlog.Errorf("unable to create authorizer: %v", err)
-	// 		allowedErr = fmt.Errorf("server error: authorization policy misconfigured")
-	// 	} else if allowedErr = auth.Authorize(ctx); allowedErr == nil {
-	// 		break
-	// 	}
-	// }
-	if allowedErr != nil {
-		return GenVector{}, nil, nil, allowedErr
-	}
-
-	// // TODO(hpucha): Need only for GC.
-	// if err := s.updateDeviceInfo(ClientID, gvIn); err != nil {
-	//	vlog.Fatalf("GetDeltas:: updateDeviceInfo failed with err %v", err)
-	// }
-
-	// Get local generation vector.
-	out, err := s.devtab.getGenVec(s.id, srid)
-	if err != nil {
-		return GenVector{}, nil, nil, err
-	}
-
-	// Diff the two generation vectors.
-	gens, err := s.devtab.diffGenVectors(out, In, srid)
-	if err != nil {
-		return GenVector{}, nil, nil, err
-	}
-
-	// Get the metadata for all the generations in the reply.
-	gensInfo := make([]*genMetadata, len(gens))
-	for pos, v := range gens {
-		gen, err := s.log.getGenMetadata(v.devID, v.srID, v.genID)
-		if err != nil || gen.Count <= 0 {
-			return GenVector{}, nil, nil, err
-		}
-		gensInfo[pos] = gen
-	}
-
-	return out, gens, gensInfo, nil
-}
-
-// getLogRec gets the log record for a given generation and lsn.
-func (s *syncd) getLogRec(dev DeviceId, srid ObjId, gen GenId, lsn SeqNum) (*LogRec, error) {
-	s.lock.RLock()
-	defer s.lock.RUnlock()
-	return s.log.getLogRec(dev, srid, gen, lsn)
-}
-
-// // GetSyncGroupStats gives the client high-level information on all SyncGroups.
-// // The streamed information is returned sorted by SyncGroup name.
-// func (s *syncd) GetSyncGroupStats(ctx SyncGetSyncGroupStatsContext) error {
-// 	// Get a snapshot of the SyncGroup names.
-// 	// The names are a copy, OK to unlock here.
-// 	s.lock.RLock()
-// 	names, err := s.sgtab.getAllSyncGroupNames()
-// 	s.lock.RUnlock()
-
-// 	if err != nil {
-// 		vlog.Errorf("GetSyncGroupStats:: cannot get SyncGroup names: %v", err)
-// 		return err
-// 	}
-// 	if len(names) == 0 {
-// 		return nil
-// 	}
-
-// 	// Send the SyncGroup stats in the stream in sorted order.
-// 	// Skip SyncGroups that got deleted in the meanwhile.
-// 	sort.Strings(names)
-// 	for _, name := range names {
-// 		// The SyncGroup object is a copy, OK to unlock here.
-// 		s.lock.RLock()
-// 		sg, err := s.sgtab.getSyncGroupByName(name)
-// 		s.lock.RUnlock()
-
-// 		if err != nil {
-// 			continue // skip deleted SyncGroup
-// 		}
-
-// 		stats := SyncGroupStats{
-// 			Name:       name,
-// 			SGObjId:      sg.SrvInfo.SGObjId,
-// 			Path:       sg.LocalPath,
-// 			RootObjId:    ObjId(sg.SrvInfo.RootObjId),
-// 			NumJoiners: uint32(len(sg.SrvInfo.Joiners)),
-// 		}
-
-// 		if err = ctx.SendStream().Send(stats); err != nil {
-// 			vlog.Errorf("GetSyncGroupStats:: cannot send stream data: %v", err)
-// 			return err
-// 		}
-// 	}
-
-// 	return nil
-// }
-
-// // GetSyncGroupMembers gives the client information on SyncGroup members.
-// // If SyncGroup names are specified, only their member information is sent.
-// // Otherwise information on members from all SyncGroups is sent.
-// func (s *syncd) GetSyncGroupMembers(ctx SyncGetSyncGroupMembersContext, sgNames []string) error {
-
-// 	// If SyncGroup names are given, fetch their IDs.
-// 	// Also get a snapshot of the SyncGroup members.
-// 	wantedGroupIds := make(map[syncgroup.Id]bool)
-
-// 	s.lock.RLock()
-// 	for _, name := range sgNames {
-// 		sgid, err := s.sgtab.getSyncGroupID(name)
-// 		if err != nil {
-// 			s.lock.RUnlock()
-// 			vlog.Errorf("GetSyncGroupMembers:: invalid SyncGroup %s: %v", name, err)
-// 			return err
-// 		}
-// 		wantedGroupIds[sgid] = true
-// 	}
-
-// 	memberMap, err := s.sgtab.getMembers() // The map is a copy, OK to unlock here.
-// 	s.lock.RUnlock()
-
-// 	if err != nil {
-// 		vlog.Errorf("GetSyncGroupMembers:: cannot get members: %v", err)
-// 		return err
-// 	}
-// 	if len(memberMap) == 0 {
-// 		return nil
-// 	}
-
-// 	// Send the member information in the stream in sorted order.
-// 	// Skip members that left in the meanwhile.
-// 	// If SyncGroup names are given, only return members in these groups.
-// 	members := make([]string, 0, len(memberMap))
-// 	for member := range memberMap {
-// 		members = append(members, member)
-// 	}
-// 	sort.Strings(members)
-
-// 	for _, member := range members {
-// 		// The member info points to in-memory data; cannot unlock
-// 		// while accessing that info.
-// 		s.lock.RLock()
-// 		info, err := s.sgtab.getMemberInfo(member)
-// 		if err != nil {
-// 			s.lock.RUnlock()
-// 			continue // skip member no longer there
-// 		}
-
-// 		replyData := make([]SyncGroupMember, 0, len(info.gids))
-// 		for sgid, meta := range info.gids {
-// 			if len(wantedGroupIds) > 0 && !wantedGroupIds[sgid] {
-// 				continue // skip unwanted SyncGroups
-// 			}
-
-// 			data := SyncGroupMember{
-// 				Name:     member,
-// 				SGObjId:    sgid,
-// 				Metadata: meta.metaData,
-// 			}
-// 			replyData = append(replyData, data)
-// 		}
-
-// 		s.lock.RUnlock()
-
-// 		for _, data := range replyData {
-// 			if err = ctx.SendStream().Send(data); err != nil {
-// 				vlog.Errorf("GetSyncGroupMembers:: cannot send stream data: %v", err)
-// 				return err
-// 			}
-// 		}
-// 	}
-
-// 	return nil
-// }
-
-// Dump writes to the Sync log internal information used for debugging.
-func (s *syncd) Dump(call rpc.ServerCall) error {
-	s.lock.RLock()
-	defer s.lock.RUnlock()
-
-	vlog.VI(1).Infof("Dump: ==== BEGIN ====")
-	s.devtab.dump()
-	// s.sgtab.dump()
-	s.log.dump()
-	s.dag.dump()
-	vlog.VI(1).Infof("Dump: ==== END ====")
-	return nil
-}
-
-// GetDeviceStats gives the client information on devices that have synchronized
-// with the server.
-func (s *syncd) GetDeviceStats(call SyncGetDeviceStatsServerCall) error {
-	// Get a snapshot of the device IDs.
-	// The device IDs are copies, OK to unlock here.
-	s.lock.RLock()
-	myID := s.id
-	devIDs, err := s.devtab.getDeviceIds()
-	s.lock.RUnlock()
-
-	if err != nil {
-		vlog.Errorf("GetDeviceStats:: cannot get device IDs: %v", err)
-		return err
-	}
-
-	// Send the device stats in the stream.
-	for _, id := range devIDs {
-		// The device info is a copy, OK to unlock here.
-		s.lock.RLock()
-		info, err := s.devtab.getDevInfo(id)
-		s.lock.RUnlock()
-
-		if err != nil {
-			continue // skip unknown devices
-		}
-
-		stats := DeviceStats{
-			DevId:      id,
-			LastSync:   info.Ts.Unix(),
-			GenVectors: info.Vectors,
-			IsSelf:     id == myID,
-		}
-
-		if err = call.SendStream().Send(stats); err != nil {
-			vlog.Errorf("GetDeviceStats:: cannot send stream data: %v", err)
-			return err
-		}
-	}
-
-	return nil
-}
-
-// GetObjectHistory gives the client the mutation history of a store object.
-// It also returns the object version that is at the "head" of the graph (DAG).
-func (s *syncd) GetObjectHistory(call SyncGetObjectHistoryServerCall, oid ObjId) (Version, error) {
-	// Get the object version numbers in reverse chronological order,
-	// starting from the "head" version back through its ancestors.
-	// Also get the DAG parents of each object version.
-	var versions []Version
-	parents := make(map[Version][]Version)
-
-	s.lock.RLock()
-	head, err := s.dag.getHead(oid)
-	if err != nil {
-		// For now ignore objects that do not yet have a "head" version.
-		// They are either not part of any SyncGroup, or this Sync server
-		// has not finished catching up (Sync or Watch operations).
-		vlog.Errorf("GetObjectHistory:: unknown object %v: %v", oid, err)
-		s.lock.RUnlock()
-		return NoVersion, err
-	}
-
-	s.dag.ancestorIter(oid, []Version{head},
-		func(oid ObjId, v Version, node *dagNode) error {
-			versions = append(versions, v)
-			parents[v] = node.Parents
-			return nil
-		})
-	s.lock.RUnlock()
-
-	// Stream the object log records in the given order.
-	for _, v := range versions {
-		// The log record is a copy, OK to unlock here.
-		s.lock.RLock()
-		rec, err := s.hdlInitiator.getLogRec(oid, v)
-		s.lock.RUnlock()
-
-		if err != nil {
-			vlog.Errorf("GetObjectHistory:: cannot get log record for %v:%v: %v", oid, v, err)
-			return NoVersion, err
-		}
-
-		// To give the client a full DAG, send the parents as seen in
-		// the DAG node instead of the ones in the log record.
-		// Note: DAG nodes contain the full sets of parents whereas
-		// log records may have subsets due to the different types of
-		// records: NodeRec-Parents + LinkRec-Parent == DAG-Parents.
-		rec.Parents = parents[v]
-
-		if err = call.SendStream().Send(*rec); err != nil {
-			vlog.Errorf("GetObjectHistory:: cannot send stream data: %v", err)
-			return NoVersion, err
-		}
-	}
-
-	return head, nil
-}
-
-// allowEveryone implements the authorization policy of ... allowing every principal.
-type allowEveryone struct{}
-
-func (allowEveryone) Authorize(security.Call) error { return nil }
diff --git a/x/ref/services/syncbase/sync/watcher.go b/x/ref/services/syncbase/sync/watcher.go
deleted file mode 100644
index f9799ae..0000000
--- a/x/ref/services/syncbase/sync/watcher.go
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vsync
-
-// Veyron Sync hook to the Store Watch API.  When applications update objects
-// in the local Veyron Store, Sync learns about these via the Watch API stream
-// of object mutations.  In turn, this Sync watcher thread updates the DAG and
-// ILog records to track the object change histories.
-
-import (
-	"time"
-
-	"v.io/v23/context"
-	"v.io/x/lib/vlog"
-)
-
-var (
-	// watchRetryDelay is how long the watcher waits before calling the Watch() RPC again
-	// after the previous call fails.
-	watchRetryDelay = 2 * time.Second
-	// streamRetryDelay is how long the watcher waits before creating a new Watch stream
-	// after the previous stream ends.
-	streamRetryDelay = 1 * time.Second
-)
-
-// syncWatcher contains the metadata for the Sync Watcher thread.
-type syncWatcher struct {
-	// syncd is a pointer to the Syncd instance owning this Watcher.
-	syncd *syncd
-}
-
-// newWatcher creates a new Sync Watcher instance attached to the given Syncd instance.
-func newWatcher(syncd *syncd) *syncWatcher {
-	return &syncWatcher{syncd: syncd}
-}
-
-func (w *syncWatcher) watchStreamCanceler(cancel context.CancelFunc, done <-chan struct{}) {
-	select {
-	case <-w.syncd.closed:
-		vlog.VI(1).Info("watchStreamCanceler: Syncd channel closed, cancel stream and exit")
-		cancel()
-	case <-done:
-		vlog.VI(1).Info("watchStreamCanceler: done, exit")
-	}
-}
-
-// watchStore consumes change records obtained by watching the store
-// for updates and applies them to the sync DBs.
-func (w *syncWatcher) watchStore() {
-	defer w.syncd.pending.Done()
-
-	// 	// If no Veyron store is configured, there is nothing to watch.
-	// 	if w.syncd.store == nil {
-	// 		vlog.VI(1).Info("watchStore: Veyron store not configured; skipping the watcher")
-	// 		return
-	// 	}
-
-	// 	// Get a Watch stream, process it, repeat till end-of-life.
-	// 	for {
-	// 		ctx, _ := vtrace.SetNewTrace(w.syncd.ctx)
-	// 		ctx, cancel := context.WithCancel(ctx)
-	// 		stream := w.getWatchStream(ctx)
-	// 		if stream == nil {
-	// 			return // Syncd is exiting.
-	// 		}
-
-	// 		// Spawn a goroutine to detect the Syncd "closed" channel and cancel the RPC stream
-	// 		// to unblock the watcher.  The "done" channel lets the watcher terminate the goroutine.
-	// 		go w.watchStreamCanceler(cancel, ctx.Done())
-
-	// 		// Process the stream of Watch updates until it closes (similar to "tail -f").
-	// 		w.processWatchStream(stream)
-
-	// 		if w.syncd.isSyncClosing() {
-	// 			return // Syncd is exiting.
-	// 		}
-
-	// 		stream.Finish()
-	// 		cancel()
-
-	// 		// TODO(rdaoud): we need a rate-limiter here in case the stream closes too quickly.
-	// 		// If the stream stays up long enough, no need to sleep before getting a new one.
-	// 		time.Sleep(streamRetryDelay)
-	// 	}
-}
-
-// // getWatchStream() returns a Watch API stream and handles retries if the Watch() call fails.
-// // If the stream is nil, it means Syncd is exiting cleanly and the caller should terminate.
-// func (w *syncWatcher) getWatchStream(ctx *context.T) watch.GlobWatcherWatchGlobCall {
-// 	for {
-// 		w.syncd.lock.RLock()
-// 		resmark := w.syncd.devtab.head.Resmark
-// 		w.syncd.lock.RUnlock()
-
-// 		req := raw.Request{}
-// 		if resmark != nil {
-// 			req.ResumeMarker = resmark
-// 		}
-
-// 		stream, err := w.syncd.store.Watch(ctx, req)
-// 		if err == nil {
-// 			return stream
-// 		}
-
-// 		if w.syncd.isSyncClosing() {
-// 			vlog.VI(1).Info("getWatchStream: exiting, Syncd closed its channel: ", err)
-// 			return nil
-// 		}
-
-// 		vlog.VI(1).Info("getWatchStream: call to Watch() failed, retrying a bit later: ", err)
-// 		time.Sleep(watchRetryDelay)
-// 	}
-// }
-
-// // processWatchStream reads the stream of Watch updates and applies the object mutations.
-// // Ideally this call does not return as the stream should be un-ending (like "tail -f").
-// // If the stream is closed, distinguish between the cases of end-of-stream vs Syncd canceling
-// // the stream to trigger a clean exit.
-// func (w *syncWatcher) processWatchStream(call watch.GlobWatcherWatchGlobCall) {
-// 	var txChanges []*types.Change = nil
-// 	var syncTime int64
-
-// 	stream := call.RecvStream()
-// 	for stream.Advance() {
-// 		change := stream.Value()
-
-// 		// Timestamp of the start of a batch of changes arriving at the Sync server.
-// 		if txChanges == nil {
-// 			syncTime = time.Now().UnixNano()
-// 		}
-// 		txChanges = append(txChanges, &change)
-
-// 		// Process the transaction when its last change record is received.
-// 		if !change.Continued {
-// 			if err := w.processTransaction(txChanges, syncTime); err != nil {
-// 				// TODO(rdaoud): don't crash, instead add retry policies to attempt some degree of
-// 				// self-healing from a data corruption where feasible, otherwise quarantine this device
-// 				// from the cluster and stop Syncd to avoid propagating data corruptions.
-// 				vlog.Fatal("processWatchStream:", err)
-// 			}
-// 			txChanges = nil
-// 		}
-// 	}
-
-// 	err := stream.Err()
-// 	if err == nil {
-// 		err = io.EOF
-// 	}
-
-// 	if w.syncd.isSyncClosing() {
-// 		vlog.VI(1).Info("processWatchStream: exiting, Syncd closed its channel: ", err)
-// 	} else {
-// 		vlog.VI(1).Info("processWatchStream: RPC stream error, re-issue Watch(): ", err)
-// 	}
-// }
-
-// // matchSyncRoot returns the SyncRoot (SyncGroup root ObjId) that matches the given object path.
-// // It traverses the object path looking if any of the IDs along the way are SyncRoots.
-// // If no match is found, it returns the invalid ID (zero).
-// // Note: the path IDs given by Watch are ordered: object, parent, grand-parent, etc.., root.
-// // As a result, this function returns the narrowest matching SyncGroup root ObjId.
-// func matchSyncRoot(objPathIDs []ObjId, sgObjIds map[ObjId]struct{}) ObjId {
-// 	for _, oid := range objPathIDs {
-// 		if _, ok := sgObjIds[oid]; ok {
-// 			return oid
-// 		}
-// 	}
-// 	return ObjId{}
-// }
-
-// // processTransaction applies a batch of changes (object mutations) that form a single transaction
-// // received from the Watch API.  The function grabs the write-lock to access the Log and DAG DBs.
-// func (w *syncWatcher) processTransaction(txBatch []*types.Change, syncTime int64) error {
-// 	w.syncd.lock.Lock()
-// 	defer w.syncd.lock.Unlock()
-
-// 	count := uint32(len(txBatch))
-// 	if count == 0 {
-// 		return nil
-// 	}
-
-// 	// Get the current set of SyncGroup Root ObjIds and use it to determine for each object
-// 	// the SyncGroup it belongs to, if any.
-// 	sgRootObjIds, err := w.syncd.sgtab.getAllSyncRoots()
-// 	if err != nil {
-// 		return err
-// 	}
-
-// 	// If the batch has more than one mutation, start a DAG transaction for it.
-// 	txID := NoTxId
-// 	if count > 1 {
-// 		txID = w.syncd.dag.addNodeTxStart(txID)
-// 		if txID == NoTxId {
-// 			return fmt.Errorf("failed to generate transaction ID")
-// 		}
-// 	}
-
-// 	vlog.VI(1).Infof("processTransaction: ready to process a batch of %d changes for transaction %v", count, txID)
-
-// 	for _, ch := range txBatch {
-// 		rc, ok := ch.Value.(*raw.RawChange)
-// 		if !ok {
-// 			return fmt.Errorf("invalid change value, not a raw change: %#v", ch)
-// 		}
-
-// 		mu := &rc.Mutation
-// 		isDeleted := ch.State == types.DoesNotExist
-
-// 		// Determine the SyncGroup root ObjId that matches this object, if any.
-// 		rootObjId := matchSyncRoot(rc.PathIDs, sgRootObjIds)
-
-// 		if rootObjId.IsValid() {
-// 			// The object matches a SyncGroup: process its watch record to track it in the Sync logs.
-// 			// All LogValues belonging to the same transaction get the same timestamp.
-// 			val := &LogValue{
-// 				Mutation: *mu,
-// 				SyncTime: syncTime,
-// 				Delete:   isDeleted,
-// 				TxId:     txID,
-// 				TxCount:  count,
-// 			}
-// 			vlog.VI(2).Infof("processTransaction: processing record %v, Tx %v", val, txID)
-
-// 			err = w.syncd.log.processWatchRecord(mu.Id, mu.Version, mu.PriorVersion, val, rootObjId)
-// 		} else {
-// 			// The object does not match any SyncGroup: stash it in the "private" table to save its info
-// 			// in case it becomes shared in the future, at which time it would be added to the Sync logs.
-// 			if isDeleted {
-// 				err = w.syncd.dag.delPrivNode(mu.Id)
-// 			} else {
-// 				priv := &privNode{Mutation: mu, PathIDs: rc.PathIDs, SyncTime: syncTime, TxId: txID, TxCount: count}
-// 				err = w.syncd.dag.setPrivNode(mu.Id, priv)
-// 			}
-// 		}
-
-// 		if err != nil {
-// 			return fmt.Errorf("cannot process mutation: %#v, mu %#v: %s", ch, mu, err)
-// 		}
-// 	}
-
-// 	// End the DAG transaction if any.
-// 	if txID != NoTxId {
-// 		// Note: if there are no shared objects in the transaction, the addNodeTxEnd() call
-// 		// does not persist the Tx state, it only cleans up the in-memory scaffolding.
-// 		// This is the desired behavior, Sync only needs to track the state of transactions
-// 		// that have at least one shared object.
-// 		if err := w.syncd.dag.addNodeTxEnd(txID, count); err != nil {
-// 			return err
-// 		}
-// 	}
-
-// 	// Update the device table with the new resume marker of the last record.
-// 	w.syncd.devtab.head.Resmark = txBatch[count-1].ResumeMarker
-// 	return nil
-// }