From 631e76519eb036ce62d9bbaf0605419ebde866d2 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 23 Mar 2016 22:11:57 -0700 Subject: [PATCH] channeldb: switch to Open/Create methods rather than New MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit includes basic tests for Open/Create. Additionally, rather than relying on btcwallet’s addmgr for encryption/decryption, this package now exposes a simple crypto system interface. --- channeldb/db.go | 127 +++++++++++++++++++++++++++++++------------ channeldb/db_test.go | 38 +++++++++++++ channeldb/error.go | 6 ++ 3 files changed, 135 insertions(+), 36 deletions(-) create mode 100644 channeldb/db_test.go diff --git a/channeldb/db.go b/channeldb/db.go index bd9755022..b58af4653 100644 --- a/channeldb/db.go +++ b/channeldb/db.go @@ -3,12 +3,12 @@ package channeldb import ( "bytes" "encoding/binary" + "fmt" "os" "path/filepath" "sync" "github.com/boltdb/bolt" - "github.com/btcsuite/btcwallet/waddrmgr" ) const ( @@ -16,64 +16,119 @@ const ( ) var ( - endian = binary.BigEndian + // Big endian is the preferred byte order, due to cursor scans over integer + // keys iterating in order. + byteOrder = binary.BigEndian ) var bufPool = &sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } -// Store... -// TODO(roasbeef): CHECKSUMS, REDUNDANCY, etc etc. +// EncryptorDecryptor... +// TODO(roasbeef): ability to rotate EncryptorDecryptor's across DB +type EncryptorDecryptor interface { + Encrypt(in []byte) ([]byte, error) + Decrypt(in []byte) ([]byte, error) + OverheadSize() uint32 +} + +// DB... type DB struct { - // TODO(roasbeef): caching, etc? - addrmgr *waddrmgr.Manager + store *bolt.DB - db *bolt.DB + cryptoSystem EncryptorDecryptor } -// Wipe... -func (d *DB) Wipe() error { - return d.db.Update(func(tx *bolt.Tx) error { - return tx.DeleteBucket(openChannelBucket) - }) -} - -// New... -// TODO(roasbeef): re-visit this dependancy... -func New(dbPath string, addrmgr *waddrmgr.Manager) (*DB, error) { - if _, err := os.Stat(dbPath); err != nil { - if os.IsNotExist(err) { - if err := os.MkdirAll(dbPath, 0700); err != nil { - return nil, err - } - } +// Open opens an existing channeldb created under the passed namespace with +// sensitive data encrypted by the passed EncryptorDecryptor implementation. +// TODO(roasbeef): versioning? +func Open(dbPath string) (*DB, error) { + if !fileExists(dbPath) { + return nil, ErrNoExists } path := filepath.Join(dbPath, dbName) - boltDB, err := bolt.Open(path, 0600, nil) + bdb, err := bolt.Open(path, 0600, nil) if err != nil { return nil, err } - return &DB{addrmgr, boltDB}, nil -} - -// Open... -// TODO(roasbeef): create+open, ditch New, fixes above -func Open() *DB { - return nil + return &DB{store: bdb}, nil } // Create... -func Create() *DB { - return nil +func Create(dbPath string) (*DB, error) { + bdb, err := createChannelDB(dbPath) + if err != nil { + return nil, err + } + + return &DB{store: bdb}, nil +} + +// RegisterCryptoSystem... +func (d *DB) RegisterCryptoSystem(ed EncryptorDecryptor) { + d.cryptoSystem = ed +} + +// Wipe... +func (d *DB) Wipe() error { + return d.store.Update(func(tx *bolt.Tx) error { + // TODO(roasbee): delete all other top-level buckets. + return tx.DeleteBucket(openChannelBucket) + }) } // Close... func (d *DB) Close() error { - return d.db.Close() + return d.store.Close() +} + +// createChannelDB... +func createChannelDB(dbPath string) (*bolt.DB, error) { + if !fileExists(dbPath) { + if err := os.MkdirAll(dbPath, 0700); err != nil { + return nil, err + } + } + + path := filepath.Join(dbPath, dbName) + bdb, err := bolt.Open(path, 0600, nil) + if err != nil { + return nil, err + } + + err = bdb.Update(func(tx *bolt.Tx) error { + if _, err := tx.CreateBucket(openChannelBucket); err != nil { + return err + } + + if _, err := tx.CreateBucket(closedChannelBucket); err != nil { + return err + } + + if _, err := tx.CreateBucket(channelLogBucket); err != nil { + return err + } + + return nil + }) + if err != nil { + return nil, fmt.Errorf("unable to create new channeldb") + } + + return bdb, nil +} + +// fileExists... +func fileExists(path string) bool { + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + return false + } + } + + return true } -// TODO(roasbeef): SetCryptoSystem method... -// * don't have waddrmgr up before.. diff --git a/channeldb/db_test.go b/channeldb/db_test.go new file mode 100644 index 000000000..f20538565 --- /dev/null +++ b/channeldb/db_test.go @@ -0,0 +1,38 @@ +package channeldb + +import ( + "io/ioutil" + "os" + "testing" +) + +func TestOpenNotCreated(t *testing.T) { + if _, err := Open("path doesn't exist"); err != ErrNoExists { + t.Fatalf("channeldb Open should fail due to non-existant dir") + } +} + +func TestCreateThenOpen(t *testing.T) { + // First, create a temporary directory to be used for the duration of + // this test. + tempDirName, err := ioutil.TempDir("", "channeldb") + if err != nil { + t.Fatalf("unable to create temp dir: %v") + } + defer os.RemoveAll(tempDirName) + + // Next, create channeldb for the first time. + cdb, err := Create(tempDirName) + if err != nil { + t.Fatalf("unable to create channeldb: %v", err) + } + if err := cdb.Close(); err != nil { + t.Fatalf("unable to close channeldb: %v", err) + } + + // Open should now succeed as the cdb was created above. + cdb, err = Open(tempDirName) + if err != nil { + t.Fatalf("unable to open channeldb: %v", err) + } +} diff --git a/channeldb/error.go b/channeldb/error.go index d03b34062..f5524526c 100644 --- a/channeldb/error.go +++ b/channeldb/error.go @@ -1 +1,7 @@ package channeldb + +import "fmt" + +var ( + ErrNoExists = fmt.Errorf("channel db has not yet been created") +)