提交 7b3da650 作者: Łukasz Magiera

Working WASM Example

License: MIT
Signed-off-by: 's avatarŁukasz Magiera <magik6k@gmail.com>
上级 4071ebe8
......@@ -53,6 +53,9 @@ include $(dir)/Rules.mk
dir := filestore/pb
include $(dir)/Rules.mk
dir := misc/wasm
include $(dir)/Rules.mk
# -------------------- #
# universal rules #
......
include mk/header.mk
# TODO: target disabling all incompatible plugins
$(d)/ipfs.wasm:
GOOS=js GOARCH=wasm $(GOCC) build -tags="nofuse purego" -ldflags="-X "github.com/ipfs/go-ipfs".CurrentCommit=47e9466ac" -o "$@" "github.com/ipfs/go-ipfs/misc/wasm"
.PHONY: $(d)/ipfs.wasm
$(d)/serve: $(d)/ipfs.wasm
go get github.com/shurcooL/goexec
@echo 'Listening on http://127.0.0.1:8000'
@(cd $(@D) && goexec 'http.ListenAndServe("127.0.0.1:8000", http.FileServer(http.Dir(".")))')
.PHONY: $(d)/serve
include mk/footer.mk
\ No newline at end of file
<!doctype html>
<!--
Copyright 2018 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
<html>
<head>
<meta charset="utf-8">
<title>Go wasm</title>
</head>
<body>
<script src="wasm_exec.js"></script>
<script>
if (!WebAssembly.instantiateStreaming) { // polyfill
WebAssembly.instantiateStreaming = async (resp, importObject) => {
const source = await (await resp).arrayBuffer();
return await WebAssembly.instantiate(source, importObject);
};
}
const go = new Go();
let mod, inst;
WebAssembly.instantiateStreaming(fetch("ipfs.wasm"), go.importObject).then((result) => {
mod = result.module;
inst = result.instance;
document.getElementById("runButton").disabled = false;
});
async function run() {
console.clear();
await go.run(inst);
inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance
}
</script>
<button onClick="run();" id="runButton" disabled>Run</button>
<form id="addForm" action="#">
<input id="toadd" placeholder="text to add" style="width: 50em">
</form>
<div id="results">
<p>-results-</p>
</div>
<div id="peers"></div>
</body>
</html>
\ No newline at end of file
package main
import (
"context"
"encoding/base64"
"fmt"
"github.com/ipfs/go-ipfs/core"
"github.com/ipfs/go-ipfs/core/coreapi"
"github.com/ipfs/go-ipfs/keystore"
"github.com/ipfs/go-ipfs/repo"
"syscall/js"
"time"
ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto"
"gx/ipfs/QmRBaUEQEeFWywfrZJ64QgsmvcqgLSK3VbvGMR2NM2Edpf/go-libp2p"
"gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files"
"gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer"
pstore "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore"
"gx/ipfs/QmcZfkbgwwwH5ZLTQRHkSQBDiDqd3skY2eU6MZRgWuXcse/go-ipfs-config"
logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log"
"gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore"
syncds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync"
p2phost "gx/ipfs/QmfD51tKgJiTMnW9JEiDiPwsCY4mqUoxkhKhBfyW12spTC/go-libp2p-host"
)
func constructPeerHost(ctx context.Context, id peer.ID, ps pstore.Peerstore, options ...libp2p.Option) (p2phost.Host, error) {
pkey := ps.PrivKey(id)
if pkey == nil {
return nil, fmt.Errorf("missing private key for node ID: %s", id.Pretty())
}
options = append(options,
libp2p.Identity(pkey),
libp2p.NoTransports,
libp2p.Transport(NewJsWs),
libp2p.Peerstore(ps),
libp2p.EnableAutoRelay(),
libp2p.EnableRelay())
return libp2p.New(ctx, options...)
}
func main() {
logging.SetDebugLogging()
ctx := context.Background()
sk, pk, err := ci.GenerateKeyPair(ci.RSA, 2048)
if err != nil {
panic(err)
}
id, err := peer.IDFromPublicKey(pk)
if err != nil {
panic(err)
}
kbytes, err := sk.Bytes()
if err != nil {
panic(err)
}
ident := config.Identity{
PeerID: id.Pretty(),
PrivKey: base64.StdEncoding.EncodeToString(kbytes),
}
c := config.Config{
Bootstrap: []string{
"/ip4/127.0.0.1/tcp/4006/ws/ipfs/QmZatNPNW8DnpMRgSuUJjmzc6nETJi21c2quikh4jbmPKk",
"/ip4/51.75.35.194/tcp/4002/ws/ipfs/QmVGX47BzePPqEzpkTwfUJogPZxHcifpSXsGdgyHjtk5t7",
},
}
c.Identity = ident
c.Swarm.DisableNatPortMap = true
r := &repo.Mock{
C: c,
D: syncds.MutexWrap(datastore.NewMapDatastore()),
K: keystore.NewMemKeystore(),
}
ncfg := &core.BuildCfg{
Repo: r,
Permanent: true, // It is temporary way to signify that node is permanent
Online: true,
DisableEncryptedConnections: false,
Host: constructPeerHost,
ExtraOpts: map[string]bool{
"pubsub": false,
"ipnsps": false,
"mplex": false,
},
}
browserNode, err := core.NewNode(ctx, ncfg)
if err != nil {
panic(err)
}
api, err := coreapi.NewCoreAPI(browserNode)
if err != nil {
panic(err)
}
p, err := api.Unixfs().Add(ctx, files.NewBytesFile([]byte("an unique string not to be found anywhere else")))
if err != nil {
panic(err)
}
println(p.String())
js.Global().Get("document").Call("getElementById", "addForm").Set("onsubmit", js.NewCallback(func(args []js.Value) {
s := js.Global().Get("document").Call("getElementById", "toadd").Get("value").String() + "\n"
p, err := api.Unixfs().Add(ctx, files.NewBytesFile([]byte(s)))
if err != nil {
panic(err)
}
pe := js.Global().Get("document").Call("createElement", "p")
pe.Call("appendChild", js.Global().Get("document").Call("createTextNode", p.String()))
results := js.Global().Get("document").Call("getElementById", "results")
results.Call("insertBefore", pe, results.Get("firstChild"))
}))
t := time.Tick(time.Second)
for range t {
peers := js.Global().Get("document").Call("getElementById", "peers")
peers.Set("innerHTML", "")
list, err := api.Swarm().Peers(ctx)
if err != nil {
panic(err)
}
for _, ci := range list {
pe := js.Global().Get("document").Call("createElement", "p")
pe.Call("appendChild", js.Global().Get("document").Call("createTextNode", ci.Address().String()))
peers.Call("appendChild", pe)
}
}
wch := make(chan struct{})
<-wch
}
package main
import (
"context"
"errors"
"fmt"
"gx/ipfs/QmQVUtnrNGtCRkCMpXgpApfzQjc8FDaDVxHqWH8cnZQeh5/go-multiaddr-net"
"io"
"net"
"syscall/js"
"time"
ma "gx/ipfs/QmRKLtwMw131aK7ugC3G7ybpumMz78YrJe5dzneyindvG1/go-multiaddr"
"gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer"
ws "gx/ipfs/QmZJpaENuYSCLnV4gWjrWhN2LMJWgTp3u2b6XZx3C12rNF/go-ws-transport"
"gx/ipfs/Qmb3qartY8DSgRaBA3Go4EEjY1ZbXhCcvmc4orsBKMjgRg/go-libp2p-transport"
tptu "gx/ipfs/Qmc9KUyhx1adPnHX2TBjEWvKej2Gg2kvisAFoQ74UiWYhd/go-libp2p-transport-upgrader"
)
const (
wsConnecting = iota
wsOpen
wsClosing
wsClosed
)
type jsws struct {
Upgrader *tptu.Upgrader
}
type jsconn struct {
ready chan struct{}
raddr ma.Multiaddr
wsock js.Value
readR *io.PipeReader
readW *io.PipeWriter
cb js.Callback
}
func (c *jsconn) Read(b []byte) (n int, err error) {
return c.readR.Read(b)
}
func (c *jsconn) onmessage(value []js.Value) {
u8a := js.Global().Get("Uint8Array").New(value[0].Get("data"))
// there is, very likely, a much, much better way
buf := make([]byte, u8a.Length())
for i := range buf {
buf[i] = byte(u8a.Index(i).Int())
}
if _, err := c.readW.Write(buf); err != nil {
panic(err)
}
}
func (c *jsconn) Write(b []byte) (n int, err error) {
<-c.ready
arr := js.TypedArrayOf(b)
defer arr.Release()
c.wsock.Call("send", arr)
return len(b), err
}
func (c *jsconn) LocalAddr() net.Addr {
a, _ := manet.ToNetAddr(c.LocalMultiaddr())
return a //TODO: probably broken
}
func (c *jsconn) RemoteAddr() net.Addr {
a, _ := manet.ToNetAddr(c.raddr)
return a //TODO: probably broken
}
func (c *jsconn) SetDeadline(t time.Time) error {
return nil
}
func (c *jsconn) SetReadDeadline(t time.Time) error {
return nil
}
func (c *jsconn) SetWriteDeadline(t time.Time) error {
return nil
}
func (c *jsconn) LocalMultiaddr() ma.Multiaddr {
m, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/80/ws")
return m
}
func (c *jsconn) RemoteMultiaddr() ma.Multiaddr {
return c.raddr
}
func (c *jsconn) Close() error {
c.wsock.Call("close")
c.cb.Release()
return nil // todo: errors?
}
func NewJsWs(u *tptu.Upgrader) *jsws {
return &jsws{u}
}
func (t *jsws) CanDial(addr ma.Multiaddr) bool {
return ws.WsFmt.Matches(addr)
}
func (t *jsws) Protocols() []int {
return []int{ws.WsProtocol.Code}
}
func (t *jsws) Proxy() bool {
return false
}
func (t *jsws) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (transport.Conn, error) {
var addr = ""
var err error
ps := raddr.Protocols()
if len(ps) != 3 {
return nil, fmt.Errorf("unexpected protocol count")
}
switch ps[0].Code {
case ma.P_IP6:
addr, err = raddr.ValueForProtocol(ps[0].Code)
if err != nil {
return nil, err
}
addr = fmt.Sprintf("[%s]", addr)
case ma.P_IP4:
addr, err = raddr.ValueForProtocol(ps[0].Code)
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported proto %d", ps[0].Code)
}
switch ps[1].Code {
case ma.P_TCP:
port, err := raddr.ValueForProtocol(ps[1].Code)
if err != nil {
return nil, err
}
addr = fmt.Sprintf("%s:%s", addr, port)
default:
return nil, fmt.Errorf("unsupported proto %d", ps[0].Code)
}
switch ps[2].Code {
case ws.WsProtocol.Code:
addr = "ws://" + addr
default:
return nil, fmt.Errorf("unsupported proto %d", ps[0].Code)
}
println("connecting to " + addr)
wsock := js.Global().Get("WebSocket").New(addr)
rr, rw := io.Pipe()
ready := make(chan struct{})
conn := &jsconn{
ready: ready,
raddr: raddr,
wsock: wsock,
readR: rr,
readW: rw,
}
conn.cb = js.NewCallback(conn.onmessage)
opencb := js.NewCallback(func(args []js.Value) {
println("onopen")
close(conn.ready)
})
go func() {
<-conn.ready
opencb.Release()
}()
var closecb js.Callback
closecb = js.NewCallback(func(args []js.Value) {
println("onclose")
_ = conn.readR.Close()
closecb.Release()
})
wsock.Set("binaryType", "arraybuffer")
wsock.Set("onmessage", conn.cb)
wsock.Set("onopen", opencb)
wsock.Set("onclose", closecb)
return t.Upgrader.UpgradeOutbound(ctx, t, conn, p)
}
func (t *jsws) Listen(laddr ma.Multiaddr) (transport.Listener, error) {
return nil, errors.New("not supported")
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论