Merge pull request #14 from filecoin-project/feat/repo
Repo interface and memory repo
This commit is contained in:
commit
0e42705e9c
1
go.mod
1
go.mod
@ -50,6 +50,7 @@ require (
|
|||||||
go.uber.org/dig v1.7.0 // indirect
|
go.uber.org/dig v1.7.0 // indirect
|
||||||
go.uber.org/fx v1.9.0
|
go.uber.org/fx v1.9.0
|
||||||
go.uber.org/goleak v0.10.0 // indirect
|
go.uber.org/goleak v0.10.0 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522
|
||||||
gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8
|
gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8
|
||||||
launchpad.net/gocheck v0.0.0-20140225173054-000000000087 // indirect
|
launchpad.net/gocheck v0.0.0-20140225173054-000000000087 // indirect
|
||||||
)
|
)
|
||||||
|
45
node/repo/interface.go
Normal file
45
node/repo/interface.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ipfs/go-datastore"
|
||||||
|
"github.com/libp2p/go-libp2p-core/crypto"
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-lotus/node/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNoAPIEndpoint = xerrors.New("no API Endpoint set")
|
||||||
|
ErrRepoAlreadyLocked = xerrors.New("repo is already locked")
|
||||||
|
ErrClosedRepo = xerrors.New("repo is no longer open")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Repo interface {
|
||||||
|
// APIEndpoint returns multiaddress for communication with Lotus API
|
||||||
|
APIEndpoint() (multiaddr.Multiaddr, error)
|
||||||
|
|
||||||
|
// Lock locks the repo for exclusive use.
|
||||||
|
Lock() (LockedRepo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type LockedRepo interface {
|
||||||
|
// Close closes repo and removes lock.
|
||||||
|
Close() error
|
||||||
|
|
||||||
|
// Returns datastore defined in this repo.
|
||||||
|
Datastore() (datastore.Datastore, error)
|
||||||
|
|
||||||
|
// Returns config in this repo
|
||||||
|
Config() (*config.Root, error)
|
||||||
|
|
||||||
|
// Libp2pIdentity returns private key for libp2p indentity
|
||||||
|
Libp2pIdentity() (crypto.PrivKey, error)
|
||||||
|
|
||||||
|
// SetAPIEndpoint sets the endpoint of the current API
|
||||||
|
// so it can be read by API clients
|
||||||
|
SetAPIEndpoint(multiaddr.Multiaddr) error
|
||||||
|
|
||||||
|
// Wallet returns store of private keys for Filecoin transactions
|
||||||
|
Wallet() (interface{}, error)
|
||||||
|
}
|
166
node/repo/memrepo.go
Normal file
166
node/repo/memrepo.go
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-datastore"
|
||||||
|
dssync "github.com/ipfs/go-datastore/sync"
|
||||||
|
"github.com/libp2p/go-libp2p-core/crypto"
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-lotus/node/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MemRepo struct {
|
||||||
|
api struct {
|
||||||
|
sync.Mutex
|
||||||
|
ma multiaddr.Multiaddr
|
||||||
|
}
|
||||||
|
|
||||||
|
repoLock chan struct{}
|
||||||
|
token *byte
|
||||||
|
|
||||||
|
datastore datastore.Datastore
|
||||||
|
configF func() *config.Root
|
||||||
|
libp2pKey crypto.PrivKey
|
||||||
|
wallet interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type lockedMemRepo struct {
|
||||||
|
mem *MemRepo
|
||||||
|
sync.RWMutex
|
||||||
|
|
||||||
|
token *byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Repo = &MemRepo{}
|
||||||
|
|
||||||
|
// MemRepoOptions contains options for memory repo
|
||||||
|
type MemRepoOptions struct {
|
||||||
|
Ds datastore.Datastore
|
||||||
|
ConfigF func() *config.Root
|
||||||
|
Libp2pKey crypto.PrivKey
|
||||||
|
Wallet interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMemory creates new memory based repo with provided options.
|
||||||
|
// opts can be nil, it will be replaced with defaults.
|
||||||
|
// Any field in opts can be nil, they will be replaced by defaults.
|
||||||
|
func NewMemory(opts *MemRepoOptions) *MemRepo {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &MemRepoOptions{}
|
||||||
|
}
|
||||||
|
if opts.ConfigF == nil {
|
||||||
|
opts.ConfigF = config.Default
|
||||||
|
}
|
||||||
|
if opts.Ds == nil {
|
||||||
|
opts.Ds = dssync.MutexWrap(datastore.NewMapDatastore())
|
||||||
|
}
|
||||||
|
if opts.Libp2pKey == nil {
|
||||||
|
pk, _, err := crypto.GenerateEd25519Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
opts.Libp2pKey = pk
|
||||||
|
}
|
||||||
|
|
||||||
|
return &MemRepo{
|
||||||
|
repoLock: make(chan struct{}, 1),
|
||||||
|
|
||||||
|
datastore: opts.Ds,
|
||||||
|
configF: opts.ConfigF,
|
||||||
|
libp2pKey: opts.Libp2pKey,
|
||||||
|
wallet: opts.Wallet,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mem *MemRepo) APIEndpoint() (multiaddr.Multiaddr, error) {
|
||||||
|
mem.api.Lock()
|
||||||
|
defer mem.api.Unlock()
|
||||||
|
if mem.api.ma == nil {
|
||||||
|
return nil, ErrNoAPIEndpoint
|
||||||
|
}
|
||||||
|
return mem.api.ma, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mem *MemRepo) Lock() (LockedRepo, error) {
|
||||||
|
select {
|
||||||
|
case mem.repoLock <- struct{}{}:
|
||||||
|
default:
|
||||||
|
return nil, ErrRepoAlreadyLocked
|
||||||
|
}
|
||||||
|
mem.token = new(byte)
|
||||||
|
|
||||||
|
return &lockedMemRepo{
|
||||||
|
mem: mem,
|
||||||
|
token: mem.token,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lmem *lockedMemRepo) checkToken() error {
|
||||||
|
lmem.RLock()
|
||||||
|
defer lmem.RUnlock()
|
||||||
|
if lmem.mem.token != lmem.token {
|
||||||
|
return ErrClosedRepo
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lmem *lockedMemRepo) Close() error {
|
||||||
|
if err := lmem.checkToken(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lmem.Lock()
|
||||||
|
defer lmem.Unlock()
|
||||||
|
|
||||||
|
if lmem.mem.token != lmem.token {
|
||||||
|
return ErrClosedRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
lmem.mem.token = nil
|
||||||
|
lmem.mem.api.Lock()
|
||||||
|
lmem.mem.api.ma = nil
|
||||||
|
lmem.mem.api.Unlock()
|
||||||
|
<-lmem.mem.repoLock // unlock
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lmem *lockedMemRepo) Datastore() (datastore.Datastore, error) {
|
||||||
|
if err := lmem.checkToken(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return lmem.mem.datastore, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lmem *lockedMemRepo) Config() (*config.Root, error) {
|
||||||
|
if err := lmem.checkToken(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return lmem.mem.configF(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lmem *lockedMemRepo) Libp2pIdentity() (crypto.PrivKey, error) {
|
||||||
|
if err := lmem.checkToken(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return lmem.mem.libp2pKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lmem *lockedMemRepo) SetAPIEndpoint(ma multiaddr.Multiaddr) error {
|
||||||
|
if err := lmem.checkToken(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lmem.mem.api.Lock()
|
||||||
|
lmem.mem.api.ma = ma
|
||||||
|
lmem.mem.api.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lmem *lockedMemRepo) Wallet() (interface{}, error) {
|
||||||
|
if err := lmem.checkToken(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return lmem.mem.wallet, nil
|
||||||
|
}
|
57
node/repo/memrepo_test.go
Normal file
57
node/repo/memrepo_test.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMemRepo(t *testing.T) {
|
||||||
|
repo := NewMemory(nil)
|
||||||
|
apima, err := repo.APIEndpoint()
|
||||||
|
if assert.Error(t, err) {
|
||||||
|
assert.Equal(t, ErrNoAPIEndpoint, err)
|
||||||
|
}
|
||||||
|
assert.Nil(t, apima, "with no api endpoint, return should be nil")
|
||||||
|
|
||||||
|
lrepo, err := repo.Lock()
|
||||||
|
assert.NoError(t, err, "should be able to lock once")
|
||||||
|
assert.NotNil(t, lrepo, "locked repo shouldn't be nil")
|
||||||
|
|
||||||
|
{
|
||||||
|
lrepo2, err := repo.Lock()
|
||||||
|
if assert.Error(t, err) {
|
||||||
|
assert.Equal(t, ErrRepoAlreadyLocked, err)
|
||||||
|
}
|
||||||
|
assert.Nil(t, lrepo2, "with locked repo errors, nil should be returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = lrepo.Close()
|
||||||
|
assert.NoError(t, err, "should be able to unlock")
|
||||||
|
|
||||||
|
lrepo, err = repo.Lock()
|
||||||
|
assert.NoError(t, err, "should be able to relock")
|
||||||
|
assert.NotNil(t, lrepo, "locked repo shouldn't be nil")
|
||||||
|
|
||||||
|
ma, err := multiaddr.NewMultiaddr("/ip4/127.0.0.1/tcp/43244")
|
||||||
|
assert.NoError(t, err, "creating multiaddr shouldn't error")
|
||||||
|
|
||||||
|
err = lrepo.SetAPIEndpoint(ma)
|
||||||
|
assert.NoError(t, err, "setting multiaddr shouldn't error")
|
||||||
|
|
||||||
|
apima, err = repo.APIEndpoint()
|
||||||
|
assert.NoError(t, err, "setting multiaddr shouldn't error")
|
||||||
|
assert.Equal(t, ma, apima, "returned API multiaddr should be the same")
|
||||||
|
|
||||||
|
err = lrepo.Close()
|
||||||
|
assert.NoError(t, err, "should be able to close")
|
||||||
|
|
||||||
|
apima, err = repo.APIEndpoint()
|
||||||
|
|
||||||
|
if assert.Error(t, err) {
|
||||||
|
assert.Equal(t, ErrNoAPIEndpoint, err, "after closing repo, api should be nil")
|
||||||
|
}
|
||||||
|
assert.Nil(t, apima, "with closed repo, apima should be set back to nil")
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user