Skip to content

Commit

Permalink
feat(mobile): Add base mobile ipfs node
Browse files Browse the repository at this point in the history
* Split pkg into repo/host/node
  * host build mobile dedicated host (noop for the moment)
  * repo build mobile dedicated repo, exactly the same as ipfs repo but add the
    ability to add some custom configuration
  * node build mobile dedicated node with the particularity to create a mobile host
* the main package can be bind using gomobile to create a ready-to-use
  framework/aar
* individual pkg can be imported instead and be used for specific project
* Add simple makefile that show how to build the thing
  • Loading branch information
gfanton authored and aeddi committed Dec 1, 2019
1 parent a4ba023 commit d91426f
Show file tree
Hide file tree
Showing 10 changed files with 1,482 additions and 0 deletions.
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
BUILD_DIR_IOS ?= ./build/ios
BUILD_DIR_ANDROID ?= ./build/android

GOMOBILES_OPT ?=

build: build.android build.ios

build.android:
@mkdir -p $(BUILD_DIR_IOS)
gomobile bind -v $(GOMOBILES_OPT) -target=android -o $(BUILD_DIR_IOS)/ipfs.aar github.com/berty/gomobile-ipfs

build.ios:
@mkdir -p $(BUILD_DIR_IOS)
gomobile bind -v $(GOMOBILES_OPT) -target=ios -o $(BUILD_DIR_IOS)/ipfs.framework github.com/berty/gomobile-ipfs
23 changes: 23 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module github.com/berty/gomobile-ipfs

go 1.12

require (
berty.tech/go-ipfs-log v0.0.0-20190805145225-165c94541925
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 // indirect
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect
github.com/ipfs/go-datastore v0.0.5
github.com/ipfs/go-filestore v0.0.2
github.com/ipfs/go-ipfs v0.4.21
github.com/ipfs/go-ipfs-config v0.0.3
github.com/libp2p/go-libp2p v0.3.0
github.com/libp2p/go-libp2p-core v0.2.0
github.com/multiformats/go-multiaddr v0.0.4
github.com/spf13/cobra v0.0.5 // indirect
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect
google.golang.org/appengine v1.4.0 // indirect
)

// @HOTFIX
replace github.com/dgraph-io/badger v2.0.0-rc.2+incompatible => github.com/dgraph-io/badger v2.0.0-rc2+incompatible
860 changes: 860 additions & 0 deletions go.sum

Large diffs are not rendered by default.

135 changes: 135 additions & 0 deletions host/host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package host

import (
"context"
"fmt"

p2p "github.com/libp2p/go-libp2p"
p2p_connmgr "github.com/libp2p/go-libp2p-core/connmgr"
p2p_event "github.com/libp2p/go-libp2p-core/event"
p2p_host "github.com/libp2p/go-libp2p-core/host"
p2p_network "github.com/libp2p/go-libp2p-core/network"
p2p_peer "github.com/libp2p/go-libp2p-core/peer"
p2p_pstore "github.com/libp2p/go-libp2p-core/peerstore"
p2p_protocol "github.com/libp2p/go-libp2p-core/protocol"
p2p_config "github.com/libp2p/go-libp2p/config"

ipfs_p2p "github.com/ipfs/go-ipfs/core/node/libp2p"

ma "github.com/multiformats/go-multiaddr"
)

// MobileHost is an host
var _ p2p_host.Host = (*MobileHost)(nil)

type MobileHost struct {
p2p_host.Host
}

func NewMobileHostOption(mcfg *MobileConfig) ipfs_p2p.HostOption {
return func(ctx context.Context, id p2p_peer.ID, ps p2p_pstore.Peerstore, options ...p2p.Option) (p2p_host.Host, error) {
pkey := ps.PrivKey(id)
if pkey == nil {
return nil, fmt.Errorf("missing private key for node ID: %s", id.Pretty())
}

options = append([]p2p.Option{p2p.Identity(pkey), p2p.Peerstore(ps)}, options...)

cfg := &p2p_config.Config{}
if err := cfg.Apply(options...); err != nil {
return nil, err
}

return NewMobileHost(ctx, mcfg, cfg)
}
}

func NewMobileHost(ctx context.Context, _ *MobileConfig, cfg *p2p.Config) (p2p_host.Host, error) {
host, err := cfg.NewNode(ctx)
if err != nil {
return nil, err
}

// @TODO: MobileHost custom config

return &MobileHost{
Host: host,
}, nil
}

// ID returns the (local) peer.ID associated with this Host
func (mh *MobileHost) ID() p2p_peer.ID {
return mh.Host.ID()
}

// Peerstore returns the Host's repository of Peer Addresses and Keys.
func (mh *MobileHost) Peerstore() p2p_pstore.Peerstore {
return mh.Host.Peerstore()
}

// Returns the listen addresses of the Host
func (mh *MobileHost) Addrs() []ma.Multiaddr {
return mh.Host.Addrs()
}

// Networks returns the Network interface of the Host
func (mh *MobileHost) Network() p2p_network.Network {
return mh.Host.Network()
}

// Mux returns the Mux multiplexing incoming streams to protocol handlers
func (mh *MobileHost) Mux() p2p_protocol.Switch {
return mh.Host.Mux()
}

// Connect ensures there is a connection between this host and the peer with
// given peer.ID. Connect will absorb the addresses in pi into its internal
// peerstore. If there is not an active connection, Connect will issue a
// h.Network.Dial, and block until a connection is open, or an error is
// returned. // TODO: Relay + NAT.
func (mh *MobileHost) Connect(ctx context.Context, pi p2p_peer.AddrInfo) error {
return mh.Host.Connect(ctx, pi)
}

// SetStreamHandler sets the protocol handler on the Host's Mux.
// This is equivalent to:
// host.Mux().SetHandler(proto, handler)
// (Threadsafe)
func (mh *MobileHost) SetStreamHandler(pid p2p_protocol.ID, handler p2p_network.StreamHandler) {
mh.Host.SetStreamHandler(pid, handler)
}

// SetStreamHandlerMatch sets the protocol handler on the Host's Mux
// using a matching function for protocol selection.
func (mh *MobileHost) SetStreamHandlerMatch(pid p2p_protocol.ID, m func(string) bool, h p2p_network.StreamHandler) {
mh.Host.SetStreamHandlerMatch(pid, m, h)
}

// RemoveStreamHandler removes a handler on the mux that was set by
// SetStreamHandler
func (mh *MobileHost) RemoveStreamHandler(pid p2p_protocol.ID) {
mh.Host.RemoveStreamHandler(pid)
}

// NewStream opens a new stream to given peer p, and writes a p2p/protocol
// header with given ProtocolID. If there is no connection to p, attempts
// to create one. If ProtocolID is "", writes no header.
// (Threadsafe)
func (mh *MobileHost) NewStream(ctx context.Context, p p2p_peer.ID, pids ...p2p_protocol.ID) (p2p_network.Stream, error) {
return mh.Host.NewStream(ctx, p, pids...)
}

// Close shuts down the host, its Network, and services.
func (mh *MobileHost) Close() error {
return mh.Host.Close()
}

// ConnManager returns this hosts connection manager
func (mh *MobileHost) ConnManager() p2p_connmgr.ConnManager {
return mh.ConnManager()
}

// EventBus returns the hosts eventbus
func (mh *MobileHost) EventBus() p2p_event.Bus {
return mh.Host.EventBus()
}
34 changes: 34 additions & 0 deletions host/host_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package host

import (
"bytes"
"encoding/json"
"fmt"
)

type MobileConfig struct {
}

func FromMap(v map[string]interface{}) (*Config, error) {
buf := new(bytes.Buffer)
if err := json.NewEncoder(buf).Encode(v); err != nil {
return nil, err
}
var conf MobileConfig
if err := json.NewDecoder(buf).Decode(&conf); err != nil {
return nil, fmt.Errorf("failure to decode config: %s", err)
}
return &conf, nil
}

func ToMap(conf *MobileConfig) (map[string]interface{}, error) {
buf := new(bytes.Buffer)
if err := json.NewEncoder(buf).Encode(conf); err != nil {
return nil, err
}
var m map[string]interface{}
if err := json.NewDecoder(buf).Decode(&m); err != nil {
return nil, fmt.Errorf("failure to decode config: %s", err)
}
return m, nil
}
22 changes: 22 additions & 0 deletions mobile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package mobile

import "fmt"

type Node interface {
// Close ipfs node
Close() error
}

type Repo interface {
// Close ipfs node
Close() error
}

func NewNode(r *Repo) (Node, error) {
if !r.IsInitialized() {
return fmt.Errorf("repo not initialized")
}

repo := r.Open()

}
43 changes: 43 additions & 0 deletions node/node.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package node

import (
"context"

mobile_host "github.com/berty/gomobile-ipfs/host"
ipfs_core "github.com/ipfs/go-ipfs/core"
ipfs_repo "github.com/ipfs/go-ipfs/core/repo"
)

// type Node interface {
// // Close ipfs node
// Close() error
// }

type IpfsMobile struct {
ipfsNode *ipfs_core.IpfsNode
}

func (im *IpfsMobile) Close() error {
return im.ipfsNode.Close()
}

func NewNode(ctx context.Context, repo *ipfs_repo.Repo) (*IpfsMobile, error) {
mcfg := mobile_host.NewMobileConfigFromRepo(repo)
cfg := &ipfs_core.BuildCfg{
Online: true,
Permanent: false,
DisableEncryptedConnections: false,
NilRepo: false,
Repo: ipfsrepo,
Host: mobile_host.NewMobileHostOption(mcfg),
}

inode, err := ipfs_core.NewNode(context.Background(), cfg)
if err != nil {
return nil, err
}

return &IpfsMobile{
ipfsNode: inode,
}, nil
}
71 changes: 71 additions & 0 deletions repo/mobile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package repo

import (
"encoding/json"
"fmt"
"io/ioutil"
"path/filepath"

ipfs_config "github.com/ipfs/go-ipfs-config"
ipfs_fsrepo "github.com/ipfs/go-ipfs/core/repo/fsrepo"
)

const mobileFile = "mobile"

func (r *MobileRepo) IsInitialized() bool {
return ipfs_fsrepo.IsInitialized(r.path)
}

func (r *MobileRepo) getPath() string {
repoPath = filepath.Clean(r.path)
return filepath.Join(repoPath, mobileFile)
}

func (r *MobileRepo) SetMobileConfig(tmp map[string]interface{}) (err error) {
// mobilePath := r.getPath()

// var template interface{}
// if err = json.Unmarshal(rawcfg, &template); err != nil {
// return
// }

if mapcfg, ok := template.(map[string]interface{}); ok {
var cfg *ipfs_config.Config

if cfg, err = ipfs_config.FromMap(mapcfg); err == nil {
r.config = cfg
}

return
}

err = fmt.Errorf("unable to cast config to map")
return
}

func (r *MobileRepo) GetMobileConfig() (rawcfg []byte, err error) {
if r.config == nil {
err = fmt.Errorf("no config loaded")
return
}

var mapcfg interface{}

if mapcfg, err = ipfs_config.ToMap(r.config); err != nil {
return
}

rawcfg, err = json.Marshal(mapcfg)
return
}

func (r *MobileRepo) Init() (err error) {
if r.config == nil {
if r.config, err = ipfs_config.Init(ioutil.Discard, 2048); err != nil {
return
}
}

err = ipfs_fsrepo.Init(r.path, r.config)
return
}

0 comments on commit d91426f

Please sign in to comment.