mirror of
https://github.com/hoernschen/dendrite.git
synced 2025-07-29 12:42:46 +00:00
Yggdrasil-based P2P demo (#1108)
* Initial work on Yggdrasil demo * Muxing? * Yamux * Updates to yamux * Updates * Comments * Update to use monolith stuff * Update go.mod/go.sum * Set defaults * Tweaks * Update yggdrasil * Update config * MarshalIndent * Change default instance name/port * add -peer switch * gocyclo, for a change * Determinate yamux roles * Fix copyright notices * Remove HTTP API checks as always false, remove unused topic
This commit is contained in:
parent
98cb0705ea
commit
464718c3e6
5 changed files with 491 additions and 0 deletions
201
cmd/dendrite-demo-yggdrasil/main.go
Normal file
201
cmd/dendrite-demo-yggdrasil/main.go
Normal file
|
@ -0,0 +1,201 @@
|
|||
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/appservice"
|
||||
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggconn"
|
||||
"github.com/matrix-org/dendrite/eduserver"
|
||||
"github.com/matrix-org/dendrite/eduserver/cache"
|
||||
"github.com/matrix-org/dendrite/federationsender"
|
||||
"github.com/matrix-org/dendrite/internal"
|
||||
"github.com/matrix-org/dendrite/internal/basecomponent"
|
||||
"github.com/matrix-org/dendrite/internal/config"
|
||||
"github.com/matrix-org/dendrite/internal/setup"
|
||||
"github.com/matrix-org/dendrite/publicroomsapi/storage"
|
||||
"github.com/matrix-org/dendrite/roomserver"
|
||||
"github.com/matrix-org/dendrite/serverkeyapi"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
instanceName = flag.String("name", "dendrite-p2p-ygg", "the name of this P2P demo instance")
|
||||
instancePort = flag.Int("port", 8008, "the port that the client API will listen on")
|
||||
instancePeer = flag.String("peer", "", "an internet Yggdrasil peer to connect to")
|
||||
)
|
||||
|
||||
type yggroundtripper struct {
|
||||
inner *http.Transport
|
||||
}
|
||||
|
||||
func (y *yggroundtripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
req.URL.Scheme = "http"
|
||||
return y.inner.RoundTrip(req)
|
||||
}
|
||||
|
||||
func createFederationClient(
|
||||
base *basecomponent.BaseDendrite, n *yggconn.Node,
|
||||
) *gomatrixserverlib.FederationClient {
|
||||
yggdialer := func(_, address string) (net.Conn, error) {
|
||||
tokens := strings.Split(address, ":")
|
||||
return n.Dial("curve25519", tokens[0])
|
||||
}
|
||||
yggdialerctx := func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return yggdialer(network, address)
|
||||
}
|
||||
tr := &http.Transport{}
|
||||
tr.RegisterProtocol(
|
||||
"matrix", &yggroundtripper{
|
||||
inner: &http.Transport{
|
||||
DialContext: yggdialerctx,
|
||||
},
|
||||
},
|
||||
)
|
||||
return gomatrixserverlib.NewFederationClientWithTransport(
|
||||
base.Cfg.Matrix.ServerName, base.Cfg.Matrix.KeyID, base.Cfg.Matrix.PrivateKey, tr,
|
||||
)
|
||||
}
|
||||
|
||||
// nolint:gocyclo
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
// Build both ends of a HTTP multiplex.
|
||||
httpServer := &http.Server{
|
||||
Addr: ":0",
|
||||
TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){},
|
||||
ReadTimeout: 30 * time.Second,
|
||||
WriteTimeout: 30 * time.Second,
|
||||
IdleTimeout: 60 * time.Second,
|
||||
BaseContext: func(_ net.Listener) context.Context {
|
||||
return context.Background()
|
||||
},
|
||||
}
|
||||
|
||||
ygg, err := yggconn.Setup(*instanceName, *instancePeer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cfg := &config.Dendrite{}
|
||||
cfg.SetDefaults()
|
||||
cfg.Matrix.ServerName = gomatrixserverlib.ServerName(ygg.EncryptionPublicKey())
|
||||
cfg.Matrix.PrivateKey = ygg.SigningPrivateKey()
|
||||
cfg.Matrix.KeyID = gomatrixserverlib.KeyID("ed25519:auto")
|
||||
cfg.Kafka.UseNaffka = true
|
||||
cfg.Kafka.Topics.OutputRoomEvent = "roomserverOutput"
|
||||
cfg.Kafka.Topics.OutputClientData = "clientapiOutput"
|
||||
cfg.Kafka.Topics.OutputTypingEvent = "typingServerOutput"
|
||||
cfg.Database.Account = config.DataSource(fmt.Sprintf("file:%s-account.db", *instanceName))
|
||||
cfg.Database.Device = config.DataSource(fmt.Sprintf("file:%s-device.db", *instanceName))
|
||||
cfg.Database.MediaAPI = config.DataSource(fmt.Sprintf("file:%s-mediaapi.db", *instanceName))
|
||||
cfg.Database.SyncAPI = config.DataSource(fmt.Sprintf("file:%s-syncapi.db", *instanceName))
|
||||
cfg.Database.RoomServer = config.DataSource(fmt.Sprintf("file:%s-roomserver.db", *instanceName))
|
||||
cfg.Database.ServerKey = config.DataSource(fmt.Sprintf("file:%s-serverkey.db", *instanceName))
|
||||
cfg.Database.FederationSender = config.DataSource(fmt.Sprintf("file:%s-federationsender.db", *instanceName))
|
||||
cfg.Database.AppService = config.DataSource(fmt.Sprintf("file:%s-appservice.db", *instanceName))
|
||||
cfg.Database.PublicRoomsAPI = config.DataSource(fmt.Sprintf("file:%s-publicroomsa.db", *instanceName))
|
||||
cfg.Database.Naffka = config.DataSource(fmt.Sprintf("file:%s-naffka.db", *instanceName))
|
||||
if err = cfg.Derive(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
base := basecomponent.NewBaseDendrite(cfg, "Monolith", false)
|
||||
defer base.Close() // nolint: errcheck
|
||||
|
||||
accountDB := base.CreateAccountsDB()
|
||||
deviceDB := base.CreateDeviceDB()
|
||||
federation := createFederationClient(base, ygg)
|
||||
|
||||
serverKeyAPI := serverkeyapi.NewInternalAPI(
|
||||
base.Cfg, federation, base.Caches,
|
||||
)
|
||||
keyRing := serverKeyAPI.KeyRing()
|
||||
|
||||
rsComponent := roomserver.NewInternalAPI(
|
||||
base, keyRing, federation,
|
||||
)
|
||||
rsAPI := rsComponent
|
||||
|
||||
eduInputAPI := eduserver.NewInternalAPI(
|
||||
base, cache.New(), deviceDB,
|
||||
)
|
||||
|
||||
asAPI := appservice.NewInternalAPI(base, accountDB, deviceDB, rsAPI)
|
||||
|
||||
fsAPI := federationsender.NewInternalAPI(
|
||||
base, federation, rsAPI, keyRing,
|
||||
)
|
||||
|
||||
rsComponent.SetFederationSenderAPI(fsAPI)
|
||||
|
||||
eduProducer := producers.NewEDUServerProducer(eduInputAPI)
|
||||
publicRoomsDB, err := storage.NewPublicRoomsServerDatabase(string(base.Cfg.Database.PublicRoomsAPI), base.Cfg.DbProperties(), cfg.Matrix.ServerName)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Panicf("failed to connect to public rooms db")
|
||||
}
|
||||
|
||||
monolith := setup.Monolith{
|
||||
Config: base.Cfg,
|
||||
AccountDB: accountDB,
|
||||
DeviceDB: deviceDB,
|
||||
FedClient: federation,
|
||||
KeyRing: keyRing,
|
||||
KafkaConsumer: base.KafkaConsumer,
|
||||
KafkaProducer: base.KafkaProducer,
|
||||
|
||||
AppserviceAPI: asAPI,
|
||||
EDUInternalAPI: eduInputAPI,
|
||||
EDUProducer: eduProducer,
|
||||
FederationSenderAPI: fsAPI,
|
||||
RoomserverAPI: rsAPI,
|
||||
ServerKeyAPI: serverKeyAPI,
|
||||
|
||||
PublicRoomsDB: publicRoomsDB,
|
||||
}
|
||||
monolith.AddAllPublicRoutes(base.PublicAPIMux)
|
||||
|
||||
internal.SetupHTTPAPI(
|
||||
http.DefaultServeMux,
|
||||
base.PublicAPIMux,
|
||||
base.InternalAPIMux,
|
||||
cfg,
|
||||
base.UseHTTPAPIs,
|
||||
)
|
||||
|
||||
go func() {
|
||||
logrus.Info("Listening on ", ygg.EncryptionPublicKey())
|
||||
logrus.Fatal(httpServer.Serve(ygg))
|
||||
}()
|
||||
go func() {
|
||||
httpBindAddr := fmt.Sprintf(":%d", *instancePort)
|
||||
logrus.Info("Listening on ", httpBindAddr)
|
||||
logrus.Fatal(http.ListenAndServe(httpBindAddr, nil))
|
||||
}()
|
||||
|
||||
select {}
|
||||
}
|
129
cmd/dendrite-demo-yggdrasil/yggconn/node.go
Normal file
129
cmd/dendrite-demo-yggdrasil/yggconn/node.go
Normal file
|
@ -0,0 +1,129 @@
|
|||
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package yggconn
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/libp2p/go-yamux"
|
||||
yggdrasiladmin "github.com/yggdrasil-network/yggdrasil-go/src/admin"
|
||||
yggdrasilconfig "github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||
yggdrasilmulticast "github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
|
||||
|
||||
gologme "github.com/gologme/log"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
core *yggdrasil.Core
|
||||
config *yggdrasilconfig.NodeConfig
|
||||
state *yggdrasilconfig.NodeState
|
||||
admin *yggdrasiladmin.AdminSocket
|
||||
multicast *yggdrasilmulticast.Multicast
|
||||
log *gologme.Logger
|
||||
listener *yggdrasil.Listener
|
||||
dialer *yggdrasil.Dialer
|
||||
sessions sync.Map // string -> yamux.Session
|
||||
incoming chan *yamux.Stream
|
||||
}
|
||||
|
||||
// nolint:gocyclo
|
||||
func Setup(instanceName, instancePeer string) (*Node, error) {
|
||||
n := &Node{
|
||||
core: &yggdrasil.Core{},
|
||||
config: yggdrasilconfig.GenerateConfig(),
|
||||
admin: &yggdrasiladmin.AdminSocket{},
|
||||
multicast: &yggdrasilmulticast.Multicast{},
|
||||
log: gologme.New(os.Stdout, "YGG ", log.Flags()),
|
||||
incoming: make(chan *yamux.Stream),
|
||||
}
|
||||
n.config.AdminListen = fmt.Sprintf("unix://./%s-yggdrasil.sock", instanceName)
|
||||
n.config.MulticastInterfaces = []string{".*"}
|
||||
|
||||
yggfile := fmt.Sprintf("%s-yggdrasil.conf", instanceName)
|
||||
if _, err := os.Stat(yggfile); !os.IsNotExist(err) {
|
||||
yggconf, e := ioutil.ReadFile(yggfile)
|
||||
if e != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(yggconf), &n.config); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
j, err := json.MarshalIndent(n.config, "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if e := ioutil.WriteFile(yggfile, j, 0600); e != nil {
|
||||
n.log.Printf("Couldn't write private key to file '%s': %s\n", yggfile, e)
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
n.log.EnableLevel("error")
|
||||
n.log.EnableLevel("warn")
|
||||
n.log.EnableLevel("info")
|
||||
n.state, err = n.core.Start(n.config, n.log)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if instancePeer != "" {
|
||||
if err = n.core.AddPeer(instancePeer, ""); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
if err = n.admin.Init(n.core, n.state, n.log, nil); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = n.admin.Start(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = n.multicast.Init(n.core, n.state, n.log, nil); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = n.multicast.Start(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
n.admin.SetupAdminHandlers(n.admin)
|
||||
n.multicast.SetupAdminHandlers(n.admin)
|
||||
n.listener, err = n.core.ConnListen()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
n.dialer, err = n.core.ConnDialer()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go n.listenFromYgg()
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (n *Node) EncryptionPublicKey() string {
|
||||
return n.core.EncryptionPublicKey()
|
||||
}
|
||||
|
||||
func (n *Node) SigningPrivateKey() ed25519.PrivateKey {
|
||||
privBytes, _ := hex.DecodeString(n.config.SigningPrivateKey)
|
||||
return ed25519.PrivateKey(privBytes)
|
||||
}
|
115
cmd/dendrite-demo-yggdrasil/yggconn/session.go
Normal file
115
cmd/dendrite-demo-yggdrasil/yggconn/session.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package yggconn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-yamux"
|
||||
)
|
||||
|
||||
func (n *Node) yamuxConfig() *yamux.Config {
|
||||
cfg := yamux.DefaultConfig()
|
||||
cfg.EnableKeepAlive = true
|
||||
cfg.KeepAliveInterval = time.Second * 30
|
||||
cfg.MaxMessageSize = 65535
|
||||
cfg.ReadBufSize = 655350
|
||||
return cfg
|
||||
}
|
||||
|
||||
func (n *Node) listenFromYgg() {
|
||||
for {
|
||||
conn, err := n.listener.Accept()
|
||||
if err != nil {
|
||||
n.log.Println("n.listener.Accept:", err)
|
||||
return
|
||||
}
|
||||
var session *yamux.Session
|
||||
if strings.Compare(n.EncryptionPublicKey(), conn.RemoteAddr().String()) < 0 {
|
||||
session, err = yamux.Client(conn, n.yamuxConfig())
|
||||
} else {
|
||||
session, err = yamux.Server(conn, n.yamuxConfig())
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go n.listenFromYggConn(session)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) listenFromYggConn(session *yamux.Session) {
|
||||
n.sessions.Store(session.RemoteAddr().String(), session)
|
||||
defer n.sessions.Delete(session.RemoteAddr())
|
||||
|
||||
for {
|
||||
st, err := session.AcceptStream()
|
||||
if err != nil {
|
||||
n.log.Println("session.AcceptStream:", err)
|
||||
return
|
||||
}
|
||||
n.incoming <- st
|
||||
}
|
||||
}
|
||||
|
||||
// Implements net.Listener
|
||||
func (n *Node) Accept() (net.Conn, error) {
|
||||
return <-n.incoming, nil
|
||||
}
|
||||
|
||||
// Implements net.Listener
|
||||
func (n *Node) Close() error {
|
||||
return n.listener.Close()
|
||||
}
|
||||
|
||||
// Implements net.Listener
|
||||
func (n *Node) Addr() net.Addr {
|
||||
return n.listener.Addr()
|
||||
}
|
||||
|
||||
// Implements http.Transport.Dial
|
||||
func (n *Node) Dial(network, address string) (net.Conn, error) {
|
||||
return n.DialContext(context.TODO(), network, address)
|
||||
}
|
||||
|
||||
// Implements http.Transport.DialContext
|
||||
func (n *Node) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
s, ok1 := n.sessions.Load(address)
|
||||
session, ok2 := s.(*yamux.Session)
|
||||
if !ok1 || !ok2 {
|
||||
conn, err := n.dialer.DialContext(ctx, network, address)
|
||||
if err != nil {
|
||||
n.log.Println("n.dialer.DialContext:", err)
|
||||
return nil, err
|
||||
}
|
||||
if strings.Compare(n.EncryptionPublicKey(), address) < 0 {
|
||||
session, err = yamux.Client(conn, n.yamuxConfig())
|
||||
} else {
|
||||
session, err = yamux.Server(conn, n.yamuxConfig())
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go n.listenFromYggConn(session)
|
||||
}
|
||||
st, err := session.OpenStream()
|
||||
if err != nil {
|
||||
n.log.Println("session.OpenStream:", err)
|
||||
return nil, err
|
||||
}
|
||||
return st, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue