store: replace go errors with verrors

Though this change is not very small, it is just a couple of
rules applied across the code.

Change-Id: I88e0088bb40e5218e24fbe093b2f726342bc607a
diff --git a/services/syncbase/store/invalid_types.go b/services/syncbase/store/invalid_types.go
index 83e73e9..6353a02 100644
--- a/services/syncbase/store/invalid_types.go
+++ b/services/syncbase/store/invalid_types.go
@@ -12,12 +12,12 @@
 
 // Close implements the store.Snapshot interface.
 func (s *InvalidSnapshot) Close() error {
-	return s.Error
+	return WrapError(s.Error)
 }
 
 // Get implements the store.StoreReader interface.
 func (s *InvalidSnapshot) Get(key, valbuf []byte) ([]byte, error) {
-	return valbuf, s.Error
+	return valbuf, WrapError(s.Error)
 }
 
 // Scan implements the store.StoreReader interface.
@@ -48,7 +48,7 @@
 
 // Err implements the store.Stream interface.
 func (s *InvalidStream) Err() error {
-	return s.Error
+	return WrapError(s.Error)
 }
 
 // Cancel implements the store.Stream interface.
@@ -68,7 +68,7 @@
 
 // Get implements the store.StoreReader interface.
 func (tx *InvalidTransaction) Get(key, valbuf []byte) ([]byte, error) {
-	return valbuf, tx.Error
+	return valbuf, WrapError(tx.Error)
 }
 
 // Scan implements the store.StoreReader interface.
@@ -78,20 +78,20 @@
 
 // Put implements the store.StoreWriter interface.
 func (tx *InvalidTransaction) Put(key, value []byte) error {
-	return tx.Error
+	return WrapError(tx.Error)
 }
 
 // Delete implements the store.StoreWriter interface.
 func (tx *InvalidTransaction) Delete(key []byte) error {
-	return tx.Error
+	return WrapError(tx.Error)
 }
 
 // Commit implements the store.Transaction interface.
 func (tx *InvalidTransaction) Commit() error {
-	return tx.Error
+	return WrapError(tx.Error)
 }
 
 // Abort implements the store.Transaction interface.
 func (tx *InvalidTransaction) Abort() error {
-	return tx.Error
+	return WrapError(tx.Error)
 }
diff --git a/services/syncbase/store/leveldb/db.go b/services/syncbase/store/leveldb/db.go
index 5e45bd3..a09c8f1 100644
--- a/services/syncbase/store/leveldb/db.go
+++ b/services/syncbase/store/leveldb/db.go
@@ -11,15 +11,11 @@
 // #include "syncbase_leveldb.h"
 import "C"
 import (
-	"errors"
 	"sync"
 	"unsafe"
 
 	"v.io/syncbase/x/ref/services/syncbase/store"
-)
-
-var (
-	errClosedStore = errors.New("closed store")
+	"v.io/v23/verror"
 )
 
 // db is a wrapper around LevelDB that implements the store.Store interface.
@@ -70,7 +66,7 @@
 	d.mu.Lock()
 	defer d.mu.Unlock()
 	if d.err != nil {
-		return d.err
+		return store.WrapError(d.err)
 	}
 	d.node.close()
 	C.leveldb_close(d.cDb)
@@ -79,7 +75,7 @@
 	d.readOptions = nil
 	C.leveldb_writeoptions_destroy(d.writeOptions)
 	d.writeOptions = nil
-	d.err = errors.New("closed store")
+	d.err = verror.New(verror.ErrCanceled, nil, "closed store")
 	return nil
 }
 
@@ -154,7 +150,7 @@
 	d.mu.RLock()
 	defer d.mu.RUnlock()
 	if d.err != nil {
-		return valbuf, d.err
+		return valbuf, store.WrapError(d.err)
 	}
 	var cError *C.char
 	var valLen C.size_t
@@ -164,7 +160,7 @@
 		return valbuf, err
 	}
 	if val == nil {
-		return valbuf, &store.ErrUnknownKey{Key: string(key)}
+		return valbuf, verror.New(store.ErrUnknownKey, nil, string(key))
 	}
 	defer C.leveldb_free(unsafe.Pointer(val))
 	return store.CopyBytes(valbuf, goBytes(val, valLen)), nil
diff --git a/services/syncbase/store/leveldb/snapshot.go b/services/syncbase/store/leveldb/snapshot.go
index 0d78f03..62dc77f 100644
--- a/services/syncbase/store/leveldb/snapshot.go
+++ b/services/syncbase/store/leveldb/snapshot.go
@@ -7,10 +7,10 @@
 // #include "leveldb/c.h"
 import "C"
 import (
-	"errors"
 	"sync"
 
 	"v.io/syncbase/x/ref/services/syncbase/store"
+	"v.io/v23/verror"
 )
 
 // snapshot is a wrapper around LevelDB snapshot that implements
@@ -49,14 +49,14 @@
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	if s.err != nil {
-		return s.err
+		return store.WrapError(s.err)
 	}
 	s.node.close()
 	C.leveldb_readoptions_destroy(s.cOpts)
 	s.cOpts = nil
 	C.leveldb_release_snapshot(s.d.cDb, s.cSnapshot)
 	s.cSnapshot = nil
-	s.err = errors.New("closed snapshot")
+	s.err = verror.New(verror.ErrCanceled, nil, "closed snapshot")
 	return nil
 }
 
@@ -65,7 +65,7 @@
 	s.mu.RLock()
 	defer s.mu.RUnlock()
 	if s.err != nil {
-		return valbuf, s.err
+		return valbuf, store.WrapError(s.err)
 	}
 	return s.d.getWithOpts(key, valbuf, s.cOpts)
 }
diff --git a/services/syncbase/store/leveldb/stream.go b/services/syncbase/store/leveldb/stream.go
index b528626..ec167c3 100644
--- a/services/syncbase/store/leveldb/stream.go
+++ b/services/syncbase/store/leveldb/stream.go
@@ -9,10 +9,10 @@
 import "C"
 import (
 	"bytes"
-	"errors"
 	"sync"
 
 	"v.io/syncbase/x/ref/services/syncbase/store"
+	"v.io/v23/verror"
 )
 
 // stream is a wrapper around LevelDB iterator that implements
@@ -113,7 +113,7 @@
 func (s *stream) Err() error {
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	return s.err
+	return store.WrapError(s.err)
 }
 
 // Cancel implements the store.Stream interface.
@@ -131,7 +131,7 @@
 		s.key = store.CopyBytes(nil, s.cKey())
 		s.value = store.CopyBytes(nil, s.cVal())
 	}
-	s.err = errors.New("canceled stream")
+	s.err = verror.New(verror.ErrCanceled, nil, "canceled stream")
 	s.destroyLeveldbIter()
 }
 
diff --git a/services/syncbase/store/leveldb/transaction.go b/services/syncbase/store/leveldb/transaction.go
index 64ea397..609e55f 100644
--- a/services/syncbase/store/leveldb/transaction.go
+++ b/services/syncbase/store/leveldb/transaction.go
@@ -7,17 +7,18 @@
 // #include "leveldb/c.h"
 import "C"
 import (
-	"errors"
 	"sync"
 
 	"v.io/syncbase/x/ref/services/syncbase/store"
+	"v.io/v23/verror"
 )
 
 // transaction is a wrapper around LevelDB WriteBatch that implements
 // the store.Transaction interface.
 type transaction struct {
-	node     *resourceNode
+	// mu protects the state of the transaction.
 	mu       sync.Mutex
+	node     *resourceNode
 	d        *db
 	snapshot store.Snapshot
 	batch    *C.leveldb_writebatch_t
@@ -60,7 +61,7 @@
 	tx.mu.Lock()
 	defer tx.mu.Unlock()
 	if tx.err != nil {
-		return valbuf, tx.err
+		return valbuf, store.WrapError(tx.err)
 	}
 	return tx.snapshot.Get(key, valbuf)
 }
@@ -80,7 +81,7 @@
 	tx.mu.Lock()
 	defer tx.mu.Unlock()
 	if tx.err != nil {
-		return tx.err
+		return store.WrapError(tx.err)
 	}
 	cKey, cKeyLen := cSlice(key)
 	cVal, cValLen := cSlice(value)
@@ -93,7 +94,7 @@
 	tx.mu.Lock()
 	defer tx.mu.Unlock()
 	if tx.err != nil {
-		return tx.err
+		return store.WrapError(tx.err)
 	}
 	cKey, cKeyLen := cSlice(key)
 	C.leveldb_writebatch_delete(tx.batch, cKey, cKeyLen)
@@ -105,17 +106,20 @@
 	tx.mu.Lock()
 	defer tx.mu.Unlock()
 	if tx.err != nil {
-		return tx.err
+		return store.WrapError(tx.err)
 	}
 	tx.d.mu.Lock()
 	defer tx.d.mu.Unlock()
 	var cError *C.char
 	C.leveldb_write(tx.d.cDb, tx.cOpts, tx.batch, &cError)
 	if err := goError(cError); err != nil {
-		tx.err = errors.New("already attempted to commit transaction")
+		// Once Commit() has failed with store.ErrConcurrentTransaction, subsequent
+		// ops on the transaction will fail with the following error.
+		tx.err = verror.New(verror.ErrBadState, nil, "already attempted to commit transaction")
+		tx.close()
 		return err
 	}
-	tx.err = errors.New("committed transaction")
+	tx.err = verror.New(verror.ErrBadState, nil, "committed transaction")
 	tx.close()
 	return nil
 }
@@ -124,10 +128,10 @@
 func (tx *transaction) Abort() error {
 	tx.mu.Lock()
 	defer tx.mu.Unlock()
-	if tx.batch == nil {
-		return tx.err
+	if tx.err != nil {
+		return store.WrapError(tx.err)
 	}
-	tx.err = errors.New("aborted transaction")
+	tx.err = verror.New(verror.ErrCanceled, nil, "aborted transaction")
 	tx.close()
 	return nil
 }
diff --git a/services/syncbase/store/leveldb/util.go b/services/syncbase/store/leveldb/util.go
index af08336..dce69bb 100644
--- a/services/syncbase/store/leveldb/util.go
+++ b/services/syncbase/store/leveldb/util.go
@@ -7,9 +7,10 @@
 // #include "leveldb/c.h"
 import "C"
 import (
-	"errors"
 	"reflect"
 	"unsafe"
+
+	"v.io/v23/verror"
 )
 
 // goError copies C error into Go heap and frees C buffer.
@@ -17,7 +18,7 @@
 	if cError == nil {
 		return nil
 	}
-	err := errors.New(C.GoString(cError))
+	err := verror.New(verror.ErrInternal, nil, C.GoString(cError))
 	C.leveldb_free(unsafe.Pointer(cError))
 	return err
 }
diff --git a/services/syncbase/store/memstore/snapshot.go b/services/syncbase/store/memstore/snapshot.go
index c7c016f..a080032 100644
--- a/services/syncbase/store/memstore/snapshot.go
+++ b/services/syncbase/store/memstore/snapshot.go
@@ -5,14 +5,10 @@
 package memstore
 
 import (
-	"errors"
 	"sync"
 
 	"v.io/syncbase/x/ref/services/syncbase/store"
-)
-
-var (
-	errClosedSnapshot = errors.New("closed snapshot")
+	"v.io/v23/verror"
 )
 
 type snapshot struct {
@@ -32,18 +28,14 @@
 	return &snapshot{data: dataCopy}
 }
 
-func (s *snapshot) error() error {
-	return s.err
-}
-
 // Close implements the store.Snapshot interface.
 func (s *snapshot) Close() error {
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	if err := s.error(); err != nil {
-		return err
+	if s.err != nil {
+		return store.WrapError(s.err)
 	}
-	s.err = errClosedSnapshot
+	s.err = verror.New(verror.ErrCanceled, nil, "closed snapshot")
 	return nil
 }
 
@@ -51,12 +43,12 @@
 func (s *snapshot) Get(key, valbuf []byte) ([]byte, error) {
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	if err := s.error(); err != nil {
-		return valbuf, err
+	if s.err != nil {
+		return valbuf, store.WrapError(s.err)
 	}
 	value, ok := s.data[string(key)]
 	if !ok {
-		return valbuf, &store.ErrUnknownKey{Key: string(key)}
+		return valbuf, verror.New(store.ErrUnknownKey, nil, string(key))
 	}
 	return store.CopyBytes(valbuf, value), nil
 }
@@ -65,8 +57,8 @@
 func (s *snapshot) Scan(start, limit []byte) store.Stream {
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	if err := s.error(); err != nil {
-		return &store.InvalidStream{err}
+	if s.err != nil {
+		return &store.InvalidStream{s.err}
 	}
 	return newStream(s, start, limit)
 }
diff --git a/services/syncbase/store/memstore/store.go b/services/syncbase/store/memstore/store.go
index 4aa7a6e..a6b5aee 100644
--- a/services/syncbase/store/memstore/store.go
+++ b/services/syncbase/store/memstore/store.go
@@ -10,11 +10,13 @@
 	"sync"
 
 	"v.io/syncbase/x/ref/services/syncbase/store"
+	"v.io/v23/verror"
 )
 
 type memstore struct {
 	mu   sync.Mutex
 	data map[string][]byte
+	err  error
 	// Most recent sequence number handed out.
 	lastSeq uint64
 	// Value of lastSeq at the time of the most recent commit.
@@ -30,7 +32,12 @@
 
 // Close implements the store.Store interface.
 func (st *memstore) Close() error {
-	// TODO(sadovsky): Make all subsequent method calls and stream advances fail.
+	st.mu.Lock()
+	defer st.mu.Unlock()
+	if st.err != nil {
+		return store.WrapError(st.err)
+	}
+	st.err = verror.New(verror.ErrCanceled, nil, "closed store")
 	return nil
 }
 
@@ -38,9 +45,12 @@
 func (st *memstore) Get(key, valbuf []byte) ([]byte, error) {
 	st.mu.Lock()
 	defer st.mu.Unlock()
+	if st.err != nil {
+		return valbuf, store.WrapError(st.err)
+	}
 	value, ok := st.data[string(key)]
 	if !ok {
-		return valbuf, &store.ErrUnknownKey{Key: string(key)}
+		return valbuf, verror.New(store.ErrUnknownKey, nil, string(key))
 	}
 	return store.CopyBytes(valbuf, value), nil
 }
@@ -49,6 +59,9 @@
 func (st *memstore) Scan(start, limit []byte) store.Stream {
 	st.mu.Lock()
 	defer st.mu.Unlock()
+	if st.err != nil {
+		return &store.InvalidStream{st.err}
+	}
 	return newStream(newSnapshot(st), start, limit)
 }
 
@@ -70,6 +83,9 @@
 func (st *memstore) NewTransaction() store.Transaction {
 	st.mu.Lock()
 	defer st.mu.Unlock()
+	if st.err != nil {
+		return &store.InvalidTransaction{st.err}
+	}
 	st.lastSeq++
 	return newTransaction(st, st.lastSeq)
 }
@@ -78,5 +94,8 @@
 func (st *memstore) NewSnapshot() store.Snapshot {
 	st.mu.Lock()
 	defer st.mu.Unlock()
+	if st.err != nil {
+		return &store.InvalidSnapshot{st.err}
+	}
 	return newSnapshot(st)
 }
diff --git a/services/syncbase/store/memstore/store_test.go b/services/syncbase/store/memstore/store_test.go
index 4211225..357cf20 100644
--- a/services/syncbase/store/memstore/store_test.go
+++ b/services/syncbase/store/memstore/store_test.go
@@ -24,13 +24,13 @@
 	runTest(t, test.RunSnapshotTest)
 }
 
-// TODO(rogulenko): Enable this test once memstore.Close causes memstore to
-// disallow subsequent operations.
-/*
 func TestStoreState(t *testing.T) {
 	runTest(t, test.RunStoreStateTest)
 }
 
+// TODO(rogulenko): Enable this test once memstore.Close closes all downstream
+// resources.
+/*
 func TestClose(t *testing.T) {
 	runTest(t, test.RunCloseTest)
 }
diff --git a/services/syncbase/store/memstore/stream.go b/services/syncbase/store/memstore/stream.go
index f60e27e..09ccfe3 100644
--- a/services/syncbase/store/memstore/stream.go
+++ b/services/syncbase/store/memstore/stream.go
@@ -5,11 +5,11 @@
 package memstore
 
 import (
-	"errors"
 	"sort"
 	"sync"
 
 	"v.io/syncbase/x/ref/services/syncbase/store"
+	"v.io/v23/verror"
 )
 
 type stream struct {
@@ -82,12 +82,15 @@
 func (s *stream) Err() error {
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	return s.err
+	return store.WrapError(s.err)
 }
 
 // Cancel implements the store.Stream interface.
 func (s *stream) Cancel() {
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	s.err = errors.New("canceled stream")
+	if s.err != nil {
+		return
+	}
+	s.err = verror.New(verror.ErrCanceled, nil, "canceled stream")
 }
diff --git a/services/syncbase/store/memstore/transaction.go b/services/syncbase/store/memstore/transaction.go
index 3298ff1..ba14496 100644
--- a/services/syncbase/store/memstore/transaction.go
+++ b/services/syncbase/store/memstore/transaction.go
@@ -5,19 +5,15 @@
 package memstore
 
 import (
-	"errors"
 	"sync"
 	"time"
 
 	"v.io/syncbase/x/ref/services/syncbase/store"
+	"v.io/v23/verror"
 )
 
-var (
-	txnTimeout         = time.Duration(5) * time.Second
-	errExpiredTxn      = errors.New("expired transaction")
-	errCommittedTxn    = errors.New("committed transaction")
-	errAbortedTxn      = errors.New("aborted transaction")
-	errAttemptedCommit = errors.New("already attempted to commit transaction")
+const (
+	txnTimeout = time.Duration(5) * time.Second
 )
 
 type transaction struct {
@@ -53,10 +49,10 @@
 
 func (tx *transaction) error() error {
 	if tx.err != nil {
-		return tx.err
+		return store.WrapError(tx.err)
 	}
 	if tx.expired() {
-		return errExpiredTxn
+		return verror.New(verror.ErrBadState, nil, "expired transaction")
 	}
 	return nil
 }
@@ -117,11 +113,11 @@
 	defer tx.st.mu.Unlock() // note, defer is last-in-first-out
 	if tx.seq <= tx.st.lastCommitSeq {
 		// Once Commit() has failed with store.ErrConcurrentTransaction, subsequent
-		// ops on the transaction will fail with errAttemptedCommit.
-		tx.err = errAttemptedCommit
-		return &store.ErrConcurrentTransaction{}
+		// ops on the transaction will fail with the following error.
+		tx.err = verror.New(verror.ErrBadState, nil, "already attempted to commit transaction")
+		return store.NewErrConcurrentTransaction(nil)
 	}
-	tx.err = errCommittedTxn
+	tx.err = verror.New(verror.ErrBadState, nil, "committed transaction")
 	for k, v := range tx.puts {
 		tx.st.data[k] = v
 	}
@@ -140,6 +136,6 @@
 		return err
 	}
 	tx.sn.Close()
-	tx.err = errAbortedTxn
+	tx.err = verror.New(verror.ErrCanceled, nil, "aborted transaction")
 	return nil
 }
diff --git a/services/syncbase/store/model.go b/services/syncbase/store/model.go
index f772f40..1fcdd55 100644
--- a/services/syncbase/store/model.go
+++ b/services/syncbase/store/model.go
@@ -108,7 +108,6 @@
 
 	// Err returns a non-nil error iff the stream encountered any errors. Err does
 	// not block.
-	// TODO(rogulenko): define an error type.
 	Err() error
 
 	// Cancel notifies the stream provider that it can stop producing elements.
@@ -118,17 +117,3 @@
 	// Cancel causes Advance to subsequently return false. Cancel does not block.
 	Cancel()
 }
-
-type ErrConcurrentTransaction struct{}
-
-func (e *ErrConcurrentTransaction) Error() string {
-	return "concurrent transaction"
-}
-
-type ErrUnknownKey struct {
-	Key string
-}
-
-func (err *ErrUnknownKey) Error() string {
-	return "unknown key: " + err.Key
-}
diff --git a/services/syncbase/store/model.vdl b/services/syncbase/store/model.vdl
new file mode 100644
index 0000000..b8dbb79
--- /dev/null
+++ b/services/syncbase/store/model.vdl
@@ -0,0 +1,14 @@
+// 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 store
+
+error (
+	// ConcurrentTransaction means that the current transaction was not committed
+	// because its read set was invalidated by some other transaction.
+	ConcurrentTransaction() {"en":"Concurrent transaction{:_}"}
+
+	// UnknownKey means the given key does not exist in the store.
+	UnknownKey() {"en":"Unknown key{:_}"}
+)
diff --git a/services/syncbase/store/model.vdl.go b/services/syncbase/store/model.vdl.go
new file mode 100644
index 0000000..12c235c
--- /dev/null
+++ b/services/syncbase/store/model.vdl.go
@@ -0,0 +1,38 @@
+// 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: model.vdl
+
+package store
+
+import (
+	// VDL system imports
+	"v.io/v23/context"
+	"v.io/v23/i18n"
+	"v.io/v23/verror"
+)
+
+var (
+	// ConcurrentTransaction means that the current transaction was not committed
+	// because its read set was invalidated by some other transaction.
+	ErrConcurrentTransaction = verror.Register("v.io/syncbase/x/ref/services/syncbase/store.ConcurrentTransaction", verror.NoRetry, "{1:}{2:} Concurrent transaction{:_}")
+	// UnknownKey means the given key does not exist in the store.
+	ErrUnknownKey = verror.Register("v.io/syncbase/x/ref/services/syncbase/store.UnknownKey", verror.NoRetry, "{1:}{2:} Unknown key{:_}")
+)
+
+func init() {
+	i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrConcurrentTransaction.ID), "{1:}{2:} Concurrent transaction{:_}")
+	i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrUnknownKey.ID), "{1:}{2:} Unknown key{:_}")
+}
+
+// NewErrConcurrentTransaction returns an error with the ErrConcurrentTransaction ID.
+func NewErrConcurrentTransaction(ctx *context.T) error {
+	return verror.New(ErrConcurrentTransaction, ctx)
+}
+
+// NewErrUnknownKey returns an error with the ErrUnknownKey ID.
+func NewErrUnknownKey(ctx *context.T) error {
+	return verror.New(ErrUnknownKey, ctx)
+}
diff --git a/services/syncbase/store/test/snapshot.go b/services/syncbase/store/test/snapshot.go
index 0276fd1..0c65de7 100644
--- a/services/syncbase/store/test/snapshot.go
+++ b/services/syncbase/store/test/snapshot.go
@@ -5,10 +5,10 @@
 package test
 
 import (
-	"strings"
 	"testing"
 
 	"v.io/syncbase/x/ref/services/syncbase/store"
+	"v.io/v23/verror"
 )
 
 // RunSnapshotTest verifies store.Snapshot operations.
@@ -31,15 +31,12 @@
 		t.Fatalf("can't close the snapshot: %v", err)
 	}
 	expectedErr := "closed snapshot"
-	if err := snapshot.Close(); !strings.Contains(err.Error(), expectedErr) {
-		t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-	}
-	if _, err := snapshot.Get(key1, nil); !strings.Contains(err.Error(), expectedErr) {
-		t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-	}
+	verifyError(t, snapshot.Close(), expectedErr, verror.ErrCanceled.ID)
+
+	_, err := snapshot.Get(key1, nil)
+	verifyError(t, err, expectedErr, verror.ErrCanceled.ID)
+
 	s = snapshot.Scan([]byte("a"), []byte("z"))
 	verifyAdvance(t, s, nil, nil)
-	if err := s.Err(); !strings.Contains(err.Error(), expectedErr) {
-		t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-	}
+	verifyError(t, s.Err(), expectedErr, verror.ErrCanceled.ID)
 }
diff --git a/services/syncbase/store/test/store.go b/services/syncbase/store/test/store.go
index 5ce2d9f..0a081fd 100644
--- a/services/syncbase/store/test/store.go
+++ b/services/syncbase/store/test/store.go
@@ -7,10 +7,10 @@
 import (
 	"fmt"
 	"math/rand"
-	"strings"
 	"testing"
 
 	"v.io/syncbase/x/ref/services/syncbase/store"
+	"v.io/v23/verror"
 )
 
 type operation int
@@ -184,31 +184,24 @@
 		t.Fatalf("can't close the store: %v", err)
 	}
 	expectedErr := "closed store"
-	if err := st.Close(); !strings.Contains(err.Error(), expectedErr) {
-		t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-	}
+	verifyError(t, st.Close(), expectedErr, verror.ErrCanceled.ID)
+
 	s = st.Scan([]byte("a"), []byte("z"))
 	verifyAdvance(t, s, nil, nil)
-	if err := s.Err(); !strings.Contains(err.Error(), expectedErr) {
-		t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-	}
+	verifyError(t, s.Err(), expectedErr, verror.ErrCanceled.ID)
+
 	snapshot := st.NewSnapshot()
-	if _, err := snapshot.Get(key1, nil); !strings.Contains(err.Error(), expectedErr) {
-		t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-	}
+	_, err := snapshot.Get(key1, nil)
+	verifyError(t, err, expectedErr, verror.ErrCanceled.ID)
+
 	tx := st.NewTransaction()
-	if _, err := tx.Get(key1, nil); !strings.Contains(err.Error(), expectedErr) {
-		t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-	}
-	if _, err := st.Get(key1, nil); !strings.Contains(err.Error(), expectedErr) {
-		t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-	}
-	if err := st.Put(key1, value1); !strings.Contains(err.Error(), expectedErr) {
-		t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-	}
-	if err := st.Delete(key1); !strings.Contains(err.Error(), expectedErr) {
-		t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-	}
+	_, err = tx.Get(key1, nil)
+	verifyError(t, err, expectedErr, verror.ErrCanceled.ID)
+
+	_, err = st.Get(key1, nil)
+	verifyError(t, err, expectedErr, verror.ErrCanceled.ID)
+	verifyError(t, st.Put(key1, value1), expectedErr, verror.ErrCanceled.ID)
+	verifyError(t, st.Delete(key1), expectedErr, verror.ErrCanceled.ID)
 }
 
 // RunCloseTest verifies that child objects are closed when the parent object is
@@ -235,20 +228,14 @@
 	st.Close()
 
 	for _, stream := range streams {
-		if got, want := stream.Err().Error(), "canceled stream"; !strings.Contains(got, want) {
-			t.Fatalf("unexpected error: got %v, want %v", got, want)
-		}
+		verifyError(t, stream.Err(), "canceled stream", verror.ErrCanceled.ID)
 	}
 	for _, snapshot := range snapshots {
 		_, err := snapshot.Get(key1, nil)
-		if got, want := err.Error(), "closed snapshot"; !strings.Contains(got, want) {
-			t.Fatalf("unexpected error: got %v, want %v", got, want)
-		}
+		verifyError(t, err, "closed snapshot", verror.ErrCanceled.ID)
 	}
 	for _, tx := range transactions {
 		_, err := tx.Get(key1, nil)
-		if got, want := err.Error(), "aborted transaction"; !strings.Contains(got, want) {
-			t.Fatalf("unexpected error: got %v, want %v", got, want)
-		}
+		verifyError(t, err, "aborted transaction", verror.ErrCanceled.ID)
 	}
 }
diff --git a/services/syncbase/store/test/stream.go b/services/syncbase/store/test/stream.go
index 4caeb7c..28e6e32 100644
--- a/services/syncbase/store/test/stream.go
+++ b/services/syncbase/store/test/stream.go
@@ -6,10 +6,10 @@
 
 import (
 	"bytes"
-	"strings"
 	"testing"
 
 	"v.io/syncbase/x/ref/services/syncbase/store"
+	"v.io/v23/verror"
 )
 
 // RunStreamTest verifies store.Stream operations.
@@ -37,7 +37,5 @@
 		}
 	}
 	verifyAdvance(t, s, nil, nil)
-	if !strings.Contains(s.Err().Error(), "canceled stream") {
-		t.Fatalf("unexpected steam error: %v", s.Err())
-	}
+	verifyError(t, s.Err(), "canceled stream", verror.ErrCanceled.ID)
 }
diff --git a/services/syncbase/store/test/transaction.go b/services/syncbase/store/test/transaction.go
index e078e49..70dac8f 100644
--- a/services/syncbase/store/test/transaction.go
+++ b/services/syncbase/store/test/transaction.go
@@ -8,28 +8,28 @@
 	"fmt"
 	"math/rand"
 	"strconv"
-	"strings"
 	"sync"
 	"testing"
 
 	"v.io/syncbase/x/ref/services/syncbase/store"
+	"v.io/v23/verror"
 )
 
 // RunTransactionTest verifies operations that modify the state of a
 // store.Transaction.
 func RunTransactionStateTest(t *testing.T, st store.Store) {
-	abortFunctions := []func(t *testing.T, tx store.Transaction) string{
-		func(t *testing.T, tx store.Transaction) string {
+	abortFunctions := []func(t *testing.T, tx store.Transaction) (string, verror.ID){
+		func(t *testing.T, tx store.Transaction) (string, verror.ID) {
 			if err := tx.Abort(); err != nil {
-				t.Fatalf("can't abort the transaction: %v", err)
+				Fatalf(t, "can't abort the transaction: %v", err)
 			}
-			return "aborted transaction"
+			return "aborted transaction", verror.ErrCanceled.ID
 		},
-		func(t *testing.T, tx store.Transaction) string {
+		func(t *testing.T, tx store.Transaction) (string, verror.ID) {
 			if err := tx.Commit(); err != nil {
-				t.Fatalf("can't commit the transaction: %v", err)
+				Fatalf(t, "can't commit the transaction: %v", err)
 			}
-			return "committed transaction"
+			return "committed transaction", verror.ErrBadState.ID
 		},
 	}
 	for _, fn := range abortFunctions {
@@ -46,27 +46,18 @@
 		verifyAdvance(t, s, nil, nil)
 
 		// Test functions after Abort.
-		expectedErr := fn(t, tx)
-		if err := tx.Abort(); !strings.Contains(err.Error(), expectedErr) {
-			t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-		}
-		if err := tx.Commit(); !strings.Contains(err.Error(), expectedErr) {
-			t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-		}
+		expectedErr, expectedID := fn(t, tx)
+		verifyError(t, tx.Abort(), expectedErr, expectedID)
+		verifyError(t, tx.Commit(), expectedErr, expectedID)
+
 		s = tx.Scan([]byte("a"), []byte("z"))
 		verifyAdvance(t, s, nil, nil)
-		if err := s.Err(); !strings.Contains(err.Error(), expectedErr) {
-			t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-		}
-		if _, err := tx.Get(key1, nil); !strings.Contains(err.Error(), expectedErr) {
-			t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-		}
-		if err := tx.Put(key1, value1); !strings.Contains(err.Error(), expectedErr) {
-			t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-		}
-		if err := tx.Delete(key1); !strings.Contains(err.Error(), expectedErr) {
-			t.Fatalf("unexpected error: got %v, want %v", err, expectedErr)
-		}
+		verifyError(t, s.Err(), expectedErr, expectedID)
+
+		_, err := tx.Get(key1, nil)
+		verifyError(t, err, expectedErr, expectedID)
+		verifyError(t, tx.Put(key1, value1), expectedErr, expectedID)
+		verifyError(t, tx.Delete(key1), expectedErr, expectedID)
 	}
 }
 
diff --git a/services/syncbase/store/test/util.go b/services/syncbase/store/test/util.go
index 3f8ca38..296a678 100644
--- a/services/syncbase/store/test/util.go
+++ b/services/syncbase/store/test/util.go
@@ -6,11 +6,12 @@
 
 import (
 	"bytes"
-	"reflect"
 	"runtime/debug"
+	"strings"
 	"testing"
 
 	"v.io/syncbase/x/ref/services/syncbase/store"
+	"v.io/v23/verror"
 )
 
 // verifyGet verifies that st.Get(key) == value. If value is nil, verifies that
@@ -27,9 +28,7 @@
 		}
 	} else {
 		valbuf, err = st.Get(key, valbuf)
-		if !reflect.DeepEqual(&store.ErrUnknownKey{Key: string(key)}, err) {
-			Fatalf(t, "unexpected get error for key %q: %v", key, err)
-		}
+		verifyError(t, err, string(key), store.ErrUnknownKey.ID)
 		valcopy := []byte("tmp")
 		// Verify that valbuf is not modified if the key is not found.
 		if !bytes.Equal(valbuf, valcopy) {
@@ -62,6 +61,15 @@
 	}
 }
 
+func verifyError(t *testing.T, err error, substr string, errorID verror.ID) {
+	if got := verror.ErrorID(err); got != errorID {
+		Fatalf(t, "unexpected error ID: got %v, want %v", got, errorID)
+	}
+	if !strings.Contains(err.Error(), substr) {
+		Fatalf(t, "unexpected error: got %v, want %v", err, substr)
+	}
+}
+
 func Fatalf(t *testing.T, format string, args ...interface{}) {
 	debug.PrintStack()
 	t.Fatalf(format, args...)
diff --git a/services/syncbase/store/util.go b/services/syncbase/store/util.go
index 82a8350..447c305 100644
--- a/services/syncbase/store/util.go
+++ b/services/syncbase/store/util.go
@@ -4,6 +4,10 @@
 
 package store
 
+import (
+	"v.io/v23/verror"
+)
+
 // TODO(sadovsky): Add retry loop.
 func RunInTransaction(st Store, fn func(st StoreReadWriter) error) error {
 	tx := st.NewTransaction()
@@ -38,3 +42,17 @@
 	copy(dst, src)
 	return dst
 }
+
+// WrapError wraps the given err with a verror. It is a no-op if the err is nil.
+// The returned error contains the current stack trace, but retains the input
+// error's IDAction pair. If the given error is not a verror, the IDAction pair
+// of the returned error will be (ErrUnknown.ID, NoRetry).
+func WrapError(err error) error {
+	if err == nil {
+		return nil
+	}
+	return verror.New(verror.IDAction{
+		verror.ErrorID(err),
+		verror.Action(err),
+	}, nil, err)
+}