From 396f0c079fab6c94739e4cde38ff372c20c71643 Mon Sep 17 00:00:00 2001 From: Alexandr Burdiyan Date: Wed, 13 Sep 2023 19:34:02 +0200 Subject: [PATCH] WIP site config --- backend/cmd/mintter-site/main.go | 97 ++++++++++++++------------ backend/config/config.go | 4 +- backend/core/coretest/coretest.go | 4 +- backend/core/coretest/coretest_test.go | 3 +- backend/core/crypto.go | 45 +++--------- backend/core/identity.go | 10 +-- backend/daemon/storage/migrations.go | 6 +- backend/daemon/storage/storage.go | 2 +- backend/lndhub/lndhub_test.go | 6 +- backend/wallet/wallet_test.go | 4 +- 10 files changed, 77 insertions(+), 104 deletions(-) diff --git a/backend/cmd/mintter-site/main.go b/backend/cmd/mintter-site/main.go index 3a139ced31..2537c8b4a6 100644 --- a/backend/cmd/mintter-site/main.go +++ b/backend/cmd/mintter-site/main.go @@ -6,95 +6,102 @@ import ( "errors" "flag" "fmt" + "net/url" "os" "mintter/backend/config" "mintter/backend/core" "mintter/backend/daemon" accounts "mintter/backend/genproto/accounts/v1alpha" - protodaemon "mintter/backend/genproto/daemon/v1alpha" - - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" "github.com/burdiyan/go/mainutil" "github.com/peterbourgon/ff/v3" ) func main() { - const envVarPrefix = "MINTTER_SITE" + const envVarPrefix = "MINTTER" mainutil.Run(func() error { ctx := mainutil.TrapSignals() fs := flag.NewFlagSet("mintter-site", flag.ExitOnError) + fs.Usage = func() { + fmt.Fprintf(fs.Output(), `Usage: %s ADDRESS [flags] - cfg := config.Default() - cfg.P2P.NoListing = true +This program is similar to our main mintterd program in a lot of ways, but has more suitable defaults for running on a server as site. - // We parse flags twice here, once without the config file setting, and then with it. - // This is because we want the config file to be in the repo path, which can be changed - // with flags or env vars. We don't allow setting a config file explicitly, but the repo path - // can change. We need to know the requested repo path in the first place, and then figure out the config file. +It requires one positional argument ADDRESS, which has to be a Web network address this site is supposed to be available at. +The address can be a DNS name, or an IP address, and it has to be a URL with a scheme and port (if applicable). +Examples: + - http://127.0.0.1:42542 + - https://mintter.com + - http://example.com - if err := ff.Parse(fs, os.Args[1:], ff.WithEnvVarPrefix(envVarPrefix)); err != nil { - return err +Flags: +`, fs.Name()) + fs.PrintDefaults() } - if err := cfg.Base.ExpandDataDir(); err != nil { - return err + // TODO(burdiyan): ignore http.port flag because it collides with the ADDRESS positional argument. + cfg := config.Default() + cfg.DataDir = "~/.mintter-site" + cfg.BindFlags(fs) + + if len(os.Args) < 2 { + fs.Usage() + fmt.Fprintln(fs.Output(), "Error: Positional argument ADDRESS is missing.") + os.Exit(2) } - cfgFile, err := config.EnsureConfigFile(cfg.DataDir) + rawURL := os.Args[1] + u, err := url.Parse(rawURL) if err != nil { - return err + return fmt.Errorf("failed to parse address: %w", err) } - if err := ff.Parse(fs, os.Args[1:], - ff.WithEnvVarPrefix(envVarPrefix), - ff.WithConfigFileParser(ff.PlainParser), - ff.WithConfigFile(cfgFile), - ff.WithAllowMissingConfigFile(false), - ); err != nil { - return err + if u.Path != "" { + return fmt.Errorf("address URL must not have a path: %s", rawURL) } - app, err := daemon.Load(ctx, cfg) - if err != nil { - return err + if u.Scheme != "http" && u.Scheme != "https" { + return fmt.Errorf("address URL only supports http or https, got = %s", rawURL) } - const mnemonicWords = 12 - mnemonic, err := core.NewBIP39Mnemonic(mnemonicWords) - if err != nil { + if err := ff.Parse(fs, os.Args[2:], ff.WithEnvVarPrefix(envVarPrefix)); err != nil { return err } - _, err = app.RPC.Daemon.Register(ctx, &protodaemon.RegisterRequest{ - Mnemonic: mnemonic, - Passphrase: "", - }) - stat, ok := status.FromError(err) - if !ok && stat.Code() != codes.AlreadyExists { + if err := cfg.Base.ExpandDataDir(); err != nil { return err } - _, err = app.Storage.Identity().Await(ctx) + dir, err := daemon.InitRepo(cfg.Base.DataDir, nil) if err != nil { return err } - const alias = "Web gateway" - const bio = "Find me at https://www.mintter.com" - acc, err := app.RPC.Accounts.UpdateProfile(ctx, &accounts.Profile{ - Alias: alias, - Bio: bio, - }) + + app, err := daemon.LoadWithStorage(ctx, cfg, dir) if err != nil { return err } - if acc.Profile.Alias != alias || acc.Profile.Bio != bio { - return fmt.Errorf("unexpected alias/bio. %s", acc.Profile.Alias+". "+acc.Profile.Bio) + + if _, ok := dir.Identity().Get(); !ok { + account, err := core.NewKeyPairRandom() + if err != nil { + return fmt.Errorf("failed to generate random account key pair: %w", err) + } + + if err := app.RPC.Daemon.RegisterAccount(ctx, account); err != nil { + return fmt.Errorf("failed to create registration: %w", err) + } + } + + if _, err := app.RPC.Accounts.UpdateProfile(ctx, &accounts.Profile{ + Alias: rawURL + " Hypermedia Site", + }); err != nil { + return fmt.Errorf("failed to update profile: %w", err) } + err = app.Wait() if errors.Is(err, context.Canceled) { return nil diff --git a/backend/config/config.go b/backend/config/config.go index d7aa4151d1..14be0be13f 100644 --- a/backend/config/config.go +++ b/backend/config/config.go @@ -203,8 +203,8 @@ type P2P struct { func (p2p *P2P) BindFlags(fs *flag.FlagSet) { fs.IntVar(&p2p.Port, "p2p.port", p2p.Port, "Port to listen for incoming P2P connections") fs.BoolVar(&p2p.NoRelay, "p2p.no-relay", p2p.NoRelay, "Disable libp2p circuit relay") - fs.Var(newAddrsFlag(p2p.BootstrapPeers, &p2p.BootstrapPeers), "p2p.bootstrap-peers", "Addresses for bootstrap nodes (comma separated)") - fs.Var(newAddrsFlag(p2p.AnnounceAddrs, &p2p.AnnounceAddrs), "p2p.announce-addrs", "Addresses will be announced for this node to be reachable at (comma separated multiaddresses format). overrides no-private-ips") + fs.Var(newAddrsFlag(p2p.BootstrapPeers, &p2p.BootstrapPeers), "p2p.bootstrap-peers", "Multiaddrs for bootstrap nodes (comma separated)") + fs.Var(newAddrsFlag(p2p.AnnounceAddrs, &p2p.AnnounceAddrs), "p2p.announce-addrs", "Multiaddrs this node will announce as being reachable at (comma separated). When set, -p2p.no-private-ips flag is ignored.") fs.BoolVar(&p2p.PublicReachability, "p2p.public-reachability", p2p.PublicReachability, "Force Reachability to public.") fs.Var(newAddrsFlag(p2p.ListenAddrs, &p2p.ListenAddrs), "p2p.listen-addrs", "Addresses to be listen at (comma separated multiaddresses format)") fs.BoolVar(&p2p.NoPrivateIps, "p2p.no-private-ips", p2p.NoPrivateIps, "Not announce local IPs.") diff --git a/backend/core/coretest/coretest.go b/backend/core/coretest/coretest.go index f28e162d81..66204eb2cc 100644 --- a/backend/core/coretest/coretest.go +++ b/backend/core/coretest/coretest.go @@ -53,7 +53,7 @@ func NewTester(name string) Tester { panic(err) } - dev, err := core.NewKeyPair(core.CodecDeviceKey, dpriv.(*crypto.Ed25519PrivateKey)) + dev, err := core.NewKeyPair(dpriv.(*crypto.Ed25519PrivateKey)) if err != nil { panic(err) } @@ -63,7 +63,7 @@ func NewTester(name string) Tester { panic(err) } - acc, err := core.NewKeyPair(core.CodecAccountKey, apriv.(*crypto.Ed25519PrivateKey)) + acc, err := core.NewKeyPair(apriv.(*crypto.Ed25519PrivateKey)) if err != nil { panic(err) } diff --git a/backend/core/coretest/coretest_test.go b/backend/core/coretest/coretest_test.go index 9974d22668..f025bfaa1e 100644 --- a/backend/core/coretest/coretest_test.go +++ b/backend/core/coretest/coretest_test.go @@ -15,7 +15,6 @@ func TestKeys(t *testing.T) { require.NoError(t, err) require.True(t, alice.Device.ID() == pid) - require.True(t, alice.Device.CID().Equals(peer.ToCid(pid))) } func TestEncoding(t *testing.T) { @@ -24,7 +23,7 @@ func TestEncoding(t *testing.T) { data, err := alice.Account.MarshalBinary() require.NoError(t, err) - pk, err := core.ParsePublicKey(core.CodecAccountKey, data) + pk, err := core.ParsePublicKey(data) require.NoError(t, err) require.Equal(t, alice.Account.String(), pk.String()) } diff --git a/backend/core/crypto.go b/backend/core/crypto.go index 6f1323de79..31e847f4e9 100644 --- a/backend/core/crypto.go +++ b/backend/core/crypto.go @@ -11,14 +11,6 @@ import ( "github.com/ipfs/go-cid" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" - "github.com/multiformats/go-multihash" -) - -// Multicodecs. -const ( - CodecDeviceKey = cid.Libp2pKey - // TODO: need to register this codec withing the multicodecs repo table. - CodecAccountKey = 1091161161 ) // Ensure interface implementations. @@ -72,15 +64,14 @@ type KeyID = peer.ID // PublicKey is the public part of a KeyPair. type PublicKey struct { - k crypto.PubKey - id KeyID - codec uint64 + k crypto.PubKey + id KeyID abbrev uint64 } // NewPublicKey creates a new public key from an existing Ed25519 public key. -func NewPublicKey(codec uint64, pub crypto.PubKey) (pk PublicKey, err error) { +func NewPublicKey(pub crypto.PubKey) (pk PublicKey, err error) { _, ok := pub.(*crypto.Ed25519PublicKey) if !ok { return pk, fmt.Errorf("only Ed25519 public keys are supported, but got %T", pub) @@ -104,7 +95,6 @@ func NewPublicKey(codec uint64, pub crypto.PubKey) (pk PublicKey, err error) { return PublicKey{ k: pub, id: pid, - codec: codec, abbrev: *(*uint64)(unsafe.Pointer(&b)), }, nil } @@ -122,11 +112,11 @@ func PublicKeyFromCID(c cid.Cid) (pk PublicKey, err error) { return pk, err } - return NewPublicKey(c.Prefix().Codec, pub.(*crypto.Ed25519PublicKey)) + return NewPublicKey(pub.(*crypto.Ed25519PublicKey)) } // ParsePublicKey parses existing libp2p-encoded key material. -func ParsePublicKey(codec uint64, data []byte) (pk PublicKey, err error) { +func ParsePublicKey(data []byte) (pk PublicKey, err error) { pub, err := crypto.UnmarshalPublicKey(data) if err != nil { return pk, err @@ -136,7 +126,7 @@ func ParsePublicKey(codec uint64, data []byte) (pk PublicKey, err error) { return pk, fmt.Errorf("only ed25519 keys are supported") } - return NewPublicKey(codec, pub.(*crypto.Ed25519PublicKey)) + return NewPublicKey(pub.(*crypto.Ed25519PublicKey)) } // Abbrev returns the abbreviated form of the public key, @@ -152,16 +142,6 @@ func (pk PublicKey) PeerID() peer.ID { return pk.id } -// CID returns CID representation of the public key. -func (pk PublicKey) CID() cid.Cid { - mh, err := multihash.Cast([]byte(pk.id)) - if err != nil { - panic(err) - } - - return cid.NewCidV1(pk.codec, mh) -} - // String creates string representation of the public key. func (pk PublicKey) String() string { return pk.Principal().String() @@ -172,11 +152,6 @@ func (pk PublicKey) Principal() Principal { return PrincipalFromPubKey(pk.k) } -// Codec returns multicodec of the public key. -func (pk PublicKey) Codec() uint64 { - return pk.codec -} - // Verify implements Verifier. func (pk PublicKey) Verify(data []byte, s Signature) error { return s.verify(pk.k, data) @@ -205,24 +180,24 @@ type KeyPair struct { } // NewKeyPairRandom creates a new random KeyPair with a given multicodec prefix. -func NewKeyPairRandom(codec uint64) (kp KeyPair, err error) { +func NewKeyPairRandom() (kp KeyPair, err error) { priv, _, err := crypto.GenerateEd25519Key(rand.Reader) if err != nil { return kp, fmt.Errorf("failed to generate device private key: %w", err) } - return NewKeyPair(codec, priv.(*crypto.Ed25519PrivateKey)) + return NewKeyPair(priv.(*crypto.Ed25519PrivateKey)) } // NewKeyPair creates a new KeyPair with a given multicodec prefix from an existing instance // of the private key. At the moment only Ed25519 keys are supported. -func NewKeyPair(codec uint64, priv crypto.PrivKey) (kp KeyPair, err error) { +func NewKeyPair(priv crypto.PrivKey) (kp KeyPair, err error) { _, ok := priv.(*crypto.Ed25519PrivateKey) if !ok { return kp, fmt.Errorf("only ed25519 keys are supported") } - pub, err := NewPublicKey(codec, priv.GetPublic().(*crypto.Ed25519PublicKey)) + pub, err := NewPublicKey(priv.GetPublic().(*crypto.Ed25519PublicKey)) if err != nil { return kp, err } diff --git a/backend/core/identity.go b/backend/core/identity.go index 16bfaaa6ca..297807e26b 100644 --- a/backend/core/identity.go +++ b/backend/core/identity.go @@ -16,14 +16,6 @@ type Identity struct { } func NewIdentity(account PublicKey, device KeyPair) Identity { - if account.Codec() != CodecAccountKey { - panic("not account key") - } - - if device.Codec() != CodecDeviceKey { - panic("not device key") - } - return Identity{ account: account, device: device, @@ -69,7 +61,7 @@ func AccountFromSeed(rand []byte) (KeyPair, error) { return KeyPair{}, err } - return NewKeyPair(CodecAccountKey, priv.(*crypto.Ed25519PrivateKey)) + return NewKeyPair(priv.(*crypto.Ed25519PrivateKey)) } // NewBIP39Mnemonic creates a new random BIP-39 compatible mnemonic words. diff --git a/backend/daemon/storage/migrations.go b/backend/daemon/storage/migrations.go index 102e6f137d..f9a954d430 100644 --- a/backend/daemon/storage/migrations.go +++ b/backend/daemon/storage/migrations.go @@ -99,7 +99,7 @@ func (d *Dir) init() (currentVersion string, err error) { } if d.device.Wrapped() == nil { - kp, err := core.NewKeyPairRandom(core.CodecDeviceKey) + kp, err := core.NewKeyPairRandom() if err != nil { return "", fmt.Errorf("failed to generate random device key: %w", err) } @@ -212,7 +212,7 @@ func (d *Dir) maybeLoadAccountKey() error { return err } - account, err := core.NewPublicKey(core.CodecAccountKey, pub) + account, err := core.NewPublicKey(pub) if err != nil { return err } @@ -283,5 +283,5 @@ func loadDeviceKeyFromFile(dir string) (kp core.KeyPair, err error) { return kp, fmt.Errorf("failed to unmarshal private key for device: %w", err) } - return core.NewKeyPair(core.CodecDeviceKey, pk) + return core.NewKeyPair(pk) } diff --git a/backend/daemon/storage/storage.go b/backend/daemon/storage/storage.go index e8f7547342..15777e1976 100644 --- a/backend/daemon/storage/storage.go +++ b/backend/daemon/storage/storage.go @@ -41,7 +41,7 @@ func NewWithDeviceKey(path string, log *zap.Logger, pk crypto.PrivKey) (*Dir, er return nil, err } - kp, err := core.NewKeyPair(core.CodecDeviceKey, pk) + kp, err := core.NewKeyPair(pk) if err != nil { return nil, err } diff --git a/backend/lndhub/lndhub_test.go b/backend/lndhub/lndhub_test.go index 69f397e832..331a9c4ef7 100644 --- a/backend/lndhub/lndhub_test.go +++ b/backend/lndhub/lndhub_test.go @@ -45,17 +45,17 @@ func TestCreate(t *testing.T) { defer cancel() identity := future.New[core.Identity]() lndHubClient := NewClient(context.Background(), &http.Client{}, pool, identity.ReadOnly, mintterDomain, lnaddressDomain) - keypair, err := core.NewKeyPairRandom(core.CodecDeviceKey) + keypair, err := core.NewKeyPairRandom() require.NoError(t, err) priv, pub, err := crypto.GenerateEd25519Key(nil) require.NoError(t, err) pubkeyBytes, err := pub.Raw() require.NoError(t, err) - pubkey, err := core.NewPublicKey(core.CodecAccountKey, pub.(*crypto.Ed25519PublicKey)) + pubkey, err := core.NewPublicKey(pub.(*crypto.Ed25519PublicKey)) require.NoError(t, err) - login := pubkey.CID().String() + login := pubkey.Principal().String() passwordBytes, err := priv.Sign([]byte(SigninMessage)) password := hex.EncodeToString(passwordBytes) require.NoError(t, err) diff --git a/backend/wallet/wallet_test.go b/backend/wallet/wallet_test.go index ee134ad29f..6bcf1fa134 100644 --- a/backend/wallet/wallet_test.go +++ b/backend/wallet/wallet_test.go @@ -60,7 +60,7 @@ func TestRequestLndHubInvoice(t *testing.T) { ctx := context.Background() require.Eventually(t, func() bool { _, ok := bob.net.Get(); return ok }, 5*time.Second, 1*time.Second) - cid := bob.net.MustGet().ID().Account().CID() + cid := bob.net.MustGet().ID().Account().Principal() var amt uint64 = 23 var wrongAmt uint64 = 24 var memo = "test invoice" @@ -100,7 +100,7 @@ func TestRequestP2PInvoice(t *testing.T) { require.NoError(t, alice.net.MustGet().Connect(ctx, bob.net.MustGet().AddrInfo())) require.Eventually(t, func() bool { _, ok := bob.net.Get(); return ok }, 3*time.Second, 1*time.Second) - cid := bob.net.MustGet().ID().Account().CID() + cid := bob.net.MustGet().ID().Account().Principal() var amt uint64 = 23 var wrongAmt uint64 = 24 var memo = "test invoice"