├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .run ├── test pkg local.run.xml └── test-pkg-ci.xml ├── LICENSE ├── README.md ├── cmd ├── bumper │ └── main.go ├── docker │ ├── doc.go │ └── release │ │ ├── builds.go │ │ ├── main.go │ │ ├── packaging.go │ │ └── sources.go └── indra │ ├── client │ └── client.go │ ├── gui │ └── gui.go │ ├── main.go │ ├── relay │ └── relay.go │ ├── root.go │ ├── seed │ ├── client │ │ ├── client.go │ │ └── unlock.go │ ├── rpc.go │ ├── seed.go │ └── serve.go │ └── version.go ├── contrib └── init │ └── indra.service ├── contributors.md ├── docker ├── build │ ├── build.Dockerfile │ ├── package.Dockerfile │ ├── scripts │ │ └── package.sh │ └── targets │ │ ├── btcd.sh │ │ ├── btcwallet.sh │ │ ├── indra.sh │ │ └── lnd.sh ├── dev.Dockerfile ├── release │ ├── scripts │ │ ├── assemble.sh │ │ ├── extract.sh │ │ ├── link.sh │ │ ├── manifest.sh │ │ ├── push.sh │ │ └── run.sh │ └── targets │ │ ├── btcd │ │ ├── bin │ │ │ ├── btcctl │ │ │ └── btcctl-wallet │ │ ├── btcctl.Dockerfile │ │ ├── btcd.Dockerfile │ │ ├── docker-compose.yml │ │ └── setup.sh │ │ ├── btcwallet │ │ └── btcwallet.Dockerfile │ │ ├── indra │ │ └── indra.Dockerfile │ │ └── lnd │ │ ├── .gitignore │ │ ├── README.md │ │ ├── bin │ │ ├── lnsim-btcctl │ │ ├── lnsim-lncli-alice │ │ ├── lnsim-lncli-bob │ │ └── lnsim-lncli-miner │ │ ├── docker-compose.yml │ │ ├── lncli.Dockerfile │ │ ├── lnd.Dockerfile │ │ └── scripts │ │ └── setup.sh ├── scratch │ ├── defaults │ │ ├── btcd.conf │ │ └── lnd.conf │ ├── root-fs.Dockerfile │ ├── run.sh │ └── scripts │ │ └── build.sh ├── sim │ ├── docker-compose-dev.yml │ ├── docker-compose.yml │ └── run.sh └── sources │ ├── btcd │ └── official.Dockerfile │ ├── btcwallet │ └── official.Dockerfile │ ├── indra │ ├── local.Dockerfile │ └── official.Dockerfile │ └── lnd │ └── official.Dockerfile ├── docs ├── .gitignore ├── 1507.05724v1.pdf ├── 2008-080.pdf ├── 2018-126.pdf ├── DanezisG09.pdf ├── acns11-certificateless.pdf ├── architecture.md ├── formats.md ├── formats.pdf ├── hidden1.png ├── hidden2.png ├── hidden3.png ├── image-20220912120917831.png ├── indra.png ├── indra.svg ├── logo-for-dark-ground.png ├── logo-for-dark-ground.svg ├── logo-for-dark-tshirt.png ├── logo-for-dark-tshirt.svg ├── logo-mono-black.svg ├── logo-mono-white.svg ├── logo.png ├── logo.svg ├── message_format.jpg ├── onions.svg ├── protocols.md ├── readme.md ├── scripts │ ├── installtoc.sh │ └── toc.sh ├── session.png ├── torrelaycount.png ├── whitepaper.md └── whitepaper.pdf ├── go.mod ├── go.sum ├── keys ├── greg.stone.asc └── херетик.asc ├── localversion.go ├── pkg ├── cert │ └── ad.go ├── cfg │ ├── doc.go │ ├── params.go │ └── seed_address.go ├── codec │ ├── ad │ │ ├── ad.go │ │ ├── addresses │ │ │ ├── addresses.go │ │ │ └── addresses_test.go │ │ ├── intro │ │ │ ├── intro.go │ │ │ └── intro_test.go │ │ ├── load │ │ │ ├── load.go │ │ │ └── load_test.go │ │ ├── peer │ │ │ ├── peer.go │ │ │ └── peer_test.go │ │ └── services │ │ │ ├── services.go │ │ │ └── services_test.go │ ├── interfaces.go │ ├── onion │ │ ├── cores │ │ │ ├── balance │ │ │ │ ├── balance.go │ │ │ │ └── balance_test.go │ │ │ ├── confirmation │ │ │ │ ├── confirmation.go │ │ │ │ └── confirmation_test.go │ │ │ ├── end │ │ │ │ └── template.go │ │ │ └── response │ │ │ │ ├── response.go │ │ │ │ └── response_test.go │ │ ├── crypt │ │ │ ├── crypt.go │ │ │ └── crypt_test.go │ │ ├── delay │ │ │ ├── delay.go │ │ │ └── delay_test.go │ │ ├── exit │ │ │ ├── exit.go │ │ │ └── exit_test.go │ │ ├── forward │ │ │ ├── forward.go │ │ │ └── forward_test.go │ │ ├── getbalance │ │ │ ├── getbalance.go │ │ │ └── getbalance_test.go │ │ ├── hidden │ │ │ ├── introquery │ │ │ │ ├── introquery.go │ │ │ │ └── introquery_test.go │ │ │ ├── ready │ │ │ │ └── ready.go │ │ │ ├── route │ │ │ │ └── route.go │ │ │ ├── services │ │ │ │ ├── hiddenservice.go │ │ │ │ └── hiddenservice_test.go │ │ │ └── whisper │ │ │ │ ├── whisper.go │ │ │ │ └── whisper_test.go │ │ ├── reverse │ │ │ ├── reverse.go │ │ │ └── reverse_test.go │ │ └── session │ │ │ ├── session.go │ │ │ └── session_test.go │ ├── ont │ │ └── interfaces.go │ └── reg │ │ └── registry.go ├── crypto │ ├── ciph │ │ └── cipher.go │ ├── cloaked_test.go │ ├── crypto.go │ ├── crypto_test.go │ ├── doc.go │ ├── libp2p_impls.go │ ├── nonce │ │ ├── id.go │ │ └── nonce.go │ ├── public_test.go │ ├── sha256 │ │ ├── sha256.go │ │ └── sha256_test.go │ ├── signature_test.go │ ├── signer_test.go │ ├── types.go │ └── util.go ├── docker │ ├── builder.go │ ├── config.go │ └── doc.go ├── engine │ ├── acct.go │ ├── ads │ │ └── ads.go │ ├── consts │ │ └── hiddenconst.go │ ├── dispatcher │ │ ├── dispatcher.go │ │ ├── dispatcher_messages.go │ │ ├── dispatcher_test.go │ │ └── doc.go │ ├── doc.go │ ├── eng_senders.go │ ├── eng_sessions.go │ ├── engine.go │ ├── engine_test.go │ ├── fail_test.go │ ├── magic │ │ └── magic.go │ ├── mock.go │ ├── mockengine.go │ ├── node │ │ └── node.go │ ├── onion_skins.go │ ├── packet │ │ ├── doc.go │ │ ├── packet.go │ │ ├── packet_test.go │ │ ├── segcalc.go │ │ ├── segcalc_test.go │ │ ├── segment.go │ │ └── segment_test.go │ ├── payments │ │ └── payments.go │ ├── peerstore.go │ ├── peerstore_test.go │ ├── protocols │ │ └── protocols.go │ ├── reply.go │ ├── responses │ │ └── responses.go │ ├── sendgetbalance_test.go │ ├── services │ │ └── services.go │ ├── sess │ │ ├── data.go │ │ ├── doc.go │ │ ├── select.go │ │ ├── send.go │ │ └── sessionmanager.go │ ├── sessions │ │ └── sessions.go │ ├── tpt │ │ └── interfaces.go │ └── transport │ │ ├── common.go │ │ ├── conn.go │ │ ├── discovery.go │ │ ├── doc.go │ │ ├── listener.go │ │ ├── listener_test.go │ │ ├── pstoreds │ │ ├── addr_book.go │ │ ├── addr_book_gc.go │ │ ├── addr_book_gc_test.go │ │ ├── cache.go │ │ ├── cyclic_batch.go │ │ ├── ds_test.go │ │ ├── keybook.go │ │ ├── metadata.go │ │ ├── pb │ │ │ ├── pstore.pb.go │ │ │ └── pstore.proto │ │ ├── peerstore.go │ │ └── protobook.go │ │ ├── pstoremem │ │ ├── addr_book.go │ │ ├── inmem_test.go │ │ ├── keybook.go │ │ ├── metadata.go │ │ ├── peerstore.go │ │ ├── protobook.go │ │ ├── sorting.go │ │ └── sorting_test.go │ │ ├── readme.md │ │ └── tpt_sim.go ├── headers │ └── headers.go ├── hidden │ └── hidden.go ├── interrupt │ ├── LICENSE │ ├── README.md │ ├── doc.go │ ├── interrupt.go │ └── sigterm.go ├── node │ └── protocol.go ├── p2p │ ├── config.go │ ├── configure.go │ ├── doc.go │ ├── flags.go │ ├── log.go │ ├── metrics │ │ └── host.go │ ├── service.go │ ├── signals.go │ ├── util.go │ └── util_test.go ├── proc │ ├── app │ │ ├── app.go │ │ └── app_test.go │ ├── cmds │ │ ├── args.go │ │ ├── args_test.go │ │ ├── commands.go │ │ ├── commands_test.go │ │ ├── env.go │ │ ├── example.go │ │ ├── help.go │ │ └── marshal.go │ ├── log │ │ ├── length.go │ │ ├── log.go │ │ ├── log_test.go │ │ └── maxlen │ │ │ └── maxlen.go │ └── opts │ │ ├── config │ │ └── interface.go │ │ ├── duration │ │ └── spec.go │ │ ├── float │ │ └── spec.go │ │ ├── integer │ │ └── spec.go │ │ ├── list │ │ └── spec.go │ │ ├── meta │ │ └── metadata.go │ │ ├── normalize │ │ ├── addresses.go │ │ └── paths.go │ │ ├── text │ │ └── spec.go │ │ └── toggle │ │ └── spec.go ├── rpc │ ├── auth.go │ ├── client.go │ ├── device.go │ ├── dialer.go │ ├── dialer_options.go │ ├── doc.go │ ├── endpoint.go │ ├── examples │ │ ├── log.go │ │ ├── tunnel_hello.go │ │ ├── unix_hello.go │ │ └── unix_unlock.go │ ├── flags.go │ ├── keys.go │ ├── log.go │ ├── server.go │ ├── server_options.go │ ├── signals.go │ ├── socket_unix.go │ ├── store.go │ ├── tunnel.go │ ├── tunnel_config.go │ └── util.go ├── seed │ ├── log.go │ ├── server.go │ └── signals.go ├── splicer │ ├── i32 │ │ ├── int32.go │ │ └── int32_test.go │ ├── magic │ │ ├── magic.go │ │ └── magic_test.go │ ├── readme.md │ ├── splicer.go │ ├── splicer_test.go │ └── t64 │ │ ├── time.go │ │ └── time_test.go ├── storage │ ├── configure.go │ ├── doc.go │ ├── flags.go │ ├── key.go │ ├── log.go │ ├── service.go │ ├── service_unlock.go │ ├── signals.go │ ├── unlock.go │ ├── unlock.pb.go │ ├── unlock.proto │ └── unlock_grpc.pb.go └── util │ ├── appdata │ ├── appdata.go │ └── appdata_test.go │ ├── b32 │ ├── based32 │ │ ├── based32.go │ │ └── based32_test.go │ ├── codec │ │ └── types.go │ ├── codecer │ │ └── interface.go │ └── constant.go │ ├── ci │ └── trace.go │ ├── cryptorand │ └── cryptorand.go │ ├── file │ └── file.go │ ├── math │ └── math.go │ ├── multi │ └── multiaddr.go │ ├── multikey │ └── multikey.go │ ├── norm │ └── norm.go │ ├── options │ └── default.go │ ├── path │ └── path.go │ ├── qu │ └── quit.go │ ├── slice │ ├── interfaces_test.go │ ├── slice.go │ └── slice_test.go │ ├── splice │ └── splice.go │ ├── tests │ └── testutils.go │ └── windows │ └── windows.go ├── release └── .gitignore ├── scripts ├── cdwork.sh ├── readme.md ├── run.sh ├── runci.sh ├── test.sh ├── testci.sh ├── testpkg.sh └── trace.sh └── version.go /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Test All in pkg 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v3 21 | with: 22 | go-version: 1.19 23 | 24 | - name: Test 25 | run: go test -v ./pkg/... 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | vendor/ 16 | /.idea/vcs.xml 17 | /.idea/modules.xml 18 | /.idea/indranet.iml 19 | /.idea/.gitignore 20 | /.idea/indra.iml 21 | /.idea/codeStyles/codeStyleConfig.xml 22 | /.idea/** 23 | -------------------------------------------------------------------------------- /.run/test pkg local.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.run/test-pkg-ci.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /cmd/docker/doc.go: -------------------------------------------------------------------------------- 1 | // Package docker contains tools for Docker deployments of Indra and components. 2 | package docker 3 | -------------------------------------------------------------------------------- /cmd/indra/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | func Init(c *cobra.Command) { 8 | c.AddCommand(clientCommand) 9 | } 10 | 11 | var clientCommand = &cobra.Command{ 12 | Use: "client", 13 | Short: "run a client", 14 | Long: "Runs indra as a client, providing a wireguard tunnel and socks5 " + 15 | "proxy as connectivity options", 16 | Run: func(cmd *cobra.Command, args []string) { 17 | 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /cmd/indra/gui/gui.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | func Init(c *cobra.Command) { 8 | c.AddCommand(clientCommand) 9 | } 10 | 11 | var clientCommand = &cobra.Command{ 12 | Use: "client", 13 | Short: "run a client", 14 | Long: "Runs indra as a client, providing a wireguard tunnel and socks5 " + 15 | "proxy as connectivity options", 16 | Run: func(cmd *cobra.Command, args []string) { 17 | 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /cmd/indra/main.go: -------------------------------------------------------------------------------- 1 | // Indra is a low latency, source routed mixnet distributed virtual private network protocol. 2 | // 3 | // This application includes a client, client GUI, relay and seed node according to the subcommand invocation. 4 | package main 5 | 6 | import ( 7 | "os" 8 | 9 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 10 | ) 11 | 12 | var ( 13 | log = log2.GetLogger() 14 | check = log.E.Chk 15 | ) 16 | 17 | func init() { 18 | log2.App.Store("indra") 19 | } 20 | 21 | func main() { 22 | 23 | var err error 24 | 25 | if err = rootCmd.Execute(); check(err) { 26 | os.Exit(1) 27 | } 28 | 29 | os.Exit(0) 30 | } 31 | -------------------------------------------------------------------------------- /cmd/indra/relay/relay.go: -------------------------------------------------------------------------------- 1 | package relay 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind" 5 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 6 | "github.com/spf13/cobra" 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | var ( 11 | log = log2.GetLogger() 12 | check = log.E.Chk 13 | ) 14 | 15 | var ( 16 | wireguardEnable bool 17 | wireguardCIDR string // todo: there must be something like this. default route to 1 18 | socksEnable bool 19 | socksListener string 20 | ) 21 | 22 | func Init(c *cobra.Command) { 23 | relayCommand.PersistentFlags().BoolVarP(&wireguardEnable, "wireguard", 24 | "w", false, "enable wireguard tunnel") 25 | relayCommand.PersistentFlags().BoolVarP(&socksEnable, "socks", 26 | "s", false, "enable socks proxy") 27 | relayCommand.PersistentFlags().StringVarP(&socksListener, "socks-listener", 28 | "l", "localhost:8080", "set address for socks 5 proxy listener") 29 | 30 | viper.BindPFlag("wireguard", relayCommand.PersistentFlags().Lookup("wireguard")) 31 | viper.BindPFlag("socks", relayCommand.PersistentFlags().Lookup("socks")) 32 | viper.BindPFlag("socks-listener", relayCommand.PersistentFlags().Lookup("socks-listener")) 33 | 34 | c.AddCommand(relayCommand) 35 | } 36 | 37 | var relayCommand = &cobra.Command{ 38 | Use: "relay", 39 | Short: "run a relay", 40 | Long: "Runs indra as a full relay, with optional client.", 41 | 42 | Run: func(cmd *cobra.Command, args []string) { 43 | log.I.Ln(log2.App.Load(), indra.SemVer) 44 | nw, _ := cmd.Parent().PersistentFlags().GetString("network") 45 | var dd string 46 | dd, _ = cmd.Parent().PersistentFlags().GetString("data-dir") 47 | log.T.S("cmd", dd, nw, args) 48 | }, 49 | } 50 | -------------------------------------------------------------------------------- /cmd/indra/seed/client/client.go: -------------------------------------------------------------------------------- 1 | // Package client is a client for the seed RPC service for remote unlock and management. 2 | package client 3 | 4 | import ( 5 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | var ( 10 | log = log2.GetLogger() 11 | check = log.E.Chk 12 | ) 13 | 14 | func init() { 15 | initUnlock(unlockRPCCmd) 16 | } 17 | 18 | func Init(c *cobra.Command) { 19 | c.AddCommand(unlockRPCCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cmd/indra/seed/client/unlock.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "github.com/spf13/cobra" 8 | "github.com/spf13/viper" 9 | "google.golang.org/grpc" 10 | 11 | "git.indra-labs.org/dev/ind/pkg/rpc" 12 | "git.indra-labs.org/dev/ind/pkg/storage" 13 | ) 14 | 15 | var ( 16 | unlockTargetFlag = "target" 17 | unlockPassFileFlag = "keyfile" 18 | ) 19 | 20 | var ( 21 | unlockTarget string 22 | unlockPassFilePath string 23 | ) 24 | 25 | func initUnlock(cmd *cobra.Command) { 26 | 27 | cmd.Flags().StringVarP(&unlockTarget, unlockTargetFlag, "", 28 | "unix:///tmp/indra.sock", 29 | "the url of the rpc server", 30 | ) 31 | 32 | viper.BindPFlag(unlockTargetFlag, cmd.Flags().Lookup(unlockTargetFlag)) 33 | 34 | cmd.Flags().StringVarP(&unlockPassFilePath, unlockPassFileFlag, "", 35 | "", 36 | "", 37 | ) 38 | 39 | viper.BindPFlag(unlockPassFileFlag, cmd.Flags().Lookup(unlockPassFileFlag)) 40 | } 41 | 42 | var unlockRPCCmd = &cobra.Command{ 43 | Use: "unlock", 44 | Short: "unlocks the encrypted storage", 45 | Long: `unlocks the encrypted storage.`, 46 | Run: func(cmd *cobra.Command, args []string) { 47 | 48 | var err error 49 | var conn *grpc.ClientConn 50 | 51 | conn, err = rpc.Dial(viper.GetString(unlockTargetFlag)) 52 | 53 | if err != nil { 54 | check(err) 55 | os.Exit(1) 56 | } 57 | 58 | var keyFileBytes []byte 59 | 60 | if keyFileBytes, err = os.ReadFile(viper.GetString(unlockPassFileFlag)); check(err) { 61 | os.Exit(0) 62 | } 63 | 64 | // var password []byte 65 | // 66 | // fmt.Print("Enter Encryption HiddenService: ") 67 | // password, err = term.ReadPassword(int(syscall.Stdin)) 68 | // fmt.Println() 69 | 70 | u := storage.NewUnlockServiceClient(conn) 71 | 72 | _, err = u.Unlock(context.Background(), &storage.UnlockRequest{ 73 | Key: string(string(keyFileBytes)), 74 | }) 75 | 76 | if err != nil { 77 | check(err) 78 | return 79 | } 80 | 81 | log.I.Ln("successfully unlocked") 82 | }, 83 | } 84 | -------------------------------------------------------------------------------- /cmd/indra/seed/rpc.go: -------------------------------------------------------------------------------- 1 | package seed 2 | 3 | import "github.com/spf13/cobra" 4 | 5 | var rpcCmd = &cobra.Command{ 6 | Use: "rpc", 7 | Short: "A list of commands for interacting with a seed", 8 | Long: `A list of commands for interacting with a seed.`, 9 | } 10 | -------------------------------------------------------------------------------- /cmd/indra/seed/seed.go: -------------------------------------------------------------------------------- 1 | // Package seed is a non-relay node that simply accepts and propagates peer advertisment gossip to clients and relays on the network. 2 | package seed 3 | 4 | import ( 5 | "git.indra-labs.org/dev/ind/cmd/indra/seed/client" 6 | "git.indra-labs.org/dev/ind/pkg/p2p" 7 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 8 | "git.indra-labs.org/dev/ind/pkg/rpc" 9 | "git.indra-labs.org/dev/ind/pkg/storage" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | var ( 14 | log = log2.GetLogger() 15 | check = log.E.Chk 16 | ) 17 | 18 | func init() { 19 | storage.InitFlags(serveCmd) 20 | p2p.InitFlags(serveCmd) 21 | rpc.InitFlags(serveCmd) 22 | } 23 | 24 | func Init(c *cobra.Command) { 25 | client.Init(rpcCmd) 26 | 27 | seedCommand.AddCommand(rpcCmd) 28 | seedCommand.AddCommand(serveCmd) 29 | 30 | c.AddCommand(seedCommand) 31 | } 32 | 33 | var seedCommand = &cobra.Command{ 34 | Use: "seed", 35 | Short: "run and manage your seed node", 36 | Long: `run and manage your seed node`, 37 | } 38 | -------------------------------------------------------------------------------- /cmd/indra/seed/serve.go: -------------------------------------------------------------------------------- 1 | package seed 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/spf13/cobra" 7 | "github.com/spf13/viper" 8 | 9 | "git.indra-labs.org/dev/ind" 10 | "git.indra-labs.org/dev/ind/pkg/cfg" 11 | "git.indra-labs.org/dev/ind/pkg/interrupt" 12 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 13 | "git.indra-labs.org/dev/ind/pkg/seed" 14 | ) 15 | 16 | var serveCmd = &cobra.Command{ 17 | Use: "serve", 18 | Short: "Serves an instance of the seed node", 19 | Long: `Serves an instance of the seed node.`, 20 | Run: func(cmd *cobra.Command, args []string) { 21 | 22 | log.I.Ln("-- ", log2.App.Load(), "("+viper.GetString("network")+") -", indra.SemVer, "- Network Freedom. --") 23 | 24 | cfg.SelectNetworkParams(viper.GetString("network")) 25 | 26 | ctx, cancel := context.WithCancel(context.Background()) 27 | interrupt.AddHandler(cancel) 28 | 29 | // Seed // 30 | 31 | go seed.Run(ctx) 32 | 33 | select { 34 | case <-seed.WhenStartFailed(): 35 | log.I.Ln("stopped") 36 | case <-seed.WhenShutdown(): 37 | log.I.Ln("shutdown complete") 38 | } 39 | 40 | log.I.Ln("-- fin --") 41 | }, 42 | } 43 | -------------------------------------------------------------------------------- /cmd/indra/version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "git.indra-labs.org/dev/ind" 6 | 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | rootCmd.AddCommand(versionCmd) 12 | } 13 | 14 | var versionCmd = &cobra.Command{ 15 | Use: "version", 16 | Short: "Prints the version number", 17 | Long: `All software has versions. This is mine. Semver formatted.`, 18 | Run: func(cmd *cobra.Command, args []string) { 19 | fmt.Println(indra.SemVer) 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /contrib/init/indra.service: -------------------------------------------------------------------------------- 1 | # A sample systemd service file for ind running with a lnd service. 2 | 3 | [Unit] 4 | Description=Indra Network Daemon 5 | 6 | # Make sure ind starts after lnd is ready 7 | # Requires=lnd.service 8 | # After=lnd.service 9 | 10 | [Service] 11 | ExecStart=/usr/local/bin/ind 12 | ExecStop=/usr/local/bin/indcli stop 13 | 14 | # Replace these with the user:group that will run lnd 15 | User=indra 16 | Group=indra 17 | 18 | # Try restarting lnd if it stops due to a failure 19 | Restart=on-failure 20 | RestartSec=60 21 | 22 | # Type=notify is required for lnd to notify systemd when it is ready 23 | Type=notify 24 | 25 | # An extended timeout period is needed to allow for time intensive operations during startup. We also extend the 26 | # stop timeout to ensure graceful shutdowns. 27 | TimeoutStartSec=10 28 | TimeoutStopSec=30 29 | 30 | # Hardening Measures 31 | #################### 32 | 33 | # Mount /usr, /boot/ and /etc read-only for the process. 34 | ProtectSystem=full 35 | 36 | # Disallow the process and all of its children to gain 37 | # new privileges through execute(). 38 | NoNewPrivileges=true 39 | 40 | # Use a new /dev namespace only populated with API pseudo devices 41 | # such as /dev/null, /dev/zero and /dev/random. 42 | PrivateDevices=true 43 | 44 | # Deny the creation of writable and executable memory mappings. 45 | MemoryDenyWriteExecute=true 46 | 47 | [Install] 48 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /docker/build/build.Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # --- 3 | # Build Process 4 | # --- 5 | 6 | ARG source_image="indralabs/indra-source" 7 | ARG source_version="dev" 8 | 9 | ARG builder_image="golang:1.19.4" 10 | 11 | FROM ${source_image}:${source_version} as source 12 | 13 | FROM ${builder_image} as build 14 | 15 | ARG source_version="dev" 16 | 17 | ARG target_name="indra" 18 | ARG target_build_script="docker/indra/build/build.sh" 19 | 20 | COPY --from=source /source /tmp/source 21 | COPY --from=source /source.tar.gz /tmp/source.tar.gz 22 | 23 | WORKDIR /tmp/source 24 | 25 | RUN set -ex echo "making directories for release and binaries" \ 26 | && mkdir -pv /tmp/${target_name}-${source_version}/release \ 27 | && mkdir -pv /tmp/${target_name}-${source_version}/bin 28 | 29 | RUN set -ex echo "migrating source files to release" \ 30 | && cp /tmp/source.tar.gz /tmp/${target_name}-${source_version}/release/${target_name}-source-${source_version}.tar.gz 31 | 32 | ADD ${target_build_script} ./build.sh 33 | 34 | RUN set -ex echo "running build" && ./build.sh 35 | 36 | FROM scratch 37 | 38 | ARG target_name="indra" 39 | ARG source_version="dev" 40 | 41 | COPY --from=build /tmp/${target_name}-${source_version} /${target_name}-${source_version} 42 | -------------------------------------------------------------------------------- /docker/build/package.Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # --- 3 | # Packaging Process 4 | # --- 5 | 6 | ARG binaries_image="indralabs/indra-build" 7 | ARG binaries_version="dev" 8 | 9 | ARG packaging_image="golang:1.19.4" 10 | 11 | FROM ${binaries_image}:${binaries_version} as build 12 | 13 | FROM ${packaging_image} as packager 14 | 15 | ARG binaries_version="dev" 16 | 17 | ARG target_name="indra" 18 | ARG target_packaging_script="docker/indra/build/package.sh" 19 | 20 | COPY --from=build /${target_name}-${binaries_version} /tmp/${target_name}-${binaries_version} 21 | 22 | WORKDIR /tmp/${target_name}-${binaries_version} 23 | 24 | ADD docker/build/scripts/package.sh /tmp/package.sh 25 | 26 | RUN set -ex echo "running packaging" \ 27 | && /tmp/package.sh 28 | 29 | WORKDIR /tmp/${target_name}-${binaries_version}/release 30 | 31 | RUN set -ex echo "generating shasum for release" \ 32 | && shasum -a 256 * > manifest-${binaries_version}.txt 33 | 34 | RUN set -ex echo "generating tar archive for the whole release" \ 35 | && tar -czvf /tmp/${target_name}-${binaries_version}.tar.gz -C /tmp/${target_name}-${binaries_version} . 36 | 37 | FROM busybox 38 | 39 | ARG target_name="indra" 40 | ARG binaries_version="dev" 41 | 42 | COPY --from=packager /tmp/${target_name}-${binaries_version} /tmp/${target_name}-${binaries_version} 43 | COPY --from=packager /tmp/${target_name}-${binaries_version}.tar.gz /tmp/${target_name}-${binaries_version}.tar.gz 44 | -------------------------------------------------------------------------------- /docker/build/scripts/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PLATFORMS=$(find ./bin -type d | grep -oP "(^.*bin/\K).*") 4 | 5 | # PACKAGE LOOP 6 | for PLATFORM in $PLATFORMS; do 7 | 8 | echo "running packaging for $PLATFORM to release/${target_name}-$PLATFORM-${binaries_version}.tar.gz" 9 | 10 | tar -czvf ./release/${target_name}-$PLATFORM-${binaries_version}.tar.gz -C ./bin/$PLATFORM/. . 11 | 12 | done 13 | -------------------------------------------------------------------------------- /docker/build/targets/btcd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | NAME="btcd" 4 | 5 | PLATFORMS=( 6 | "linux/386" 7 | "linux/amd64" 8 | "linux/arm/v5" 9 | "linux/arm/v6" 10 | "linux/arm/v7" 11 | "linux/arm64" 12 | # "linux/loong64" 13 | "linux/mips" 14 | "linux/mips64" 15 | "linux/mips64le" 16 | "linux/mipsle" 17 | "linux/ppc64" 18 | "linux/ppc64le" 19 | # "linux/riscv64" 20 | "linux/s390x" 21 | ) 22 | 23 | #PLATFORMS=( 24 | # "linux/amd64" 25 | # "linux/arm64" 26 | # "linux/arm/v7" 27 | #) 28 | 29 | 30 | # C bad 31 | export CGO_ENABLED=0 32 | 33 | # BUIDL LOOP 34 | for i in "${PLATFORMS[@]}"; do 35 | 36 | OS=$(echo $i | cut -f1 -d/) 37 | ARCH=$(echo $i | cut -f2 -d/) 38 | ARM=$(echo $i | cut -f3 -d/) 39 | 40 | ARM_VARIANT="" 41 | 42 | if [ "$ARM" != "" ]; then 43 | ARM_VARIANT="-${ARM}" 44 | fi 45 | 46 | echo "running build for $OS-$ARCH$ARM_VARIANT" 47 | 48 | GOOS=$OS GOARCH=$ARCH GOARM=$(echo $ARM | cut -f1 -dv) go build --ldflags '-w -s' \ 49 | -o /tmp/$NAME-${source_version}/bin/$OS-$ARCH$ARM_VARIANT/btcd . 50 | GOOS=$OS GOARCH=$ARCH GOARM=$(echo $ARM | cut -f1 -dv) go build --ldflags '-w -s' \ 51 | -o /tmp/$NAME-${source_version}/bin/$OS-$ARCH$ARM_VARIANT/btcctl ./cmd/btcctl/. 52 | GOOS=$OS GOARCH=$ARCH GOARM=$(echo $ARM | cut -f1 -dv) go build --ldflags '-w -s' \ 53 | -o /tmp/$NAME-${source_version}/bin/$OS-$ARCH$ARM_VARIANT/gencerts ./cmd/gencerts/. 54 | 55 | done 56 | -------------------------------------------------------------------------------- /docker/build/targets/btcwallet.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | NAME="btcwallet" 4 | 5 | PLATFORMS=( 6 | "linux/386" 7 | "linux/amd64" 8 | "linux/arm/v5" 9 | "linux/arm/v6" 10 | "linux/arm/v7" 11 | "linux/arm64" 12 | # "linux/loong64" 13 | "linux/mips" 14 | "linux/mips64" 15 | "linux/mips64le" 16 | "linux/mipsle" 17 | "linux/ppc64" 18 | "linux/ppc64le" 19 | # "linux/riscv64" 20 | "linux/s390x" 21 | ) 22 | 23 | #PLATFORMS=( 24 | # "linux/amd64" 25 | # "linux/arm64" 26 | # "linux/arm/v7" 27 | #) 28 | 29 | 30 | # C bad 31 | export CGO_ENABLED=0 32 | 33 | # BUIDL LOOP 34 | for i in "${PLATFORMS[@]}"; do 35 | 36 | OS=$(echo $i | cut -f1 -d/) 37 | ARCH=$(echo $i | cut -f2 -d/) 38 | ARM=$(echo $i | cut -f3 -d/) 39 | 40 | ARM_VARIANT="" 41 | 42 | if [ "$ARM" != "" ]; then 43 | ARM_VARIANT="-${ARM}" 44 | fi 45 | 46 | echo "running build for $OS-$ARCH$ARM_VARIANT" 47 | 48 | GOOS=$OS GOARCH=$ARCH GOARM=$(echo $ARM | cut -f1 -dv) go build --ldflags '-w -s' \ 49 | -o /tmp/$NAME-${source_version}/bin/$OS-$ARCH$ARM_VARIANT/btcwallet . 50 | 51 | done 52 | -------------------------------------------------------------------------------- /docker/build/targets/indra.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | NAME="indra" 4 | 5 | #PLATFORMS=( 6 | # "linux/386" 7 | # "linux/amd64" 8 | # "linux/arm/v5" 9 | # "linux/arm/v6" 10 | # "linux/arm/v7" 11 | # "linux/arm64" 12 | ## "linux/loong64" 13 | # "linux/mips" 14 | # "linux/mips64" 15 | # "linux/mips64le" 16 | # "linux/mipsle" 17 | # "linux/ppc64" 18 | # "linux/ppc64le" 19 | ## "linux/riscv64" 20 | # "linux/s390x" 21 | #) 22 | 23 | PLATFORMS=( 24 | "linux/amd64" 25 | "linux/arm64" 26 | "linux/arm/v7" 27 | ) 28 | 29 | # C bad 30 | export CGO_ENABLED=0 31 | 32 | # BUIDL LOOP 33 | for i in "${PLATFORMS[@]}"; do 34 | 35 | OS=$(echo $i | cut -f1 -d/) 36 | ARCH=$(echo $i | cut -f2 -d/) 37 | ARM=$(echo $i | cut -f3 -d/) 38 | 39 | ARM_VARIANT="" 40 | 41 | if [ "$ARM" != "" ]; then 42 | ARM_VARIANT="-${ARM}" 43 | fi 44 | 45 | echo "running build for $OS-$ARCH$ARM_VARIANT" 46 | 47 | GOOS=$OS GOARCH=$ARCH GOARM=$(echo $ARM | cut -f1 -dv) go build --ldflags '-w -s' \ 48 | -o /tmp/$NAME-${source_version}/bin/$OS-$ARCH$ARM_VARIANT/indra ./cmd/indra/. 49 | 50 | done 51 | -------------------------------------------------------------------------------- /docker/build/targets/lnd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | NAME="lnd" 4 | 5 | #PLATFORMS=( 6 | # "linux/386" 7 | # "linux/amd64" 8 | # "linux/arm/v5" 9 | # "linux/arm/v6" 10 | # "linux/arm/v7" 11 | # "linux/arm64" 12 | ## "linux/loong64" 13 | # "linux/mips" 14 | # "linux/mips64" 15 | # "linux/mips64le" 16 | # "linux/mipsle" 17 | # "linux/ppc64" 18 | # "linux/ppc64le" 19 | ## "linux/riscv64" 20 | # "linux/s390x" 21 | #) 22 | 23 | PLATFORMS=( 24 | "linux/amd64" 25 | "linux/arm64" 26 | "linux/arm/v7" 27 | ) 28 | 29 | # C bad 30 | export CGO_ENABLED=0 31 | 32 | # BUIDL LOOP 33 | for i in "${PLATFORMS[@]}"; do 34 | 35 | OS=$(echo $i | cut -f1 -d/) 36 | ARCH=$(echo $i | cut -f2 -d/) 37 | ARM=$(echo $i | cut -f3 -d/) 38 | 39 | ARM_VARIANT="" 40 | 41 | if [ "$ARM" != "" ]; then 42 | ARM_VARIANT="-${ARM}" 43 | fi 44 | 45 | echo "running build for $OS-$ARCH$ARM_VARIANT" 46 | 47 | GOOS=$OS GOARCH=$ARCH GOARM=$(echo $ARM | cut -f1 -dv) go build -tags="signrpc walletrpc chainrpc invoicesrpc" --ldflags '-w -s' \ 48 | -o /tmp/$NAME-${source_version}/bin/$OS-$ARCH$ARM_VARIANT/lnd ./cmd/lnd/. 49 | GOOS=$OS GOARCH=$ARCH GOARM=$(echo $ARM | cut -f1 -dv) go build -tags="signrpc walletrpc chainrpc invoicesrpc" --ldflags '-w -s' \ 50 | -o /tmp/$NAME-${source_version}/bin/$OS-$ARCH$ARM_VARIANT/lncli ./cmd/lncli/. 51 | 52 | done 53 | -------------------------------------------------------------------------------- /docker/dev.Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM golang:1.19.4 3 | 4 | # Source/Target release defaults 5 | ARG ARCH=amd64 6 | ARG GOARCH=amd64 7 | ENV GO111MODULE=on GOOS=linux 8 | 9 | RUN set -ex \ 10 | && apt update && apt install net-tools 11 | 12 | WORKDIR /indra 13 | 14 | # ENV defaults 15 | # ENV IND_LOGFILEPATH="" 16 | 17 | # Set the data volume 18 | # VOLUME ["/var/indra"] 19 | 20 | # :8337 indra peer-to-peer port 21 | # :8338 indra RPC port 22 | EXPOSE 8337 8338 23 | 24 | ENTRYPOINT ["/indra/docker/sim/run.sh"] 25 | -------------------------------------------------------------------------------- /docker/release/scripts/assemble.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "${TARGET_NAME}" ]; then 4 | echo "-- error: a release name is required to run the assembler" 5 | exit 1 6 | fi 7 | 8 | if [ -z "${TARGET_TAG}" ]; then 9 | echo "-- error: a release tag is required to run an extract" 10 | exit 1 11 | fi 12 | 13 | DOCKERFILES=$(find ./docker/release/targets/$TARGET_NAME -maxdepth 1 -type f -iname "*.Dockerfile" | grep -oP "(.$TARGET_NAME/\K).*") 14 | 15 | PLATFORMS=$(find release/$TARGET_NAME-$TARGET_TAG/bin -type d | grep -oP "(^.*/bin/\K).*" | cut -f1 -d/) 16 | 17 | if [ -z "${RELEASE}" ]; then 18 | PLATFORMS=( 19 | "linux-amd64" 20 | "linux-arm64" 21 | "linux-arm-v7" 22 | ) 23 | fi 24 | 25 | echo "-- assembling images for package ${TARGET_NAME}-${TARGET_TAG}" 26 | 27 | for DOCKERFILE in $DOCKERFILES; do 28 | 29 | IMAGE=$(echo $DOCKERFILE | cut -f1 -d.) 30 | TARGET_REPOSITORY="indralabs/$(echo $DOCKERFILE | cut -f1 -d.)-multi-arch" 31 | 32 | echo "-- running assembler on $IMAGE" 33 | 34 | docker image ls --filter="reference=$TARGET_REPOSITORY*-$TARGET_TAG" -q | xargs docker image rm -f 35 | 36 | for PLATFORM in $PLATFORMS; do 37 | 38 | echo "--- assembling $TARGET_REPOSITORY:$PLATFORM-$TARGET_TAG" 39 | 40 | DOCKER_PLATFORM=$(echo $PLATFORM | tr '-' '/') 41 | DOCKER_PLATFORM_TAG=$PLATFORM-$TARGET_TAG 42 | SCRATCH_PLATFORM_TAG=$PLATFORM-latest 43 | 44 | if [ -z "${RELEASE}" ]; then 45 | DOCKER_PLATFORM_TAG=$PLATFORM-dev 46 | fi 47 | 48 | docker build --quiet --platform=$DOCKER_PLATFORM \ 49 | --build-arg platform=$PLATFORM \ 50 | --build-arg version=$TARGET_TAG \ 51 | --build-arg scratch_version=$SCRATCH_PLATFORM_TAG \ 52 | -t "$TARGET_REPOSITORY:$DOCKER_PLATFORM_TAG" \ 53 | -f ./docker/release/targets/$TARGET_NAME/$DOCKERFILE . 54 | 55 | done 56 | done 57 | 58 | #PKGS="indralabs/indra-package:dev,indralabs/btcd-package:v0.23.3,indralabs/lnd-package:v0.15.5-beta" docker/release/scripts/run.sh 59 | -------------------------------------------------------------------------------- /docker/release/scripts/extract.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "${PKG}" ]; then 4 | echo "-- error: a pkg name is required to run an extract" 5 | exit 1 6 | fi 7 | 8 | if [ -z "${PKG_REPOSITORY}" ]; then 9 | echo "-- error: a pkg repository is required to run an extract" 10 | exit 1 11 | fi 12 | 13 | echo "- finding package $PKG" 14 | 15 | if [ ! -d "release/${PKG}" ]; then 16 | 17 | echo "-- binaries not found in release/${PKG}" 18 | echo "-- checking if archive exists release/${PKG}.tar.gz" 19 | 20 | if [ ! -e "release/${PKG}.tar.gz" ]; then 21 | 22 | echo "-- archive does not exist, attempting to extract from $PKG_REPOSITORY" 23 | 24 | docker run --rm -it --user=$UID:$GID \ 25 | --volume=${PWD}/release:/tmp/release $PKG_REPOSITORY \ 26 | find /tmp/ -maxdepth 1 -name *.tar.gz -exec cp {} /tmp/release \; 27 | fi 28 | 29 | echo "-- extracting archive from release/${PKG}.tar.gz" 30 | 31 | mkdir release/${PKG} 32 | tar -xzf release/${PKG}.tar.gz --directory ./release/${PKG} 33 | fi 34 | 35 | echo "-- found $PKG" 36 | -------------------------------------------------------------------------------- /docker/release/scripts/link.sh: -------------------------------------------------------------------------------- 1 | 2 | if [ -z "${RELEASE_TAG}" ]; then 3 | echo "a release tag is required. use RELEASE_TAG='' to continue." 4 | exit 1 5 | fi 6 | 7 | if [ -z "${PKGS}" ]; then 8 | PKGS="indralabs/indra-package:dev" 9 | fi 10 | 11 | PKGS=$(echo $PKGS | tr ",", "\n") 12 | 13 | echo "- pushing packages to docker repositories" 14 | 15 | for PKG in $PKGS 16 | do 17 | 18 | echo "-- linking $PKG to $RELEASE_TAG" 19 | 20 | TARGET_NAME=$(echo $PKG | cut -f2 -d/ | cut -f1 -d:) 21 | TARGET_TAG=$(echo $PKG | cut -f2 -d:) 22 | 23 | SERVICES=$(find ./docker/release/targets/$TARGET_NAME -maxdepth 1 -type f -iname "*.Dockerfile" | grep -oP ".*$TARGET_NAME/\K(.*)(?=.Dockerfile)") 24 | 25 | for SERVICE in $SERVICES 26 | do 27 | 28 | echo "-- creating manifest for indralabs/$SERVICE:$RELEASE_TAG" 29 | 30 | docker manifest rm indralabs/$SERVICE:$RELEASE_TAG 31 | 32 | CMD="docker manifest create indralabs/$SERVICE:$RELEASE_TAG" 33 | 34 | IMAGES=$(docker image ls --filter="reference=indralabs/$SERVICE-multi-arch*$TARGET_TAG" --format="{{.Repository}}:{{.Tag}}") 35 | 36 | for image in $IMAGES; do 37 | CMD+=" --amend $image" 38 | done 39 | 40 | $CMD 41 | 42 | docker manifest push indralabs/$SERVICE:$RELEASE_TAG 43 | docker manifest inspect indralabs/$SERVICE:$RELEASE_TAG > release/$TARGET_NAME-$TARGET_TAG/release/manifest-docker-$SERVICE-$RELEASE_TAG.json 44 | 45 | done 46 | 47 | done 48 | 49 | # RELEASE_TAG="latest" PKGS="indralabs/btcd:v0.23.3" docker/release/scripts/link.sh 50 | -------------------------------------------------------------------------------- /docker/release/scripts/manifest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | MANIFEST_REPOSITORY="indralabs/indra" 4 | TARGET_REPOSITORY="indralabs/indra-multi-arch" 5 | TARGET_TAG="dev" 6 | 7 | IMAGES=$(docker image ls --filter="reference=$TARGET_REPOSITORY*-$TARGET_TAG" --format="{{.Repository}}:{{.Tag}}") 8 | 9 | echo "removing old manifest" 10 | 11 | docker manifest rm indralabs/indra:$TARGET_TAG 12 | 13 | CMD="docker manifest create $MANIFEST_REPOSITORY:$TARGET_TAG" 14 | 15 | for image in $IMAGES; do 16 | CMD+=" --amend $image" 17 | done 18 | 19 | $CMD 20 | 21 | docker manifest push $MANIFEST_REPOSITORY:$TARGET_TAG 22 | docker manifest inspect $MANIFEST_REPOSITORY:$TARGET_TAG > release/indra-$TARGET_TAG/release/manifest-docker-$TARGET_TAG.json 23 | -------------------------------------------------------------------------------- /docker/release/scripts/push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TARGET_REPOSITORY="indralabs/indra-multi-arch" 4 | TARGET_TAG="dev" 5 | 6 | IMAGES=$(docker image ls --filter="reference=$TARGET_REPOSITORY*-$TARGET_TAG" --format="{{.Repository}}:{{.Tag}}") 7 | 8 | for i in $IMAGES; do 9 | 10 | echo "pushing "$i 11 | 12 | docker push $i 13 | 14 | done 15 | -------------------------------------------------------------------------------- /docker/release/targets/btcd/bin/btcctl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run --rm -it --network=btcd_indranet \ 4 | --volume=btcd_config:/etc/btcd:ro \ 5 | indralabs/btcctl-multi-arch:linux-amd64-dev \ 6 | --rpcserver=172.16.42.2:8334 \ 7 | --rpcuser=simnet --rpcpass=simnet \ 8 | --simnet $@ 9 | -------------------------------------------------------------------------------- /docker/release/targets/btcd/bin/btcctl-wallet: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run --rm -it --network=btcd_indranet \ 4 | --volume=btcd_btcwallet_config:/etc/btcwallet:ro \ 5 | --volume=btcd_btcwallet_data:/var/btcwallet \ 6 | indralabs/btcctl-multi-arch:linux-amd64-dev \ 7 | --configfile=/dev/null \ 8 | --rpcserver=172.16.42.3:8332 --rpccert=/etc/btcwallet/rpc.cert \ 9 | --rpcuser=simnet --rpcpass=simnet \ 10 | --simnet --wallet $@ 11 | 12 | -------------------------------------------------------------------------------- /docker/release/targets/btcd/btcctl.Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # --- 3 | # Target Configuration 4 | # --- 5 | 6 | ARG scratch_version="latest" 7 | 8 | FROM indralabs/scratch-multi-arch:${scratch_version} 9 | 10 | ARG platform 11 | ARG version 12 | 13 | ## We can't use 'COPY --from=...' here. Using ADD will enable multi-architecture releases 14 | ADD ./release/btcd-${version}/bin/${platform}/btcctl /bin 15 | ADD ./release/btcd-${version}/bin/${platform}/gencerts /bin 16 | 17 | # Enable the btcd user 18 | USER btcd:btcd 19 | 20 | # Set the data volumes 21 | #VOLUME ["/etc/btcd"] 22 | #VOLUME ["/var/btcd"] 23 | 24 | ENTRYPOINT ["/bin/btcctl", "--configfile=/dev/null", "--rpccert=/etc/btcd/keys/rpc.cert"] 25 | 26 | -------------------------------------------------------------------------------- /docker/release/targets/btcd/btcd.Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # --- 3 | # Target Configuration 4 | # --- 5 | 6 | ARG scratch_version="latest" 7 | 8 | FROM indralabs/scratch-multi-arch:${scratch_version} 9 | 10 | ARG platform 11 | ARG version 12 | 13 | ## We can't use 'COPY --from=...' here. Using ADD will enable multi-architecture releases 14 | ADD ./release/btcd-${version}/bin/${platform}/btcd /bin 15 | 16 | # Enable the btcd user 17 | USER btcd:btcd 18 | 19 | # Set the data volumes 20 | #VOLUME ["/etc/btcd"] 21 | #VOLUME ["/var/btcd"] 22 | 23 | # :8333 btcd peer-to-peer port 24 | # :8334 btcd RPC port 25 | EXPOSE 8333 8334 26 | 27 | ENTRYPOINT ["/bin/btcd", "--configfile=/dev/null", "--datadir=/var/btcd", "--logdir=/var/btcd", "--listen=0.0.0.0:8333", "--rpckey=/etc/btcd/keys/rpc.key", "--rpccert=/etc/btcd/keys/rpc.cert", "--rpclisten=0.0.0.0:8334"] 28 | -------------------------------------------------------------------------------- /docker/release/targets/btcd/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | btcd: 4 | image: indralabs/btcd-multi-arch:linux-amd64-dev 5 | volumes: 6 | - config:/etc/btcd 7 | - data:/var/btcd 8 | networks: 9 | indranet: 10 | ipv4_address: 172.16.42.2 11 | command: 12 | - "--rpcuser=simnet" 13 | - "--rpcpass=simnet" 14 | - "--simnet" 15 | - "--txindex" 16 | - "--miningaddr=SQWX48N37PFYbSrNqbZ8b4ZeYA3SPwwApR" 17 | btcwallet: 18 | image: indralabs/btcwallet-multi-arch:linux-amd64-dev 19 | volumes: 20 | - config:/etc/btcd:ro 21 | - btcwallet_config:/etc/btcwallet 22 | - btcwallet_data:/var/btcwallet 23 | networks: 24 | indranet: 25 | ipv4_address: 172.16.42.3 26 | depends_on: 27 | - btcd 28 | command: 29 | - "--simnet" 30 | - "--rpcconnect=172.16.42.2:8334" 31 | - "--username=simnet" 32 | - "--password=simnet" 33 | volumes: 34 | config: 35 | data: 36 | btcwallet_config: 37 | btcwallet_data: 38 | networks: 39 | indranet: 40 | driver: bridge 41 | ipam: 42 | driver: default 43 | config: 44 | - subnet: 172.16.42.0/24 45 | gateway: 172.16.42.1 46 | 47 | # docker-compose -f docker/btcd/docker-compose.yml up -------------------------------------------------------------------------------- /docker/release/targets/btcd/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Remove existing containers 4 | docker rm btcd-btcd-1 btcd-btcctl-1 btcd-btcwallet-1 2>/dev/null 5 | 6 | # Remove existing volumes 7 | docker volume rm btcd_config btcd_data btcd_btcwallet_config btcd_btcwallet_data 2>/dev/null 8 | 9 | # Setup an rpc key/cert for the btcwallet daemon 10 | docker run --rm -it \ 11 | --volume=btcd_btcwallet_config:/etc/btcwallet \ 12 | --entrypoint="/bin/gencerts" \ 13 | --user=8332:8332 \ 14 | indralabs/btcd-multi-arch:linux-amd64-dev \ 15 | --directory=/etc/btcwallet -H 172.16.42.3 -f 16 | 17 | # Create a new wallet 18 | docker run --rm -it \ 19 | --volume=btcd_btcwallet_config:/etc/btcwallet \ 20 | --volume=btcd_btcwallet_data:/var/btcwallet \ 21 | indralabs/btcwallet-multi-arch:linux-amd64-dev \ 22 | --simnet --createtemp 23 | 24 | #docker run --rm -it \ 25 | # --volume=btcd_btcwallet_config:/etc/btcwallet \ 26 | # --volume=btcd_btcwallet_data:/var/btcwallet \ 27 | # indralabs/btcwallet-multi-arch:linux-amd64-dev \ 28 | # --simnet importprivkey FuarsNCxniX277tBYt1BDGPB6cRTUfeEhUBXNAjrg3cdsWZTNcPj 29 | -------------------------------------------------------------------------------- /docker/release/targets/btcwallet/btcwallet.Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # --- 3 | # Target Configuration 4 | # --- 5 | 6 | ARG scratch_version="latest" 7 | 8 | FROM indralabs/scratch-multi-arch:${scratch_version} 9 | 10 | ARG platform 11 | ARG version 12 | 13 | ## We can't use 'COPY --from=...' here. Using ADD will enable multi-architecture releases 14 | ADD ./release/btcwallet-${version}/bin/${platform}/btcwallet /bin 15 | 16 | # Enable the btcd user 17 | USER btcwallet:btcwallet 18 | 19 | # Set the data volumes 20 | #VOLUME ["/etc/btcd"] 21 | #VOLUME ["/var/btcd"] 22 | 23 | # :8332 btcwallet RPC port 24 | EXPOSE 8332 25 | 26 | ENTRYPOINT ["/bin/btcwallet", "--configfile=/dev/null", "--appdata=/var/btcwallet", "--logdir=/var/btcwallet", "--cafile=/etc/btcd/keys/rpc.cert", "--rpckey=/etc/btcwallet/rpc.key", "--rpccert=/etc/btcwallet/rpc.cert", "--rpclisten=0.0.0.0:8332"] 27 | -------------------------------------------------------------------------------- /docker/release/targets/indra/indra.Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # --- 3 | # Target Configuration 4 | # --- 5 | 6 | ARG scratch_version="latest" 7 | 8 | FROM indralabs/scratch-multi-arch:${scratch_version} 9 | 10 | ARG platform 11 | ARG version 12 | 13 | ## We can't use 'COPY --from=...' here. Using ADD will enable multi-architecture releases 14 | ADD ./release/indra-${version}/bin/${platform}/indra /bin 15 | 16 | # Enable the btcd user 17 | USER indra:indra 18 | 19 | # Set the data volumes 20 | #VOLUME ["/etc/indra"] 21 | #VOLUME ["/var/indra"] 22 | #VOLUME ["/var/log/indra"] 23 | 24 | # :8333 indra peer-to-peer port 25 | # :8334 indra RPC port 26 | EXPOSE 8337 8338 27 | 28 | ENV INDRA_CONF_FILE=/etc/indra/indra.conf 29 | ENV INDRA_DATA_DIR=/var/indra 30 | ENV INDRA_LOGS_DIR=/var/log/indra 31 | 32 | ENV INDRA_RPC_UNIX_LISTEN=/var/run/indra/indra.sock 33 | 34 | ENTRYPOINT ["/bin/indra"] 35 | -------------------------------------------------------------------------------- /docker/release/targets/lnd/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /docker/release/targets/lnd/bin/lnsim-btcctl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run --rm -it --network=lnd_indranet \ 4 | --volume=lnd_btcd_config:/etc/btcd:ro \ 5 | indralabs/btcctl-multi-arch:linux-amd64-dev \ 6 | --rpcserver=172.16.43.2:8334 \ 7 | --rpcuser=simnet --rpcpass=simnet \ 8 | --simnet $@ 9 | -------------------------------------------------------------------------------- /docker/release/targets/lnd/bin/lnsim-lncli-alice: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run --rm -it \ 4 | --network=lnd_indranet \ 5 | --volume=lnd_lnd_alice_config:/etc/lnd:ro \ 6 | --volume=lnd_lnd_alice_data:/var/lnd:ro \ 7 | indralabs/lncli-multi-arch:linux-amd64-dev \ 8 | --rpcserver=172.16.43.10 \ 9 | --tlscertpath=/etc/lnd/keys/rpc.cert \ 10 | --chain=bitcoin --network=simnet $@ 11 | -------------------------------------------------------------------------------- /docker/release/targets/lnd/bin/lnsim-lncli-bob: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run --rm -it \ 4 | --network=lnd_indranet \ 5 | --volume=lnd_lnd_bob_config:/etc/lnd:ro \ 6 | --volume=lnd_lnd_bob_data:/var/lnd:ro \ 7 | indralabs/lncli-multi-arch:linux-amd64-dev \ 8 | --rpcserver=172.16.43.11 \ 9 | --tlscertpath=/etc/lnd/keys/rpc.cert \ 10 | --chain=bitcoin --network=simnet $@ 11 | -------------------------------------------------------------------------------- /docker/release/targets/lnd/bin/lnsim-lncli-miner: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run --rm -it \ 4 | --network=lnd_indranet \ 5 | --volume=lnd_lnd_miner_config:/etc/lnd:ro \ 6 | --volume=lnd_lnd_miner_data:/var/lnd:ro \ 7 | indralabs/lncli-multi-arch:linux-amd64-dev \ 8 | --rpcserver=172.16.43.9 \ 9 | --tlscertpath=/etc/lnd/keys/rpc.cert \ 10 | --chain=bitcoin --network=simnet $@ 11 | -------------------------------------------------------------------------------- /docker/release/targets/lnd/lncli.Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # --- 3 | # Target Configuration 4 | # --- 5 | 6 | ARG scratch_version="latest" 7 | 8 | FROM indralabs/scratch-multi-arch:${scratch_version} 9 | 10 | ARG platform 11 | ARG version 12 | 13 | ## We can't use 'COPY --from=...' here. Using ADD will enable multi-architecture releases 14 | ADD ./release/lnd-${version}/bin/${platform}/lncli /bin 15 | 16 | # Enable the btcd user 17 | USER lnd:lnd 18 | 19 | # Set the data volumes 20 | #VOLUME ["/etc/lnd"] 21 | #VOLUME ["/var/lnd"] 22 | 23 | ENTRYPOINT ["/bin/lncli", "--lnddir=/var/lnd", "--tlscertpath=/etc/lnd/keys/rpc.cert"] 24 | -------------------------------------------------------------------------------- /docker/release/targets/lnd/lnd.Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # --- 3 | # Target Configuration 4 | # --- 5 | 6 | ARG scratch_version="latest" 7 | 8 | FROM indralabs/scratch-multi-arch:${scratch_version} 9 | 10 | ARG platform 11 | ARG version 12 | 13 | ## We can't use 'COPY --from=...' here. Using ADD will enable multi-architecture releases 14 | ADD ./release/lnd-${version}/bin/${platform}/lnd /bin 15 | 16 | # Enable the btcd user 17 | USER lnd:lnd 18 | 19 | # Set the data volumes 20 | #VOLUME ["/etc/lnd"] 21 | #VOLUME ["/var/lnd"] 22 | 23 | # :9735 lnd peer-to-peer port 24 | # :10009 lnd RPC port 25 | EXPOSE 9735 10009 26 | 27 | ENTRYPOINT ["/bin/lnd", "--configfile=/dev/null", "--lnddir=/var/lnd", "--datadir=/var/lnd", "--logdir=/var/lnd", "--tlscertpath=/etc/lnd/keys/tls.cert", "--tlskeypath=/etc/lnd/keys/tls.key", "--feeurl=https://nodes.lightning.computer/fees/v1/btc-fee-estimates.json", "--listen=0.0.0.0:9735", "--rpclisten=0.0.0.0:10009"] 28 | CMD ["--bitcoin.active", "--bitcoin.mainnet", "--bitcoin.node=neutrino"] 29 | 30 | -------------------------------------------------------------------------------- /docker/scratch/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PLATFORMS=${SYS:-" 4 | linux/386 5 | linux/amd64 6 | linux/arm/v5 7 | linux/arm/v6 8 | linux/arm/v7 9 | linux/arm64 10 | linux/loong64 11 | linux/mips 12 | linux/mips64 13 | linux/mips64le 14 | linux/mipsle 15 | linux/ppc64 16 | linux/ppc64le 17 | linux/riscv64 18 | linux/s390x 19 | "} 20 | 21 | if [ -z "${TAG}" ]; then 22 | TAG="latest" 23 | fi 24 | 25 | if [ -z "${TARGET_TAG}" ]; then 26 | TARGET_TAG=$TAG 27 | fi 28 | 29 | for PLATFORM in $PLATFORMS; do 30 | 31 | DOCKER_PLATFORM_TAG=$(echo $PLATFORM | tr '/', '-')"-$TAG" 32 | 33 | echo "generating image: "$PLATFORM 34 | 35 | touch ${PWD}/release/scratch-$TAG/arch 36 | 37 | echo -e "FROM scratch\nADD release/scratch-$TAG/arch /$(echo $PLATFORM | tr '/', '-').arch\nADD release/scratch-$TAG/root-fs.tar.gz /\n" | \ 38 | docker build -t indralabs/scratch-multi-arch:$DOCKER_PLATFORM_TAG --platform=$PLATFORM -f- . 39 | 40 | done 41 | 42 | for PLATFORM in $PLATFORMS; do 43 | 44 | DOCKER_PLATFORM_TAG=$(echo $PLATFORM | tr '/', '-')"-$TAG" 45 | 46 | docker push indralabs/scratch-multi-arch:$DOCKER_PLATFORM_TAG 47 | 48 | done 49 | 50 | docker manifest rm indralabs/scratch:$TARGET_TAG 51 | 52 | CMD="docker manifest create indralabs/scratch:$TARGET_TAG" 53 | 54 | for PLATFORM in $PLATFORMS; do 55 | 56 | DOCKER_PLATFORM_TAG=$(echo $PLATFORM | tr '/', '-')"-$TAG" 57 | 58 | CMD+=" --amend indralabs/scratch-multi-arch:$DOCKER_PLATFORM_TAG" 59 | 60 | done 61 | 62 | $CMD 63 | 64 | docker manifest push indralabs/scratch:$TARGET_TAG 65 | -------------------------------------------------------------------------------- /docker/scratch/scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "${TAG}" ]; then 4 | TAG="latest" 5 | fi 6 | 7 | if [ -z "${TARGET_TAG}" ]; then 8 | TARGET_TAG=$TAG 9 | fi 10 | 11 | docker build -f ./docker/scratch/root-fs.Dockerfile -t indralabs/scratch-root-fs:$TAG . 12 | 13 | rm -rf release/scratch-$TARGET_TAG && mkdir -pv release/scratch-$TARGET_TAG 14 | 15 | docker run --rm -it --user=$UID:$GID \ 16 | --volume=${PWD}/release:/tmp/release indralabs/scratch-root-fs:$TAG \ 17 | cp /tmp/root-fs.tar.gz /tmp/release/scratch-$TARGET_TAG/root-fs.tar.gz 18 | -------------------------------------------------------------------------------- /docker/sim/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | #volumes: 3 | #vol_name: 4 | services: 5 | seed_1: 6 | image: indralabs/indra:latest 7 | container_name: indra-seed-1 8 | networks: 9 | indranet: 10 | ipv4_address: 172.16.238.2 11 | #ports: 12 | #- 8337:8337 13 | #- 8338:8338 14 | #environment: 15 | #POSTGRES_DB: indra-local 16 | #POSTGRES_USER: indra 17 | #POSTGRES_PASSWORD: password 18 | command: ["serve"] 19 | peer_1: 20 | image: indralabs/indra:latest 21 | container_name: indra-peer-1 22 | networks: 23 | indranet: 24 | ipv4_address: 172.16.238.3 25 | depends_on: 26 | - seed_1 27 | #ports: 28 | #- 8337:8337 29 | #- 8338:8338 30 | #environment: 31 | #POSTGRES_DB: indra-local 32 | #POSTGRES_USER: indra 33 | #POSTGRES_PASSWORD: password 34 | command: ["serve"] 35 | peer_2: 36 | image: indralabs/indra:latest 37 | container_name: indra-peer-2 38 | networks: 39 | indranet: 40 | ipv4_address: 172.16.238.4 41 | depends_on: 42 | - seed_1 43 | #ports: 44 | #- 8337:8337 45 | #- 8338:8338 46 | #environment: 47 | #POSTGRES_DB: indra-local 48 | #POSTGRES_USER: indra 49 | #POSTGRES_PASSWORD: password 50 | command: ["serve"] 51 | networks: 52 | indranet: 53 | driver: bridge 54 | ipam: 55 | driver: default 56 | config: 57 | - subnet: 172.16.238.0/24 58 | gateway: 172.16.238.1 59 | -------------------------------------------------------------------------------- /docker/sim/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | GOINSECURE="git-indra.lan/*" GOPRIVATE="git-indra.lan/*" go mod tidy 4 | 5 | IPFS_LOGGING=info go run ./cmd/indra/. $@ -------------------------------------------------------------------------------- /docker/sources/btcd/official.Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # --- 3 | # Build Process 4 | # --- 5 | 6 | ARG sourcing_image="golang" 7 | 8 | FROM indralabs/scratch:latest as scratch 9 | 10 | FROM ${sourcing_image} as source 11 | 12 | ARG source_url="https://github.com/btcsuite/btcd/releases/download" 13 | ARG source_version="v0.23.3" 14 | 15 | WORKDIR /tmp 16 | 17 | RUN set -ex echo "downloading source and binaries with manifest and signature." \ 18 | && wget ${source_url}/${source_version}/manifest-${source_version}.txt \ 19 | && wget ${source_url}/${source_version}/manifest-guggero-${source_version}.sig \ 20 | && wget ${source_url}/${source_version}/btcd-source-${source_version}.tar.gz 21 | 22 | # Importing keys from scratch 23 | COPY --from=scratch /etc/btcd/keys/guggero.asc /tmp/guggero.asc 24 | 25 | RUN set -ex echo "importing keys" \ 26 | && cat guggero.asc | gpg --import 27 | 28 | RUN set -ex echo "running signature verification on manifest" \ 29 | && gpg --verify manifest-guggero-${source_version}.sig manifest-${source_version}.txt 30 | 31 | RUN set -ex echo "verifying checksum on btcd-source-${source_version}.tar.gz" \ 32 | && cat manifest-${source_version}.txt | grep btcd-source-${source_version}.tar.gz | shasum -a 256 -c 33 | 34 | RUN set -ex echo "untarring binaries and source code" \ 35 | && mv btcd-source-${source_version}.tar.gz /tmp/btcd-source.tar.gz \ 36 | && mkdir -pv /tmp/btcd-source \ 37 | && tar -xzvf btcd-source.tar.gz --directory /tmp/btcd-source 38 | 39 | WORKDIR /tmp/btcd-source 40 | 41 | RUN set -ex echo "downloading modules" \ 42 | && go mod vendor 43 | 44 | FROM scratch 45 | 46 | COPY --from=source /tmp/btcd-source /source 47 | COPY --from=source /tmp/btcd-source.tar.gz /source.tar.gz 48 | -------------------------------------------------------------------------------- /docker/sources/btcwallet/official.Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # --- 3 | # Build Process 4 | # --- 5 | 6 | ARG sourcing_image="golang" 7 | 8 | FROM indralabs/scratch:latest as scratch 9 | 10 | FROM ${sourcing_image} as source 11 | 12 | ARG source_url="https://github.com/btcsuite/btcwallet" 13 | ARG source_version="v0.16.5" 14 | 15 | WORKDIR /tmp 16 | 17 | RUN set -ex echo "downloading source" \ 18 | && git clone ${source_url} 19 | 20 | # Importing keys from scratch 21 | COPY --from=scratch /etc/btcd/keys/guggero.asc /tmp/guggero.asc 22 | 23 | RUN set -ex echo "importing keys" \ 24 | && cat guggero.asc | gpg --import 25 | 26 | WORKDIR /tmp/btcwallet 27 | 28 | RUN set -ex echo "checking out tag" \ 29 | && git checkout -b ${source_version} 30 | 31 | RUN set -ex echo "running signature verification on tag" \ 32 | && git verify-tag ${source_version} 33 | 34 | RUN set -ex "archiving indra source files" \ 35 | && git archive --format=tar.gz -o /tmp/btcwallet-source.tar.gz ${source_version} 36 | 37 | WORKDIR /tmp 38 | 39 | RUN set -ex echo "untarring archived source code" \ 40 | && rm -rf btcwallet \ 41 | && mkdir -pv btcwallet-source \ 42 | && tar -xzvf btcwallet-source.tar.gz --directory btcwallet-source 43 | 44 | WORKDIR /tmp/btcwallet-source 45 | 46 | RUN set -ex echo "downloading modules" \ 47 | && go mod vendor 48 | 49 | FROM scratch 50 | 51 | COPY --from=source /tmp/btcwallet-source /source 52 | COPY --from=source /tmp/btcwallet-source.tar.gz /source.tar.gz 53 | -------------------------------------------------------------------------------- /docker/sources/indra/local.Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # --- 3 | # Build Process 4 | # --- 5 | 6 | ARG sourcing_image="golang" 7 | 8 | FROM ${sourcing_image} as source 9 | 10 | ADD . /tmp/indra-source 11 | 12 | WORKDIR /tmp/indra-source 13 | 14 | RUN set -ex "archiving indra source files" \ 15 | && git archive --format=tar.gz -o /tmp/indra-source.tar.gz HEAD 16 | 17 | WORKDIR /tmp 18 | 19 | RUN set -ex "cleaning up repository" \ 20 | && rm -rf /tmp/indra-source && mkdir /tmp/indra-source \ 21 | && tar -xzvf /tmp/indra-source.tar.gz --directory indra-source 22 | 23 | WORKDIR /tmp/indra-source 24 | 25 | RUN set -ex echo "downloading modules" \ 26 | && GOINSECURE=git-indra.lan/* GOPRIVATE=git-indra.lan/* go mod vendor 27 | 28 | FROM scratch 29 | 30 | COPY --from=source /tmp/indra-source /source 31 | COPY --from=source /tmp/indra-source.tar.gz /source.tar.gz 32 | -------------------------------------------------------------------------------- /docker/sources/indra/official.Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # --- 3 | # Build Process 4 | # --- 5 | 6 | ARG sourcing_image="golang" 7 | 8 | FROM indralabs/scratch:latest as scratch 9 | 10 | FROM ${sourcing_image} as source 11 | 12 | ARG source_url="https://git-indra.lan/indra-labs/indra/releases/download" 13 | ARG source_version="v0.1.10" 14 | 15 | WORKDIR /tmp 16 | 17 | RUN set -ex echo "downloading source and binaries with manifest and signature." \ 18 | && wget ${source_url}/${source_version}/manifest-${source_version}.txt \ 19 | && wget ${source_url}/${source_version}/manifest-greg.stone-${source_version}.sig \ 20 | && wget ${source_url}/${source_version}/manifest-херетик-${source_version}.sig \ 21 | && wget ${source_url}/${source_version}/indra-source-${source_version}.tar.gz 22 | 23 | # Importing keys from scratch 24 | COPY --from=scratch /etc/indra/keys/greg.stone.asc /tmp/greg.stone.asc 25 | COPY --from=scratch /etc/indra/keys/херетик.asc /tmp/херетик.asc 26 | 27 | RUN set -ex echo "importing keys" \ 28 | && cat a.asc | gpg --import \ 29 | && cat b.asc | gpg --import 30 | 31 | RUN set -ex echo "running signature verification on manifest" \ 32 | && gpg --verify manifest-greg.stone-${source_version}.sig manifest-${source_version}.txt \ 33 | && gpg --verify manifest-херетик-${source_version}.sig manifest-${source_version}.txt 34 | 35 | RUN set -ex echo "verifying checksum on indra-source-${source_version}.tar.gz" \ 36 | && cat manifest-${source_version}.txt | grep indra-source-${source_version}.tar.gz | shasum -a 256 -c 37 | 38 | RUN set -ex echo "untarring binaries and source code" \ 39 | && mv indra-source-${source_version}.tar.gz /tmp/indra-source.tar.gz \ 40 | && mkdir -pv /tmp/indra-source \ 41 | && tar -xzvf indra-source.tar.gz --directory /tmp/indra-source 42 | 43 | WORKDIR /tmp/indra-source 44 | 45 | RUN set -ex echo "downloading modules" \ 46 | && go mod vendor 47 | 48 | FROM scratch 49 | 50 | COPY --from=source /tmp/indra-source /source 51 | COPY --from=source /tmp/indra-source.tar.gz /source.tar.gz 52 | -------------------------------------------------------------------------------- /docker/sources/lnd/official.Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # --- 3 | # Build Process 4 | # --- 5 | 6 | ARG sourcing_image="golang" 7 | 8 | FROM indralabs/scratch:latest as scratch 9 | 10 | FROM ${sourcing_image} as source 11 | 12 | ARG source_url="https://github.com/lightningnetwork/lnd/releases/download" 13 | ARG source_version="v0.15.5-beta" 14 | 15 | WORKDIR /tmp 16 | 17 | RUN set -ex echo "downloading source and binaries with manifest and signature." \ 18 | && wget ${source_url}/${source_version}/manifest-${source_version}.txt \ 19 | && wget ${source_url}/${source_version}/manifest-roasbeef-${source_version}.sig \ 20 | && wget ${source_url}/${source_version}/lnd-source-${source_version}.tar.gz 21 | 22 | # Importing keys from scratch 23 | COPY --from=scratch /etc/lnd/keys/roasbeef.asc /tmp/roasbeef.asc 24 | 25 | RUN set -ex echo "importing keys" \ 26 | && cat roasbeef.asc | gpg --import 27 | 28 | RUN set -ex echo "running signature verification on manifest" \ 29 | && gpg --verify manifest-roasbeef-${source_version}.sig manifest-${source_version}.txt 30 | 31 | RUN set -ex echo "verifying checksum on lnd-source-${source_version}.tar.gz" \ 32 | && cat manifest-${source_version}.txt | grep lnd-source-${source_version}.tar.gz | shasum -a 256 -c 33 | 34 | RUN set -ex echo "untarring binaries and source code" \ 35 | && mv lnd-source-${source_version}.tar.gz /tmp/lnd-source.tar.gz \ 36 | && mkdir -pv /tmp/lnd-source \ 37 | && tar -xzvf lnd-source.tar.gz --directory /tmp/lnd-source 38 | 39 | WORKDIR /tmp/lnd-source/lnd-source 40 | 41 | RUN set -ex echo "downloading modules" \ 42 | && go mod vendor 43 | 44 | FROM scratch 45 | 46 | COPY --from=source /tmp/lnd-source/lnd-source /source 47 | COPY --from=source /tmp/lnd-source.tar.gz /source.tar.gz 48 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /docs/1507.05724v1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/1507.05724v1.pdf -------------------------------------------------------------------------------- /docs/2008-080.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/2008-080.pdf -------------------------------------------------------------------------------- /docs/2018-126.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/2018-126.pdf -------------------------------------------------------------------------------- /docs/DanezisG09.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/DanezisG09.pdf -------------------------------------------------------------------------------- /docs/acns11-certificateless.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/acns11-certificateless.pdf -------------------------------------------------------------------------------- /docs/architecture.md: -------------------------------------------------------------------------------- 1 | # architecture 2 | 3 | ## Overview 4 | 5 | 6 | 7 | ## Relay Engine 8 | 9 | 10 | 11 | ## Client 12 | 13 | -------------------------------------------------------------------------------- /docs/formats.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/formats.pdf -------------------------------------------------------------------------------- /docs/hidden1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/hidden1.png -------------------------------------------------------------------------------- /docs/hidden2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/hidden2.png -------------------------------------------------------------------------------- /docs/hidden3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/hidden3.png -------------------------------------------------------------------------------- /docs/image-20220912120917831.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/image-20220912120917831.png -------------------------------------------------------------------------------- /docs/indra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/indra.png -------------------------------------------------------------------------------- /docs/logo-for-dark-ground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/logo-for-dark-ground.png -------------------------------------------------------------------------------- /docs/logo-for-dark-tshirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/logo-for-dark-tshirt.png -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/logo.png -------------------------------------------------------------------------------- /docs/message_format.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/message_format.jpg -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | # docs 2 | 3 | [white paper](./whitepaper.md) 4 | 5 | [architecture](./architecture.md) 6 | 7 | [protocols](./protocols.md) -------------------------------------------------------------------------------- /docs/scripts/installtoc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo apt install -y fswatch 4 | go install github.com/nochso/tocenize/cmd/tocenize@latest 5 | -------------------------------------------------------------------------------- /docs/scripts/toc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | find .|grep md$|xargs -n1 tocenize -max 3 -min 2 4 | -------------------------------------------------------------------------------- /docs/session.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/session.png -------------------------------------------------------------------------------- /docs/torrelaycount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/torrelaycount.png -------------------------------------------------------------------------------- /docs/whitepaper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indra-labs/indra/089a0df491fd76ac393875053625f9fd4fdbe140/docs/whitepaper.pdf -------------------------------------------------------------------------------- /keys/greg.stone.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Comment: User-ID: greg stone 3 | Comment: Valid from: 27/01/2023 17:05 4 | Comment: Valid until: 27/01/2025 12:00 5 | Comment: Type: 256-bit EdDSA (secret key available) 6 | Comment: Usage: Signing, Encryption, Certifying User-IDs, SSH Authentication 7 | Comment: Fingerprint: E6657BDDAFC53ACDDA4F330E374F2004300A0D84 8 | 9 | mDMEY9QEXBYJKwYBBAHaRw8BAQdAMGN2R95Vn8/g9UF4ELR5DbSaIIULZ5SHXja4 10 | Jya/eta0IWdyZWcgc3RvbmUgPGdyZWcuc3RvbmVAaW5kcmEub3JnPoiWBBMWCAA+ 11 | FiEE5mV73a/FOs3aTzMON08gBDAKDYQFAmPUBFwCGyMFCQPDcOQFCwkIBwIGFQoJ 12 | CAsCBBYCAwECHgECF4AACgkQN08gBDAKDYRyNwD8Cjo/6jZaEUYAM8buTkiACmue 13 | Mny4tnMnOiYXv5q287AA/RtMUe6U2shzVi3gcNfYxvIlEJMI/FGiuFvqjNOSWIoI 14 | uDgEY9QEXBIKKwYBBAGXVQEFAQEHQPqEQ8QVXed0OEDmzSnNc216JqLvHuvovShH 15 | gjee+NgHAwEIB4h+BBgWCAAmFiEE5mV73a/FOs3aTzMON08gBDAKDYQFAmPUBFwC 16 | GwwFCQPDcOQACgkQN08gBDAKDYT9HgD+JvaMM2FPu5v4mCPyYRFZdwIlPcKQ5m8V 17 | hX/KBQ8bprAA/2cL6cNwWAv8RwpEUOt4yeRi07F0TDLuRoZhM/G76GAC 18 | =sseN 19 | -----END PGP PUBLIC KEY BLOCK----- -------------------------------------------------------------------------------- /keys/херетик.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Comment: User-ID: херетик 3 | Comment: Created: 02/07/2023 17:05 4 | Comment: Expires: 02/07/2025 12:00 5 | Comment: Type: 256-bit EdDSA (secret key available) 6 | Comment: Usage: Signing, Encryption, Certifying User-IDs, SSH Authentication 7 | Comment: Fingerprint: BB2D7666C9A72E94A8F25BE8CCE6B9B04F59BBDC 8 | 9 | mDMEZKGgPhYJKwYBBAHaRw8BAQdA0hxkL6gwZv79AGwAQ7JrzEugJxGxRXibUYDj 10 | yb0UlEO0ItGF0LXRgNC10YLQuNC6IDxoZXJldGlrQGluZHJhLm9yZz6IlgQTFggA 11 | PhYhBLstdmbJpy6UqPJb6MzmubBPWbvcBQJkoaA+AhsjBQkDw3DyBQsJCAcCBhUK 12 | CQgLAgQWAgMBAh4BAheAAAoJEMzmubBPWbvcYCQBAIhihZQ2b5GCU7mtQ3Dge/yc 13 | SEgkX7l9IvYpX8cXg8R5AQCAosx5EuXqu1gR1InVE3FxreT8e4ywLJ5hEi+EVbFM 14 | A7g4BGShoD4SCisGAQQBl1UBBQEBB0AR+gxrrIMixoPLx318412FGTeY+gcb+wO+ 15 | iNcCyLfrRgMBCAeIfgQYFggAJhYhBLstdmbJpy6UqPJb6MzmubBPWbvcBQJkoaA+ 16 | AhsMBQkDw3DyAAoJEMzmubBPWbvcNaMBAKfCmCMg7fBywZVdm0I+G7bV3scXnQGs 17 | ypm5LYxFFogVAQC3mMVrl/OnYoh6l49YA5K9l+HlN1nLNBo+DFArLATqBA== 18 | =YRa/ 19 | -----END PGP PUBLIC KEY BLOCK----- 20 | -------------------------------------------------------------------------------- /localversion.go: -------------------------------------------------------------------------------- 1 | //go:build local 2 | 3 | // Package indra is the root of the repository for the Indra distributed VPN, containing mainly the version information for included executables to use for information and identification on the network. 4 | // 5 | // todo: need to make cmd/bumper in use again so the below inaccurate git repo details are accurate. 6 | // 7 | // See [pkg/git.indra-labs.org/dev/ind/cmd/indra] for the main server executable. 8 | // 9 | // Put invocations to run all the generators in here check [pkg/git.indra-labs.org/dev/ind/cmd/bumper] to add them, and they will automatically run with: 10 | // 11 | // $ go generate . 12 | // 13 | // which will run all these generators below and finish with a go install. 14 | // 15 | //go:generate go install ./... 16 | package indra 17 | 18 | import "fmt" 19 | 20 | const ( 21 | // URL is the git URL for the repository. 22 | URL = "git.indra-labs.org/dev/ind" 23 | // GitRef is the gitref, as in refs/heads/branchname. 24 | GitRef = "refs/heads/master" 25 | // ParentGitCommit is the commit hash of the parent HEAD. 26 | ParentGitCommit = "5380d5f01e7d712258d7c1baa99172cc4bf014ef" 27 | // BuildTime stores the time when the current binary was built. 28 | BuildTime = "2023-06-17T09:14:01+01:00" 29 | // SemVer lists the (latest) git tag on the release. 30 | SemVer = "v0.1.20" 31 | // Major is the major number from the tag. 32 | Major = 0 33 | // Minor is the minor number from the tag. 34 | Minor = 1 35 | // Patch is the patch version number from the tag. 36 | Patch = 20 37 | ) 38 | 39 | var CI = "false" 40 | 41 | // Version returns a pretty printed version information string. 42 | func Version() string { 43 | return fmt.Sprint( 44 | "\nRepository Information\n", 45 | "\tGit repository: "+URL+"\n", 46 | "\tBranch: "+GitRef+"\n", 47 | "\tParentGitCommit: "+ParentGitCommit+"\n", 48 | "\tBuilt: "+BuildTime+"\n", 49 | "\tSemVer: "+SemVer+"\n", 50 | ) 51 | } 52 | -------------------------------------------------------------------------------- /pkg/cert/ad.go: -------------------------------------------------------------------------------- 1 | // Package cert is an interface for messages bearing signatures of network participants. 2 | package cert 3 | 4 | import ( 5 | "git.indra-labs.org/dev/ind/pkg/codec" 6 | "git.indra-labs.org/dev/ind/pkg/crypto" 7 | "github.com/libp2p/go-libp2p/core/peer" 8 | ) 9 | 10 | // Act is an interface for the signed messages stored in the PeerStore of the 11 | // libp2p host inside an indra engine. 12 | // 13 | // Note the abstract name and the open ended possibility of composing this into 14 | // other types of contract documents. In the language of Law an Act is the 15 | // prototype of a declaration or claim, a land title is an example of a type of 16 | // Act. 17 | type Act interface { 18 | codec.Codec 19 | Sign(key *crypto.Prv) (e error) 20 | Validate() bool 21 | PubKey() (pubKey *crypto.Pub) 22 | GetID() (id peer.ID, e error) 23 | Expired() (is bool) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/cfg/doc.go: -------------------------------------------------------------------------------- 1 | // Package cfg contains settings for the various network modes and seeds for those networks. 2 | package cfg 3 | -------------------------------------------------------------------------------- /pkg/cfg/seed_address.go: -------------------------------------------------------------------------------- 1 | package cfg 2 | 3 | // SeedAddress is a form of the key and network address for seeds on a network. 4 | type SeedAddress struct { 5 | 6 | // ID is the p2p identifier (peer identity key). 7 | ID string 8 | 9 | // DNSAddress is the hostname of the seed node 10 | DNSAddress string 11 | } 12 | 13 | // NewSeedAddress creates a new seed from a network address and seed public key in base58 encoding. 14 | func NewSeedAddress(dns string, id string) *SeedAddress { 15 | 16 | return &SeedAddress{ 17 | ID: id, 18 | DNSAddress: dns, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pkg/codec/ad/ad.go: -------------------------------------------------------------------------------- 1 | // Package ad is an abstract message type that composes the common elements of all ads - nonce ID, public key (identity), expiry and signature. 2 | // 3 | // The concrete ad types are in subfolders of this package. 4 | package ad 5 | 6 | import ( 7 | "git.indra-labs.org/dev/ind/pkg/crypto" 8 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 9 | "git.indra-labs.org/dev/ind/pkg/engine/magic" 10 | "git.indra-labs.org/dev/ind/pkg/util/slice" 11 | "time" 12 | ) 13 | 14 | const ( 15 | Len = magic.Len + 16 | nonce.IDLen + 17 | crypto.PubKeyLen + 18 | slice.Uint64Len + 19 | crypto.SigLen 20 | ) 21 | 22 | // Ad is an abstract message that isn't actually used, but rather is composed 23 | // into the rest being the common fields to all ads. 24 | type Ad struct { 25 | 26 | // ID is a random number generated for each new add that practically guarantees 27 | // no repeated hash for a message to be signed with the same peer identity key. 28 | ID nonce.ID 29 | 30 | // Key is the public identity key of the peer. 31 | Key *crypto.Pub 32 | 33 | // Expiry is the time after which this ad is considered no longer current. 34 | Expiry time.Time 35 | 36 | // Sig is the signature, which must match the Key above. 37 | Sig crypto.SigBytes 38 | } 39 | -------------------------------------------------------------------------------- /pkg/codec/ad/addresses/addresses_test.go: -------------------------------------------------------------------------------- 1 | package addresses 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 6 | "git.indra-labs.org/dev/ind/pkg/crypto" 7 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 8 | "git.indra-labs.org/dev/ind/pkg/util/ci" 9 | "git.indra-labs.org/dev/ind/pkg/util/splice" 10 | "github.com/multiformats/go-multiaddr" 11 | "testing" 12 | "time" 13 | ) 14 | 15 | func TestNew(t *testing.T) { 16 | ci.TraceIfNot() 17 | var e error 18 | pr, _, _ := crypto.NewSigner() 19 | id := nonce.NewID() 20 | var ma4, ma6 multiaddr.Multiaddr 21 | if ma4, e = multiaddr.NewMultiaddr("/ip4/127.0.0.1/tcp/4242"); fails(e) { 22 | t.FailNow() 23 | } 24 | if ma6, e = multiaddr.NewMultiaddr("/ip6/::1/tcp/4242"); fails(e) { 25 | t.FailNow() 26 | } 27 | aa := New(id, pr, []multiaddr.Multiaddr{ma4, ma6}, 28 | time.Now().Add(time.Hour*24*7)) 29 | l := aa.Len() 30 | log.I.Ln("l", l) 31 | s := splice.New(l) 32 | if e = aa.Encode(s); fails(e) { 33 | t.FailNow() 34 | } 35 | s.SetCursor(0) 36 | var onc codec.Codec 37 | if onc = reg.Recognise(s); onc == nil { 38 | t.Error("did not unwrap") 39 | t.FailNow() 40 | } 41 | if e = onc.Decode(s); fails(e) { 42 | t.Error("did not decode") 43 | t.FailNow() 44 | } 45 | var ad *Ad 46 | var ok bool 47 | if ad, ok = onc.(*Ad); !ok { 48 | t.Error("did not unwrap expected type") 49 | t.FailNow() 50 | } 51 | if ad.Expiry.Unix() != aa.Expiry.Unix() { 52 | t.Errorf("expiry did not decode correctly") 53 | t.FailNow() 54 | } 55 | log.D.S("received", ad) 56 | if ad.ID != aa.ID { 57 | t.Errorf("ID did not decode correctly") 58 | t.FailNow() 59 | } 60 | for i := range ad.Addresses { 61 | if ad.Addresses[i].String() != aa.Addresses[i].String() { 62 | t.Errorf("address did not decode correctly") 63 | t.FailNow() 64 | } 65 | } 66 | if !ad.Key.Equals(crypto.DerivePub(pr)) { 67 | t.Errorf("public key did not decode correctly") 68 | t.FailNow() 69 | } 70 | if !ad.Validate() { 71 | t.Errorf("received ad did not validate") 72 | t.FailNow() 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pkg/codec/ad/intro/intro_test.go: -------------------------------------------------------------------------------- 1 | package intro 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 6 | "git.indra-labs.org/dev/ind/pkg/util/ci" 7 | "git.indra-labs.org/dev/ind/pkg/util/splice" 8 | "testing" 9 | "time" 10 | 11 | "git.indra-labs.org/dev/ind/pkg/crypto" 12 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 13 | ) 14 | 15 | func TestNew(t *testing.T) { 16 | ci.TraceIfNot() 17 | var e error 18 | pr, ks, _ := crypto.NewSigner() 19 | introducer := ks.Next() 20 | id := nonce.NewID() 21 | in := New(id, pr, crypto.DerivePub(introducer), 22 | 20000, 80, time.Now().Add(time.Hour)) 23 | s := splice.New(in.Len()) 24 | if e = in.Encode(s); fails(e) { 25 | t.FailNow() 26 | } 27 | s.SetCursor(0) 28 | var onc codec.Codec 29 | if onc = reg.Recognise(s); onc == nil { 30 | t.Error("did not unwrap") 31 | t.FailNow() 32 | } 33 | if e = onc.Decode(s); fails(e) { 34 | t.Error("did not decode") 35 | t.FailNow() 36 | } 37 | var ad *Ad 38 | var ok bool 39 | if ad, ok = onc.(*Ad); !ok { 40 | t.Error("did not unwrap expected type") 41 | t.FailNow() 42 | } 43 | log.D.S(ad) 44 | if ad.ID != in.ID { 45 | t.Errorf("ID did not decode correctly") 46 | t.FailNow() 47 | } 48 | if ad.Port != in.Port { 49 | t.Errorf("port did not decode correctly") 50 | t.FailNow() 51 | } 52 | if ad.RelayRate != in.RelayRate { 53 | t.Errorf("relay rate did not decode correctly") 54 | t.FailNow() 55 | } 56 | if ad.Expiry.Unix() != in.Expiry.Unix() { 57 | t.Errorf("expiry did not decode correctly") 58 | t.FailNow() 59 | } 60 | if !ad.Key.Equals(crypto.DerivePub(pr)) { 61 | t.Errorf("public key did not decode correctly") 62 | t.FailNow() 63 | } 64 | if !ad.Introducer.Equals(crypto.DerivePub(introducer)) { 65 | t.Errorf("public key did not decode correctly") 66 | t.FailNow() 67 | } 68 | if !ad.Validate() { 69 | t.Errorf("received intro did not validate") 70 | t.FailNow() 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /pkg/codec/ad/load/load_test.go: -------------------------------------------------------------------------------- 1 | package load 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/util/ci" 6 | "testing" 7 | "time" 8 | 9 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 10 | "git.indra-labs.org/dev/ind/pkg/crypto" 11 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 12 | "git.indra-labs.org/dev/ind/pkg/util/splice" 13 | ) 14 | 15 | func TestNew(t *testing.T) { 16 | ci.TraceIfNot() 17 | var e error 18 | pr, _, _ := crypto.NewSigner() 19 | id := nonce.NewID() 20 | aa := New(id, pr, 10, time.Now().Add(time.Hour)) 21 | s := splice.New(aa.Len()) 22 | if e = aa.Encode(s); fails(e) { 23 | t.FailNow() 24 | } 25 | s.SetCursor(0) 26 | var onc codec.Codec 27 | if onc = reg.Recognise(s); onc == nil { 28 | t.Error("did not unwrap") 29 | t.FailNow() 30 | } 31 | if e = onc.Decode(s); fails(e) { 32 | t.Error("did not decode") 33 | t.FailNow() 34 | } 35 | var ad *Ad 36 | var ok bool 37 | if ad, ok = onc.(*Ad); !ok { 38 | t.Error("did not unwrap expected type") 39 | t.FailNow() 40 | } 41 | log.D.S(ad) 42 | if ad.ID != aa.ID { 43 | t.Errorf("ID did not decode correctly") 44 | t.FailNow() 45 | } 46 | if ad.Expiry.Unix() != aa.Expiry.Unix() { 47 | t.Errorf("expiry did not decode correctly") 48 | t.FailNow() 49 | } 50 | if !ad.Key.Equals(crypto.DerivePub(pr)) { 51 | t.Errorf("public key did not decode correctly") 52 | t.FailNow() 53 | } 54 | if ad.Load != aa.Load { 55 | t.Errorf("received ad did not have same load") 56 | t.FailNow() 57 | } 58 | if !ad.Validate() { 59 | t.Errorf("received ad did not validate") 60 | t.FailNow() 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pkg/codec/ad/peer/peer_test.go: -------------------------------------------------------------------------------- 1 | package peer 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/util/ci" 6 | "testing" 7 | "time" 8 | 9 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 10 | "git.indra-labs.org/dev/ind/pkg/crypto" 11 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 12 | "git.indra-labs.org/dev/ind/pkg/util/splice" 13 | ) 14 | 15 | func TestNew(t *testing.T) { 16 | ci.TraceIfNot() 17 | var e error 18 | pr, _, _ := crypto.NewSigner() 19 | id := nonce.NewID() 20 | aa := New(id, pr, 20000, time.Now().Add(time.Hour)) 21 | s := splice.New(aa.Len()) 22 | if e = aa.Encode(s); fails(e) { 23 | t.FailNow() 24 | } 25 | s.SetCursor(0) 26 | var onc codec.Codec 27 | if onc = reg.Recognise(s); onc == nil { 28 | t.Error("did not unwrap") 29 | t.FailNow() 30 | } 31 | if e = onc.Decode(s); fails(e) { 32 | t.Error("did not decode") 33 | t.FailNow() 34 | } 35 | var ad *Ad 36 | var ok bool 37 | if ad, ok = onc.(*Ad); !ok { 38 | t.Error("did not unwrap expected type") 39 | t.FailNow() 40 | } 41 | log.T.S(ad) 42 | if ad.ID != aa.ID { 43 | t.Errorf("ID did not decode correctly") 44 | t.FailNow() 45 | } 46 | if ad.Expiry.Unix() != aa.Expiry.Unix() { 47 | t.Errorf("expiry did not decode correctly") 48 | t.FailNow() 49 | } 50 | if !ad.Key.Equals(crypto.DerivePub(pr)) { 51 | t.Errorf("public key did not decode correctly") 52 | t.FailNow() 53 | } 54 | if ad.RelayRate != aa.RelayRate { 55 | t.Errorf("received ad did not have same relay rate") 56 | t.FailNow() 57 | } 58 | if !ad.Validate() { 59 | t.Errorf("received ad did not validate") 60 | t.FailNow() 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pkg/codec/ad/services/services_test.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 6 | "git.indra-labs.org/dev/ind/pkg/crypto" 7 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 8 | "git.indra-labs.org/dev/ind/pkg/util/ci" 9 | "git.indra-labs.org/dev/ind/pkg/util/splice" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | func TestServiceAd(t *testing.T) { 15 | ci.TraceIfNot() 16 | var e error 17 | pr, _, _ := crypto.NewSigner() 18 | id := nonce.NewID() 19 | sv := New(id, pr, []Service{{80, 62346}, {443, 42216}}, 20 | time.Now().Add(time.Hour)) 21 | log.D.S("service", sv) 22 | s := splice.New(sv.Len()) 23 | if e = sv.Encode(s); fails(e) { 24 | t.FailNow() 25 | } 26 | s.SetCursor(0) 27 | var onc codec.Codec 28 | if onc = reg.Recognise(s); onc == nil { 29 | t.Error("did not unwrap") 30 | t.FailNow() 31 | } 32 | if e = onc.Decode(s); fails(e) { 33 | t.Error("did not decode") 34 | t.FailNow() 35 | } 36 | log.T.S(onc) 37 | var svcAd *Ad 38 | var ok bool 39 | if svcAd, ok = onc.(*Ad); !ok { 40 | t.Error("did not unwrap expected type") 41 | t.FailNow() 42 | } 43 | if len(sv.Services) != len(svcAd.Services) { 44 | t.Errorf("number of services incorrectly decoded") 45 | t.FailNow() 46 | } 47 | for i := range sv.Services { 48 | if svcAd.Services[i].RelayRate != sv.Services[i].RelayRate { 49 | t.Errorf("relay rate did not decode correctly") 50 | t.FailNow() 51 | } 52 | if svcAd.Services[i].Port != sv.Services[i].Port { 53 | t.Errorf("port did not decode correctly") 54 | t.FailNow() 55 | } 56 | } 57 | if !svcAd.Key.Equals(crypto.DerivePub(pr)) { 58 | t.Errorf("public key did not decode correctly") 59 | t.FailNow() 60 | } 61 | if !svcAd.Validate() { 62 | t.Errorf("received service ad did not validate") 63 | t.FailNow() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /pkg/codec/interfaces.go: -------------------------------------------------------------------------------- 1 | // Package codec defines an interface for encoding and decoding message packets in the Indra network. 2 | // 3 | // These are implemented for onion messages, advertisements and other peer messages that are not for relaying. 4 | package codec 5 | 6 | import ( 7 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 8 | "git.indra-labs.org/dev/ind/pkg/util/splice" 9 | ) 10 | 11 | var ( 12 | log = log2.GetLogger() 13 | fails = log.E.Chk 14 | ) 15 | 16 | // Codec is a unit of data that can be read and written from a binary form. All 17 | // Onion are Codec but not all Codec are Onion. Codec is also used for the 18 | // Dispatcher's message headers. 19 | type Codec interface { 20 | 21 | // Magic is a 4 byte string identifying the type of the following message bytes. 22 | Magic() string 23 | 24 | // Encode uses the Codec's contents to encode into the splice.Splice next bytes. 25 | Encode(s *splice.Splice) (e error) 26 | 27 | // Decode reads in the data in the next bytes of the splice.Splice to populate this Codec. 28 | Decode(s *splice.Splice) (e error) 29 | 30 | // Len returns the number of bytes required to encode this Codec message (including Magic). 31 | // 32 | // This function must panic if called on a nil pointer as unconfigured 33 | // messages cannot yield a valid length value in many cases. 34 | Len() int 35 | 36 | // Unwrap gives access to any further layers embedded inside this (specifically, the Onion inside). 37 | Unwrap() interface{} 38 | } 39 | 40 | func MustNotBeNil(c Codec) { 41 | if c == nil { 42 | panic("cannot compute length without struct fields") 43 | } 44 | } 45 | 46 | // Encode is the generic encoder for a Codec, all can be encoded with it. 47 | func Encode(d Codec) (s *splice.Splice) { 48 | s = splice.New(d.Len()) 49 | fails(d.Encode(s)) 50 | return 51 | } 52 | -------------------------------------------------------------------------------- /pkg/codec/onion/cores/balance/balance_test.go: -------------------------------------------------------------------------------- 1 | package balance 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/ont" 6 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 7 | "git.indra-labs.org/dev/ind/pkg/util/ci" 8 | "testing" 9 | 10 | "github.com/lightningnetwork/lnd/lnwire" 11 | 12 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 13 | ) 14 | 15 | func TestOnions_Balance(t *testing.T) { 16 | ci.TraceIfNot() 17 | id := nonce.NewID() 18 | sats := lnwire.MilliSatoshi(10000) 19 | on := ont.Assemble([]ont.Onion{New(id, sats)}) 20 | s := codec.Encode(on) 21 | s.SetCursor(0) 22 | var onc codec.Codec 23 | if onc = reg.Recognise(s); onc == nil { 24 | t.Error("did not unwrap") 25 | t.FailNow() 26 | } 27 | if e := onc.Decode(s); fails(e) { 28 | t.Error("did not decode") 29 | t.FailNow() 30 | } 31 | var ci *Balance 32 | var ok bool 33 | if ci, ok = onc.(*Balance); !ok { 34 | t.Error("did not unwrap expected type") 35 | t.FailNow() 36 | } 37 | if ci.ID != id { 38 | t.Error("Keys did not decode correctly") 39 | t.FailNow() 40 | } 41 | if ci.MilliSatoshi != sats { 42 | t.Error("amount did not decode correctly") 43 | t.FailNow() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pkg/codec/onion/cores/confirmation/confirmation_test.go: -------------------------------------------------------------------------------- 1 | package confirmation 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/ont" 6 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 7 | "git.indra-labs.org/dev/ind/pkg/util/ci" 8 | "testing" 9 | 10 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 11 | ) 12 | 13 | func TestOnions_Confirmation(t *testing.T) { 14 | ci.TraceIfNot() 15 | id := nonce.NewID() 16 | on := ont.Assemble([]ont.Onion{New(id)}) 17 | s := codec.Encode(on) 18 | s.SetCursor(0) 19 | var onc codec.Codec 20 | if onc = reg.Recognise(s); onc == nil { 21 | 22 | t.Error("did not unwrap") 23 | t.FailNow() 24 | } 25 | if e := onc.Decode(s); fails(e) { 26 | t.Error("did not decode") 27 | t.FailNow() 28 | } 29 | var ci *Confirmation 30 | var ok bool 31 | if ci, ok = onc.(*Confirmation); !ok { 32 | t.Error("did not unwrap expected type") 33 | t.FailNow() 34 | } 35 | if ci.ID != id { 36 | t.Error("Keys did not decode correctly") 37 | t.FailNow() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pkg/codec/onion/cores/end/template.go: -------------------------------------------------------------------------------- 1 | // Package end is a null tombstone type onion message that indicates there is no more data in the onion (used with encoding only). 2 | package end 3 | 4 | import ( 5 | "git.indra-labs.org/dev/ind/pkg/codec" 6 | "git.indra-labs.org/dev/ind/pkg/codec/ont" 7 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 8 | "git.indra-labs.org/dev/ind/pkg/engine/sess" 9 | "git.indra-labs.org/dev/ind/pkg/engine/sessions" 10 | "git.indra-labs.org/dev/ind/pkg/util/splice" 11 | ) 12 | 13 | const ( 14 | Magic = "!!!!" 15 | Len = 0 16 | ) 17 | 18 | type End struct{} 19 | 20 | func (x *End) Account(res *sess.Data, sm *sess.Manager, 21 | s *sessions.Data, last bool) (skip bool, sd *sessions.Data) { 22 | return 23 | } 24 | 25 | func (x *End) Decode(s *splice.Splice) (e error) { return } 26 | func (x *End) Encode(s *splice.Splice) (e error) { return } 27 | func EndGen() codec.Codec { return &End{} } 28 | func (x *End) Unwrap() interface{} { return x } 29 | func (x *End) Handle(s *splice.Splice, p ont.Onion, ni ont.Ngin) (e error) { return } 30 | func (x *End) Len() int { 31 | 32 | codec.MustNotBeNil(x) 33 | 34 | return Len 35 | } 36 | func (x *End) Magic() string { return Magic } 37 | func (x *End) Wrap(inner ont.Onion) {} 38 | func NewEnd() *End { return &End{} } 39 | func init() { reg.Register(Magic, EndGen) } 40 | -------------------------------------------------------------------------------- /pkg/codec/onion/cores/response/response_test.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/onion/cores/end" 6 | "git.indra-labs.org/dev/ind/pkg/codec/ont" 7 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 8 | "testing" 9 | 10 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 11 | "git.indra-labs.org/dev/ind/pkg/crypto/sha256" 12 | "git.indra-labs.org/dev/ind/pkg/util/cryptorand" 13 | "git.indra-labs.org/dev/ind/pkg/util/slice" 14 | "git.indra-labs.org/dev/ind/pkg/util/tests" 15 | ) 16 | 17 | func TestOnions_Response(t *testing.T) { 18 | var e error 19 | id := nonce.NewID() 20 | var msg slice.Bytes 21 | var hash sha256.Hash 22 | if msg, hash, e = tests.GenMessage(10000, ""); fails(e) { 23 | t.Error(e) 24 | t.FailNow() 25 | } 26 | port := uint16(cryptorand.IntN(65536)) 27 | on := ont.Assemble([]ont.Onion{ 28 | New(id, port, msg, 0), 29 | end.NewEnd(), 30 | }) 31 | s := codec.Encode(on) 32 | s.SetCursor(0) 33 | var onc codec.Codec 34 | if onc = reg.Recognise(s); onc == nil { 35 | t.Error("did not unwrap") 36 | t.FailNow() 37 | } 38 | if e = onc.Decode(s); fails(e) { 39 | t.Error("did not decode") 40 | t.FailNow() 41 | } 42 | var rs *Response 43 | var ok bool 44 | if rs, ok = onc.(*Response); !ok { 45 | t.Error("did not unwrap expected type") 46 | t.FailNow() 47 | } 48 | plH := sha256.Single(rs.Bytes) 49 | if plH != hash { 50 | t.Errorf("exit message did not unwrap correctly") 51 | t.FailNow() 52 | } 53 | if rs.ID != id { 54 | t.Error("Keys did not decode correctly") 55 | t.FailNow() 56 | } 57 | if rs.Port != port { 58 | t.Error("port did not decode correctly") 59 | t.FailNow() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /pkg/codec/onion/crypt/crypt_test.go: -------------------------------------------------------------------------------- 1 | package crypt 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/onion/cores/confirmation" 6 | "git.indra-labs.org/dev/ind/pkg/codec/ont" 7 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 8 | "git.indra-labs.org/dev/ind/pkg/util/ci" 9 | "testing" 10 | 11 | "git.indra-labs.org/dev/ind/pkg/crypto" 12 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 13 | ) 14 | 15 | func TestOnions_SimpleCrypt(t *testing.T) { 16 | ci.TraceIfNot() 17 | var e error 18 | n := nonce.NewID() 19 | n1 := nonce.New() 20 | prv1, prv2 := crypto.GetTwoPrvKeys() 21 | pub1, pub2 := crypto.DerivePub(prv1), crypto.DerivePub(prv2) 22 | on := ont.Assemble([]ont.Onion{ 23 | New(pub1, pub2, prv2, n1, 0), 24 | confirmation.New(n), 25 | }) 26 | s := codec.Encode(on) 27 | s.SetCursor(0) 28 | log.D.S("encoded, encrypted onion:\n", s.GetAll().ToBytes()) 29 | var oncr codec.Codec 30 | if oncr = reg.Recognise(s); oncr == nil { 31 | t.Error("did not unwrap") 32 | t.FailNow() 33 | } 34 | if e = oncr.Decode(s); fails(e) { 35 | t.Error("did not decode") 36 | t.FailNow() 37 | } 38 | oncr.(*Crypt).Decrypt(prv1, s) 39 | var oncn codec.Codec 40 | if oncn = reg.Recognise(s); oncn == nil { 41 | t.Error("did not unwrap") 42 | t.FailNow() 43 | } 44 | if e = oncn.Decode(s); fails(e) { 45 | t.Error("did not decode") 46 | t.FailNow() 47 | } 48 | if cn, ok := oncn.(*confirmation.Confirmation); !ok { 49 | t.Error("did not get expected confirmation") 50 | t.FailNow() 51 | } else { 52 | if cn.ID != n { 53 | t.Error("did not get expected confirmation Keys") 54 | t.FailNow() 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /pkg/codec/onion/delay/delay_test.go: -------------------------------------------------------------------------------- 1 | package delay 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/ont" 6 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 7 | "git.indra-labs.org/dev/ind/pkg/util/ci" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestOnions_Delay(t *testing.T) { 13 | ci.TraceIfNot() 14 | dur := time.Second 15 | on := ont.Assemble([]ont.Onion{New(dur)}) 16 | s := codec.Encode(on) 17 | s.SetCursor(0) 18 | var onc codec.Codec 19 | if onc = reg.Recognise(s); onc == nil { 20 | t.Error("did not unwrap") 21 | t.FailNow() 22 | } 23 | if e := onc.Decode(s); fails(e) { 24 | t.Error("did not decode") 25 | t.FailNow() 26 | 27 | } 28 | var dl *Delay 29 | var ok bool 30 | if dl, ok = onc.(*Delay); !ok { 31 | t.Error("did not decode expected type") 32 | t.FailNow() 33 | } 34 | if dl.Duration != dur { 35 | t.Error("did not unwrap expected duration") 36 | t.FailNow() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pkg/codec/onion/exit/exit_test.go: -------------------------------------------------------------------------------- 1 | package exit 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/ont" 6 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 7 | "math/rand" 8 | "testing" 9 | 10 | "git.indra-labs.org/dev/ind/pkg/crypto" 11 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 12 | "git.indra-labs.org/dev/ind/pkg/crypto/sha256" 13 | "git.indra-labs.org/dev/ind/pkg/engine/sessions" 14 | "git.indra-labs.org/dev/ind/pkg/util/slice" 15 | "git.indra-labs.org/dev/ind/pkg/util/tests" 16 | ) 17 | 18 | func TestOnions_Exit(t *testing.T) { 19 | var e error 20 | prvs, pubs := crypto.GetCipherSet() 21 | ciphers := crypto.GenCiphers(prvs, pubs) 22 | var msg slice.Bytes 23 | var hash sha256.Hash 24 | if msg, hash, e = tests.GenMessage(512, "aoeu"); fails(e) { 25 | t.Error(e) 26 | t.FailNow() 27 | } 28 | n3 := crypto.Gen3Nonces() 29 | p := uint16(rand.Uint32()) 30 | id := nonce.NewID() 31 | ep := &ExitPoint{ 32 | Routing: &Routing{ 33 | Sessions: [3]*sessions.Data{}, 34 | Keys: prvs, 35 | Nonces: n3, 36 | }, 37 | ReturnPubs: pubs, 38 | } 39 | on := ont.Assemble([]ont.Onion{New(id, p, msg, ep)}) 40 | s := codec.Encode(on) 41 | s.SetCursor(0) 42 | var onc codec.Codec 43 | if onc = reg.Recognise(s); onc == nil { 44 | t.Error("did not unwrap") 45 | t.FailNow() 46 | } 47 | if e = onc.Decode(s); fails(e) { 48 | t.Error("did not decode") 49 | t.FailNow() 50 | } 51 | var ex *Exit 52 | var ok bool 53 | if ex, ok = onc.(*Exit); !ok { 54 | t.Error("did not unwrap expected type") 55 | t.FailNow() 56 | } 57 | if ex.ID != id { 58 | t.Error("Keys did not decode correctly") 59 | t.FailNow() 60 | } 61 | for i := range ex.Ciphers { 62 | if ex.Ciphers[i] != ciphers[i] { 63 | t.Errorf("cipher %d did not unwrap correctly", i) 64 | t.FailNow() 65 | } 66 | } 67 | for i := range ex.Nonces { 68 | if ex.Nonces[i] != n3[i] { 69 | t.Errorf("nonce %d did not unwrap correctly", i) 70 | t.FailNow() 71 | } 72 | } 73 | plH := sha256.Single(ex.Bytes) 74 | if plH != hash { 75 | t.Errorf("exit message did not unwrap correctly") 76 | t.FailNow() 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /pkg/codec/onion/forward/forward_test.go: -------------------------------------------------------------------------------- 1 | package forward 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/ont" 6 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 7 | "git.indra-labs.org/dev/ind/pkg/util/ci" 8 | "git.indra-labs.org/dev/ind/pkg/util/multi" 9 | "github.com/multiformats/go-multiaddr" 10 | "math/rand" 11 | "net" 12 | "net/netip" 13 | "reflect" 14 | "testing" 15 | 16 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 17 | ) 18 | 19 | func TestOnions_Forward(t *testing.T) { 20 | ci.TraceIfNot() 21 | ipSizes := []int{net.IPv6len, net.IPv4len} 22 | for i := range ipSizes { 23 | n := nonce.New() 24 | ip := net.IP(n[:ipSizes[i]]) 25 | var adr netip.Addr 26 | if ipSizes[i] == net.IPv4len { 27 | ip = ip.To4() 28 | } 29 | if ipSizes[i] == net.IPv6len { 30 | ip = ip.To16() 31 | } 32 | var ok bool 33 | if adr, ok = netip.AddrFromSlice(ip); !ok { 34 | t.Error("unable to get netip.Addrs") 35 | t.FailNow() 36 | } 37 | port := uint16(rand.Uint32()) 38 | ap := netip.AddrPortFrom(adr, port) 39 | var ma multiaddr.Multiaddr 40 | var e error 41 | if ma, e = multi.AddrFromAddrPort(ap); fails(e) { 42 | t.FailNow() 43 | } 44 | log.D.S("ma", ma) 45 | on := ont.Assemble([]ont.Onion{New(ma)}) 46 | s := codec.Encode(on) 47 | log.D.S("forward", s.GetAll().ToBytes()) 48 | s.SetCursor(0) 49 | var onr codec.Codec 50 | if onr = reg.Recognise(s); onr == nil { 51 | t.Error("did not unwrap") 52 | t.FailNow() 53 | } 54 | if e := onr.Decode(s); fails(e) { 55 | t.Error("did not decode") 56 | t.FailNow() 57 | 58 | } 59 | var cf *Forward 60 | if cf, ok = onr.(*Forward); !ok { 61 | t.Error("did not unwrap expected type expected *Forward got", 62 | reflect.TypeOf(onr)) 63 | t.FailNow() 64 | } 65 | if cf.Multiaddr.String() != ma.String() { 66 | t.Error("reverse Addresses did not unwrap correctly") 67 | t.FailNow() 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pkg/codec/onion/getbalance/getbalance_test.go: -------------------------------------------------------------------------------- 1 | package getbalance 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/onion/exit" 6 | "git.indra-labs.org/dev/ind/pkg/codec/ont" 7 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 8 | "testing" 9 | 10 | "git.indra-labs.org/dev/ind/pkg/crypto" 11 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 12 | "git.indra-labs.org/dev/ind/pkg/engine/sessions" 13 | ) 14 | 15 | func TestOnions_GetBalance(t *testing.T) { 16 | var e error 17 | n3 := crypto.Gen3Nonces() 18 | id := nonce.NewID() 19 | _, ks, _ := crypto.NewSigner() 20 | var prvs crypto.Privs 21 | for i := range prvs { 22 | prvs[i] = ks.Next() 23 | } 24 | var pubs crypto.Pubs 25 | for i := range pubs { 26 | pubs[i] = crypto.DerivePub(prvs[i]) 27 | } 28 | ep := &exit.ExitPoint{ 29 | Routing: &exit.Routing{ 30 | Sessions: [3]*sessions.Data{}, 31 | Keys: prvs, 32 | Nonces: n3, 33 | }, 34 | ReturnPubs: pubs, 35 | } 36 | on := ont.Assemble([]ont.Onion{NewGetBalance(id, ep)}) 37 | s := codec.Encode(on) 38 | s.SetCursor(0) 39 | var onc codec.Codec 40 | if onc = reg.Recognise(s); onc == nil { 41 | t.Error("did not unwrap") 42 | t.FailNow() 43 | } 44 | if e = onc.Decode(s); fails(e) { 45 | t.Error("did not decode") 46 | t.FailNow() 47 | } 48 | var ex *GetBalance 49 | var ok bool 50 | if ex, ok = onc.(*GetBalance); !ok { 51 | t.Error("did not unwrap expected type") 52 | t.FailNow() 53 | } 54 | if ex.ID != id { 55 | t.Error("Keys did not decode correctly") 56 | t.FailNow() 57 | } 58 | for i := range ex.Ciphers { 59 | if ex.Ciphers[i] != on.(*GetBalance).Ciphers[i] { 60 | t.Errorf("cipher %d did not unwrap correctly", i) 61 | t.FailNow() 62 | } 63 | } 64 | for i := range ex.Nonces { 65 | if ex.Nonces[i] != n3[i] { 66 | t.Errorf("nonce %d did not unwrap correctly", i) 67 | t.FailNow() 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pkg/codec/onion/hidden/introquery/introquery_test.go: -------------------------------------------------------------------------------- 1 | package introquery 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/onion/cores/end" 6 | "git.indra-labs.org/dev/ind/pkg/codec/onion/exit" 7 | "git.indra-labs.org/dev/ind/pkg/codec/ont" 8 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 9 | "git.indra-labs.org/dev/ind/pkg/util/ci" 10 | "testing" 11 | 12 | "git.indra-labs.org/dev/ind/pkg/crypto" 13 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 14 | "git.indra-labs.org/dev/ind/pkg/engine/sessions" 15 | ) 16 | 17 | func TestOnions_IntroQuery(t *testing.T) { 18 | ci.TraceIfNot() 19 | var e error 20 | prvs, pubs := crypto.GetCipherSet() 21 | ciphers := crypto.GenCiphers(prvs, pubs) 22 | prv1, _ := crypto.GetTwoPrvKeys() 23 | pub1 := crypto.DerivePub(prv1) 24 | n3 := crypto.Gen3Nonces() 25 | ep := &exit.ExitPoint{ 26 | Routing: &exit.Routing{ 27 | Sessions: [3]*sessions.Data{}, 28 | Keys: prvs, 29 | Nonces: n3, 30 | }, 31 | ReturnPubs: pubs, 32 | } 33 | id := nonce.NewID() 34 | on := ont.Assemble([]ont.Onion{ 35 | New(id, crypto.DerivePub(prv1), ep), 36 | end.NewEnd(), 37 | }) 38 | s := codec.Encode(on) 39 | s.SetCursor(0) 40 | var onc codec.Codec 41 | if onc = reg.Recognise(s); onc == nil { 42 | t.Error("did not unwrap") 43 | t.FailNow() 44 | } 45 | if e = onc.Decode(s); fails(e) { 46 | t.Error("did not decode") 47 | t.FailNow() 48 | } 49 | log.D.Ln(s) 50 | var ex *IntroQuery 51 | var ok bool 52 | if ex, ok = onc.(*IntroQuery); !ok { 53 | t.Error("did not unwrap expected type") 54 | t.FailNow() 55 | } 56 | for i := range ex.Ciphers { 57 | if ex.Ciphers[i] != ciphers[i] { 58 | t.Errorf("cipher %d did not unwrap correctly", i) 59 | t.FailNow() 60 | } 61 | } 62 | for i := range ex.Nonces { 63 | if ex.Nonces[i] != n3[i] { 64 | t.Errorf("nonce %d did not unwrap correctly", i) 65 | t.FailNow() 66 | } 67 | } 68 | if !ex.Key.Equals(pub1) { 69 | t.Error("HiddenService did not decode correctly") 70 | t.FailNow() 71 | } 72 | if ex.ID != id { 73 | t.Error("Keys did not decode correctly") 74 | t.FailNow() 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /pkg/codec/onion/hidden/whisper/whisper_test.go: -------------------------------------------------------------------------------- 1 | package whisper 2 | -------------------------------------------------------------------------------- /pkg/codec/onion/reverse/reverse_test.go: -------------------------------------------------------------------------------- 1 | package reverse 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/ont" 6 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 7 | "git.indra-labs.org/dev/ind/pkg/util/ci" 8 | "git.indra-labs.org/dev/ind/pkg/util/multi" 9 | "github.com/multiformats/go-multiaddr" 10 | "math/rand" 11 | "net" 12 | "net/netip" 13 | "reflect" 14 | "testing" 15 | 16 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 17 | ) 18 | 19 | func TestOnions_Reverse(t *testing.T) { 20 | ci.TraceIfNot() 21 | ipSizes := []int{net.IPv4len, net.IPv6len} 22 | for i := range ipSizes { 23 | n := nonce.New() 24 | ip := net.IP(n[:ipSizes[i]]) 25 | var adr netip.Addr 26 | if ipSizes[i] == net.IPv4len { 27 | ip = ip.To4() 28 | } 29 | if ipSizes[i] == net.IPv6len { 30 | ip = ip.To16() 31 | } 32 | var ok bool 33 | if adr, ok = netip.AddrFromSlice(ip); !ok { 34 | t.Error("unable to get netip.Addrs") 35 | t.FailNow() 36 | } 37 | port := uint16(rand.Uint32()) 38 | ap := netip.AddrPortFrom(adr, port) 39 | var ma multiaddr.Multiaddr 40 | var e error 41 | if ma, e = multi.AddrFromAddrPort(ap); fails(e) { 42 | t.FailNow() 43 | } 44 | on := ont.Assemble([]ont.Onion{New(ma)}) 45 | s := codec.Encode(on) 46 | s.SetCursor(0) 47 | var onr codec.Codec 48 | if onr = reg.Recognise(s); onr == nil { 49 | t.Error("did not unwrap") 50 | t.FailNow() 51 | } 52 | if e := onr.Decode(s); fails(e) { 53 | t.Error("did not decode") 54 | t.FailNow() 55 | 56 | } 57 | var cf *Reverse 58 | if cf, ok = onr.(*Reverse); !ok { 59 | t.Error("did not unwrap expected type expected *Return got", 60 | reflect.TypeOf(onr)) 61 | t.FailNow() 62 | } 63 | if cf.Multiaddr.String() != ma.String() { 64 | log.I.S(cf.Multiaddr, ap) 65 | t.Error("reverse Addresses did not unwrap correctly") 66 | t.FailNow() 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /pkg/codec/onion/session/session_test.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/reg" 6 | "git.indra-labs.org/dev/ind/pkg/util/ci" 7 | "testing" 8 | ) 9 | 10 | func TestOnions_Session(t *testing.T) { 11 | ci.TraceIfNot() 12 | sess := New(1) 13 | ss := sess.(*Session) 14 | s := codec.Encode(sess) 15 | s.SetCursor(0) 16 | var onc codec.Codec 17 | if onc = reg.Recognise(s); onc == nil { 18 | t.Error("did not unwrap") 19 | t.FailNow() 20 | } 21 | if e := onc.Decode(s); fails(e) { 22 | t.Error("did not decode") 23 | t.FailNow() 24 | 25 | } 26 | var ci *Session 27 | var ok bool 28 | if ci, ok = onc.(*Session); !ok { 29 | t.Error("did not unwrap expected type") 30 | t.FailNow() 31 | } 32 | if !ci.Header.Prv.Equals(ss.Header.Prv) { 33 | t.Error("header key did not unwrap correctly") 34 | t.FailNow() 35 | } 36 | if !ci.Payload.Prv.Equals(ss.Payload.Prv) { 37 | t.Error("payload key did not unwrap correctly") 38 | t.FailNow() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pkg/crypto/ciph/cipher.go: -------------------------------------------------------------------------------- 1 | // Package ciph manages encryption ciphers and encrypting blobs of data. Keys 2 | // are generated using ECDH from a public and private secp256k1 combined, as 3 | // well as directly from a 32 byte secret in the form of a static array as used 4 | // in most cryptographic hash function implementations in Go. 5 | package ciph 6 | 7 | import ( 8 | "crypto/aes" 9 | "crypto/cipher" 10 | "git.indra-labs.org/dev/ind/pkg/crypto" 11 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 12 | "git.indra-labs.org/dev/ind/pkg/crypto/sha256" 13 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 14 | ) 15 | 16 | var ( 17 | log = log2.GetLogger() 18 | fails = log.E.Chk 19 | ) 20 | 21 | // BlockFromHash creates an AES block cipher from an sha256.Hash. 22 | func BlockFromHash(h sha256.Hash) (block cipher.Block) { 23 | 24 | // We can ignore the error because sha256.Hash is a valid key size. 25 | block, _ = aes.NewCipher(h[:]) 26 | return 27 | } 28 | 29 | // Encipher XORs the data with the block stream. This encrypts unencrypted data 30 | // and decrypts encrypted data. If the cipher.Block is nil, it panics (this 31 | // should never happen). 32 | func Encipher(blk cipher.Block, n nonce.IV, b []byte) { 33 | 34 | if blk == nil { 35 | panic("Encipher called without a block cipher provided") 36 | 37 | } else { 38 | cipher.NewCTR(blk, n[:]).XORKeyStream(b, b) 39 | 40 | } 41 | } 42 | 43 | // GetBlock returns a block cipher with a secret generated from the provided 44 | // keys using ECDH. 45 | func GetBlock(from *crypto.Prv, to *crypto.Pub) (block cipher.Block) { 46 | 47 | secret := crypto.ComputeSharedSecret(from, to) 48 | 49 | block, _ = aes.NewCipher(secret[:]) 50 | 51 | return 52 | } 53 | -------------------------------------------------------------------------------- /pkg/crypto/cloaked_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestCloakedPubKey(t *testing.T) { 10 | var e error 11 | var sendPriv *Prv 12 | if sendPriv, e = GeneratePrvKey(); fails(e) { 13 | return 14 | } 15 | sendPub := DerivePub(sendPriv) 16 | sendBytes := sendPub.ToBytes() 17 | var cloaked CloakedPubKey 18 | cloaked = GetCloak(sendPub) 19 | if !Match(cloaked, sendBytes) { 20 | t.Error("failed to recognise cloaked address") 21 | } 22 | rand.Seed(time.Now().Unix()) 23 | flip := rand.Intn(CloakLen) 24 | var broken CloakedPubKey 25 | copy(broken[:], cloaked[:]) 26 | broken[flip] = ^broken[flip] 27 | if Match(broken, sendBytes) { 28 | t.Error("recognised incorrectly broken cloaked address") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pkg/crypto/crypto_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | rand2 "crypto/rand" 5 | "git.indra-labs.org/dev/ind/pkg/util/ci" 6 | "testing" 7 | 8 | "git.indra-labs.org/dev/ind/pkg/crypto/sha256" 9 | ) 10 | 11 | func TestFromBased32(t *testing.T) { 12 | ci.TraceIfNot() 13 | var rBytes sha256.Hash 14 | var n int 15 | var e error 16 | if n, e = rand2.Read(rBytes[:]); n != 8 && fails(e) { 17 | t.FailNow() 18 | } 19 | var pr *Prv 20 | if pr, e = GeneratePrvKey(); fails(e) { 21 | t.FailNow() 22 | } 23 | for i := 0; i < 1<<16; i++ { 24 | var s SigBytes 25 | if s, e = Sign(pr, rBytes); fails(e) { 26 | t.FailNow() 27 | } 28 | // fmt.Println("sig", s) 29 | var sb SigBytes 30 | if sb, e = SigFromBased32(s.String()); fails(e) { 31 | t.FailNow() 32 | } 33 | if s != sb { 34 | t.Error("sig mismatch") 35 | t.FailNow() 36 | } 37 | rBytes = sha256.Single(rBytes[:]) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pkg/crypto/doc.go: -------------------------------------------------------------------------------- 1 | // Package crypto contains all the cryptographic primitives used in Indra. 2 | // 3 | // There is the standard public/private keys, signature and validation functions, cloaked public keys used in message and packets. 4 | // 5 | // A set of implementations of these primitives based on the above are present for implementing interfaces to use them with libp2p. 6 | // 7 | // A set of helper functions for frequently used operations involving multiple items or derivations are here also. 8 | package crypto 9 | -------------------------------------------------------------------------------- /pkg/crypto/nonce/id.go: -------------------------------------------------------------------------------- 1 | package nonce 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/base32" 6 | "git.indra-labs.org/dev/ind/pkg/crypto/sha256" 7 | "git.indra-labs.org/dev/ind/pkg/util/b32" 8 | "sync" 9 | ) 10 | 11 | const IDLen = 8 12 | 13 | var ( 14 | counter uint16 15 | // enc is a raw base32 encoder as IDs have a consistent set of extraneous 16 | // characters after 13 digits and do not need check bytes as they are compact 17 | // large numbers used as collision resistant nonces to identify items in lists. 18 | enc = base32.NewEncoding(b32.Based32Ciphers).EncodeToString 19 | idMx sync.Mutex 20 | seed sha256.Hash 21 | ) 22 | 23 | // ID is a value generated by the first 8 bytes truncated from the values of a 24 | // hash chain that reseeds from a CSPRNG at first use and every time it 25 | // generates 2^16 (65536) new ID's. 26 | type ID [IDLen]byte 27 | 28 | // NewID returns a random 8 byte nonce to be used as identifiers. 29 | func NewID() (t ID) { 30 | idMx.Lock() 31 | defer idMx.Unlock() 32 | if counter == 0 { 33 | // We reseed when the counter value overflows. 34 | reseed() 35 | } 36 | s := sha256.Single(seed[:]) 37 | copy(seed[:], s[:]) 38 | copy(t[:], seed[:IDLen]) 39 | counter++ 40 | return 41 | } 42 | 43 | // String encodes the ID using Based32. 44 | func (id ID) String() string { 45 | return enc(id[:])[:13] 46 | } 47 | 48 | func reseed() { 49 | if c, e := rand.Read(seed[:]); fails(e) && c != IDLen { 50 | } 51 | counter++ 52 | } 53 | -------------------------------------------------------------------------------- /pkg/crypto/nonce/nonce.go: -------------------------------------------------------------------------------- 1 | // Package nonce provides a simple interface for generating standard AES 2 | // encryption nonces that give strong cryptographic entropy to message 3 | // encryption, as well as 8 byte (64 bit) random private identifiers for 4 | // references between types. 5 | package nonce 6 | 7 | import ( 8 | "crypto/aes" 9 | "crypto/rand" 10 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 11 | ) 12 | 13 | // IVLen is the length of Initialization Vectors used in Indra. 14 | const IVLen = aes.BlockSize 15 | 16 | var ( 17 | log = log2.GetLogger() 18 | fails = log.E.Chk 19 | ) 20 | 21 | // IV is an Initialization Vector for AES-CTR encryption used in Indra. 22 | type IV [IVLen]byte 23 | 24 | // New reads a nonce from a cryptographically secure random number source. 25 | func New() (n IV) { 26 | if c, e := rand.Read(n[:]); fails(e) && c != IDLen { 27 | } 28 | return 29 | } 30 | -------------------------------------------------------------------------------- /pkg/crypto/public_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/util/ci" 5 | "testing" 6 | ) 7 | 8 | func TestBase32(t *testing.T) { 9 | ci.TraceIfNot() 10 | for i := 0; i < 10000; i++ { 11 | var k *Prv 12 | var e error 13 | if k, e = GeneratePrvKey(); fails(e) { 14 | t.Error(e) 15 | t.FailNow() 16 | } 17 | p := DerivePub(k) 18 | b32 := p.ToBased32() 19 | pr32 := k.ToBased32() 20 | // log.D.F("pub key: %d %s priv key: %d %s\n", len(b32), b32, len(pr32), pr32) 21 | var kk *Pub 22 | if kk, e = PubFromBased32(b32); fails(e) { 23 | t.Error(e) 24 | t.FailNow() 25 | } 26 | if b32 != kk.ToBased32() { 27 | t.Error("failed to decode public key") 28 | t.FailNow() 29 | } 30 | var pk *Prv 31 | if pk, e = PrvFromBased32(pr32); fails(e) { 32 | t.Error(e) 33 | t.FailNow() 34 | } 35 | if pr32 != pk.ToBased32() { 36 | t.Error("failed to decode private key") 37 | t.FailNow() 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pkg/crypto/sha256/sha256.go: -------------------------------------------------------------------------------- 1 | // Package sha256 provides a simple interface for single and double SHA256 2 | // hashes, used with secp256k1 signatures, message digest checksums, cloaked 3 | // public key "addresses" and so on. 4 | package sha256 5 | 6 | import ( 7 | "encoding/base32" 8 | "encoding/hex" 9 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 10 | "git.indra-labs.org/dev/ind/pkg/util/b32" 11 | "github.com/minio/sha256-simd" 12 | ) 13 | 14 | // Len is the number of bytes in a sha256 hash. 15 | const Len = 32 16 | 17 | var ( 18 | // enc is a raw base32 encoder as 256-bit hashes have a consistent set of 19 | // extraneous characters after 52 digits from padding and do not need check 20 | // bytes as they are compact large numbers for logs and message digests for 21 | // other things. 22 | enc = base32.NewEncoding(b32.Based32Ciphers).EncodeToString 23 | log = log2.GetLogger() 24 | fails = log.E.Chk 25 | ) 26 | 27 | // String returns the hex encoded form of a SHA256 hash. 28 | func (h Hash) String() string { 29 | return hex.EncodeToString(h[:]) 30 | } 31 | 32 | // Based32String returns the Basde32 encoded form of a SHA256 hash. 33 | func (h Hash) Based32String() string { 34 | return enc(h[:])[:52] 35 | } 36 | 37 | // Hash is just a 256-bit hash. 38 | type Hash [32]byte 39 | 40 | // Double runs a standard double SHA256 hash and does all the slicing for you. 41 | func Double(b []byte) Hash { 42 | h := Single(b) 43 | return Single(h[:]) 44 | } 45 | 46 | // New creates a correctly sized slice for a Hash. 47 | func New() Hash { return Hash{} } 48 | 49 | // Single runs a standard SHA256 hash. 50 | func Single(b []byte) Hash { return sha256.Sum256(b) } 51 | 52 | // Zero copies a cleanly initialised empty slice over top of the provided Hash. 53 | func Zero(h Hash) { copy(h[:], zero()) } 54 | 55 | // Zero out the values in the hash. Hashes can be used as secrets. 56 | func (h Hash) Zero() { Zero(h) } 57 | 58 | func zero() []byte { return make([]byte, Len) } 59 | -------------------------------------------------------------------------------- /pkg/crypto/sha256/sha256_test.go: -------------------------------------------------------------------------------- 1 | package sha256 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestHash_Equals(t *testing.T) { 8 | var h1, h2 Hash 9 | if h1 != h2 { 10 | t.FailNow() 11 | } 12 | h2[0] = 1 13 | if h1 == h2 { 14 | t.FailNow() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pkg/crypto/signature_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | // commenting out with change to schnorr and non-key-embedded signatures 4 | 5 | //func TestSignRecover(t *testing.T) { 6 | // msgSize := mrand.Intn(3072) + 1024 7 | // payload := make([]byte, msgSize) 8 | // var e error 9 | // var n int 10 | // if n, e = rand.Read(payload); log.E.Chk(e) && n != msgSize { 11 | // t.Error(e) 12 | // } 13 | // var prv1 *Prv 14 | // var pub1, rec1 *Pub 15 | // if prv1, e = GeneratePrvKey(); fails(e) { 16 | // t.Error(e) 17 | // } 18 | // pub1 = DerivePub(prv1) 19 | // var s SigBytes 20 | // hash := sha256.Single(payload) 21 | // if s, e = Sign(prv1, hash); fails(e) { 22 | // t.Error(e) 23 | // } 24 | // if rec1, e = s.Recover(hash); fails(e) { 25 | // t.Error(e) 26 | // } 27 | // if !pub1.Equals(rec1) { 28 | // t.Error(errors.New("recovery did not extract same key")) 29 | // } 30 | //} 31 | 32 | //func TestSignRecoverFail(t *testing.T) { 33 | // msgSize := mrand.Intn(3072) + 1024 34 | // payload := make([]byte, msgSize) 35 | // var e error 36 | // var n int 37 | // if n, e = rand.Read(payload); log.E.Chk(e) && n != msgSize { 38 | // t.Error(e) 39 | // } 40 | // var prv1 *Prv 41 | // var pub1, rec1 *Pub 42 | // if prv1, e = GeneratePrvKey(); fails(e) { 43 | // t.Error(e) 44 | // } 45 | // pub1 = DerivePub(prv1) 46 | // var s SigBytes 47 | // hash := sha256.Single(payload) 48 | // if s, e = Sign(prv1, hash); fails(e) { 49 | // t.Error(e) 50 | // } 51 | // copy(payload, make([]byte, 10)) 52 | // hash2 := sha256.Single(payload) 53 | // if rec1, e = s.Recover(hash2); fails(e) { 54 | // t.Error(e) 55 | // } 56 | // if pub1.Equals(rec1) { 57 | // t.Error(errors.New("recovery extracted the same key")) 58 | // } 59 | //} 60 | -------------------------------------------------------------------------------- /pkg/crypto/signer_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "testing" 5 | 6 | "git.indra-labs.org/dev/ind/pkg/crypto/sha256" 7 | "git.indra-labs.org/dev/ind/pkg/util/tests" 8 | ) 9 | 10 | // this just really demonstrates how the keys generated are not linkable. 11 | func TestKeySet_Next(t *testing.T) { 12 | var counter int 13 | const nRounds = 10000 14 | for rounds := 0; rounds < nRounds; rounds++ { 15 | key, ks, e := NewSigner() 16 | if fails(e) { 17 | t.FailNow() 18 | } 19 | var hx PubBytes 20 | if hx = DerivePub(key).ToBytes(); fails(e) { 21 | t.Error(e) 22 | } 23 | oddness := hx[0] 24 | // for i := 0; i < 100; i++ { 25 | key = ks.Next() 26 | if hx = DerivePub(key).ToBytes(); hx[0] == oddness { 27 | counter++ 28 | } 29 | // } 30 | } 31 | if counter == nRounds || counter == 0 { 32 | t.Error("all keys same oddness", counter) 33 | } 34 | } 35 | 36 | func BenchmarkKeySet_Next(b *testing.B) { 37 | _, ks, e := NewSigner() 38 | if fails(e) { 39 | b.FailNow() 40 | } 41 | for n := 0; n < b.N; n++ { 42 | _ = ks.Next() 43 | } 44 | } 45 | 46 | func BenchmarkKeySet_Next_Derive(b *testing.B) { 47 | _, ks, e := NewSigner() 48 | if fails(e) { 49 | b.FailNow() 50 | } 51 | for n := 0; n < b.N; n++ { 52 | k := ks.Next() 53 | DerivePub(k) 54 | } 55 | } 56 | 57 | func BenchmarkKeySet_Next_Sign(b *testing.B) { 58 | _, ks, e := NewSigner() 59 | if fails(e) { 60 | b.FailNow() 61 | } 62 | var msg []byte 63 | const msgLen = 1382 - 4 - SigLen 64 | msg, _, e = tests.GenMessage(msgLen, "herpderp") 65 | for n := 0; n < b.N; n++ { 66 | k := ks.Next() 67 | hash := sha256.Single(msg) 68 | if _, e = Sign(k, hash); fails(e) { 69 | b.Error("failed to sign") 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /pkg/crypto/util.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | // GenerateTestKeyPair generates a key pair. 4 | func GenerateTestKeyPair() (sp *Prv, sP *Pub, e error) { 5 | if sp, e = GeneratePrvKey(); fails(e) { 6 | return 7 | } 8 | sP = DerivePub(sp) 9 | return 10 | } 11 | 12 | // GenerateTestKeyPairs generates two public/private key pairs. 13 | func GenerateTestKeyPairs() (sp, rp *Prv, sP, rP *Pub, e error) { 14 | sp, sP, e = GenerateTestKeyPair() 15 | rp, rP, e = GenerateTestKeyPair() 16 | return 17 | } 18 | -------------------------------------------------------------------------------- /pkg/docker/config.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "github.com/docker/docker/api/types" 5 | ) 6 | 7 | type BuildConfiguration struct { 8 | Name string 9 | ContextFilePath string 10 | BuildOpts types.ImageBuildOptions 11 | PushOpts types.ImagePushOptions 12 | } 13 | 14 | // FixTagPrefix returns the full set of tags in the BuildConfiguration. 15 | func (bc *BuildConfiguration) FixTagPrefix() (fullTags []string) { 16 | 17 | for _, tag := range bc.BuildOpts.Tags { 18 | 19 | fullTags = append(fullTags, bc.Name+":"+tag) 20 | } 21 | 22 | return fullTags 23 | } 24 | -------------------------------------------------------------------------------- /pkg/docker/doc.go: -------------------------------------------------------------------------------- 1 | // Package docker contains a library for building and pushing docker images for Indra to a configured docker repository. 2 | package docker 3 | -------------------------------------------------------------------------------- /pkg/engine/acct.go: -------------------------------------------------------------------------------- 1 | package engine 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/onion/crypt" 6 | "git.indra-labs.org/dev/ind/pkg/codec/ont" 7 | "git.indra-labs.org/dev/ind/pkg/engine/sess" 8 | "git.indra-labs.org/dev/ind/pkg/engine/sessions" 9 | ) 10 | 11 | // PostAcctOnion takes a slice of Skins and calculates their costs and 12 | // the list of sessions inside them and attaches accounting operations to 13 | // apply when the associated confirmation(s) or response hooks are executed. 14 | func PostAcctOnion(sm *sess.Manager, o Skins) (res *sess.Data) { 15 | res = &sess.Data{} 16 | assembled := ont.Assemble(o) 17 | sp := codec.Encode(assembled) 18 | res.B = sp.GetAll() 19 | // do client accounting 20 | skip := false 21 | var last bool 22 | for i := range o { 23 | if skip { 24 | skip = false 25 | continue 26 | } 27 | switch on := o[i].(type) { 28 | case *crypt.Crypt: 29 | if i == len(o)-1 { 30 | last = true 31 | } 32 | var s *sessions.Data 33 | skip, s = on.Account(res, sm, nil, last) 34 | if last { 35 | break 36 | } 37 | o[i+1].Account(res, sm, s, last) 38 | } 39 | } 40 | return 41 | } 42 | -------------------------------------------------------------------------------- /pkg/engine/consts/hiddenconst.go: -------------------------------------------------------------------------------- 1 | // Package consts is a series of constants common to several different onion message types. 2 | package consts 3 | 4 | import ( 5 | "git.indra-labs.org/dev/ind/pkg/crypto" 6 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 7 | "git.indra-labs.org/dev/ind/pkg/engine/magic" 8 | "git.indra-labs.org/dev/ind/pkg/util/splice" 9 | ) 10 | 11 | // ReverseCryptLen is 12 | // 13 | // Deprecated: this is now a variable length structure, Reverse is being 14 | // obsoleted in favour of Offset. 15 | const ReverseCryptLen = ReverseLen + CryptLen 16 | 17 | // RoutingHeaderLen is 18 | // 19 | // Deprecated: this is now a variable length structure. 20 | const RoutingHeaderLen = 3 * ReverseCryptLen 21 | 22 | const CryptLen = magic.Len + 23 | nonce.IVLen + 24 | crypto.CloakLen + 25 | crypto.PubKeyLen 26 | 27 | // ReverseLen is 28 | // 29 | // Deprecated: Reverse is being obsoleted in favour of Offset. 30 | const ReverseLen = magic.Len + 1 + splice.AddrLen 31 | -------------------------------------------------------------------------------- /pkg/engine/dispatcher/doc.go: -------------------------------------------------------------------------------- 1 | // Package dispatcher is a network packet send/receive handler for peer to peer connections between relays. 2 | // 3 | // Messages between peers are usually somewhat large, multi-layered onion messages that contain forwarding instructions for sending, and the dispatcher breaks them down into uniform sized segments and randomises their order. 4 | // 5 | // On the receiving side, there is a buffer for incoming message segments, and when sufficient segments are received to enable reconstruction, reconstruction is attempted. 6 | // 7 | // Messages are broken up into pieces with additional segments added to ensure the receiver gets enough pieces to decode the message without a message retransmit request, using Reed Solomon encoding (accelerated by AVX). 8 | // 9 | // The dispatcher operates with reliable TCP connections, and does not directly influence retransmit but instead monitors the latency of the messages and identifies when there has been a retransmit and increases the redundant data added to the stream of message packet segments. 10 | // 11 | // In this way, the dispatcher aims to always see sufficient data arrive in one message cycle so to minimise the latency of connections between peers, and the subsequent latency of client's routed packets. 12 | // 13 | // Another feature of the dispatcher is key change processing - this is implemented as a concurrent update from the receiver specifying a new public key to use, it keeps the old key for a time to deal with in transit messages that were encrypted with the old key and the peer should update its key to use for all messages after the key has been received and updated. 14 | package dispatcher 15 | -------------------------------------------------------------------------------- /pkg/engine/doc.go: -------------------------------------------------------------------------------- 1 | // Package engine is the implementation of the core Indra relay and client. 2 | // 3 | // Contains controlling code that interacts with the various subsystems of the engine such as the dispatcher, session manager, libp2p based peer to peer network transport and peer information gossip. 4 | package engine 5 | -------------------------------------------------------------------------------- /pkg/engine/engine_test.go: -------------------------------------------------------------------------------- 1 | package engine 2 | 3 | // All broken currently because listeners don't have code to operate without a libp2p Host. 4 | -------------------------------------------------------------------------------- /pkg/engine/magic/magic.go: -------------------------------------------------------------------------------- 1 | // Package magic is a simple specification and error helper for message identifying 4 byte strings that are used for the switching logic of a relay. 2 | package magic 3 | 4 | import "fmt" 5 | 6 | const ( 7 | // Len is the length in bytes of the magic bytes that prefixes all Indra 8 | // messages. 9 | Len = 4 10 | 11 | // ErrTooShort is an error for codec.Codec implementations to signal a message 12 | // buffer is shorter than the minimum defined for the message type. 13 | ErrTooShort = "'%s' message minimum size: %d got: %d" 14 | ) 15 | 16 | // TooShort is a helper function to return an error for a truncated packet. 17 | func TooShort(got, found int, magic string) (e error) { 18 | if got >= found { 19 | return 20 | } 21 | e = fmt.Errorf(ErrTooShort, magic, found, got) 22 | return 23 | 24 | } 25 | -------------------------------------------------------------------------------- /pkg/engine/packet/doc.go: -------------------------------------------------------------------------------- 1 | // Package packet handles segmenting messages into uniform sized packets and generating a stream of cipher halves and receiver cloaked addresses to encrypt them with, and reassembling the segments into the original messages. 2 | package packet 3 | -------------------------------------------------------------------------------- /pkg/engine/packet/packet_test.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "crypto/rand" 5 | "errors" 6 | "testing" 7 | 8 | "git.indra-labs.org/dev/ind/pkg/crypto" 9 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 10 | "git.indra-labs.org/dev/ind/pkg/crypto/sha256" 11 | ) 12 | 13 | func TestEncode_Decode(t *testing.T) { 14 | msgSize := 1382 15 | payload := make([]byte, msgSize) 16 | var e error 17 | var n int 18 | if n, e = rand.Read(payload); fails(e) && n != msgSize { 19 | t.Error(e) 20 | } 21 | payload = append([]byte("payload"), payload...) 22 | pHash := sha256.Single(payload) 23 | var sp, rp *crypto.Prv 24 | var sP, rP *crypto.Pub 25 | if sp, rp, sP, rP, e = crypto.GenerateTestKeyPairs(); fails(e) { 26 | t.FailNow() 27 | } 28 | addr := rP 29 | var pkt []byte 30 | params := &PacketParams{ 31 | To: addr, 32 | From: sp, 33 | Data: payload, 34 | Seq: 234, 35 | Parity: 64, 36 | Length: msgSize, 37 | } 38 | if pkt, e = EncodePacket(params); fails(e) { 39 | t.Error(e) 40 | } 41 | var from *crypto.Pub 42 | var to crypto.CloakedPubKey 43 | _ = to 44 | var iv nonce.IV 45 | if from, to, iv, e = GetKeysFromPacket(pkt); fails(e) { 46 | t.Error(e) 47 | } 48 | if !sP.ToBytes().Equals(from.ToBytes()) { 49 | t.Error(e) 50 | } 51 | var f *Packet 52 | if f, e = DecodePacket(pkt, from, rp, iv); fails(e) { 53 | t.Error(e) 54 | } 55 | dHash := sha256.Single(f.Data) 56 | if pHash != dHash { 57 | t.Error(errors.New("encode/decode unsuccessful")) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pkg/engine/packet/segcalc_test.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | var Expected = []string{ 9 | ` 10 | Segments{ 11 | Segment{ DStart: 0, DEnd: 192, PEnd: 256, SLen: 195, Last: 195}, 12 | Segment{ DStart: 256, DEnd: 448, PEnd: 512, SLen: 195, Last: 195}, 13 | Segment{ DStart: 512, DEnd: 704, PEnd: 768, SLen: 195, Last: 195}, 14 | Segment{ DStart: 768, DEnd: 960, PEnd: 1024, SLen: 195, Last: 195}, 15 | Segment{ DStart: 1024, DEnd: 1216, PEnd: 1280, SLen: 195, Last: 195}, 16 | Segment{ DStart: 1280, DEnd: 1472, PEnd: 1536, SLen: 195, Last: 195}, 17 | Segment{ DStart: 1536, DEnd: 1728, PEnd: 1792, SLen: 195, Last: 195}, 18 | Segment{ DStart: 1792, DEnd: 1793, PEnd: 1794, SLen: 195, Last: 175}, 19 | } 20 | `, 21 | ` 22 | Segments{ 23 | Segment{ DStart: 0, DEnd: 130, PEnd: 130, SLen: 4035, Last: 3773}, 24 | } 25 | `, 26 | ` 27 | Segments{ 28 | Segment{ DStart: 0, DEnd: 128, PEnd: 256, SLen: 4035, Last: 4035}, 29 | Segment{ DStart: 256, DEnd: 258, PEnd: 260, SLen: 4035, Last: 3773}, 30 | } 31 | `, 32 | ` 33 | Segments{ 34 | Segment{ DStart: 0, DEnd: 65, PEnd: 65, SLen: 4035, Last: 3904}, 35 | } 36 | `, 37 | } 38 | 39 | func TestNewSegments(t *testing.T) { 40 | msgSize := 2<<17 + 111 41 | segSize := 256 42 | s := NewSegments(msgSize, segSize, Overhead, 64) 43 | o := fmt.Sprint(s) 44 | if o != Expected[0] { 45 | t.Errorf( 46 | "Failed to correctly generate.\ngot:\n'%s'\nexpected:\n'%s'", 47 | o, Expected[0]) 48 | } 49 | msgSize = 2 << 18 50 | segSize = 4096 51 | s = NewSegments(msgSize, segSize, Overhead, 0) 52 | o = fmt.Sprint(s) 53 | if o != Expected[1] { 54 | t.Errorf( 55 | "Failed to correctly generate.\ngot:\n%s\nexpected:\n%s", 56 | o, Expected[1]) 57 | } 58 | s = NewSegments(msgSize, segSize, Overhead, 128) 59 | o = fmt.Sprint(s) 60 | if o != Expected[2] { 61 | t.Errorf( 62 | "Failed to correctly generate.\ngot:\n%s\nexpected:\n%s", 63 | o, Expected[2]) 64 | } 65 | msgSize = 2 << 17 66 | segSize = 4096 67 | s = NewSegments(msgSize, segSize, Overhead, 0) 68 | o = fmt.Sprint(s) 69 | if o != Expected[3] { 70 | t.Errorf( 71 | "Failed to correctly generate.\ngot:\n%s\nexpected:\n%s", 72 | o, Expected[3]) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pkg/engine/protocols/protocols.go: -------------------------------------------------------------------------------- 1 | package protocols 2 | 3 | type NetworkProtocols byte 4 | 5 | const ( 6 | IP4 NetworkProtocols = 1 7 | IP6 NetworkProtocols = 1 << iota 8 | // add more here if any such thing of this kind can be used ??? 9 | ) 10 | -------------------------------------------------------------------------------- /pkg/engine/reply.go: -------------------------------------------------------------------------------- 1 | package engine 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/codec" 5 | "git.indra-labs.org/dev/ind/pkg/codec/onion/exit" 6 | "git.indra-labs.org/dev/ind/pkg/codec/ont" 7 | "git.indra-labs.org/dev/ind/pkg/crypto" 8 | "git.indra-labs.org/dev/ind/pkg/engine/sessions" 9 | "git.indra-labs.org/dev/ind/pkg/hidden" 10 | ) 11 | 12 | // MakeReplyHeader constructs a reply header for hidden service messages. 13 | func MakeReplyHeader(ng *Engine) (returnHeader *hidden.ReplyHeader) { 14 | n := crypto.GenNonces(3) 15 | rvKeys := ng.KeySet.Next3() 16 | hops := []byte{3, 4, 5} 17 | s := make(sessions.Sessions, len(hops)) 18 | ng.Mgr().SelectHops(hops, s, "make message reply header") 19 | rt := &exit.Routing{ 20 | Sessions: [3]*sessions.Data{s[0], s[1], s[2]}, 21 | Keys: crypto.Privs{rvKeys[0], rvKeys[1], rvKeys[2]}, 22 | Nonces: crypto.Nonces{n[0], n[1], n[2]}, 23 | } 24 | rh := Skins{}.RoutingHeader(rt, ng.Mgr().Protocols) 25 | rHdr := codec.Encode(ont.Assemble(rh)) 26 | rHdr.SetCursor(0) 27 | ep := exit.ExitPoint{ 28 | Routing: rt, 29 | ReturnPubs: crypto.Pubs{ 30 | crypto.DerivePub(s[0].Payload.Prv), 31 | crypto.DerivePub(s[1].Payload.Prv), 32 | crypto.DerivePub(s[2].Payload.Prv), 33 | }, 34 | } 35 | returnHeader = &hidden.ReplyHeader{ 36 | RoutingHeaderBytes: hidden.GetRoutingHeaderFromCursor(rHdr), 37 | Ciphers: crypto.GenCiphers(ep.Routing.Keys, ep.ReturnPubs), 38 | Nonces: ep.Routing.Nonces, 39 | } 40 | return 41 | } 42 | -------------------------------------------------------------------------------- /pkg/engine/sendgetbalance_test.go: -------------------------------------------------------------------------------- 1 | package engine 2 | -------------------------------------------------------------------------------- /pkg/engine/services/services.go: -------------------------------------------------------------------------------- 1 | // Package services defines the base data structure for a service. 2 | // 3 | // This includes the port specification, the fee rate on the service, and the transport abstraction that opens a channel for messages to the service, or its listener depending on which side this structure is used. 4 | package services 5 | 6 | import "git.indra-labs.org/dev/ind/pkg/engine/tpt" 7 | 8 | type ( 9 | // Service is a specification for a publicly accessible service available at a 10 | // relay. 11 | // 12 | // Through this mechanism relay operators can effectively create a paywall for a 13 | // service, or at least cover their operating costs. Hidden services can do this 14 | // also, with server side anonymity. 15 | // 16 | // todo: hidden services need a session type. 17 | Service struct { 18 | 19 | // Port specifies the type of service based on the well known port used by the 20 | // protocol. For bitcoin, for example, this would be 8333 for its peer to peer 21 | // listener, for SSH it would be 22, and so on. 22 | Port uint16 23 | 24 | // RelayRate is the fee in mSAT for megabytes forwarded to and returned from the 25 | // service. 26 | RelayRate uint32 27 | 28 | // Transport is a channel that will have a network handler at the other end to 29 | // dispatch and return replies to the Engine. 30 | tpt.Transport 31 | } 32 | 33 | // Services is a collection of services. 34 | Services []*Service 35 | ) 36 | -------------------------------------------------------------------------------- /pkg/engine/sess/data.go: -------------------------------------------------------------------------------- 1 | package sess 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/crypto" 5 | "git.indra-labs.org/dev/ind/pkg/crypto/nonce" 6 | "git.indra-labs.org/dev/ind/pkg/engine/sessions" 7 | "git.indra-labs.org/dev/ind/pkg/util/slice" 8 | ) 9 | 10 | // Data is a data structure returned from engine.PostAcctOnion that tracks the 11 | // information related to the use of a session. 12 | type Data struct { 13 | 14 | // B is the bytes of data that were sent out. 15 | B slice.Bytes 16 | 17 | // Sessions are the list of sessions in the circuit. 18 | Sessions sessions.Sessions 19 | 20 | // Billable is the public keys of the sessions used in the circuit. 21 | // 22 | // todo: is this actually used??? 23 | Billable []crypto.PubBytes 24 | 25 | // Ret is the return session, which isn't billable. 26 | Ret crypto.PubBytes 27 | 28 | // ID is the transmission nonce.ID, as found in PendingResponses. 29 | ID nonce.ID 30 | 31 | // Port is the well-known port designating the protocol of the message. 32 | Port uint16 33 | 34 | // PostAcct is a collection of hooks that are to be run on the successful 35 | // receiving of the response or confirmation and the completion of all relaying 36 | // from source back to source. 37 | PostAcct []func() 38 | } 39 | -------------------------------------------------------------------------------- /pkg/engine/sess/doc.go: -------------------------------------------------------------------------------- 1 | // Package sess provides the Session Manager, which keeps track of a client's sessions. 2 | // 3 | // This package provides access to subsystems related to it such as pending payments, handles accounting to attempt to keep clients and relays session balances in sync without any unnecessary communication. 4 | package sess 5 | -------------------------------------------------------------------------------- /pkg/engine/tpt/interfaces.go: -------------------------------------------------------------------------------- 1 | // Package tpt provides the definition of the interface Transport, which is an abstraction used for reading and writing to peers via transport.Transport. 2 | package tpt 3 | 4 | import "git.indra-labs.org/dev/ind/pkg/util/slice" 5 | 6 | // Transport is a generic interface for sending and receiving slices of bytes. 7 | type Transport interface { 8 | 9 | // Send delivers a byte buffer along the Transport. 10 | Send(b slice.Bytes) (e error) 11 | 12 | // Receive waits for incoming messages and delivers the byte buffer to the caller 13 | // via a channel. 14 | Receive() <-chan slice.Bytes 15 | } 16 | -------------------------------------------------------------------------------- /pkg/engine/transport/doc.go: -------------------------------------------------------------------------------- 1 | // Package transport provides a set of definitions of abstractions that layer above the implementation enabling the use of simple functions that interact on channels to queue and receive messages from the tpt.Transport of which several variants are here implemented. 2 | package transport 3 | -------------------------------------------------------------------------------- /pkg/engine/transport/pstoreds/cache.go: -------------------------------------------------------------------------------- 1 | package pstoreds 2 | 3 | // cache abstracts all methods we access from ARCCache, to enable alternate 4 | // implementations such as a no-op one. 5 | type cache[K comparable, V any] interface { 6 | Get(key K) (value V, ok bool) 7 | Add(key K, value V) 8 | Remove(key K) 9 | Contains(key K) bool 10 | Peek(key K) (value V, ok bool) 11 | Keys() []K 12 | } 13 | 14 | // noopCache is a dummy implementation that's used when the cache is disabled. 15 | type noopCache[K comparable, V any] struct { 16 | } 17 | 18 | var _ cache[int, int] = (*noopCache[int, int])(nil) 19 | 20 | func (*noopCache[K, V]) Get(key K) (value V, ok bool) { 21 | return *new(V), false 22 | } 23 | 24 | func (*noopCache[K, V]) Add(key K, value V) { 25 | } 26 | 27 | func (*noopCache[K, V]) Remove(key K) { 28 | } 29 | 30 | func (*noopCache[K, V]) Contains(key K) bool { 31 | return false 32 | } 33 | 34 | func (*noopCache[K, V]) Peek(key K) (value V, ok bool) { 35 | return *new(V), false 36 | } 37 | 38 | func (*noopCache[K, V]) Keys() (keys []K) { 39 | return keys 40 | } 41 | -------------------------------------------------------------------------------- /pkg/engine/transport/pstoreds/cyclic_batch.go: -------------------------------------------------------------------------------- 1 | package pstoreds 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | 8 | ds "github.com/ipfs/go-datastore" 9 | ) 10 | 11 | // how many operations are queued in a cyclic batch before we flush it. 12 | var defaultOpsPerCyclicBatch = 20 13 | 14 | // cyclicBatch buffers ds write operations and automatically flushes them after 15 | // defaultOpsPerCyclicBatch (20) have been queued. An explicit `Commit()` closes 16 | // this cyclic batch, erroring all further operations. 17 | // 18 | // It is similar to go-ds autobatch, but it's driven by an actual Batch facility 19 | // offered by the ds. 20 | type cyclicBatch struct { 21 | threshold int 22 | ds.Batch 23 | ds ds.Batching 24 | pending int 25 | } 26 | 27 | func newCyclicBatch(ds ds.Batching, threshold int) (ds.Batch, error) { 28 | batch, err := ds.Batch(context.TODO()) 29 | if err != nil { 30 | return nil, err 31 | } 32 | return &cyclicBatch{Batch: batch, ds: ds}, nil 33 | } 34 | 35 | func (cb *cyclicBatch) cycle() (err error) { 36 | if cb.Batch == nil { 37 | return errors.New("cyclic batch is closed") 38 | } 39 | if cb.pending < cb.threshold { 40 | // we haven't reached the threshold yet. 41 | return nil 42 | } 43 | // commit and renew the batch. 44 | if err = cb.Batch.Commit(context.TODO()); err != nil { 45 | return fmt.Errorf("failed while committing cyclic batch: %w", err) 46 | } 47 | if cb.Batch, err = cb.ds.Batch(context.TODO()); err != nil { 48 | return fmt.Errorf("failed while renewing cyclic batch: %w", err) 49 | } 50 | return nil 51 | } 52 | 53 | func (cb *cyclicBatch) Put(ctx context.Context, key ds.Key, val []byte) error { 54 | if err := cb.cycle(); err != nil { 55 | return err 56 | } 57 | cb.pending++ 58 | return cb.Batch.Put(ctx, key, val) 59 | } 60 | 61 | func (cb *cyclicBatch) Delete(ctx context.Context, key ds.Key) error { 62 | if err := cb.cycle(); err != nil { 63 | return err 64 | } 65 | cb.pending++ 66 | return cb.Batch.Delete(ctx, key) 67 | } 68 | 69 | func (cb *cyclicBatch) Commit(ctx context.Context) error { 70 | if cb.Batch == nil { 71 | return errors.New("cyclic batch is closed") 72 | } 73 | if err := cb.Batch.Commit(ctx); err != nil { 74 | return err 75 | } 76 | cb.pending = 0 77 | cb.Batch = nil 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /pkg/engine/transport/pstoreds/pb/pstore.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package pstore.pb; 3 | 4 | // AddrBookRecord represents a record for a peer in the address book. 5 | message AddrBookRecord { 6 | // The peer ID. 7 | bytes id = 1; 8 | 9 | // The multiaddresses. This is a sorted list where element 0 expires the soonest. 10 | repeated AddrEntry addrs = 2; 11 | 12 | // The most recently received signed PeerRecord. 13 | CertifiedRecord certified_record = 3; 14 | 15 | // AddrEntry represents a single multiaddress. 16 | message AddrEntry { 17 | bytes addr = 1; 18 | 19 | // The point in time when this address expires. 20 | int64 expiry = 2; 21 | 22 | // The original TTL of this address. 23 | int64 ttl = 3; 24 | } 25 | 26 | // CertifiedRecord contains a serialized signed PeerRecord used to 27 | // populate the signedAddrs list. 28 | message CertifiedRecord { 29 | // The Seq counter from the signed PeerRecord envelope 30 | uint64 seq = 1; 31 | 32 | // The serialized bytes of the SignedEnvelope containing the PeerRecord. 33 | bytes raw = 2; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pkg/engine/transport/pstoremem/metadata.go: -------------------------------------------------------------------------------- 1 | package pstoremem 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/libp2p/go-libp2p/core/peer" 7 | pstore "github.com/libp2p/go-libp2p/core/peerstore" 8 | ) 9 | 10 | type memoryPeerMetadata struct { 11 | // store other data, like versions 12 | ds map[peer.ID]map[string]interface{} 13 | dslock sync.RWMutex 14 | } 15 | 16 | var _ pstore.PeerMetadata = (*memoryPeerMetadata)(nil) 17 | 18 | func NewPeerMetadata() *memoryPeerMetadata { 19 | return &memoryPeerMetadata{ 20 | ds: make(map[peer.ID]map[string]interface{}), 21 | } 22 | } 23 | 24 | func (ps *memoryPeerMetadata) Put(p peer.ID, key string, 25 | val interface{}) error { 26 | 27 | ps.dslock.Lock() 28 | defer ps.dslock.Unlock() 29 | m, ok := ps.ds[p] 30 | if !ok { 31 | m = make(map[string]interface{}) 32 | ps.ds[p] = m 33 | } 34 | m[key] = val 35 | return nil 36 | } 37 | 38 | func (ps *memoryPeerMetadata) Get(p peer.ID, 39 | key string) (interface{}, error) { 40 | 41 | ps.dslock.RLock() 42 | defer ps.dslock.RUnlock() 43 | m, ok := ps.ds[p] 44 | if !ok { 45 | return nil, pstore.ErrNotFound 46 | } 47 | val, ok := m[key] 48 | if !ok { 49 | return nil, pstore.ErrNotFound 50 | } 51 | return val, nil 52 | } 53 | 54 | func (ps *memoryPeerMetadata) RemovePeer(p peer.ID) { 55 | ps.dslock.Lock() 56 | delete(ps.ds, p) 57 | ps.dslock.Unlock() 58 | } 59 | -------------------------------------------------------------------------------- /pkg/engine/transport/pstoremem/sorting.go: -------------------------------------------------------------------------------- 1 | package pstoremem 2 | 3 | import ( 4 | "bytes" 5 | 6 | ma "github.com/multiformats/go-multiaddr" 7 | mafmt "github.com/multiformats/go-multiaddr-fmt" 8 | manet "github.com/multiformats/go-multiaddr/net" 9 | ) 10 | 11 | func isFDCostlyTransport(a ma.Multiaddr) bool { 12 | return mafmt.TCP.Matches(a) 13 | } 14 | 15 | type addrList []ma.Multiaddr 16 | 17 | func (al addrList) Len() int { return len(al) } 18 | func (al addrList) Swap(i, j int) { al[i], al[j] = al[j], al[i] } 19 | 20 | func (al addrList) Less(i, j int) bool { 21 | a := al[i] 22 | b := al[j] 23 | 24 | // dial localhost addresses next, they should fail immediately 25 | lba := manet.IsIPLoopback(a) 26 | lbb := manet.IsIPLoopback(b) 27 | if lba && !lbb { 28 | return true 29 | } 30 | 31 | // dial utp and similar 'non-fd-consuming' addresses first 32 | fda := isFDCostlyTransport(a) 33 | fdb := isFDCostlyTransport(b) 34 | if !fda { 35 | return fdb 36 | } 37 | 38 | // if 'b' doesnt take a file descriptor 39 | if !fdb { 40 | return false 41 | } 42 | 43 | // if 'b' is loopback and both take file descriptors 44 | if lbb { 45 | return false 46 | } 47 | 48 | // for the rest, just sort by bytes 49 | return bytes.Compare(a.Bytes(), b.Bytes()) > 0 50 | } 51 | -------------------------------------------------------------------------------- /pkg/engine/transport/pstoremem/sorting_test.go: -------------------------------------------------------------------------------- 1 | package pstoremem 2 | 3 | import ( 4 | "sort" 5 | "testing" 6 | 7 | ma "github.com/multiformats/go-multiaddr" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestAddressSorting(t *testing.T) { 12 | u1 := ma.StringCast("/ip4/152.12.23.53/udp/1234/utp") 13 | u2l := ma.StringCast("/ip4/127.0.0.1/udp/1234/utp") 14 | local := ma.StringCast("/ip4/127.0.0.1/tcp/1234") 15 | norm := ma.StringCast("/ip4/6.5.4.3/tcp/1234") 16 | 17 | l := addrList{local, u1, u2l, norm} 18 | sort.Sort(l) 19 | require.Equal(t, l, addrList{u2l, u1, local, norm}) 20 | } 21 | -------------------------------------------------------------------------------- /pkg/engine/transport/readme.md: -------------------------------------------------------------------------------- 1 | # pkg/engine/transport 2 | 3 | This is an implementation of typical network handling features, a listener, 4 | which has an `Accept` method that returns a channel that will pick up a new 5 | inbound connection. 6 | 7 | ## Warning 8 | 9 | `pstoreds` and `pstoremem` both store the `libp2p.Host`'s private key in 10 | cleartext. Consequently it is necessary to ensure to use `options.Default()` and 11 | use an encryption key with it. The key has to be kept hot for ads, for finding 12 | the LN node being controlled by Indra, and sending/receiving payments. 13 | 14 | todo: need a key change protocol for this identity key that handles session 15 | migration correctly. 16 | 17 | ## License Notes 18 | 19 | `pstoreds` and `pstoremem` are under MIT license as seen at 20 | the [libp2p repository](https://github.com/libp2p/go-libp2p/LICENSE). 21 | -------------------------------------------------------------------------------- /pkg/interrupt/LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2013-2017 The btcsuite developers 4 | Copyright (c) 2015-2016 The Decred developers 5 | 6 | Permission to use, copy, modify, and distribute this software for any 7 | purpose with or without fee is hereby granted, provided that the above 8 | copyright notice and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /pkg/interrupt/README.md: -------------------------------------------------------------------------------- 1 | # interrupt 2 | 3 | Handle shutdowns cleanly and enable hot reload 4 | 5 | Based on the shutdown handling code in 6 | [btcwallet](https://github.com/btcsuite/btcwallet). 7 | 8 | As such the ISC license applies to this code. -------------------------------------------------------------------------------- /pkg/interrupt/doc.go: -------------------------------------------------------------------------------- 1 | // Package interrupt provides a set of services for handling OS interrupt signals and in-place restarting of a server. 2 | package interrupt 3 | -------------------------------------------------------------------------------- /pkg/interrupt/sigterm.go: -------------------------------------------------------------------------------- 1 | //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris 2 | 3 | package interrupt 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | ) 9 | 10 | func init() { 11 | signals = []os.Signal{os.Interrupt, syscall.SIGTERM} 12 | } 13 | -------------------------------------------------------------------------------- /pkg/node/protocol.go: -------------------------------------------------------------------------------- 1 | // Package node provides the magic keys that identify each network swarm in the Indra network - mainnet, testnet and simnet. 2 | package node 3 | 4 | // Swarm is an Indra network. Encodes a network identifier for mainnet, testnet and simnet. 5 | type Swarm uint32 6 | 7 | const ( 8 | // MainNet represents the main indra network. 9 | MainNet Swarm = 0xd9b4bef9 10 | 11 | // TestNet represents the regression test network. 12 | TestNet Swarm = 0xdab5bffa 13 | 14 | // SimNet represents the simulation test network. 15 | SimNet Swarm = 0x12141c16 16 | ) 17 | -------------------------------------------------------------------------------- /pkg/p2p/config.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/cfg" 5 | "github.com/libp2p/go-libp2p/core/crypto" 6 | "github.com/multiformats/go-multiaddr" 7 | ) 8 | 9 | func NewMultiAddr(addr string) (maddr multiaddr.Multiaddr) { 10 | 11 | var err error 12 | 13 | if maddr, err = multiaddr.NewMultiaddr(addr); check(err) { 14 | panic("Not a valid multiaddress.") 15 | } 16 | 17 | return 18 | } 19 | 20 | var DefaultConfig = &Config{ 21 | ListenAddresses: []multiaddr.Multiaddr{}, 22 | SeedAddresses: []multiaddr.Multiaddr{}, 23 | ConnectAddresses: []multiaddr.Multiaddr{}, 24 | } 25 | 26 | type Config struct { 27 | PrivKey crypto.PrivKey 28 | 29 | PublicAddress multiaddr.Multiaddr 30 | SeedAddresses []multiaddr.Multiaddr 31 | ConnectAddresses []multiaddr.Multiaddr 32 | ListenAddresses []multiaddr.Multiaddr 33 | 34 | Params *cfg.Params 35 | } 36 | 37 | func (c *Config) SetNetwork(network string) { 38 | 39 | c.Params = cfg.SelectNetworkParams(network) 40 | } 41 | -------------------------------------------------------------------------------- /pkg/p2p/doc.go: -------------------------------------------------------------------------------- 1 | // Package p2p provides the implementation for the p2p gossip and libp2p swarm membership used for a seed node, which is a non-relaying, non-client node only providing network metadata to new connections. 2 | package p2p 3 | -------------------------------------------------------------------------------- /pkg/p2p/flags.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | "github.com/spf13/viper" 6 | ) 7 | 8 | var ( 9 | listenFlag = "p2p-listen" 10 | seedFlag = "p2p-seed" 11 | connectFlag = "p2p-connect" 12 | ) 13 | 14 | var ( 15 | listeners []string 16 | seeds []string 17 | connectors []string 18 | ) 19 | 20 | func InitFlags(cmd *cobra.Command) { 21 | 22 | cmd.PersistentFlags().StringSliceVarP(&listeners, listenFlag, "", 23 | []string{ 24 | "/ip4/0.0.0.0/tcp/8337", 25 | "/ip6/::/tcp/8337", 26 | }, 27 | "binds to an interface", 28 | ) 29 | 30 | viper.BindPFlag(listenFlag, cmd.PersistentFlags().Lookup(listenFlag)) 31 | 32 | cmd.PersistentFlags().StringSliceVarP(&seeds, seedFlag, "", 33 | []string{}, 34 | "adds an additional seed connection (e.g /dns4/seed0.indra.org/tcp/8337/p2p/)", 35 | ) 36 | 37 | viper.BindPFlag(seedFlag, cmd.PersistentFlags().Lookup(seedFlag)) 38 | 39 | cmd.PersistentFlags().StringSliceVarP(&connectors, connectFlag, "", 40 | []string{}, 41 | "connects only to the seed multi-addresses specified", 42 | ) 43 | 44 | viper.BindPFlag(connectFlag, cmd.PersistentFlags().Lookup(connectFlag)) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/p2p/log.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 5 | ) 6 | 7 | var ( 8 | log = log2.GetLogger() 9 | check = log.E.Chk 10 | ) 11 | -------------------------------------------------------------------------------- /pkg/p2p/metrics/host.go: -------------------------------------------------------------------------------- 1 | // Package metrics provides a simple logging update of the status of a seed node and its peer and connection counts. 2 | package metrics 3 | 4 | import ( 5 | "context" 6 | "sync" 7 | "time" 8 | 9 | "github.com/libp2p/go-libp2p/core/host" 10 | 11 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 12 | ) 13 | 14 | var ( 15 | log = log2.GetLogger() 16 | check = log.E.Chk 17 | ) 18 | 19 | var ( 20 | hostStatusInterval = 10 * time.Second 21 | ) 22 | 23 | var ( 24 | mutex sync.Mutex 25 | ) 26 | 27 | func SetInterval(timeout time.Duration) { 28 | hostStatusInterval = timeout 29 | } 30 | 31 | func HostStatus(ctx context.Context, host host.Host) { 32 | 33 | log.I.Ln("starting [metrics.hoststatus]") 34 | 35 | // Guarding against multiple instantiations 36 | if !mutex.TryLock() { 37 | return 38 | } 39 | 40 | log.I.Ln("[metrics.hoststatus] is ready") 41 | 42 | go func() { 43 | for { 44 | select { 45 | case <-time.After(hostStatusInterval): 46 | 47 | log.I.Ln() 48 | log.I.Ln("---- host status ----") 49 | log.I.Ln("-- peers:", len(host.Network().Peers())) 50 | log.I.Ln("-- connections:", len(host.Network().Conns())) 51 | log.I.Ln("---- ---- ------ ----") 52 | 53 | case <-ctx.Done(): 54 | 55 | log.I.Ln("shutting down [metrics.hoststatus]") 56 | 57 | return 58 | } 59 | } 60 | }() 61 | } 62 | -------------------------------------------------------------------------------- /pkg/p2p/signals.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | var ( 4 | startupErrors = make(chan error, 32) 5 | isReadyChan = make(chan bool, 1) 6 | isShutdownChan = make(chan bool, 1) 7 | ) 8 | 9 | func WhenStartFailed() chan error { 10 | return startupErrors 11 | } 12 | 13 | func WhenReady() chan bool { 14 | return isReadyChan 15 | } 16 | 17 | func WhenShutdown() chan bool { 18 | return isShutdownChan 19 | } 20 | -------------------------------------------------------------------------------- /pkg/p2p/util.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "github.com/btcsuite/btcd/btcutil/base58" 5 | "github.com/libp2p/go-libp2p/core/crypto" 6 | ) 7 | 8 | func Base58Encode(priv crypto.PrivKey) (key string, err error) { 9 | 10 | var raw []byte 11 | 12 | raw, err = priv.Raw() 13 | 14 | key = base58.Encode(raw) 15 | 16 | return 17 | } 18 | 19 | func Base58Decode(key string) (priv crypto.PrivKey, err error) { 20 | 21 | var raw []byte 22 | 23 | raw = base58.Decode(key) 24 | 25 | if priv, _ = crypto.UnmarshalSecp256k1PrivateKey(raw); check(err) { 26 | return 27 | } 28 | 29 | return 30 | } 31 | -------------------------------------------------------------------------------- /pkg/p2p/util_test.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "crypto/rand" 5 | "github.com/libp2p/go-libp2p/core/crypto" 6 | "testing" 7 | ) 8 | 9 | func TestBase58(t *testing.T) { 10 | 11 | var err error 12 | var priv1, priv2 crypto.PrivKey 13 | var keyStr1, keyStr2 string 14 | 15 | // Generate priv 16 | priv1, _, err = crypto.GenerateSecp256k1Key(rand.Reader) 17 | 18 | if keyStr1, err = Base58Encode(priv1); err != nil { 19 | t.Error("base58encode error: ", err) 20 | } 21 | 22 | if priv2, err = Base58Decode(keyStr1); err != nil { 23 | t.Error("base58decode error: ", err) 24 | } 25 | 26 | if !priv1.Equals(priv2) { 27 | t.Error("Keys are not equal!") 28 | } 29 | 30 | if keyStr2, err = Base58Encode(priv2); err != nil { 31 | t.Error("base58encode error: ", err) 32 | } 33 | 34 | if keyStr1 != keyStr2 { 35 | t.Error("Keys are not equal!") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /pkg/proc/app/app.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | cmds2 "git.indra-labs.org/dev/ind/pkg/proc/cmds" 5 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 6 | ) 7 | 8 | var ( 9 | log = log2.GetLogger() 10 | check = log.E.Chk 11 | ) 12 | 13 | type App struct { 14 | *cmds2.Command 15 | launch *cmds2.Command 16 | runArgs []string 17 | cmds2.Envs 18 | } 19 | 20 | func New(c *cmds2.Command, args []string) (a *App, e error) { 21 | log2.App.Store(c.Name) 22 | // AddIntro the default configuration items for datadir/configfile 23 | log.T.Ln("test") 24 | cmds2.GetConfigBase(c.Configs, c.Name, false) 25 | log.T.Ln("test") 26 | // AddIntro the help function 27 | c.AddCommand(cmds2.Help()) 28 | a = &App{Command: c} 29 | log.T.Ln("test") 30 | if a.Command, e = cmds2.Init(c, nil); check(e) { 31 | return 32 | } 33 | log.T.Ln("test") 34 | // We first parse the CLI args, in case config file location has been 35 | // specified 36 | if a.launch, _, e = a.Command.ParseCLIArgs(args); check(e) { 37 | return 38 | } 39 | if e = c.LoadConfig(); log.E.Chk(e) { 40 | return 41 | } 42 | a.Envs = c.GetEnvs() 43 | if e = a.Envs.LoadFromEnvironment(); check(e) { 44 | return 45 | } 46 | // This is done again, to ensure the effect of CLI args take precedence 47 | if a.launch, a.runArgs, e = a.Command.ParseCLIArgs(args); check(e) { 48 | return 49 | } 50 | return 51 | } 52 | 53 | func (a *App) Launch() (e error) { 54 | e = a.launch.Entrypoint(a.launch, a.runArgs) 55 | log.E.Chk(e) 56 | return 57 | } 58 | -------------------------------------------------------------------------------- /pkg/proc/app/app_test.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/util/ci" 5 | "os" 6 | "strings" 7 | "testing" 8 | 9 | "git.indra-labs.org/dev/ind/pkg/proc/cmds" 10 | ) 11 | 12 | func TestNew(t *testing.T) { 13 | ci.TraceIfNot() 14 | args1 := "/random/path/to/server_binary --cafile ~/some/cafile --LC=cn node -addrindex --BD 48h30s" 15 | args1s := strings.Split(args1, " ") 16 | var a *App 17 | var e error 18 | if a, e = New(cmds.GetExampleCommands(), args1s); log.E.Chk(e) { 19 | t.FailNow() 20 | } 21 | if e = a.Launch(); check(e) { 22 | t.FailNow() 23 | } 24 | if e = os.RemoveAll(a.Command.Configs["DataDir"]. 25 | Expanded()); check(e) { 26 | 27 | t.FailNow() 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /pkg/proc/cmds/args_test.go: -------------------------------------------------------------------------------- 1 | package cmds 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/util/ci" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestCommand_ParseCLIArgs(t *testing.T) { 10 | ci.TraceIfNot() 11 | ec := GetExampleCommands() 12 | o, _ := Init(ec, nil) 13 | args6 := "/random/path/to/server_binary --cafile ~/some/cafile --LC=cn " + 14 | "node -addrindex false --blocksonly" 15 | args6s := strings.Split(args6, " ") 16 | run, _, e := o.ParseCLIArgs(args6s) 17 | if log.E.Chk(e) { 18 | t.Error(e) 19 | t.FailNow() 20 | } 21 | args1 := "/random/path/to/server_binary --cafile ~/some/cafile --LC=cn " + 22 | "node -addrindex --BD=5m" 23 | args1s := strings.Split(args1, " ") 24 | run, _, e = o.ParseCLIArgs(args1s) 25 | if log.E.Chk(e) { 26 | t.Error(e) 27 | t.FailNow() 28 | } 29 | _, _ = run, e 30 | args3 := "node -addrindex --BD 48h30s dropaddrindex somegarbage " + 31 | "--autoports" 32 | args3s := strings.Split(args3, " ") 33 | run, _, e = o.ParseCLIArgs(args3s) 34 | // This one must fail, 'somegarbage' is not a command and has no -/-- prefix 35 | if e == nil { 36 | t.Error(e) 37 | t.FailNow() 38 | } 39 | args5 := "/random/path/to/server_binary --cafile ~/some/cafile --LC=cn " 40 | args5s := strings.Split(args5, " ") 41 | run, _, e = o.ParseCLIArgs(args5s) 42 | if log.E.Chk(e) { 43 | t.Error(e) 44 | t.FailNow() 45 | } 46 | args2 := "/random/path/to/server_binary node -addrindex --BD=48h30s " + 47 | "-RPCMaxConcurrentReqs -16 dropaddrindex" 48 | args2s := strings.Split(args2, " ") 49 | run, _, e = o.ParseCLIArgs(args2s) 50 | if log.E.Chk(e) { 51 | t.Error(e) 52 | t.FailNow() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /pkg/proc/log/length.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | const maxLen = 44 4 | 5 | -------------------------------------------------------------------------------- /pkg/proc/log/log_test.go: -------------------------------------------------------------------------------- 1 | package log_test 2 | 3 | import ( 4 | "errors" 5 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 6 | "testing" 7 | ) 8 | 9 | var ( 10 | log = log2.GetLogger() 11 | fails = log.E.Chk 12 | ) 13 | 14 | func TestGetLogger(t *testing.T) { 15 | log.I.Ln("info") 16 | fails(errors.New("dummy error")) 17 | } 18 | -------------------------------------------------------------------------------- /pkg/proc/log/maxlen/maxlen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/fs" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | var max int 13 | var longest string 14 | filepath.Walk(os.Args[1], func(path string, info fs.FileInfo, err error) error { 15 | if strings.HasPrefix(path, ".") || 16 | !strings.HasSuffix(path, ".go") || 17 | strings.HasSuffix(path, "_test.go") { // doesn't matter as much if test logs rel path grow initially 18 | return nil 19 | } 20 | if len(path) > max { 21 | max = len(path) 22 | longest = path 23 | } 24 | return nil 25 | }) 26 | if e := os.WriteFile("pkg/proc/log/length.go", []byte(fmt.Sprintf("package log\n\nconst maxLen = %d\n\n", max)), 0600); e != nil { 27 | fmt.Println(e) 28 | } 29 | fmt.Println(longest) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/proc/opts/config/interface.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/util/path" 5 | "time" 6 | 7 | "git.indra-labs.org/dev/ind/pkg/proc/opts/meta" 8 | ) 9 | 10 | // Concrete is a struct of functions that return the concrete values. Only the 11 | // intended type will return a value, the rest always return zero. 12 | type Concrete struct { 13 | Bool func() bool 14 | Duration func() time.Duration 15 | Float func() float64 16 | Integer func() int64 17 | List func() []string 18 | Text func() string 19 | } 20 | 21 | // NewConcrete provides a Concrete with all functions returning zero values 22 | func NewConcrete() Concrete { 23 | return Concrete{ 24 | func() bool { return false }, 25 | func() time.Duration { return 0 }, 26 | func() float64 { return 0 }, 27 | func() int64 { return 0 }, 28 | func() []string { return nil }, 29 | func() string { return "" }, 30 | } 31 | } 32 | 33 | // Option interface reads and writes string formats for options and returns a 34 | // Concrete value to the appropriate concrete value, with the type indicated. 35 | type Option interface { 36 | FromString(s string) (e error) 37 | String() (s string) 38 | Expanded() (s string) 39 | SetExpanded(s string) 40 | Value() (c Concrete) 41 | Type() (t meta.Type) 42 | Meta() (md meta.Metadata) 43 | RunHooks() (err error) 44 | Path() (p path.Path) 45 | SetPath(p path.Path) 46 | } 47 | 48 | type Opts map[string]Option 49 | -------------------------------------------------------------------------------- /pkg/proc/opts/duration/spec.go: -------------------------------------------------------------------------------- 1 | package duration 2 | 3 | import ( 4 | "fmt" 5 | "git.indra-labs.org/dev/ind/pkg/util/path" 6 | "strings" 7 | "time" 8 | 9 | "go.uber.org/atomic" 10 | 11 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 12 | "git.indra-labs.org/dev/ind/pkg/proc/opts/config" 13 | "git.indra-labs.org/dev/ind/pkg/proc/opts/meta" 14 | ) 15 | 16 | var ( 17 | log = log2.GetLogger() 18 | check = log.E.Chk 19 | ) 20 | 21 | type Opt struct { 22 | p path.Path 23 | m meta.Metadata 24 | v atomic.Duration 25 | h []Hook 26 | } 27 | 28 | func (o *Opt) Path() (p path.Path) { return o.p } 29 | 30 | func (o *Opt) SetPath(p path.Path) { o.p = p } 31 | 32 | var _ config.Option = &Opt{} 33 | 34 | type Hook func(*Opt) 35 | 36 | func New(m meta.Data, h ...Hook) (o *Opt) { 37 | o = &Opt{m: meta.New(m, meta.Duration), h: h} 38 | _ = o.FromString(m.Default) 39 | return 40 | } 41 | 42 | func (o *Opt) Meta() meta.Metadata { return o.m } 43 | func (o *Opt) Type() meta.Type { return o.m.Typ } 44 | func (o *Opt) ToOption() config.Option { return o } 45 | 46 | func (o *Opt) RunHooks() (e error) { 47 | for i := range o.h { 48 | o.h[i](o) 49 | } 50 | return 51 | } 52 | 53 | func (o *Opt) FromValue(v time.Duration) *Opt { 54 | o.v.Store(v) 55 | return o 56 | } 57 | 58 | func (o *Opt) FromString(s string) (e error) { 59 | s = strings.TrimSpace(s) 60 | var d time.Duration 61 | d, e = time.ParseDuration(s) 62 | if e != nil { 63 | return e 64 | } 65 | o.v.Store(d) 66 | e = o.RunHooks() 67 | return 68 | } 69 | 70 | func (o *Opt) String() (s string) { 71 | return fmt.Sprint(o.v.Load()) 72 | } 73 | 74 | func (o *Opt) Expanded() (s string) { return o.String() } 75 | 76 | func (o *Opt) SetExpanded(s string) { 77 | if err := o.FromString(s); log.E.Chk(err) { 78 | } 79 | } 80 | 81 | func (o *Opt) Value() (c config.Concrete) { 82 | c = config.NewConcrete() 83 | c.Duration = func() time.Duration { return o.v.Load() } 84 | return 85 | } 86 | 87 | func Clamp(o *Opt, min, max time.Duration) func(*Opt) { 88 | return func(o *Opt) { 89 | v := o.v.Load() 90 | if v < min { 91 | o.v.Store(min) 92 | } else if v > max { 93 | o.v.Store(max) 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /pkg/proc/opts/float/spec.go: -------------------------------------------------------------------------------- 1 | package float 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/util/path" 5 | "strconv" 6 | "strings" 7 | 8 | "go.uber.org/atomic" 9 | 10 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 11 | "git.indra-labs.org/dev/ind/pkg/proc/opts/config" 12 | "git.indra-labs.org/dev/ind/pkg/proc/opts/meta" 13 | ) 14 | 15 | var ( 16 | log = log2.GetLogger() 17 | check = log.E.Chk 18 | ) 19 | 20 | type Opt struct { 21 | p path.Path 22 | m meta.Metadata 23 | v atomic.Float64 24 | h []Hook 25 | } 26 | 27 | func (o *Opt) Path() (p path.Path) { return o.p } 28 | 29 | func (o *Opt) SetPath(p path.Path) { o.p = p } 30 | 31 | var _ config.Option = &Opt{} 32 | 33 | type Hook func(*Opt) error 34 | 35 | func New(m meta.Data, h ...Hook) (o *Opt) { 36 | o = &Opt{m: meta.New(m, meta.Float), h: h} 37 | _ = o.FromString(m.Default) 38 | return 39 | } 40 | 41 | func (o *Opt) Meta() meta.Metadata { return o.m } 42 | func (o *Opt) Type() meta.Type { return o.m.Typ } 43 | func (o *Opt) ToOption() config.Option { return o } 44 | 45 | func (o *Opt) RunHooks() (e error) { 46 | for i := range o.h { 47 | e = o.h[i](o) 48 | if e != nil { 49 | return 50 | } 51 | } 52 | return 53 | } 54 | 55 | func (o *Opt) FromValue(v float64) *Opt { 56 | o.v.Store(v) 57 | return o 58 | } 59 | 60 | func (o *Opt) FromString(s string) (e error) { 61 | s = strings.TrimSpace(s) 62 | var p float64 63 | p, e = strconv.ParseFloat(s, 64) 64 | if e != nil { 65 | return e 66 | } 67 | o.v.Store(p) 68 | e = o.RunHooks() 69 | return 70 | } 71 | 72 | func (o *Opt) String() (s string) { 73 | return strconv.FormatFloat(o.v.Load(), 'f', -1, 64) 74 | } 75 | 76 | func (o *Opt) Expanded() (s string) { return o.String() } 77 | 78 | func (o *Opt) SetExpanded(s string) { 79 | err := o.FromString(s) 80 | log.E.Chk(err) 81 | } 82 | 83 | func (o *Opt) Value() (c config.Concrete) { 84 | c = config.NewConcrete() 85 | c.Float = func() float64 { return o.v.Load() } 86 | return 87 | } 88 | 89 | func Clamp(o *Opt, min, max float64) func(*Opt) { 90 | return func(o *Opt) { 91 | v := o.v.Load() 92 | if v < min { 93 | o.v.Store(min) 94 | } else if v > max { 95 | o.v.Store(max) 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /pkg/proc/opts/integer/spec.go: -------------------------------------------------------------------------------- 1 | package integer 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/util/path" 5 | "strconv" 6 | "strings" 7 | 8 | "go.uber.org/atomic" 9 | 10 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 11 | "git.indra-labs.org/dev/ind/pkg/proc/opts/config" 12 | "git.indra-labs.org/dev/ind/pkg/proc/opts/meta" 13 | ) 14 | 15 | var ( 16 | log = log2.GetLogger() 17 | check = log.E.Chk 18 | ) 19 | 20 | type Opt struct { 21 | p path.Path 22 | m meta.Metadata 23 | v atomic.Int64 24 | h []Hook 25 | } 26 | 27 | func (o *Opt) Path() (p path.Path) { return o.p } 28 | 29 | func (o *Opt) SetPath(p path.Path) { o.p = p } 30 | 31 | var _ config.Option = &Opt{} 32 | 33 | type Hook func(*Opt) error 34 | 35 | func New(m meta.Data, h ...Hook) (o *Opt) { 36 | o = &Opt{m: meta.New(m, meta.Integer), h: h} 37 | _ = o.FromString(m.Default) 38 | return 39 | } 40 | 41 | func (o *Opt) Meta() meta.Metadata { return o.m } 42 | func (o *Opt) Type() meta.Type { return o.m.Typ } 43 | func (o *Opt) ToOption() config.Option { return o } 44 | 45 | func (o *Opt) RunHooks() (e error) { 46 | for i := range o.h { 47 | e = o.h[i](o) 48 | if e != nil { 49 | return 50 | } 51 | } 52 | return 53 | } 54 | 55 | func (o *Opt) FromValue(v int64) *Opt { 56 | o.v.Store(v) 57 | return o 58 | } 59 | 60 | func (o *Opt) FromString(s string) (e error) { 61 | s = strings.TrimSpace(s) 62 | var p int64 63 | if p, e = strconv.ParseInt(s, 10, 64); check(e) { 64 | return e 65 | } 66 | o.v.Store(p) 67 | e = o.RunHooks() 68 | return 69 | } 70 | 71 | func (o *Opt) String() (s string) { 72 | return strconv.FormatInt(o.v.Load(), 10) 73 | } 74 | 75 | func (o *Opt) Expanded() (s string) { return o.String() } 76 | 77 | func (o *Opt) SetExpanded(s string) { 78 | err := o.FromString(s) 79 | check(err) 80 | } 81 | 82 | func (o *Opt) Value() (c config.Concrete) { 83 | c = config.NewConcrete() 84 | c.Integer = func() int64 { return o.v.Load() } 85 | return 86 | } 87 | 88 | func Clamp(o *Opt, min, max int64) func(*Opt) { 89 | return func(o *Opt) { 90 | v := o.v.Load() 91 | if v < min { 92 | o.v.Store(min) 93 | } else if v > max { 94 | o.v.Store(max) 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /pkg/proc/opts/meta/metadata.go: -------------------------------------------------------------------------------- 1 | package meta 2 | 3 | type Type string 4 | 5 | // Type has same name as string for neater comparisons. 6 | const ( 7 | Bool Type = "Bool" 8 | Duration Type = "Duration" 9 | Float Type = "Float" 10 | Integer Type = "Integer" 11 | List Type = "List" 12 | Text Type = "Text" 13 | ) 14 | 15 | // Data is the specification for a Metadata 16 | type Data struct { 17 | Aliases []string 18 | Tags []string 19 | Label string 20 | Description string 21 | Documentation string 22 | Default string 23 | Options []string 24 | } 25 | 26 | // Metadata is a set of accessor functions that never write to the store and 27 | // thus do not create race conditions. 28 | type Metadata struct { 29 | Aliases func() []string 30 | Tags func() []string 31 | Label func() string 32 | Description func() string 33 | Documentation func() string 34 | Default func() string 35 | Options func() []string 36 | Typ Type 37 | } 38 | 39 | // New loads Data into a Metadata. 40 | func New(d Data, t Type) Metadata { 41 | return Metadata{ 42 | func() []string { return d.Aliases }, 43 | func() []string { return d.Tags }, 44 | func() string { return d.Label }, 45 | func() string { return d.Description }, 46 | func() string { return d.Documentation }, 47 | func() string { return d.Default }, 48 | func() []string { return d.Options }, 49 | t, 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /pkg/proc/opts/normalize/addresses.go: -------------------------------------------------------------------------------- 1 | package normalize 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | 7 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 8 | ) 9 | 10 | var ( 11 | log = log2.GetLogger() 12 | check = log.E.Chk 13 | ) 14 | 15 | // Address returns addr with the passed default port appended if there is not 16 | // already a port specified. 17 | func Address(addr, defaultPort string, userOnly bool) (a string, e error) { 18 | var p string 19 | a, p, e = net.SplitHostPort(addr) 20 | if log.E.Chk(e) || p == "" { 21 | return net.JoinHostPort(a, defaultPort), e 22 | } 23 | if userOnly { 24 | p = ClampPortRange(p, defaultPort, 1024, 65535) 25 | } else { 26 | p = ClampPortRange(p, defaultPort, 1, 65535) 27 | } 28 | return net.JoinHostPort(a, p), e 29 | } 30 | 31 | // Addresses returns a new slice with all the passed peer addresses normalized 32 | // with the given default port, and all duplicates removed. 33 | func Addresses(addrs []string, defaultPort string, userOnly bool) (a []string, 34 | e error) { 35 | 36 | for i := range addrs { 37 | addrs[i], e = Address(addrs[i], defaultPort, userOnly) 38 | } 39 | a = RemoveDuplicateAddresses(addrs) 40 | return 41 | } 42 | 43 | // RemoveDuplicateAddresses returns a new slice with all duplicate entries in 44 | // addrs removed. 45 | func RemoveDuplicateAddresses(addrs []string) (result []string) { 46 | result = make([]string, 0, len(addrs)) 47 | seen := map[string]struct{}{} 48 | for _, val := range addrs { 49 | if _, ok := seen[val]; !ok { 50 | result = append(result, val) 51 | seen[val] = struct{}{} 52 | } 53 | } 54 | return result 55 | } 56 | 57 | func ClampPortRange(port, defaultPort string, min, max int) string { 58 | p, err := strconv.Atoi(port) 59 | if err != nil { 60 | return defaultPort 61 | } 62 | if p < min { 63 | port = strconv.FormatInt(int64(min), 10) 64 | } else if p > max { 65 | port = strconv.FormatInt(int64(max), 10) 66 | } 67 | return port 68 | } 69 | -------------------------------------------------------------------------------- /pkg/proc/opts/normalize/paths.go: -------------------------------------------------------------------------------- 1 | package normalize 2 | 3 | import ( 4 | "os" 5 | "os/user" 6 | "path/filepath" 7 | "strings" 8 | 9 | "git.indra-labs.org/dev/ind/pkg/util/appdata" 10 | ) 11 | 12 | func ResolvePath(input, appName string, abs bool) (cleaned string, e error) { 13 | if strings.HasPrefix(input, "~") { 14 | homeDir := getHomeDir() 15 | input = strings.Replace(input, "~", homeDir, 1) 16 | cleaned = filepath.Clean(input) 17 | } else { 18 | if abs { 19 | if cleaned, e = filepath.Abs(cleaned); log.E.Chk(e) { 20 | return 21 | } 22 | // if the path is relative, either ./ or not starting 23 | // with a / then we assume the path is relative to the 24 | // app data directory 25 | } else if !strings.HasPrefix(string(os.PathSeparator), cleaned) || 26 | strings.HasPrefix("."+string(os.PathSeparator), cleaned) { 27 | 28 | cleaned = filepath.Join(appdata.Dir(appName, false), cleaned) 29 | } 30 | } 31 | return 32 | } 33 | 34 | func getHomeDir() (homeDir string) { 35 | var usr *user.User 36 | var e error 37 | if usr, e = user.Current(); !log.E.Chk(e) { 38 | homeDir = usr.HomeDir 39 | } 40 | // Fall back to standard HOME environment variable that works for most 41 | // POSIX OSes if the directory from the Go standard lib failed. 42 | if e != nil || homeDir == "" { 43 | homeDir = os.Getenv("HOME") 44 | } 45 | return homeDir 46 | } 47 | -------------------------------------------------------------------------------- /pkg/proc/opts/toggle/spec.go: -------------------------------------------------------------------------------- 1 | package toggle 2 | 3 | import ( 4 | "fmt" 5 | "git.indra-labs.org/dev/ind/pkg/util/path" 6 | "strconv" 7 | "strings" 8 | 9 | "go.uber.org/atomic" 10 | 11 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 12 | "git.indra-labs.org/dev/ind/pkg/proc/opts/config" 13 | "git.indra-labs.org/dev/ind/pkg/proc/opts/meta" 14 | ) 15 | 16 | var ( 17 | log = log2.GetLogger() 18 | check = log.E.Chk 19 | ) 20 | 21 | type Opt struct { 22 | p path.Path 23 | m meta.Metadata 24 | v atomic.Bool 25 | h []Hook 26 | } 27 | 28 | func (o *Opt) Path() (p path.Path) { return o.p } 29 | 30 | func (o *Opt) SetPath(p path.Path) { o.p = p } 31 | 32 | var _ config.Option = &Opt{} 33 | 34 | type Hook func(*Opt) error 35 | 36 | func New(m meta.Data, h ...Hook) (o *Opt) { 37 | 38 | if m.Default == "" { 39 | m.Default = "false" 40 | } 41 | 42 | o = &Opt{m: meta.New(m, meta.Bool), h: h} 43 | 44 | return 45 | } 46 | 47 | func (o *Opt) Meta() meta.Metadata { return o.m } 48 | func (o *Opt) Type() meta.Type { return o.m.Typ } 49 | func (o *Opt) ToOption() config.Option { return o } 50 | 51 | func (o *Opt) RunHooks() (e error) { 52 | for i := range o.h { 53 | e = o.h[i](o) 54 | if e != nil { 55 | return 56 | } 57 | } 58 | return 59 | } 60 | 61 | func (o *Opt) FromValue(v bool) *Opt { 62 | o.v.Store(v) 63 | return o 64 | } 65 | 66 | func (o *Opt) FromString(s string) (e error) { 67 | s = strings.TrimSpace(s) 68 | switch s { 69 | case "f", "false", "off", "-": 70 | o.v.Store(false) 71 | case "t", "true", "on", "+": 72 | o.v.Store(true) 73 | default: 74 | return fmt.Errorf("string '%s' does not parse to boolean", s) 75 | } 76 | e = o.RunHooks() 77 | return 78 | } 79 | 80 | func (o *Opt) String() (s string) { 81 | return strconv.FormatBool(o.v.Load()) 82 | } 83 | 84 | func (o *Opt) Expanded() (s string) { return o.String() } 85 | 86 | func (o *Opt) SetExpanded(s string) { 87 | err := o.FromString(s) 88 | log.E.Chk(err) 89 | } 90 | 91 | func (o *Opt) Value() (c config.Concrete) { 92 | c = config.NewConcrete() 93 | c.Bool = func() bool { return o.v.Load() } 94 | return 95 | } 96 | -------------------------------------------------------------------------------- /pkg/rpc/auth.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "github.com/lightningnetwork/lnd/macaroons" 5 | "gopkg.in/macaroon.v2" 6 | ) 7 | 8 | type MacaroonCredential struct{ macaroons.MacaroonCredential } 9 | 10 | // RequireTransportSecurity implements the PerRPCCredentials interface. 11 | func (m MacaroonCredential) RequireTransportSecurity() bool { 12 | return false 13 | } 14 | 15 | // NewMacaroonCredential returns a copy of the passed macaroon wrapped in a 16 | // MacaroonCredential struct which implements PerRPCCredentials. 17 | func NewMacaroonCredential(m *macaroon.Macaroon) (MacaroonCredential, error) { 18 | ms := MacaroonCredential{} 19 | 20 | // The macaroon library's Clone() method has a subtle bug that doesn't 21 | // correctly clone all caveats. We need to use our own, safe clone 22 | // function instead. 23 | var err error 24 | ms.Macaroon, err = macaroons.SafeCopyMacaroon(m) 25 | if err != nil { 26 | return ms, err 27 | } 28 | 29 | return ms, nil 30 | } 31 | -------------------------------------------------------------------------------- /pkg/rpc/client.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "golang.zx2c4.com/wireguard/conn" 5 | "golang.zx2c4.com/wireguard/device" 6 | "golang.zx2c4.com/wireguard/tun" 7 | "golang.zx2c4.com/wireguard/tun/netstack" 8 | "net/netip" 9 | "strconv" 10 | ) 11 | 12 | func getNetworkInstance(opts *dialOptions) (net *netstack.Net, err error) { 13 | 14 | var tunnel tun.Device 15 | 16 | if tunnel, net, err = netstack.CreateNetTUN([]netip.Addr{netip.MustParseAddr(opts.peerRPCIP)}, []netip.Addr{}, opts.mtu); check(err) { 17 | return nil, err 18 | } 19 | 20 | dev := device.NewDevice(tunnel, conn.NewDefaultBind(), device.NewLogger(device.LogLevelError, "client ")) 21 | 22 | dev.SetPrivateKey(opts.key.AsDeviceKey()) 23 | 24 | deviceConf := "" + 25 | "public_key=" + opts.peerPubKey.HexString() + "\n" + 26 | "endpoint=" + opts.endpoint.String() + "\n" + 27 | "allowed_ip=" + opts.rpcEndpoint.Address() + "/32\n" + 28 | "persistent_keepalive_interval=" + strconv.Itoa(opts.keepAliveInterval) + "\n" 29 | 30 | if err = dev.IpcSet(deviceConf); check(err) { 31 | return 32 | } 33 | 34 | return net, nil 35 | } 36 | -------------------------------------------------------------------------------- /pkg/rpc/device.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "golang.zx2c4.com/wireguard/device" 5 | "net/netip" 6 | "strconv" 7 | ) 8 | 9 | var ( 10 | dev *device.Device 11 | deviceRPCIP netip.Addr = netip.MustParseAddr("192.168.37.1") 12 | deviceRPCPort uint16 = 80 13 | ) 14 | 15 | func configureDevice() { 16 | 17 | var err error 18 | 19 | dev.SetPrivateKey(tunKey.AsDeviceKey()) 20 | dev.IpcSet("listen_port=" + strconv.Itoa(int(o.tunPort))) 21 | 22 | for _, peer_whitelist := range tunWhitelist { 23 | 24 | deviceConf := "" + 25 | "public_key=" + peer_whitelist.HexString() + "\n" + 26 | "allowed_ip=192.168.37.2/32\n" 27 | 28 | if err = dev.IpcSet(deviceConf); check(err) { 29 | startupErrors <- err 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pkg/rpc/dialer.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "golang.zx2c4.com/wireguard/tun/netstack" 7 | "google.golang.org/grpc" 8 | "google.golang.org/grpc/credentials/insecure" 9 | "net" 10 | "strings" 11 | ) 12 | 13 | var ( 14 | rpcEndpointIp string = "192.168.37.1" 15 | rpcEndpointPort string = "80" 16 | ) 17 | 18 | func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *grpc.ClientConn, err error) { 19 | 20 | if strings.HasPrefix(target, "unix://") { 21 | return grpc.Dial( 22 | target, 23 | grpc.WithTransportCredentials(insecure.NewCredentials()), 24 | ) 25 | } 26 | 27 | if !strings.HasPrefix(target, "noise://") { 28 | return nil, errors.New("Unsupported protocol. Only unix:// or noise://") 29 | } 30 | 31 | dialOpts := &dialOptions{ 32 | endpoint: EndpointString(target), 33 | rpcEndpoint: EndpointString("192.168.37.1:80"), 34 | peerRPCIP: "192.168.37.2", 35 | mtu: 1420, 36 | } 37 | 38 | for _, opt := range opts { 39 | opt.apply(dialOpts) 40 | } 41 | 42 | var network *netstack.Net 43 | 44 | if network, err = getNetworkInstance(dialOpts); check(err) { 45 | return 46 | } 47 | 48 | return grpc.DialContext(ctx, 49 | dialOpts.rpcEndpoint.String(), 50 | grpc.WithTransportCredentials(insecure.NewCredentials()), 51 | grpc.WithContextDialer(func(ctx context.Context, address string) (net.Conn, error) { 52 | return network.DialContext(ctx, "tcp4", address) 53 | })) 54 | } 55 | 56 | func Dial(target string, opts ...DialOption) (conn *grpc.ClientConn, err error) { 57 | return DialContext(context.Background(), target, opts...) 58 | } 59 | -------------------------------------------------------------------------------- /pkg/rpc/dialer_options.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | // dialOptions configure a Dial call. dialOptions are set by the DialOption 4 | // values passed to Dial. 5 | type dialOptions struct { 6 | endpoint Endpoint 7 | rpcEndpoint Endpoint 8 | key PrivateKey 9 | peerPubKey PublicKey 10 | peerRPCIP string 11 | keepAliveInterval int 12 | mtu int 13 | } 14 | 15 | // DialOption configures how we set up the connection. 16 | type DialOption interface { 17 | apply(*dialOptions) 18 | } 19 | 20 | // funcDialOption wraps a function that modifies dialOptions into an 21 | // implementation of the DialOption interface. 22 | type funcDialOption struct { 23 | f func(*dialOptions) 24 | } 25 | 26 | func (fdo *funcDialOption) apply(do *dialOptions) { 27 | fdo.f(do) 28 | } 29 | 30 | func newFuncDialOption(f func(*dialOptions)) *funcDialOption { 31 | return &funcDialOption{ 32 | f: f, 33 | } 34 | } 35 | 36 | type joinDialOption struct { 37 | opts []DialOption 38 | } 39 | 40 | func (jdo *joinDialOption) apply(do *dialOptions) { 41 | for _, opt := range jdo.opts { 42 | opt.apply(do) 43 | } 44 | } 45 | 46 | func newJoinDialOption(opts ...DialOption) DialOption { 47 | return &joinDialOption{opts: opts} 48 | } 49 | 50 | func WithKeepAliveInterval(seconds int) DialOption { 51 | return newFuncDialOption(func(o *dialOptions) { 52 | o.keepAliveInterval = seconds 53 | }) 54 | } 55 | 56 | func WithPeer(pubKey string) DialOption { 57 | return newFuncDialOption(func(o *dialOptions) { 58 | o.peerPubKey = DecodePublicKey(pubKey) 59 | }) 60 | } 61 | 62 | func WithPrivateKey(key string) DialOption { 63 | return newFuncDialOption(func(o *dialOptions) { 64 | o.key = DecodePrivateKey(key) 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /pkg/rpc/doc.go: -------------------------------------------------------------------------------- 1 | // Package rpc provides an RPC server for use in remote control and external application integration. 2 | package rpc 3 | -------------------------------------------------------------------------------- /pkg/rpc/endpoint.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import "strings" 4 | 5 | type Endpoint string 6 | 7 | func (e Endpoint) Address() string { 8 | 9 | before, _, found := strings.Cut(string(e), ":") 10 | 11 | if !found { 12 | return "" 13 | } 14 | 15 | return before 16 | } 17 | 18 | func (e Endpoint) Port() string { 19 | 20 | _, after, found := strings.Cut(string(e), ":") 21 | 22 | if !found { 23 | return "" 24 | } 25 | 26 | return after 27 | } 28 | 29 | func (e Endpoint) String() string { 30 | return string(e) 31 | } 32 | 33 | func EndpointString(endpoint string) (ep Endpoint) { 34 | 35 | _, after, found := strings.Cut(string(endpoint), "//") 36 | 37 | if !found { 38 | ep = Endpoint(endpoint) 39 | 40 | return 41 | } 42 | 43 | ep = Endpoint(after) 44 | 45 | return 46 | } 47 | -------------------------------------------------------------------------------- /pkg/rpc/examples/log.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 5 | ) 6 | 7 | var ( 8 | log = log2.GetLogger() 9 | check = log.E.Chk 10 | ) 11 | -------------------------------------------------------------------------------- /pkg/rpc/examples/tunnel_hello.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "github.com/tutorialedge/go-grpc-tutorial/chat" 8 | "google.golang.org/grpc" 9 | 10 | "git.indra-labs.org/dev/ind/pkg/rpc" 11 | ) 12 | 13 | func TunnelHello(ctx context.Context) { 14 | 15 | var err error 16 | var conn *grpc.ClientConn 17 | 18 | conn, err = rpc.DialContext(ctx, 19 | "noise://[::1]:18222", 20 | rpc.WithPrivateKey("Aj9CfbE1pXEVxPfjSaTwdY3B4kYHbwsTSyT3nrc34ATN"), 21 | rpc.WithPeer("G52UmsQpUmN2zFMkJaP9rwCvqQJzi1yHKA9RTrLJTk9f"), 22 | rpc.WithKeepAliveInterval(5), 23 | ) 24 | 25 | if err != nil { 26 | check(err) 27 | os.Exit(1) 28 | } 29 | 30 | c := chat.NewChatServiceClient(conn) 31 | 32 | response, err := c.SayHello(context.Background(), &chat.Message{Body: "Hello From Alice!"}) 33 | 34 | if err != nil { 35 | check(err) 36 | return 37 | } 38 | 39 | log.I.F(response.Body) 40 | } 41 | -------------------------------------------------------------------------------- /pkg/rpc/examples/unix_hello.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "github.com/tutorialedge/go-grpc-tutorial/chat" 8 | "google.golang.org/grpc" 9 | 10 | "git.indra-labs.org/dev/ind/pkg/rpc" 11 | ) 12 | 13 | func UnixHello(ctx context.Context) { 14 | 15 | var err error 16 | var conn *grpc.ClientConn 17 | 18 | conn, err = rpc.Dial("unix:///tmp/indra.sock") 19 | 20 | if err != nil { 21 | check(err) 22 | os.Exit(1) 23 | } 24 | 25 | c := chat.NewChatServiceClient(conn) 26 | 27 | response, err := c.SayHello(context.Background(), &chat.Message{Body: "Hello From Alice!"}) 28 | 29 | if err != nil { 30 | check(err) 31 | return 32 | } 33 | 34 | log.I.F(response.Body) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/rpc/examples/unix_unlock.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "context" 5 | "git.indra-labs.org/dev/ind/pkg/rpc" 6 | "git.indra-labs.org/dev/ind/pkg/storage" 7 | "google.golang.org/grpc" 8 | "os" 9 | ) 10 | 11 | func UnixUnlock(ctx context.Context) { 12 | 13 | var err error 14 | var conn *grpc.ClientConn 15 | 16 | conn, err = rpc.Dial("unix:///tmp/indra.sock") 17 | 18 | if err != nil { 19 | check(err) 20 | os.Exit(1) 21 | } 22 | 23 | u := storage.NewUnlockServiceClient(conn) 24 | 25 | _, err = u.Unlock(ctx, &storage.UnlockRequest{ 26 | Key: "979nrx9ry9Re6UqWXYaGqLEne8NS7TzgHFiS8KARABV8", 27 | }) 28 | 29 | if err != nil { 30 | check(err) 31 | return 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /pkg/rpc/flags.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | "github.com/spf13/viper" 6 | ) 7 | 8 | var ( 9 | UnixPathFlag = "rpc-unix-listen" 10 | TunEnableFlag = "rpc-tun-enable" 11 | TunPortFlag = "rpc-tun-port" 12 | TunPeersFlag = "rpc-tun-peer" 13 | ) 14 | var ( 15 | unixPath string 16 | tunEnabled bool = false 17 | tunPort int = 0 18 | tunPeersRaw = []string{} 19 | ) 20 | 21 | func InitFlags(cmd *cobra.Command) { 22 | 23 | cmd.PersistentFlags().StringVarP(&unixPath, UnixPathFlag, "", 24 | "", 25 | "binds to a unix socket with path (default is $HOME/.indra/indra.sock)", 26 | ) 27 | 28 | viper.BindPFlag(UnixPathFlag, cmd.PersistentFlags().Lookup(UnixPathFlag)) 29 | 30 | cmd.PersistentFlags().BoolVarP(&tunEnabled, TunEnableFlag, "", 31 | false, 32 | "enables the rpc server tunnel (default false)", 33 | ) 34 | 35 | viper.BindPFlag(TunEnableFlag, cmd.PersistentFlags().Lookup(TunEnableFlag)) 36 | 37 | cmd.PersistentFlags().IntVarP(&tunPort, TunPortFlag, "", 38 | tunPort, 39 | "binds the udp server to port (random if not selected)", 40 | ) 41 | 42 | viper.BindPFlag(TunPortFlag, cmd.PersistentFlags().Lookup(TunPortFlag)) 43 | 44 | cmd.PersistentFlags().StringSliceVarP(&tunPeersRaw, TunPeersFlag, "", 45 | tunPeersRaw, 46 | "adds a peer id to the whitelist for access", 47 | ) 48 | 49 | viper.BindPFlag(TunPeersFlag, cmd.PersistentFlags().Lookup(TunPeersFlag)) 50 | } 51 | -------------------------------------------------------------------------------- /pkg/rpc/log.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 5 | ) 6 | 7 | var ( 8 | log = log2.GetLogger() 9 | check = log.E.Chk 10 | ) 11 | -------------------------------------------------------------------------------- /pkg/rpc/server.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "google.golang.org/grpc" 5 | "sync" 6 | ) 7 | 8 | var ( 9 | server *grpc.Server 10 | o *ServerOptions 11 | ) 12 | 13 | var ( 14 | inUse sync.Mutex 15 | isRunning bool 16 | ) 17 | 18 | func RunWith(r func(srv *grpc.Server), opts ...ServerOption) { 19 | 20 | if !inUse.TryLock() { 21 | log.E.Ln("rpc server is in use.") 22 | return 23 | } 24 | 25 | log.I.Ln("initializing the rpc server") 26 | 27 | o = &ServerOptions{ 28 | &storeMem{}, 29 | unixPathDefault, 30 | false, 31 | NullPort, 32 | []string{}, 33 | } 34 | 35 | for _, opt := range opts { 36 | opt.apply(o) 37 | } 38 | 39 | if o.unixPath != "" { 40 | log.I.Ln("enabling rpc unix listener:") 41 | log.I.F("- [/unix%s]", o.unixPath) 42 | 43 | isUnixSockEnabled = true 44 | } 45 | 46 | if o.tunEnable { 47 | configureTunnel() 48 | } 49 | 50 | isConfigured <- true 51 | 52 | server = grpc.NewServer( 53 | //grpc.WithPerRPCCredentials(), 54 | ) 55 | r(server) 56 | 57 | go start() 58 | } 59 | 60 | func Options() *ServerOptions { 61 | return o 62 | } 63 | 64 | func start() { 65 | 66 | log.I.Ln("starting rpc server") 67 | 68 | var err error 69 | 70 | createTunnel() 71 | 72 | if err = startTunnel(server); check(err) { 73 | startupErrors <- err 74 | return 75 | } 76 | 77 | if err = startUnixSocket(server); check(err) { 78 | startupErrors <- err 79 | return 80 | } 81 | 82 | isRunning = true 83 | 84 | log.I.Ln("rpc server is ready") 85 | isReady <- true 86 | } 87 | 88 | func Shutdown() { 89 | 90 | if !isRunning { 91 | return 92 | } 93 | 94 | log.I.Ln("shutting down rpc server") 95 | 96 | server.GracefulStop() 97 | 98 | var err error 99 | 100 | //err = stopUnixSocket() 101 | // 102 | //check(err) 103 | 104 | err = stopTunnel() 105 | 106 | check(err) 107 | 108 | isRunning = false 109 | 110 | inUse.Unlock() 111 | 112 | log.I.Ln("- rpc server shutdown completed") 113 | } 114 | -------------------------------------------------------------------------------- /pkg/rpc/server_options.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | type ServerOptions struct { 4 | store Store 5 | unixPath string 6 | tunEnable bool 7 | tunPort uint16 8 | tunPeers []string 9 | } 10 | 11 | func (s *ServerOptions) GetTunPort() uint16 { return s.tunPort } 12 | 13 | type ServerOption interface { 14 | apply(*ServerOptions) 15 | } 16 | 17 | type funcServerOption struct { 18 | f func(*ServerOptions) 19 | } 20 | 21 | func (fdo *funcServerOption) apply(do *ServerOptions) { 22 | fdo.f(do) 23 | } 24 | 25 | func newFuncServerOption(f func(*ServerOptions)) *funcServerOption { 26 | return &funcServerOption{ 27 | f: f, 28 | } 29 | } 30 | 31 | func WithDisableTunnel() ServerOption { 32 | return newFuncServerOption(func(o *ServerOptions) { 33 | o.tunEnable = false 34 | }) 35 | } 36 | 37 | func WithStore(store Store) ServerOption { 38 | return newFuncServerOption(func(o *ServerOptions) { 39 | o.store = store 40 | }) 41 | } 42 | 43 | func WithUnixPath(path string) ServerOption { 44 | return newFuncServerOption(func(o *ServerOptions) { 45 | o.unixPath = path 46 | }) 47 | } 48 | 49 | func WithTunOptions(port uint16, peers []string) ServerOption { 50 | return newFuncServerOption(func(o *ServerOptions) { 51 | o.tunEnable = true 52 | o.tunPort = port 53 | o.tunPeers = peers 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /pkg/rpc/signals.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | var ( 4 | startupErrors = make(chan error, 128) 5 | isConfigured = make(chan bool, 1) 6 | isReady = make(chan bool, 1) 7 | ) 8 | 9 | func WhenStartFailed() chan error { 10 | return startupErrors 11 | } 12 | 13 | func IsConfigured() chan bool { 14 | return isConfigured 15 | } 16 | 17 | func IsReady() chan bool { 18 | return isReady 19 | } 20 | -------------------------------------------------------------------------------- /pkg/rpc/socket_unix.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "google.golang.org/grpc" 5 | "net" 6 | "os" 7 | ) 8 | 9 | const unixPathDefault = "/tmp/indra.sock" 10 | 11 | var ( 12 | isUnixSockEnabled bool = false 13 | unixSock net.Listener 14 | ) 15 | 16 | func startUnixSocket(srv *grpc.Server) (err error) { 17 | 18 | if !isUnixSockEnabled { 19 | return 20 | } 21 | 22 | if unixSock, err = net.Listen("unix", o.unixPath); err != nil { 23 | return 24 | } 25 | 26 | go srv.Serve(unixSock) 27 | 28 | return 29 | } 30 | 31 | func stopUnixSocket() (err error) { 32 | 33 | if !isUnixSockEnabled { 34 | return 35 | } 36 | 37 | if unixSock != nil { 38 | if err = unixSock.Close(); err != nil { 39 | // continue 40 | } 41 | } 42 | 43 | os.Remove(o.unixPath) 44 | 45 | return 46 | } 47 | -------------------------------------------------------------------------------- /pkg/rpc/store.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "errors" 5 | "github.com/dgraph-io/badger/v3" 6 | ) 7 | 8 | var ( 9 | ErrKeyNotExists error = errors.New("key not found") 10 | ) 11 | 12 | var ( 13 | tunKeyKey = "rpc-tun-key" 14 | ) 15 | 16 | type Store interface { 17 | Reset() 18 | 19 | SetKey(key *PrivateKey) error 20 | 21 | GetKey() (*PrivateKey, error) 22 | } 23 | 24 | type storeMem struct { 25 | key *PrivateKey 26 | } 27 | 28 | func (s *storeMem) Reset() { 29 | s.key = nil 30 | } 31 | 32 | func (s *storeMem) SetKey(key *PrivateKey) error { 33 | s.key = key 34 | return nil 35 | } 36 | 37 | func (s *storeMem) GetKey() (*PrivateKey, error) { 38 | 39 | if s.key == nil { 40 | return nil, ErrKeyNotExists 41 | } 42 | 43 | if s.key.IsZero() { 44 | return nil, ErrKeyNotExists 45 | } 46 | 47 | return s.key, nil 48 | } 49 | 50 | type BadgerStore struct { 51 | *badger.DB 52 | } 53 | 54 | func (s *BadgerStore) Reset() { 55 | 56 | s.Update(func(txn *badger.Txn) error { 57 | txn.Delete([]byte(tunKeyKey)) 58 | 59 | return nil 60 | }) 61 | } 62 | 63 | func (s *BadgerStore) SetKey(key *PrivateKey) error { 64 | 65 | s.Update(func(txn *badger.Txn) error { 66 | err := txn.Set([]byte(tunKeyKey), key.Bytes()) 67 | return err 68 | }) 69 | 70 | return nil 71 | } 72 | 73 | func (s *BadgerStore) GetKey() (*PrivateKey, error) { 74 | 75 | var err error 76 | var item *badger.Item 77 | 78 | err = s.View(func(txn *badger.Txn) error { 79 | item, err = txn.Get([]byte(tunKeyKey)) 80 | return err 81 | }) 82 | 83 | if err == badger.ErrKeyNotFound { 84 | return nil, ErrKeyNotExists 85 | } 86 | 87 | var key PrivateKey 88 | 89 | err = item.Value(func(val []byte) error { 90 | key.DecodeBytes(val) 91 | return nil 92 | }) 93 | 94 | return &key, err 95 | } 96 | -------------------------------------------------------------------------------- /pkg/rpc/tunnel.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "golang.zx2c4.com/wireguard/conn" 5 | "golang.zx2c4.com/wireguard/device" 6 | "golang.zx2c4.com/wireguard/tun" 7 | "golang.zx2c4.com/wireguard/tun/netstack" 8 | "google.golang.org/grpc" 9 | "net" 10 | "net/netip" 11 | ) 12 | 13 | const NullPort = 0 14 | 15 | var ( 16 | network *netstack.Net 17 | tunnel tun.Device 18 | tcpSock net.Listener 19 | ) 20 | 21 | var ( 22 | tunKey *PrivateKey 23 | tunWhitelist []PublicKey 24 | tunnelMTU int = 1420 25 | ) 26 | 27 | func createTunnel() { 28 | 29 | var err error 30 | 31 | if tunnel, network, err = netstack.CreateNetTUN([]netip.Addr{deviceRPCIP}, []netip.Addr{}, tunnelMTU); check(err) { 32 | startupErrors <- err 33 | return 34 | } 35 | 36 | dev = device.NewDevice(tunnel, conn.NewDefaultBind(), device.NewLogger(device.LogLevelError, "server ")) 37 | } 38 | 39 | func startTunnel(srv *grpc.Server) (err error) { 40 | 41 | if !o.tunEnable { 42 | return 43 | } 44 | 45 | configureDevice() 46 | 47 | if err = dev.Up(); check(err) { 48 | startupErrors <- err 49 | return 50 | } 51 | 52 | if tcpSock, err = network.ListenTCPAddrPort(netip.AddrPortFrom(deviceRPCIP, deviceRPCPort)); check(err) { 53 | startupErrors <- err 54 | return 55 | } 56 | 57 | go srv.Serve(tcpSock) 58 | 59 | return 60 | } 61 | 62 | func stopTunnel() (err error) { 63 | 64 | if !o.tunEnable { 65 | return 66 | } 67 | 68 | if err = tcpSock.Close(); check(err) { 69 | // continue 70 | } 71 | 72 | dev.Close() 73 | 74 | return 75 | } 76 | -------------------------------------------------------------------------------- /pkg/rpc/tunnel_config.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import "errors" 4 | 5 | func configureTunnel() { 6 | 7 | if !o.tunEnable { 8 | return 9 | } 10 | 11 | log.I.Ln("enabling rpc tunnel") 12 | 13 | configureTunnelPort() 14 | 15 | log.I.Ln("rpc tunnel listeners:") 16 | log.I.F("- [/ip4/0.0.0.0/udp/%d /ip6/:::/udp/%d]", o.tunPort, o.tunPort) 17 | 18 | configureTunnelKey() 19 | configurePeerWhitelist() 20 | } 21 | 22 | func configureTunnelKey() { 23 | 24 | log.I.Ln("looking for key in storage") 25 | 26 | var err error 27 | 28 | tunKey, err = o.store.GetKey() 29 | 30 | if err == nil { 31 | 32 | log.I.Ln("rpc tunnel public key:") 33 | log.I.Ln("-", tunKey.PubKey().Encode()) 34 | 35 | return 36 | } 37 | 38 | if !errors.Is(err, ErrKeyNotExists) { 39 | return 40 | } 41 | 42 | log.I.Ln("key not provided, generating a new one.") 43 | 44 | tunKey, _ = NewPrivateKey() 45 | 46 | o.store.SetKey(tunKey) 47 | 48 | log.I.Ln("rpc tunnel public key:") 49 | log.I.Ln("-", tunKey.PubKey().Encode()) 50 | } 51 | 52 | func configureTunnelPort() { 53 | 54 | if o.tunPort != NullPort { 55 | return 56 | } 57 | 58 | log.I.Ln("rpc tunnel port not provided, generating a random one.") 59 | 60 | o.tunPort = genRandomPort(10000) // no paricular reason why 10000 minimum, would be better to just use 0 bind? 61 | } 62 | 63 | func configurePeerWhitelist() { 64 | 65 | if len(o.tunPeers) == 0 { 66 | return 67 | } 68 | 69 | log.I.Ln("rpc tunnel whitelisted peers:") 70 | 71 | for _, peer := range o.tunPeers { 72 | 73 | var pubKey PublicKey 74 | 75 | pubKey.Decode(peer) 76 | 77 | log.I.Ln("-", pubKey.Encode()) 78 | 79 | tunWhitelist = append(tunWhitelist, pubKey) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /pkg/rpc/util.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | func genRandomPort(offset int) uint16 { 9 | 10 | rand.Seed(time.Now().Unix()) 11 | 12 | return uint16(rand.Intn(65534-offset) + offset) 13 | } 14 | -------------------------------------------------------------------------------- /pkg/seed/log.go: -------------------------------------------------------------------------------- 1 | package seed 2 | 3 | import ( 4 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 5 | ) 6 | 7 | var ( 8 | log = log2.GetLogger() 9 | check = log.E.Chk 10 | ) 11 | -------------------------------------------------------------------------------- /pkg/seed/signals.go: -------------------------------------------------------------------------------- 1 | package seed 2 | 3 | var ( 4 | startupErrors = make(chan error, 32) 5 | isReadyChan = make(chan bool, 1) 6 | isShutdownChan = make(chan bool, 1) 7 | ) 8 | 9 | func WhenStartFailed() chan error { 10 | return startupErrors 11 | } 12 | 13 | func WhenReady() chan bool { 14 | return isReadyChan 15 | } 16 | 17 | func WhenShutdown() chan bool { 18 | return isShutdownChan 19 | } 20 | -------------------------------------------------------------------------------- /pkg/splicer/i32/int32.go: -------------------------------------------------------------------------------- 1 | package i32 2 | 3 | import "encoding/binary" 4 | 5 | const Len = 4 6 | 7 | // S is a 4 byte value that stores an int32. S here to signify it is signed. 8 | type S struct { 9 | b []byte 10 | } 11 | 12 | // New allocates bytes to store a new i32.S in. Note that this allocates memory. 13 | func New() *S { return &S{b: make([]byte, Len)} } 14 | 15 | // NewFrom creates a 32-bit integer from raw bytes, if the slice is at least Len 16 | // bytes long. This can be used to snip out an encoded segment which should 17 | // return a value from a call to Get. 18 | // 19 | // The remaining bytes, if any, are returned for further processing. 20 | func NewFrom(b []byte) (s *S, rem []byte) { 21 | // If the bytes are less than Len the input is invalid and nil will be 22 | // returned. 23 | if len(b) < Len { 24 | return 25 | } 26 | // This slices the input, meaning no copy is required, only allocating the 27 | // slice pointer. 28 | s = &S{b: b[:Len]} 29 | if len(b) > Len { 30 | rem = b[Len:] 31 | } 32 | return 33 | } 34 | 35 | func (s *S) Read() (out []byte) { 36 | if len(s.b) >= Len { 37 | out = s.b[:Len] 38 | } 39 | return 40 | } 41 | 42 | func (s *S) Write(by []byte) (out []byte) { 43 | if len(by) >= Len { 44 | s.b = []byte{by[0], by[1], by[2], by[3]} 45 | if len(by) > Len { 46 | out = by[Len:] 47 | } 48 | } 49 | return 50 | } 51 | 52 | func (s *S) Len() int { return len(s.b) } 53 | 54 | func (s *S) Get() (v interface{}) { 55 | if len(s.b) >= Len { 56 | val := int32(binary.BigEndian.Uint32(s.b)) 57 | return &val 58 | } 59 | return 60 | } 61 | 62 | func (s *S) Put(v interface{}) interface{} { 63 | if len(s.b) < Len { 64 | s.b = make([]byte, Len) 65 | } 66 | var tv *int32 67 | var ok bool 68 | if tv, ok = v.(*int32); ok { 69 | binary.BigEndian.PutUint32(s.b[:Len], uint32(*tv)) 70 | } 71 | return s 72 | } 73 | 74 | // Assert takes an interface and if it is a t64.Time, returns the time.Time 75 | // value. If it is not the expected type, nil is returned. 76 | func Assert(v interface{}) (t *int32) { 77 | var tv *S 78 | var ok bool 79 | if tv, ok = v.(*S); ok { 80 | tt := tv.Get() 81 | // If this fails the return is nil, indicating failure. 82 | t, _ = tt.(*int32) 83 | } 84 | return 85 | } 86 | -------------------------------------------------------------------------------- /pkg/splicer/i32/int32_test.go: -------------------------------------------------------------------------------- 1 | package i32 2 | 3 | import ( 4 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 5 | "git.indra-labs.org/dev/ind/pkg/util/ci" 6 | "testing" 7 | ) 8 | 9 | var ( 10 | log = log2.GetLogger() 11 | fails = log.E.Chk 12 | ) 13 | 14 | func TestS(t *testing.T) { 15 | ci.TraceIfNot() 16 | 17 | t1, t2 := New(), New() 18 | 19 | var val int32 = 234234 20 | 21 | // Encode in the value. 22 | t1.Put(&val) 23 | 24 | // Copy to the other. 25 | t2.Write(t1.Read()) 26 | 27 | // Verify accessors work. 28 | var ta1, ta2 *int32 29 | if ta1 = Assert(t1); ta1 == nil { 30 | log.E.Ln("did not get expected time value") 31 | t.FailNow() 32 | } 33 | if ta2 = Assert(t2); ta2 == nil { 34 | log.E.Ln("did not get expected time value") 35 | t.FailNow() 36 | } 37 | 38 | // Verify the value survived the encode/decode. 39 | if *ta1 != *ta2 { 40 | t.FailNow() 41 | } 42 | 43 | // Test NewFrom correctly decodes and returns the trimmings. 44 | b1 := t1.Read() 45 | nb1, rem := NewFrom(append(b1, make([]byte, 5)...)) 46 | if len(rem) != 5 || rem == nil { 47 | t.FailNow() 48 | } 49 | v := Assert(nb1) 50 | if *v != *ta1 { 51 | t.FailNow() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pkg/splicer/magic/magic.go: -------------------------------------------------------------------------------- 1 | package magic 2 | 3 | const Len = 4 4 | 5 | type Bytes struct { 6 | bytes []byte 7 | } 8 | 9 | // New allocates bytes to store a new magic.Bytes in. Note that this allocates 10 | // memory. 11 | func New() *Bytes { return &Bytes{bytes: make([]byte, Len)} } 12 | 13 | // NewFrom creates a 32-bit integer from raw bytes, if the slice is at least Len 14 | // bytes long. This can be used to snip out an encoded segment which should 15 | // return a value from a call to Get. 16 | // 17 | // The remaining bytes, if any, are returned for further processing. 18 | func NewFrom(b []byte) (s *Bytes, rem []byte) { 19 | if len(b) < Len { 20 | return 21 | } 22 | // This slices the input, meaning no copy is required, only allocating the 23 | // slice pointer. 24 | s = &Bytes{bytes: b[:Len]} 25 | if len(b) > Len { 26 | rem = b[Len:] 27 | } 28 | return 29 | } 30 | 31 | func (m Bytes) Len() (l int) { return len(m.bytes) } 32 | 33 | func (m Bytes) Read() (o []byte) { 34 | if len(m.bytes) >= Len { 35 | o = m.bytes[:Len] 36 | } 37 | return 38 | } 39 | 40 | func (m Bytes) Write(b []byte) (o []byte) { 41 | if len(b) >= Len { 42 | m.bytes = b[:3] 43 | // If there is more, return the excess. 44 | if len(b) > Len { 45 | o = b[Len:] 46 | } 47 | } 48 | return 49 | } 50 | 51 | func (m Bytes) Get() (v interface{}) { 52 | if len(m.bytes) >= Len { 53 | val := string(m.bytes[:Len]) 54 | v = &val 55 | } 56 | return 57 | } 58 | 59 | func (m Bytes) Put(v interface{}) (o interface{}) { 60 | var tv *string 61 | var ok bool 62 | if tv, ok = v.(*string); ok { 63 | bytes := []byte(*tv) 64 | if len(bytes) == Len { 65 | m.bytes = bytes 66 | } 67 | } 68 | return 69 | } 70 | 71 | // Assert takes an interface and if it is a duration.Time, returns the time.Time 72 | // value. If it is not the expected type, nil is returned. 73 | func Assert(v interface{}) (t *string) { 74 | var tv *Bytes 75 | var ok bool 76 | if tv, ok = v.(*Bytes); ok { 77 | tt := tv.Get() 78 | // If this fails the return is nil, indicating failure. 79 | t, _ = tt.(*string) 80 | } 81 | return 82 | } 83 | -------------------------------------------------------------------------------- /pkg/splicer/magic/magic_test.go: -------------------------------------------------------------------------------- 1 | package magic 2 | -------------------------------------------------------------------------------- /pkg/splicer/splicer_test.go: -------------------------------------------------------------------------------- 1 | package splicer 2 | 3 | // todo: a test is a good idea 4 | -------------------------------------------------------------------------------- /pkg/splicer/t64/time_test.go: -------------------------------------------------------------------------------- 1 | package t64 2 | 3 | import ( 4 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 5 | "git.indra-labs.org/dev/ind/pkg/util/ci" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | var ( 11 | log = log2.GetLogger() 12 | fails = log.E.Chk 13 | ) 14 | 15 | func TestNew(t *testing.T) { 16 | ci.TraceIfNot() 17 | 18 | t1, t2 := New(), New() 19 | nao := time.Now() 20 | 21 | // Encode in the time value. 22 | t1.Put(&nao) 23 | 24 | // Copy to other Time. 25 | t2.Write(t1.Read()) 26 | 27 | // Verify accessors work. 28 | var ta1, ta2 *time.Time 29 | if ta1 = Assert(t1); ta1 == nil { 30 | log.E.Ln("did not get expected time value") 31 | t.FailNow() 32 | } 33 | if ta2 = Assert(t2); ta2 == nil { 34 | log.E.Ln("did not get expected time value") 35 | t.FailNow() 36 | } 37 | 38 | // Verify the value survived the encode/decode. 39 | if !(*ta1).Equal(*ta2) { 40 | t.FailNow() 41 | } 42 | 43 | // Test NewFrom correctly decodes and returns the trimmings. 44 | b1 := t1.Read() 45 | nb1, rem := NewFrom(append(b1, make([]byte, 5)...)) 46 | if rem == nil || len(rem) != 5 { 47 | t.FailNow() 48 | } 49 | val := Assert(nb1) 50 | if !(*val).Equal(*ta1) { 51 | t.FailNow() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pkg/storage/doc.go: -------------------------------------------------------------------------------- 1 | // Package storage provides an encrypted key value store and an RPC service for remote unlocking to prevent secrets being stored on remote nonvolatile storage. 2 | package storage 3 | -------------------------------------------------------------------------------- /pkg/storage/flags.go: -------------------------------------------------------------------------------- 1 | package storage 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/util/appdata" 5 | "github.com/spf13/cobra" 6 | "github.com/spf13/viper" 7 | "path/filepath" 8 | ) 9 | 10 | var ( 11 | storeKeyFlag = "store-key" 12 | storeKeyFileFlag = "store-keyfile" 13 | //storeKeyRPCFlag = "store-key-rpc" 14 | storeFilePathFlag = "store-path" 15 | //storeAskPassFlag = "store-ask-pass" 16 | ) 17 | 18 | var ( 19 | storeEncryptionKey string 20 | storeEncryptionKeyFile string 21 | //storeEncryptionKeyRPC bool 22 | storeFilePath string 23 | //storeAskPass bool 24 | ) 25 | 26 | func InitFlags(cmd *cobra.Command) { 27 | 28 | cmd.Flags().StringVarP(&storeEncryptionKey, storeKeyFlag, "", 29 | "", 30 | "the key required to unlock storage (NOT recommended)", 31 | ) 32 | 33 | viper.BindPFlag(storeKeyFlag, cmd.Flags().Lookup(storeKeyFlag)) 34 | 35 | cmd.PersistentFlags().StringVarP(&storeEncryptionKeyFile, storeKeyFileFlag, "", 36 | "", 37 | "the path of the keyfile required to unlock storage", 38 | ) 39 | 40 | viper.BindPFlag(storeKeyFileFlag, cmd.PersistentFlags().Lookup(storeKeyFileFlag)) 41 | 42 | cmd.PersistentFlags().StringVarP(&storeFilePath, storeFilePathFlag, "", 43 | filepath.Join(appdata.Dir("indra", false), "indra.db"), 44 | "the path of the database (default is /indra.db)", 45 | ) 46 | 47 | viper.BindPFlag(storeFilePathFlag, cmd.PersistentFlags().Lookup(storeFilePathFlag)) 48 | 49 | //cmd.PersistentFlags().BoolVarP(&storeEncryptionKeyRPC, storeKeyRPCFlag, "", 50 | // false, 51 | // "looks for the encryption key via RPC", 52 | //) 53 | // 54 | //viper.BindPFlag(storeKeyRPCFlag, cmd.PersistentFlags().Lookup(storeKeyRPCFlag)) 55 | 56 | //cmd.PersistentFlags().BoolVarP(&storeAskPass, storeAskPassFlag, "", 57 | // false, 58 | // "prompts the user for a password to unlock storage", 59 | //) 60 | // 61 | //viper.BindPFlag(storeAskPassFlag, cmd.PersistentFlags().Lookup(storeAskPassFlag)) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/storage/key.go: -------------------------------------------------------------------------------- 1 | package storage 2 | 3 | import ( 4 | "crypto/rand" 5 | "github.com/btcsuite/btcd/btcutil/base58" 6 | "strings" 7 | ) 8 | 9 | type Key [32]byte 10 | 11 | func (k Key) Bytes() []byte { 12 | return k[:] 13 | } 14 | 15 | func (k Key) Encode() string { 16 | return base58.Encode(k[:]) 17 | } 18 | 19 | func (k *Key) Decode(key string) { 20 | 21 | key = strings.TrimSpace(key) 22 | 23 | copy(k[:], base58.Decode(key)) 24 | } 25 | 26 | func KeyGen() (Key, error) { 27 | 28 | var err error 29 | var sk [32]byte 30 | var key Key 31 | 32 | _, err = rand.Read(sk[:]) 33 | 34 | sk[0] &= 248 35 | sk[31] = (sk[31] & 127) | 64 36 | 37 | key = sk 38 | 39 | return key, err 40 | } 41 | -------------------------------------------------------------------------------- /pkg/storage/log.go: -------------------------------------------------------------------------------- 1 | package storage 2 | 3 | import ( 4 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 5 | ) 6 | 7 | var ( 8 | log = log2.GetLogger() 9 | check = log.E.Chk 10 | ) 11 | -------------------------------------------------------------------------------- /pkg/storage/service_unlock.go: -------------------------------------------------------------------------------- 1 | package storage 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type Service struct{} 8 | 9 | func (s *Service) Unlock(ctx context.Context, req *UnlockRequest) (res *UnlockResponse, err error) { 10 | 11 | log.I.Ln("attempting to unlock database") 12 | 13 | key.Decode(req.Key) 14 | 15 | isUnlocked, err := attempt_unlock() 16 | 17 | if !isUnlocked { 18 | 19 | log.I.Ln("unlock attempt failed:", err) 20 | 21 | return &UnlockResponse{Success: false}, err 22 | } 23 | 24 | log.I.Ln("successfully unlocked database") 25 | isUnlockedChan <- true 26 | 27 | return &UnlockResponse{Success: true}, nil 28 | } 29 | 30 | func (s *Service) mustEmbedUnimplementedUnlockServiceServer() {} 31 | 32 | func NewUnlockService() *Service { return &Service{} } 33 | -------------------------------------------------------------------------------- /pkg/storage/signals.go: -------------------------------------------------------------------------------- 1 | package storage 2 | 3 | var ( 4 | startupErrors = make(chan error, 128) 5 | isLockedChan = make(chan bool, 1) 6 | isUnlockedChan = make(chan bool, 1) 7 | isReadyChan = make(chan bool, 1) 8 | ) 9 | 10 | var ( 11 | isReady bool 12 | ) 13 | 14 | func WhenStartFailed() chan error { 15 | return startupErrors 16 | } 17 | 18 | func WhenIsLocked() chan bool { 19 | return isLockedChan 20 | } 21 | 22 | func WhenIsUnlocked() chan bool { 23 | return isUnlockedChan 24 | } 25 | 26 | func WhenReady() chan bool { 27 | return isReadyChan 28 | } 29 | -------------------------------------------------------------------------------- /pkg/storage/unlock.go: -------------------------------------------------------------------------------- 1 | package storage 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/util/options" 5 | "github.com/dgraph-io/badger/v3" 6 | "github.com/spf13/viper" 7 | ) 8 | 9 | func attempt_unlock() (isUnlocked bool, err error) { 10 | 11 | opts = options.Default(viper.GetString(storeFilePathFlag), key[:]) 12 | 13 | if db, err = badger.Open(*opts); err != nil { 14 | 15 | db = nil 16 | 17 | return false, err 18 | } 19 | 20 | return true, nil 21 | } 22 | -------------------------------------------------------------------------------- /pkg/storage/unlock.proto: -------------------------------------------------------------------------------- 1 | 2 | syntax = "proto3"; 3 | 4 | option go_package = "."; 5 | 6 | package rpc; 7 | 8 | message UnlockRequest { 9 | string key = 1; 10 | } 11 | 12 | message UnlockResponse { 13 | bool success = 1; 14 | optional string message = 2; 15 | } 16 | 17 | service UnlockService { 18 | rpc Unlock(UnlockRequest) returns (UnlockResponse) {} 19 | } 20 | -------------------------------------------------------------------------------- /pkg/util/b32/constant.go: -------------------------------------------------------------------------------- 1 | // Package b32 is an isolated package for storing the based32 cipher set. 2 | package b32 3 | 4 | // Based32Ciphers is the list of symbols forming the digits for Indra's 5 | // distinctive Based32 encoding. It is base 32, but has all lower case letters 6 | // so that it is readily sensible for use in "vanity" mining for public keys and 7 | // other types of very large numbers. 8 | const Based32Ciphers = "abcdefghijklmnopqrstuvwxyz234679" 9 | -------------------------------------------------------------------------------- /pkg/util/ci/trace.go: -------------------------------------------------------------------------------- 1 | package ci 2 | 3 | import ( 4 | indra "git.indra-labs.org/dev/ind" 5 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 6 | ) 7 | 8 | func TraceIfNot() { 9 | if indra.CI == "false" { 10 | log2.SetLogLevel(log2.Trace) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /pkg/util/cryptorand/cryptorand.go: -------------------------------------------------------------------------------- 1 | // Package cryptorand augments the standard math/rand library with cryptographic entropy seeding. 2 | package cryptorand 3 | 4 | import ( 5 | rand2 "crypto/rand" 6 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 7 | "git.indra-labs.org/dev/ind/pkg/util/slice" 8 | "math/rand" 9 | ) 10 | 11 | var ( 12 | log = log2.GetLogger() 13 | check = log.E.Chk 14 | ) 15 | 16 | func GetSeed() int64 { 17 | rBytes := make([]byte, 8) 18 | if n, e := rand2.Read(rBytes); n != 8 && check(e) { 19 | return 0 20 | } 21 | return int64(slice.DecodeUint64(rBytes)) 22 | } 23 | 24 | func IntN(n int) int { 25 | rand.Seed(GetSeed()) 26 | return rand.Intn(n) 27 | } 28 | 29 | func Shuffle(l int, fn func(i, j int)) { 30 | rand.Seed(GetSeed()) 31 | rand.Shuffle(l, fn) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/util/file/file.go: -------------------------------------------------------------------------------- 1 | // Package file provides some helpers for working with files and folders, specifically making possibly multi-level deep new directories based on a pathspec and testing whether a file exists. 2 | package file 3 | 4 | import ( 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | // EnsureDir checks a file could be written to a path, creates the directories as needed 10 | func EnsureDir(fileName string) { 11 | dirName := filepath.Dir(fileName) 12 | if _, serr := os.Stat(dirName); serr != nil { 13 | merr := os.MkdirAll(dirName, os.ModePerm) 14 | if merr != nil { 15 | panic(merr) 16 | } 17 | } 18 | } 19 | 20 | // FileExists reports whether the named file or directory exists. 21 | func FileExists(filePath string) bool { 22 | _, e := os.Stat(filePath) 23 | return e == nil 24 | } 25 | -------------------------------------------------------------------------------- /pkg/util/math/math.go: -------------------------------------------------------------------------------- 1 | // Package math provides a simple 32 bit unsigned integer minimum function. 2 | package math 3 | 4 | // MinUint32 is a helper function to return the minimum of two uint32s. This avoids a math import and the need to cast 5 | // to floats. 6 | func MinUint32(a, b uint32) uint32 { 7 | if a < b { 8 | return a 9 | } 10 | return b 11 | } 12 | -------------------------------------------------------------------------------- /pkg/util/multikey/multikey.go: -------------------------------------------------------------------------------- 1 | package multikey 2 | 3 | import ( 4 | "git.indra-labs.org/dev/ind/pkg/crypto" 5 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 6 | "github.com/libp2p/go-libp2p/core/peer" 7 | "github.com/multiformats/go-multiaddr" 8 | ) 9 | 10 | var ( 11 | log = log2.GetLogger() 12 | fails = log.E.Chk 13 | ) 14 | 15 | func AddKeyToMultiaddr(in multiaddr.Multiaddr, pub *crypto.Pub) (ma multiaddr.Multiaddr) { 16 | var pid peer.ID 17 | var e error 18 | if pid, e = peer.IDFromPublicKey(pub); fails(e) { 19 | return 20 | } 21 | var k multiaddr.Multiaddr 22 | if k, e = multiaddr.NewMultiaddr("/p2p/" + pid.String()); fails(e) { 23 | return 24 | } 25 | ma = in.Encapsulate(k) 26 | return 27 | } 28 | -------------------------------------------------------------------------------- /pkg/util/norm/norm.go: -------------------------------------------------------------------------------- 1 | // Package norm is a string comparison library that makes everything lowercase before comparison for case insensitive equality testing. 2 | package norm 3 | 4 | import ( 5 | "strings" 6 | ) 7 | 8 | func Eq(a, b string) bool { 9 | an, bn := Norm(a), Norm(b) 10 | return an == bn 11 | } 12 | 13 | func Norm(s string) string { return strings.ToLower(s) } 14 | -------------------------------------------------------------------------------- /pkg/util/options/default.go: -------------------------------------------------------------------------------- 1 | package options 2 | 3 | import "github.com/dgraph-io/badger/v3" 4 | 5 | // Default returns a pointer to badger.Options to be used to open 6 | // Indra's main data store. 7 | // 8 | // This is separated from the seed's usage of it in order to make test data 9 | // stores without duplicating this common configuration setting. 10 | func Default(filePath string, key []byte) *badger.Options { 11 | 12 | o := badger.DefaultOptions(filePath) 13 | 14 | // If log level is above info maybe we do want this enabled? 15 | o.Logger = nil 16 | 17 | // This works out as 1 << 27, ie 256kb. Should it be 1<<30 1Mb? 18 | o.IndexCacheSize = 128 << 20 19 | 20 | o.EncryptionKey = key[:] 21 | 22 | return &o 23 | } 24 | -------------------------------------------------------------------------------- /pkg/util/path/path.go: -------------------------------------------------------------------------------- 1 | // Package path provides a simple string slice representation for paths, equally usable for filesystems or HD keychain schemes. 2 | package path 3 | 4 | import ( 5 | "strings" 6 | 7 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 8 | "git.indra-labs.org/dev/ind/pkg/util/norm" 9 | ) 10 | 11 | type ( 12 | Path []string 13 | ) 14 | 15 | var ( 16 | log = log2.GetLogger() 17 | check = log.E.Chk 18 | ) 19 | 20 | func (p Path) Child(child string) (p1 Path) { return append(p, child) } 21 | 22 | func (p Path) Common(p2 Path) (o Path) { 23 | for i := range p { 24 | if len(p2) < i { 25 | if p[i] == p2[i] { 26 | o = append(o, p[i]) 27 | } 28 | } 29 | } 30 | return 31 | } 32 | 33 | func (p Path) Equal(p2 Path) bool { 34 | if len(p) == len(p2) { 35 | for i := range p { 36 | if norm.Norm(p[i]) != 37 | norm.Norm(p2[i]) { 38 | return false 39 | } 40 | } 41 | return true 42 | } 43 | return false 44 | } 45 | 46 | func From(s string) (p Path) { return strings.Split(s, " ") } 47 | 48 | func GetIndent(d int) string { return strings.Repeat("\t", d) } 49 | 50 | func (p Path) Parent() (p1 Path) { 51 | if len(p) > 0 { 52 | p1 = p[:len(p)-1] 53 | } 54 | return 55 | } 56 | 57 | func (p Path) String() string { return strings.Join(p, " ") } 58 | 59 | func (p Path) TrimPrefix() Path { 60 | if len(p) > 1 { 61 | return p[1:] 62 | } 63 | return p[:0] 64 | } 65 | -------------------------------------------------------------------------------- /pkg/util/slice/interfaces_test.go: -------------------------------------------------------------------------------- 1 | package slice 2 | 3 | import ( 4 | "bytes" 5 | "git.indra-labs.org/dev/ind/pkg/crypto/sha256" 6 | "testing" 7 | ) 8 | 9 | func TestMessage_ToU64Slice(t *testing.T) { 10 | var e error 11 | var msg1 Bytes 12 | if msg1, _, e = GenMessage(33, ""); check(e) { 13 | t.Error(e) 14 | t.FailNow() 15 | } 16 | uMsg1 := msg1.ToU64Slice() 17 | umsg1 := uMsg1.ToMessage() 18 | if bytes.Compare(msg1, umsg1) != 0 { 19 | t.Error("conversion to U64Slice and back to []byte failed") 20 | t.FailNow() 21 | } 22 | } 23 | 24 | func TestU64Slice_XOR(t *testing.T) { 25 | const ml = 1024 26 | var e error 27 | var msg1 Bytes 28 | if msg1, _, e = GenMessage(ml, ""); check(e) { 29 | t.Error(e) 30 | t.FailNow() 31 | } 32 | hash1 := sha256.Single(msg1) 33 | uMsg1 := msg1.ToU64Slice() 34 | var msg2 Bytes 35 | if msg2, _, e = GenMessage(ml, ""); check(e) { 36 | t.Error(e) 37 | t.FailNow() 38 | } 39 | // log.I.S(msg2) 40 | uMsg2 := msg2.ToU64Slice() 41 | var msg3 Bytes 42 | if msg3, _, e = GenMessage(ml, ""); check(e) { 43 | t.Error(e) 44 | t.FailNow() 45 | } 46 | // log.I.S(msg3) 47 | uMsg3 := msg3.ToU64Slice() 48 | uMsg1.XOR(uMsg2) 49 | uMsg1.XOR(uMsg3) 50 | uMsg1.XOR(uMsg2) 51 | uMsg1.XOR(uMsg3) 52 | hash2 := sha256.Single(uMsg1.ToMessage()) 53 | if hash1 != hash2 { 54 | t.Error("XOR failed") 55 | t.FailNow() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /pkg/util/slice/slice_test.go: -------------------------------------------------------------------------------- 1 | package slice 2 | 3 | import ( 4 | "crypto/rand" 5 | "errors" 6 | "git.indra-labs.org/dev/ind/pkg/crypto/sha256" 7 | "testing" 8 | ) 9 | 10 | func GenMessage(msgSize int, hrp string) (msg []byte, hash sha256.Hash, e error) { 11 | msg = make([]byte, msgSize) 12 | var n int 13 | if n, e = rand.Read(msg); check(e) && n != msgSize { 14 | return 15 | } 16 | if hrp == "" { 17 | hrp = "payload" 18 | } 19 | copy(msg, hrp) 20 | hash = sha256.Single(msg) 21 | return 22 | } 23 | 24 | func TestSegment(t *testing.T) { 25 | msgSize := 2 << 17 26 | segSize := 1382 27 | var msg []byte 28 | var hash sha256.Hash 29 | var e error 30 | if msg, hash, e = GenMessage(msgSize, ""); check(e) { 31 | t.Error(e) 32 | } 33 | segs := Segment(msg, segSize) 34 | pkt := Cat(segs...)[:len(msg)] 35 | hash2 := sha256.Single(pkt) 36 | if hash != hash2 { 37 | t.Error(errors.New("message did not decode" + 38 | " correctly")) 39 | } 40 | } 41 | 42 | func TestSize24(t *testing.T) { 43 | n := 1<<24 - 1 44 | u := NewUint24() 45 | EncodeUint24(u, n) 46 | u2 := DecodeUint24(u) 47 | if n != u2 { 48 | t.Error("failed to encode/decode") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pkg/util/tests/testutils.go: -------------------------------------------------------------------------------- 1 | // Package tests provides some helpers for tests. 2 | package tests 3 | 4 | import ( 5 | "crypto/rand" 6 | 7 | "git.indra-labs.org/dev/ind/pkg/crypto/sha256" 8 | log2 "git.indra-labs.org/dev/ind/pkg/proc/log" 9 | ) 10 | 11 | var ( 12 | log = log2.GetLogger() 13 | fails = log.E.Chk 14 | ) 15 | 16 | func GenMessage(msgSize int, hrp string) (msg []byte, hash sha256.Hash, e error) { 17 | msg = make([]byte, msgSize) 18 | var n int 19 | if n, e = rand.Read(msg); fails(e) && n != msgSize { 20 | return 21 | } 22 | if hrp == "" { 23 | hrp = "payload" 24 | } 25 | copy(msg, hrp) 26 | hash = sha256.Single(msg) 27 | return 28 | } 29 | -------------------------------------------------------------------------------- /pkg/util/windows/windows.go: -------------------------------------------------------------------------------- 1 | // Package windows provides some tools for handling launching subprocesses on windows using cmd.exe. 2 | package windows 3 | 4 | import ( 5 | "runtime" 6 | ) 7 | 8 | // PrependForWindows runs a command with a terminal 9 | func PrependForWindows(args []string) []string { 10 | if runtime.GOOS == "windows" { 11 | args = append( 12 | []string{ 13 | "cmd.exe", 14 | "/C", 15 | }, 16 | args..., 17 | ) 18 | } 19 | return args 20 | } 21 | 22 | // PrependForWindowsWithStart runs a process independently 23 | func PrependForWindowsWithStart(args []string) []string { 24 | if runtime.GOOS == "windows" { 25 | args = append( 26 | []string{ 27 | "cmd.exe", 28 | "/C", 29 | "start", 30 | }, 31 | args..., 32 | ) 33 | } 34 | return args 35 | } 36 | -------------------------------------------------------------------------------- /release/.gitignore: -------------------------------------------------------------------------------- 1 | scratch-* 2 | btcd-* 3 | lnd-* 4 | indra-* 5 | btcwallet-* 6 | -------------------------------------------------------------------------------- /scripts/cdwork.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | # populate ~/.workpath thus: `cd path/to/repo/root; pwd>~/.workpath 3 | export INDRAROOT=$(cat ~/.workpath) 4 | export PATH=$INDRAROOT/scripts:$PATH 5 | # put the path of the root of the repository in ./scripts/path 6 | cd $INDRAROOT 7 | -------------------------------------------------------------------------------- /scripts/readme.md: -------------------------------------------------------------------------------- 1 | # scripts 2 | 3 | populate ~/.workpath thus: `cd path/to/repo/root; pwd>~/.workpath` 4 | 5 | add this to your `~/.bashrc` or `~/.zshrc`: 6 | 7 | export PATH=$(cat ~/.workpath)/scripts:$HOME/sdk/go1.19.10/bin:$PATH 8 | export GOBIN=$HOME/.local/bin 9 | 10 | `source` the `rc` file or open a new terminal session and type `cdwork.sh` and you will have a number of useful commands 11 | that handle paths and special build parameters to make the code locations work without hard coding them anywhere. -------------------------------------------------------------------------------- /scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | reset 3 | go run -tags local -gcflags "all=-trimpath=$INDRAROOT" $1 $2 $3 $4 $5 -------------------------------------------------------------------------------- /scripts/runci.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | reset 3 | go run -gcflags "all=-trimpath=$INDRAROOT" $1 $2 $3 $4 $5 -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | reset 3 | go test -v -tags local -gcflags "all=-trimpath=$INDRAROOT" $1 $2 $3 $4 $5 -------------------------------------------------------------------------------- /scripts/testci.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | reset 3 | go test -v -gcflags "all=-trimpath=$INDRAROOT" $1 $2 $3 $4 $5 -------------------------------------------------------------------------------- /scripts/testpkg.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd pkg 3 | go test -v -tags local -gcflags "all=-trimpath=$INDRAROOT" ./... -------------------------------------------------------------------------------- /scripts/trace.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | reset 3 | go run -tags local -gcflags "all=-trimpath=$INDRAROOT" $1 --logs-level=trace $2 $3 $4 $5 -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | //go:build !local 2 | 3 | // Package indra is the root of the repository for the Indra distributed VPN, containing mainly the version information for included executables to use for information and identification on the network. 4 | // 5 | // See [pkg/git.indra-labs.org/dev/ind/cmd/indra] for the main server executable. 6 | // 7 | // Put invocations to run all the generators in here check [cmd/bumper] to add them, and they will automatically run with: 8 | // 9 | // $ go generate . 10 | // 11 | // which will run all these generators below and finish with a go install. 12 | // 13 | //go:generate go install ./... 14 | package indra 15 | 16 | import "fmt" 17 | 18 | const ( 19 | // URL is the git URL for the repository. 20 | URL = "git.indra-labs.org/dev/ind" 21 | // GitRef is the gitref, as in refs/heads/branchname. 22 | GitRef = "refs/heads/15" 23 | // ParentGitCommit is the commit hash of the parent HEAD. 24 | ParentGitCommit = "742a44445cc6862042a76fdb02e64236fd4340bb" 25 | // BuildTime stores the time when the current binary was built. 26 | BuildTime = "2023-08-09T21:04:58+01:00" 27 | // SemVer lists the (latest) git tag on the release. 28 | SemVer = "v0.1.20" 29 | // Major is the major number from the tag. 30 | Major = 0 31 | // Minor is the minor number from the tag. 32 | Minor = 1 33 | // Patch is the patch version number from the tag. 34 | Patch = 20 35 | ) 36 | 37 | var CI = "false" 38 | 39 | // Version returns a pretty printed version information string. 40 | func Version() string { 41 | return fmt.Sprint( 42 | "\nRepository Information\n", 43 | "\tGit repository: "+URL+"\n", 44 | "\tBranch: "+GitRef+"\n", 45 | "\tParentGitCommit: "+ParentGitCommit+"\n", 46 | "\tBuilt: "+BuildTime+"\n", 47 | "\tSemVer: "+SemVer+"\n", 48 | ) 49 | } 50 | --------------------------------------------------------------------------------