diff --git a/README.md b/README.md index a2059e1f..77d2ed22 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,12 @@ # Indranet -[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/indra-labs/indra) +[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/git-indra.lan/indra-labs/indra) [![License: Unlicense](https://img.shields.io/badge/license-Unlicense-blue.svg)](http://unlicense.org/) [![](https://img.shields.io/badge/chat-telegram-blue)](https://t.me/indranet) -Lightning powered distributed virtual private network for anonymising traffic on decentralised protocol networks. +Lightning powered distributed virtual private network for anonymising traffic on +decentralised protocol networks. [White Paper](docs/whitepaper.md) @@ -19,8 +20,10 @@ First Amendment, by literally printing the source code on paper and then posting it, it became recognised that code, and encryption, are protected speech. -With ubiquitous 128 and 256 bit AES encryption now in use by default, the content of -messages is secure. However, the volume of messages and endpoints of signals are still +With ubiquitous 128 and 256 bit AES encryption now in use by default, the +content of +messages is secure. However, the volume of messages and endpoints of signals are +still useful intelligence data, enabling state level actors to attack internet users and violate their privacy and threaten their safety. @@ -35,12 +38,27 @@ worse, the most common use case is tunneling back out of the network to anonymize location, is largely abused and led to a lot of automated block systems arising on many internet services to prevent this abuse. -Indranet does not set itself up to be a direct competitor for the Tor network. In its first few years of operation it will not have any mechanism for tunneling out of the network, and if it ever does, this will be user-contributed functionality, and not encouraged since any node providing exit becomes a target for retaliation when used to abuse such external systems. +Indranet does not set itself up to be a direct competitor for the Tor network. +In its first few years of operation it will not have any mechanism for +tunneling out of the network, and if it ever does, this will be user-contributed +functionality, and not encouraged since any node providing exit becomes a target +for retaliation when used to abuse such external systems. -Indranet's purpose is to form an interconnect layer for decentralised network protocols. It requires Lightning Network, as a primary requirement, to enable the payment for bandwidth, and thus it also requires connectivity to the Bitcoin network. Thus, all nodes, both relays and clients, will provide exit traffic for these two protocols, especially Bitcoin, which has a very low bandwidth requirement for simple transaction publishing. +Indranet's purpose is to form an interconnect layer for decentralised network +protocols. It requires Lightning Network, as a primary requirement, to enable +the payment for bandwidth, and thus it also requires connectivity to the +Bitcoin network. Thus, all nodes, both relays and clients, will provide exit +traffic for these two protocols, especially Bitcoin, which has a very low +bandwidth requirement for simple transaction publishing. -Users will potentially be able to set up arbitrary exit services, but the core project will only target connectivity with decentralised peer to peer services. Secondarily, it will be possible for users to set up private, non advertised exit services, protected via certificate authentication, such as SSH and other remote access systems. This will serve to displace the use cases for Tor with SSH and web services. +Users will potentially be able to set up arbitrary exit services, but the core +project will only target connectivity with decentralised peer to peer services. +Secondarily, it will be possible for users to set up private, non advertised +exit services, protected via certificate authentication, such as SSH and other +remote access systems. This will serve to displace the use cases for Tor with +SSH and web services. -Later, rendezvous access protocols will be added and enable the creation of arbitrary hidden service addresses such as web applications. +Later, rendezvous access protocols will be added and enable the creation of +arbitrary hidden service addresses such as web applications. # fin diff --git a/cmd/buidl/main.go b/cmd/buidl/main.go index da85fb66..2aa7d854 100644 --- a/cmd/buidl/main.go +++ b/cmd/buidl/main.go @@ -11,11 +11,12 @@ import ( "strings" "time" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" "gopkg.in/src-d/go-git.v4" "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/storer" + + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/cmd/bumper/main.go b/cmd/bumper/main.go index 929de427..3c445371 100644 --- a/cmd/bumper/main.go +++ b/cmd/bumper/main.go @@ -16,11 +16,12 @@ import ( "strings" "time" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" "gopkg.in/src-d/go-git.v4" "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/storer" + + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( @@ -32,6 +33,7 @@ var ( Major, Minor, Patch int PathBase string ) + var ( log = log2.GetLogger(indra.PathBase) check = log.E.Chk diff --git a/cmd/docker/release/main.go b/cmd/docker/release/main.go index 43177996..9d6959a5 100644 --- a/cmd/docker/release/main.go +++ b/cmd/docker/release/main.go @@ -4,14 +4,15 @@ import ( "context" "github.com/docker/docker/client" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/docker" - "github.com/indra-labs/indra/pkg/proc/app" - "github.com/indra-labs/indra/pkg/proc/cmds" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/proc/opts/config" - "github.com/indra-labs/indra/pkg/proc/opts/meta" - "github.com/indra-labs/indra/pkg/proc/opts/toggle" + + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/docker" + "git-indra.lan/indra-labs/indra/pkg/proc/app" + "git-indra.lan/indra-labs/indra/pkg/proc/cmds" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/config" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/meta" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/toggle" "os" "time" diff --git a/cmd/gimport/main.go b/cmd/gimport/main.go deleted file mode 100644 index 2f269677..00000000 --- a/cmd/gimport/main.go +++ /dev/null @@ -1,100 +0,0 @@ -package main - -import ( - "fmt" - "io/fs" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "gopkg.in/src-d/go-git.v4" -) - -var ( - log = log2.GetLogger(indra.PathBase) - check = log.E.Chk -) - -func main() { - remote := os.Args[1] - tag := os.Args[2] - dir := os.Args[3] - e := os.RemoveAll(dir) - if e != nil { - fmt.Println(e) - os.Exit(1) - } - repo, e := git.PlainClone(dir, false, &git.CloneOptions{ - URL: remote, - Progress: os.Stdout, - }) - if e != nil { - fmt.Println(e) - os.Exit(1) - } - branch, e := repo.Tag(tag) - if e != nil { - fmt.Println(e) - os.Exit(1) - } - w, e := repo.Worktree() - if e != nil { - fmt.Println(e) - os.Exit(1) - } - e = w.Checkout(&git.CheckoutOptions{ - Branch: branch.Name(), - }) - if e != nil { - fmt.Println(e) - os.Exit(1) - } - e = os.RemoveAll(filepath.Join(dir, ".git")) - check(e) - e = os.RemoveAll(filepath.Join(dir, ".github")) - check(e) - e = os.RemoveAll(filepath.Join(dir, ".vscode")) - check(e) - // e = os.RemoveAll(filepath.Join(dir, "cmd")) - // check(e) - e = os.Remove(filepath.Join(dir, "go.mod")) - check(e) - e = os.Remove(filepath.Join(dir, "go.sum")) - check(e) - e = filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error { - file, e := os.ReadFile(path) - if e != nil { - return nil - } - _, filename := filepath.Split(path) - if filename == "go.mod" || - filename == "go.sum" || - strings.HasSuffix(filename, "_test.go") || - strings.HasSuffix(filename, ".md") { - e = os.Remove(path) - check(e) - } else { - if filename == "Makefile" { - file = []byte(strings.ReplaceAll(string(file), - "github.com/lightningnetwork/lnd", - "github.com/indra-labs/lnd/lnd")) - } - e = os.WriteFile(path, - []byte(strings.ReplaceAll(string(file), - "\"github.com/lightningnetwork/lnd", - "\"github.com/indra-labs/lnd/lnd")), 0755) - check(e) - } - return nil - }) - check(e) - runCmdWithoutOutput("go", "mod", "tidy") -} - -func runCmdWithoutOutput(cmd ...string) { - c := exec.Command(cmd[0], cmd[1:]...) - check(c.Run()) -} diff --git a/cmd/indra/main.go b/cmd/indra/main.go index 6f7c6968..4cf00541 100644 --- a/cmd/indra/main.go +++ b/cmd/indra/main.go @@ -4,18 +4,19 @@ import ( "fmt" "os" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/cfg" - "github.com/indra-labs/indra/pkg/proc/app" - "github.com/indra-labs/indra/pkg/proc/cmds" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/proc/opts/config" - "github.com/indra-labs/indra/pkg/proc/opts/list" - "github.com/indra-labs/indra/pkg/proc/opts/meta" - "github.com/indra-labs/indra/pkg/proc/opts/text" - "github.com/indra-labs/indra/pkg/server" "github.com/libp2p/go-libp2p/core/crypto" "github.com/multiformats/go-multiaddr" + + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/cfg" + "git-indra.lan/indra-labs/indra/pkg/proc/app" + "git-indra.lan/indra-labs/indra/pkg/proc/cmds" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/config" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/list" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/meta" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/text" + "git-indra.lan/indra-labs/indra/pkg/server" ) var ( diff --git a/docker/sources/indra/official.Dockerfile b/docker/sources/indra/official.Dockerfile index 5d2baa2b..953bb92d 100644 --- a/docker/sources/indra/official.Dockerfile +++ b/docker/sources/indra/official.Dockerfile @@ -9,7 +9,7 @@ FROM indralabs/scratch:latest as scratch FROM ${sourcing_image} as source -ARG source_url="https://github.com/indra-labs/indra/releases/download" +ARG source_url="https://git-indra.lan/indra-labs/indra/releases/download" ARG source_version="v0.1.10" WORKDIR /tmp diff --git a/docs/indra.svg b/docs/indra.svg index d00fea8c..f01499b1 100644 --- a/docs/indra.svg +++ b/docs/indra.svg @@ -9,7 +9,7 @@ id="svg5" inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" sodipodi:docname="indra.svg" - inkscape:export-filename="/home/loki/src/github.com/indra-labs/indra/doc/indra.png" + inkscape:export-filename="/home/loki/src/git-indra.lan/indra-labs/indra/doc/indra.png" inkscape:export-xdpi="96" inkscape:export-ydpi="96" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" diff --git a/docs/logo-for-dark-ground.svg b/docs/logo-for-dark-ground.svg index bdf2cb3f..4efd7425 100644 --- a/docs/logo-for-dark-ground.svg +++ b/docs/logo-for-dark-ground.svg @@ -9,7 +9,7 @@ id="svg5" inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" sodipodi:docname="logo-for-dark-ground.svg" - inkscape:export-filename="/home/loki/src/github.com/indra-labs/indra/doc/logo-for-dark-ground.png" + inkscape:export-filename="/home/loki/src/git-indra.lan/indra-labs/indra/doc/logo-for-dark-ground.png" inkscape:export-xdpi="332.32001" inkscape:export-ydpi="332.32001" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" diff --git a/docs/logo-for-dark-tshirt.svg b/docs/logo-for-dark-tshirt.svg index be8c5d96..2e1cd397 100644 --- a/docs/logo-for-dark-tshirt.svg +++ b/docs/logo-for-dark-tshirt.svg @@ -9,7 +9,7 @@ id="svg5" inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" sodipodi:docname="logo-for-dark-tshirt.svg" - inkscape:export-filename="/home/loki/src/github.com/indra-labs/indra/doc/logo-for-dark-tshirt.png" + inkscape:export-filename="/home/loki/src/git-indra.lan/indra-labs/indra/doc/logo-for-dark-tshirt.png" inkscape:export-xdpi="528.73993" inkscape:export-ydpi="528.73993" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" diff --git a/docs/logo.svg b/docs/logo.svg index 2eba6fc1..8f57bdab 100644 --- a/docs/logo.svg +++ b/docs/logo.svg @@ -9,7 +9,7 @@ id="svg5" inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" sodipodi:docname="logo.svg" - inkscape:export-filename="/home/loki/src/github.com/indra-labs/indra/doc/logo.png" + inkscape:export-filename="/home/loki/src/git-indra.lan/indra-labs/indra/doc/logo.png" inkscape:export-xdpi="313.37" inkscape:export-ydpi="313.37" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" diff --git a/docs/whitepaper.md b/docs/whitepaper.md index a246188e..c07d48b2 100644 --- a/docs/whitepaper.md +++ b/docs/whitepaper.md @@ -3,8 +3,8 @@ ![logo](logo.png) #### Onion routed distributed virtual private network protocol with anonymised payments to create scaling incentives. -> херетик September 2022 - January 2023 +> херетик September 2022 - January 2023 # Abstract @@ -13,7 +13,8 @@ the 20 years since the inception of the [Tor network](https://torproject.org). The primary use case has always been obscuring the location information of users from clear net sites, and the more it has been used for this purpose, the more -hostile clear net sites have become towards this network, due to its frequent use +hostile clear net sites have become towards this network, due to its frequent +use to launch attacks on web services. With the increasing amounts of value being transported in data packets on the @@ -25,7 +26,8 @@ However, without any way for users to pay routers without creating an audit trail, the anonymising networks have not grown in nearly a decade, and thus well heeled attackers have largely been able to keep pace and pluck off high value targets, such as -the [Carnegie Mellon University](https://blog.torproject.org/did-fbi-pay-university-attack-tor-users/) - implicated in part of what led +the [Carnegie Mellon University](https://blog.torproject.org/did-fbi-pay-university-attack-tor-users/) - +implicated in part of what led to the arrest of the Silk Road founder, Ross Ulbricht. It is the central thesis of this paper to demonstrate how obfuscating @@ -38,28 +40,28 @@ improving latency and stability of routed connections. - [Tor Isn't Scaling, But Bitcoin Needs Onion Routing](#tor-isnt-scaling-but-bitcoin-needs-onion-routing) - [How Indranet Improves Upon Existing Mixnet Designs](#how-indranet-improves-upon-existing-mixnet-designs) - - [Active Attacks](#active-attacks) - - [Artificial Gap (packet dropping/delay) and Artificial Bursts](#artificial-gap-packet-droppingdelay-and-artificial-bursts) - - [Timing Analysis](#timing-analysis) + - [Active Attacks](#active-attacks) + - [Artificial Gap (packet dropping/delay) and Artificial Bursts](#artificial-gap-packet-droppingdelay-and-artificial-bursts) + - [Timing Analysis](#timing-analysis) - [Why We Need Indranet](#why-we-need-indranet) - [General Principles of Indranet Protocol](#general-principles-of-indranet-protocol) - [Protocol Concepts](#protocol-concepts) - - [Packet and Message Encryption](#packet-and-message-encryption) - - [Signing/Encryption Key Generation and Message Segmentation](#signingencryption-key-generation-and-message-segmentation) - - [Onion Path Topology](#onion-path-topology) - - [Return Path Routing](#return-path-routing) - - [Ping Messages and Path Failure Diagnosis](#ping-messages-and-path-failure-diagnosis) - - [Client](#client) + - [Packet and Message Encryption](#packet-and-message-encryption) + - [Signing/Encryption Key Generation and Message Segmentation](#signingencryption-key-generation-and-message-segmentation) + - [Onion Path Topology](#onion-path-topology) + - [Return Path Routing](#return-path-routing) + - [Ping Messages and Path Failure Diagnosis](#ping-messages-and-path-failure-diagnosis) + - [Client](#client) - [Payment for Traffic](#payment-for-traffic) - [Proof of HODL Consensus](#proof-of-hodl-consensus) - - [Anonymous Probabilistic Feedback Propagation](#anonymous-probabilistic-feedback-propagation) - - [Rate Limiting](#rate-limiting) + - [Anonymous Probabilistic Feedback Propagation](#anonymous-probabilistic-feedback-propagation) + - [Rate Limiting](#rate-limiting) - [Relay to Relay Traffic](#relay-to-relay-traffic) - - [Relay to Relay Encryption](#relay-to-relay-encryption) - - [Dynamic error correction adjustment for Re-transmit Avoidance](#dynamic-error-correction-adjustment-for-re-transmit-avoidance) + - [Relay to Relay Encryption](#relay-to-relay-encryption) + - [Dynamic error correction adjustment for Re-transmit Avoidance](#dynamic-error-correction-adjustment-for-re-transmit-avoidance) - [Client Path Generation Configuration](#client-path-generation-configuration) - [Hidden Services](#hidden-services) - - [Fully Anonymous VPS Hosting](#fully-anonymous-vps-hosting) + - [Fully Anonymous VPS Hosting](#fully-anonymous-vps-hosting) - [Proxy Service](#proxy-service) - [Private Relay Services](#private-relay-services) - [The Indra Tax](#the-indra-tax) @@ -85,274 +87,719 @@ large component of Bitcoin connectivity. ## How Indranet Improves Upon Existing Mixnet Designs -Indranet, in contrast to other anonymising network designs, is pure [source routed](https://en.wikipedia.org/wiki/Source_routing). This means that only the clients know the full path along which their traffic moves, and relays have no influence on the path of traffic aside from failing to deliver it. It is similar in many ways to the [HORNET](./1507.05724v1.pdf) mixnet protocol. +Indranet, in contrast to other anonymising network designs, is +pure [source routed](https://en.wikipedia.org/wiki/Source_routing). This means +that only the clients know the full path along which their traffic moves, and +relays have no influence on the path of traffic aside from failing to deliver +it. It is similar in many ways to the [HORNET](./1507.05724v1.pdf) mixnet +protocol. -Many of the [vulnerabilities](https://en.wikipedia.org/wiki/Mix_network#Vulnerabilities) of mixnets relate to the relays having the ability to change the path of traffic. In Indranet, relays either forward traffic as instructed, or not. To some degree, source routing creates protection against byzantine type faults, because failure to deliver and most malicious attack methods result in clients distrusting the malicious nodes, the same way as they distrust unreliable nodes. Because unreliable nodes cost clients money, effectively, malicious nodes will be quickly forgotten and end up being used by nobody. +Many of +the [vulnerabilities](https://en.wikipedia.org/wiki/Mix_network#Vulnerabilities) +of mixnets relate to the relays having the ability to change the path of +traffic. In Indranet, relays either forward traffic as instructed, or not. To +some degree, source routing creates protection against byzantine type faults, +because failure to deliver and most malicious attack methods result in clients +distrusting the malicious nodes, the same way as they distrust unreliable nodes. +Because unreliable nodes cost clients money, effectively, malicious nodes will +be quickly forgotten and end up being used by nobody. -### Active Attacks +### Active Attacks -It is not possible for adversaries to modify packets without effectively breaking the path, as messages are integrity protected by cryptography that is opaque to other than the client and the at each layer, the relay that will forward or process the message. This can adversely effect the randomly selected next path because it may be inferred that possibly the receiver is faulty rather than the sender. The client will use probes to check the liveness and latency of the path, and by a process of elimination it will determine the sender is faulty and deprecate it from further use, potentially eventually fully blacklisting it and not sharing the node's existence with peers. +It is not possible for adversaries to modify packets without effectively +breaking the path, as messages are integrity protected by cryptography that is +opaque to other than the client and the at each layer, the relay that will +forward or process the message. This can adversely effect the randomly selected +next path because it may be inferred that possibly the receiver is faulty rather +than the sender. The client will use probes to check the liveness and latency of +the path, and by a process of elimination it will determine the sender is faulty +and deprecate it from further use, potentially eventually fully blacklisting it +and not sharing the node's existence with peers. ### Artificial Gap (packet dropping/delay) and Artificial Bursts -These attacks fail because the client will determine via ping probes that nodes are failing to execute the forwarding/returning of packets and this will gradually result in the malicious nodes not being shared by clients altogether, not just way down the scale of reliability. It is part of the contract of operating a relay on Indranet that your node follows the instructions given to it. +These attacks fail because the client will determine via ping probes that nodes +are failing to execute the forwarding/returning of packets and this will +gradually result in the malicious nodes not being shared by clients altogether, +not just way down the scale of reliability. It is part of the contract of +operating a relay on Indranet that your node follows the instructions given to +it. ### Timing Analysis -Discovering the relationships between clients and the services they are connecting to is the essence of all of the attacks on mixnets. Indra takes several approaches to resolving this issue: +Discovering the relationships between clients and the services they are +connecting to is the essence of all of the attacks on mixnets. Indra takes +several approaches to resolving this issue: -- **Packet scheduling is shuffled constantly** - Relays do not simply behave as First In First Out buffers, but rather, mix together the messages they receive. All messages have to be segmented for network transport, and as these segments are fed into the outbound queue, they are shuffled so that the ordering is broken up. This slightly increases latency but it decreases associativity between streams of packets constituting messages. -- **Deliberate delays** - This is a tactic that is more often seen in email mixnets but can be used to a degree in lower latency mixnets like Indranet because not all traffic is time critical. Bitcoin transactions and blocks are relatively time critical, but DNS requests can comfortably take up to 100 ms without disrupting the functioning of ancillary centralised type services using them (IE, DNS for web traffic). Email is also a potential service type on Indranet, and because it is inherently slow and non-interactive, the delays can be even longer, potentially several seconds to deliver, and so the segments of the packets as they pass through will not have strong timing correlation. The delays are defined by the client, and raise the cost of the data from a regular non-delayed message by a percentage in proportion with the length of the delay. +- **Packet scheduling is shuffled constantly** - Relays do not simply behave as + First In First Out buffers, but rather, mix together the messages they + receive. All messages have to be segmented for network transport, and as these + segments are fed into the outbound queue, they are shuffled so that the + ordering is broken up. This slightly increases latency but it decreases + associativity between streams of packets constituting messages. +- **Deliberate delays** - This is a tactic that is more often seen in email + mixnets but can be used to a degree in lower latency mixnets like Indranet + because not all traffic is time critical. Bitcoin transactions and blocks are + relatively time critical, but DNS requests can comfortably take up to 100 ms + without disrupting the functioning of ancillary centralised type services + using them (IE, DNS for web traffic). Email is also a potential service type + on Indranet, and because it is inherently slow and non-interactive, the delays + can be even longer, potentially several seconds to deliver, and so the + segments of the packets as they pass through will not have strong timing + correlation. The delays are defined by the client, and raise the cost of the + data from a regular non-delayed message by a percentage in proportion with the + length of the delay. ## Why We Need Indranet Three key elements of the Tor protocol make it less than desirable in general. -1. **High Latency** - Establishment of circuits is quite slow, taking a large number of steps +1. **High Latency** - Establishment of circuits is quite slow, taking a large + number of steps to "telescope" into a circuit. -2. **Low Reliability** - Once a circuit is running, when it fails, the failure is opaque to the client +2. **Low Reliability** - Once a circuit is running, when it fails, the failure + is opaque to the client side, and there is no way to provide a latency guarantee or connection stability. It is unsuitable for interactive *and* long living connections. -3. **Poor Scaling Incentives** - There is no profit motive to drive expansion of relaying - capacity, weakening anonymity by not expanding the anonymity set to cope with a rise in the number of users. As the charts showed in the foregoing, there is around 8,000 nodes, of which 6,000 are relays and the remainder private bridges, but the average daily user count of Tor is around 100,000 users. Both numbers could be a lot higher if running a relay wasn't a money losing exercise, and if the system could handle interactive grade latency and very long living connections. +3. **Poor Scaling Incentives** - There is no profit motive to drive expansion of + relaying + capacity, weakening anonymity by not expanding the anonymity set to cope with + a rise in the number of users. As the charts showed in the foregoing, there + is around 8,000 nodes, of which 6,000 are relays and the remainder private + bridges, but the average daily user count of Tor is around 100,000 users. + Both numbers could be a lot higher if running a relay wasn't a money losing + exercise, and if the system could handle interactive grade latency and very + long living connections. Tor is a poor solution for a very limited subset of the use cases that benefit from the security of route obfuscation. Indra aims to provide what Tor -has definitely now failed to achieve for a large majority of internet users for all purposes: +has definitely now failed to achieve for a large majority of internet users for +all purposes: location privacy. -Indranet does not aim to compete with Tor for the use case of tunneling out to clear-net websites and services: the focus is on obscuring the source of traffic within decentralised, peer to peer protocols like Bitcoin, Lightning Network, Bittorrent, IPFS, and other similar, decentralised protocols. Enabling such services are possible for relay operators to do, since they can offer a Socks5 tunnel exit service on well known web service ports, though this feature may be integrated later, but not enabled by default. +Indranet does not aim to compete with Tor for the use case of tunneling out to +clear-net websites and services: the focus is on obscuring the source of traffic +within decentralised, peer to peer protocols like Bitcoin, Lightning Network, +Bittorrent, IPFS, and other similar, decentralised protocols. Enabling such +services are possible for relay operators to do, since they can offer a Socks5 +tunnel exit service on well known web service ports, though this feature may be +integrated later, but not enabled by default. ## General Principles of Indranet Protocol There is four main types of traffic in Indranet: -1. **Peer to peer protocol chatter** - sharing lists of known network nodes, their advertised exit services, and collaboratively generated statistics on bandwidth and uptime, their long lived public keys for session initiation, and hidden service introducers. +1. **Peer to peer protocol chatter** - sharing lists of known network nodes, + their advertised exit services, and collaboratively generated statistics on + bandwidth and uptime, their long lived public keys for session initiation, + and hidden service introducers. -2. **Purchase and topping up of bandwidth sessions** - Combining with the use of Lightning Network to perform payments to proxy nodes, and specially formed layered encryption of messages, enabling clients to acquire sessions that grant users the ability to relay arbitrary traffic through relays. +2. **Purchase and topping up of bandwidth sessions** - Combining with the use of + Lightning Network to perform payments to proxy nodes, and specially formed + layered encryption of messages, enabling clients to acquire sessions that + grant users the ability to relay arbitrary traffic through relays. -3. **Liveness diagnostics** - When messages fail to circle back to the client that are expected to, an Indranet client can perform a diagnostic message protocol to discover which nodes are failing automatically to avoid using them and causing failed transmissions. +3. **Liveness diagnostics** - When messages fail to circle back to the client + that are expected to, an Indranet client can perform a diagnostic message + protocol to discover which nodes are failing automatically to avoid using + them and causing failed transmissions. -4. **Relaying messages to network services** - This is the bulk of traffic, relaying messages from clients to their intended destinations inside the network. +4. **Relaying messages to network services** - This is the bulk of traffic, + relaying messages from clients to their intended destinations inside the + network. ## Protocol Concepts ### Packet and Message Encryption -Indranet uses a message encryption scheme based on [Elliptic Curve Diffie Hellman](https://en.wikipedia.org/wiki/Elliptic-curve_Diffie%E2%80%93Hellman) (ECDH) key exchange. +Indranet uses a message encryption scheme based +on [Elliptic Curve Diffie Hellman](https://en.wikipedia.org/wiki/Elliptic-curve_Diffie%E2%80%93Hellman) ( +ECDH) key exchange. The message and packet headers contain the following elements: -- **Message checksum** - 4 bytes of the truncated hash of the remainder of the message or packet, for preventing tampering and ensuring integrity of the message. -- **Initialisation Vector** - cryptographically secure random value used for the payload encryption. -- **Cloaked public key** - generated via the use of a strongly random 3 byte value that is concatenated with the receiver's public key, and the first 5 bytes of the combined hash is concatenated to the 3 byte nonce value to prevent inferring association of a stream of message packets with each other. This key also acts as a session identifier, and must be cloaked in order to not provide information to malicious nodes who would then be able to correlate messages. -- **Public key** - In order to enable the receiver, who knows the cloaked public key's private key, to be able to generate the message encryption cipher, the public key is included in the header of each message and packet. +- **Message checksum** - 4 bytes of the truncated hash of the remainder of the + message or packet, for preventing tampering and ensuring integrity of the + message. +- **Initialisation Vector** - cryptographically secure random value used for the + payload encryption. +- **Cloaked public key** - generated via the use of a strongly random 3 byte + value that is concatenated with the receiver's public key, and the first 5 + bytes of the combined hash is concatenated to the 3 byte nonce value to + prevent inferring association of a stream of message packets with each other. + This key also acts as a session identifier, and must be cloaked in order to + not provide information to malicious nodes who would then be able to correlate + messages. +- **Public key** - In order to enable the receiver, who knows the cloaked public + key's private key, to be able to generate the message encryption cipher, the + public key is included in the header of each message and packet. ### Signing/Encryption Key Generation and Message Segmentation -The signatures on messages must be different for each subsequent message, and when a message exceeds 1382 bytes (based on a 1410 byte MTU, typical for mobile networks) the message will be segmented into pieces of this size, the last packet padded out with random based hash chain generated noise. +The signatures on messages must be different for each subsequent message, and +when a message exceeds 1382 bytes (based on a 1410 byte MTU, typical for mobile +networks) the message will be segmented into pieces of this size, the last +packet padded out with random based hash chain generated noise. -These keys are generated by creating two secure, secp256k1 private keys, the base and the secondary, and the base is scalar summed with the secondary to produce a new key, and this new key is then used again the same way for subsequent keys. +These keys are generated by creating two secure, secp256k1 private keys, the +base and the secondary, and the base is scalar summed with the secondary to +produce a new key, and this new key is then used again the same way for +subsequent keys. -This scalar sum operation guarantees that the new private key is also a valid secp256k1 curve point (values not in the curve weaken the encryption), and can be performed very quickly without the resultant key being outside of the curve. +This scalar sum operation guarantees that the new private key is also a valid +secp256k1 curve point (values not in the curve weaken the encryption), and can +be performed very quickly without the resultant key being outside of the curve. -This scheme is more aggressive than the Signal Protocol's Double Ratchet algorithm, which was designed for low bandwidth short message systems, and it consumes a fair bit of processing power. Benchmarks of initial implementations show that a single thread of a Ryzen 7 2021 mobile processor can process more than 100 mbit per CPU thread, which is more than fast enough for a gigabit dedicated relay with at least 10 CPU threads (and usually, 6 to spare for the rest of the work). +This scheme is more aggressive than the Signal Protocol's Double Ratchet +algorithm, which was designed for low bandwidth short message systems, and it +consumes a fair bit of processing power. Benchmarks of initial implementations +show that a single thread of a Ryzen 7 2021 mobile processor can process more +than 100 mbit per CPU thread, which is more than fast enough for a gigabit +dedicated relay with at least 10 CPU threads (and usually, 6 to spare for the +rest of the work). ### Onion Path Topology ![](onions.svg) -Indra uses a single topology that provides two hops between the client and the exit/endpoint being connected to. Only two are required to provide optimal anonymity - the first hop can infer it received a message from a client, but the second hop cannot, as it did not, and while most clients are not also providing relay service, a lot will also run one it, especially such as hidden p2p services or multi-server setups that forward inbound requests one or more separate servers providing the services offered at the exit. +Indra uses a single topology that provides two hops between the client and the +exit/endpoint being connected to. Only two are required to provide optimal +anonymity - the first hop can infer it received a message from a client, but the +second hop cannot, as it did not, and while most clients are not also providing +relay service, a lot will also run one it, especially such as hidden p2p +services or multi-server setups that forward inbound requests one or more +separate servers providing the services offered at the exit. -Because it is mostly not possible to fully hide the fact that a node is a client, there is separate sessions for each of the 5 hops in the circuit. First and last hop sessions can make session balance queries directly, whereas for the other 3 hops it uses the standard path, except in reverse for the second last, and randomly, exit point to perform these queries, and the last two hops and the return hop carry back the response. +Because it is mostly not possible to fully hide the fact that a node is a +client, there is separate sessions for each of the 5 hops in the circuit. First +and last hop sessions can make session balance queries directly, whereas for the +other 3 hops it uses the standard path, except in reverse for the second last, +and randomly, exit point to perform these queries, and the last two hops and the +return hop carry back the response. -Because Indra is source routed, every single request can pass through different paths, eliminating observable correlations between clients and relays for attempts to unmask users, and eliminating any discretion a relay can have about where traffic is forwarded - it either goes, or it does not. Attempts to attack users anonymity by delaying or dropping messages by evil relays will violate expected relaying performance parameters and the offending nodes will be downgraded in their selection frequency as punishment. Tor and I2P have limitations on how many paths they can open at any given time, whereas Indra can, and by default, does choose different paths for every single message cycle. +Because Indra is source routed, every single request can pass through different +paths, eliminating observable correlations between clients and relays for +attempts to unmask users, and eliminating any discretion a relay can have about +where traffic is forwarded - it either goes, or it does not. Attempts to attack +users anonymity by delaying or dropping messages by evil relays will violate +expected relaying performance parameters and the offending nodes will be +downgraded in their selection frequency as punishment. Tor and I2P have +limitations on how many paths they can open at any given time, whereas Indra +can, and by default, does choose different paths for every single message cycle. ### Return Path Routing -As distinct from most mixnet implementations, interactive connections are not built out of chains of bidirectional connections between the hops in the circuit. The outbound message path is entirely unrelated to the inbound message path, except that the client initiates the messages outbound first, and must always. +As distinct from most mixnet implementations, interactive connections are not +built out of chains of bidirectional connections between the hops in the +circuit. The outbound message path is entirely unrelated to the inbound message +path, except that the client initiates the messages outbound first, and must +always. -This means that for "push" type interactive services, the client must send out a message containing the reverse path to deliver "pushed" messages. But fundamentally this is how websockets work anyway, using a subscribe message to start listening, the difference being that the client must send new return message headers in order to get responses. +This means that for "push" type interactive services, the client must send out a +message containing the reverse path to deliver "pushed" messages. But +fundamentally this is how websockets work anyway, using a subscribe message to +start listening, the difference being that the client must send new return +message headers in order to get responses. -To support the connection type model of HTTP, the protocol always pushes a few extra reply message packets in addition to the ones associated with a given outbound message, so that if more replies come back it can keep prompting for more to come. This is a limitation of source routing with anonymity, as the reply path is entirely under the control of the client. +To support the connection type model of HTTP, the protocol always pushes a few +extra reply message packets in addition to the ones associated with a given +outbound message, so that if more replies come back it can keep prompting for +more to come. This is a limitation of source routing with anonymity, as the +reply path is entirely under the control of the client. ### Ping Messages and Path Failure Diagnosis -Because there is several intermediaries in paths in Indranet, and a failure for the response or confirmation to arrive in a timely fashion can mean a failure in any of the nodes in a circuit. The first and last hops can be openly probed for operation using a Get Balance query (which is a single hop out and back), but the others must be diagnosed differently. The reason being that using relays for the outer 3 hops at the same time as using them as first hops would potentially unmask the location of the client associated with the session. +Because there is several intermediaries in paths in Indranet, and a failure for +the response or confirmation to arrive in a timely fashion can mean a failure in +any of the nodes in a circuit. The first and last hops can be openly probed for +operation using a Get Balance query (which is a single hop out and back), but +the others must be diagnosed differently. The reason being that using relays for +the outer 3 hops at the same time as using them as first hops would potentially +unmask the location of the client associated with the session. -Thus, the ping message, which consists of 5 hops using the forward-only relaying instruction message, can be sent out several times to pass through these inner hops of the failed path. The failing node will not forward these messages and thus the confirmation of the ping will not arrive back at the client. Of course it can be that the randomly selected other relays in the ping path also are failing, which can then require further probing, using sessions that already proved to work until the one or more hops in the failed path are identified. While the process of diagnosis is occurring, Indra will not choose the relays in the failed path until the diagnostic is completed. +Thus, the ping message, which consists of 5 hops using the forward-only relaying +instruction message, can be sent out several times to pass through these inner +hops of the failed path. The failing node will not forward these messages and +thus the confirmation of the ping will not arrive back at the client. Of course +it can be that the randomly selected other relays in the ping path also are +failing, which can then require further probing, using sessions that already +proved to work until the one or more hops in the failed path are identified. +While the process of diagnosis is occurring, Indra will not choose the relays in +the failed path until the diagnostic is completed. -When a delivery failure occurs, Indra will inform the client application by returning a connection reset by peer message so the client retries, and Indra then uses a different path that does not include any of the relays from the failed path until they are diagnosed. +When a delivery failure occurs, Indra will inform the client application by +returning a connection reset by peer message so the client retries, and Indra +then uses a different path that does not include any of the relays from the +failed path until they are diagnosed. ### Client -Unlike Tor and other anonymising protocols, every client has the capacity to act as an **exit** for traffic while it is online, for at minimum, Bitcoin and Lightning Network messages. They advertise themselves as "unreliable" exit nodes, this descriptor indicating that they are intermittently offline, and do not attempt to stay online. This will also mean they don't get a lot of traffic but users on the network will be able to use them when they see a status update on the peer to peer network. +Unlike Tor and other anonymising protocols, every client has the capacity to act +as an **exit** for traffic while it is online, for at minimum, Bitcoin and +Lightning Network messages. They advertise themselves as "unreliable" exit +nodes, this descriptor indicating that they are intermittently offline, and do +not attempt to stay online. This will also mean they don't get a lot of traffic +but users on the network will be able to use them when they see a status update +on the peer to peer network. -Users primarily using Indranet as clients can also gain an increase in their anonymity by also running a relay. This is generally not advisable on mobile devices since their intermittency and the latency of the p2p network DHT updates make them impractical for frequent use, but when they are online, they can be used, and the payments on their sessions can be used later to balance their channels, and amortise some of their relay session costs. +Users primarily using Indranet as clients can also gain an increase in their +anonymity by also running a relay. This is generally not advisable on mobile +devices since their intermittency and the latency of the p2p network DHT updates +make them impractical for frequent use, but when they are online, they can be +used, and the payments on their sessions can be used later to balance their +channels, and amortise some of their relay session costs. -Because of the unreliability of especially mobile clients providing relay service, they are not subject to the same consensus about reliability, but will mainly be selected as exit points when they are online as a way to further boost the anonymity of the services being provided. And of course this will include hidden services, which can be leveraged for peer to peer protocols on the mobile devices. +Because of the unreliability of especially mobile clients providing relay +service, they are not subject to the same consensus about reliability, but will +mainly be selected as exit points when they are online as a way to further boost +the anonymity of the services being provided. And of course this will include +hidden services, which can be leveraged for peer to peer protocols on the mobile +devices. ## Payment for Traffic -Using [Atomic Multi-path Payment](https://docs.lightning.engineering/lightning-network-tools/lnd/amp) (AMP), relays advertise their LN node public keys alongside their Indra identity keys, they send a message constructed as follows: +Using [Atomic Multi-path Payment](https://docs.lightning.engineering/lightning-network-tools/lnd/amp) ( +AMP), relays advertise their LN node public keys alongside their Indra identity +keys, they send a message constructed as follows: -- Preimage hash of the forward and return private keys that will be delivered after payment to prove payment and provide the keys for handling return messages. +- Preimage hash of the forward and return private keys that will be delivered + after payment to prove payment and provide the keys for handling return + messages. - Amount of bytes the payment is for. -The payment will be for an amount of satoshis in accordance with the rate advertised by the seller, and the amount of advance payment the client is willing to put forward. There is no direct return confirmation in this process. As with the rest of Indranet's design, the client is in control of everything, tightening the security. +The payment will be for an amount of satoshis in accordance with the rate +advertised by the seller, and the amount of advance payment the client is +willing to put forward. There is no direct return confirmation in this process. +As with the rest of Indranet's design, the client is in control of everything, +tightening the security. -In the initial bootstrap, the client will send out 5 such payments to establish enough hops to form a secure path. With 5 payments made, relating to 5 sets of header/payload keys by the hash of these keys being the preimage used. +In the initial bootstrap, the client will send out 5 such payments to establish +enough hops to form a secure path. With 5 payments made, relating to 5 sets of +header/payload keys by the hash of these keys being the preimage used. -Then after sending out the payments with the preimages to the relevant relays, the client then sends out a "SendKeys" message which provides the relay with the two private keys that both identify the session being used in a hop in an onion message, as well as provide the relay with the necessary keys to unwrap their layer to be processed. +Then after sending out the payments with the preimages to the relevant relays, +the client then sends out a "SendKeys" message which provides the relay with the +two private keys that both identify the session being used in a hop in an onion +message, as well as provide the relay with the necessary keys to unwrap their +layer to be processed. ## Proof of HODL Consensus -Following from the model of loyalty building between customers and businesses in the open market, a number of consensus rules and protocols help reward honest nodes and reduce the impact of bad behaviour in both clients and relays that harm the security and value of the network. +Following from the model of loyalty building between customers and businesses in +the open market, a number of consensus rules and protocols help reward honest +nodes and reduce the impact of bad behaviour in both clients and relays that +harm the security and value of the network. -Decentralised, and especially anonymous networks have a primary common vulnerability to Sybil attacks, since identities can be created in large numbers and used to overwhelm a network with false information. +Decentralised, and especially anonymous networks have a primary common +vulnerability to Sybil attacks, since identities can be created in large numbers +and used to overwhelm a network with false information. -In order to prevent this attack there is a number of rules that honest peers follow, and mechanisms by which peers on the network evaluate each other on the relay and client side both. +In order to prevent this attack there is a number of rules that honest peers +follow, and mechanisms by which peers on the network evaluate each other on the +relay and client side both. -1. To rate limit the creation of new nodes on the network, nodes must make an on chain Bitcoin payment with a time delay back to themselves, and after a period of time they can then repeat the transaction, the latest head of the chain of transactions tied to the original TXID must be active in order for nodes to use the relay. +1. To rate limit the creation of new nodes on the network, nodes must make an on + chain Bitcoin payment with a time delay back to themselves, and after a + period of time they can then repeat the transaction, the latest head of the + chain of transactions tied to the original TXID must be active in order for + nodes to use the relay. - The size of the value of the time locked UTXO is a factor in node selection to weight probability, lower than the factor of age in first time usage by clients. + The size of the value of the time locked UTXO is a factor in node selection + to weight probability, lower than the factor of age in first time usage by + clients. - The UTXO hash is signed by the UTXO's private key in the node identity, proving ownership. It leverages the strong security of Bitcoin to anchor a relay identity to a definite time as well as raising the new relay identity creation cost in the short term. + The UTXO hash is signed by the UTXO's private key in the node identity, + proving ownership. It leverages the strong security of Bitcoin to anchor a + relay identity to a definite time as well as raising the new relay identity + creation cost in the short term. - The fees a relay pays to renew can be zero, but this will reduce their ranking during the interim periods after one expiry and the next activation. + The fees a relay pays to renew can be zero, but this will reduce their + ranking during the interim periods after one expiry and the next activation. - Bigger UTXOs expire later than smaller, so the more a relay defers spending the more reliable they intend to be, and cutting down their transaction fee overheads. Sybil attacks rely on cheap identities, this creates a dilemma for attackers, who must lock up more for longer if they want to be more likely to win new loyal client customers, and putting a cap on how many Sybil clones they can create for any hypothetical get paid but not deliver attacks. + Bigger UTXOs expire later than smaller, so the more a relay defers spending + the more reliable they intend to be, and cutting down their transaction fee + overheads. Sybil attacks rely on cheap identities, this creates a dilemma for + attackers, who must lock up more for longer if they want to be more likely to + win new loyal client customers, and putting a cap on how many Sybil clones + they can create for any hypothetical get paid but not deliver attacks. - The expiry times must conform to the rules of the network, as a ratio of satoshis and blocks, some deviation is allowed, but the point is that smaller time locked spends must expire sooner and thus cost more in transaction fee overhead, it is just better to sink more than less, as well as it raising the probability of first time selection by clients. + The expiry times must conform to the rules of the network, as a ratio of + satoshis and blocks, some deviation is allowed, but the point is that smaller + time locked spends must expire sooner and thus cost more in transaction fee + overhead, it is just better to sink more than less, as well as it raising the + probability of first time selection by clients. -2. The age and cumulative time active for a TL UTXO is also used as a factor in the evaluation of the reliability of a peer. No matter how big the UTXO the clients will not pick it with higher probability until the relay has remained operational in a time/value weighting formula. +2. The age and cumulative time active for a TL UTXO is also used as a factor in + the evaluation of the reliability of a peer. No matter how big the UTXO the + clients will not pick it with higher probability until the relay has remained + operational in a time/value weighting formula. -3. For relays a client has used, the ones with the highest rates of fulfillment are weighted above all else. The previous two criteria are more used for distributing risk of first time use of a relay, so as to minimise the unfulfilled sessions versus delivered. At the same time, a client also needs to slowly shift its session usage around, and thus also try new relays out, and intermittently cease using some of its known and trusted relays for traffic for some period, which is necessary also for the feedback system described in the next section. +3. For relays a client has used, the ones with the highest rates of fulfillment + are weighted above all else. The previous two criteria are more used for + distributing risk of first time use of a relay, so as to minimise the + unfulfilled sessions versus delivered. At the same time, a client also needs + to slowly shift its session usage around, and thus also try new relays out, + and intermittently cease using some of its known and trusted relays for + traffic for some period, which is necessary also for the feedback system + described in the next section. ### Anonymous Probabilistic Feedback Propagation -In order to create a feedback loop between relays and clients, both relays and clients share random selections of known peers with each other that have been given a weighted probability of selection in the message of this small subset. +In order to create a feedback loop between relays and clients, both relays and +clients share random selections of known peers with each other that have been +given a weighted probability of selection in the message of this small subset. -The p2p layer ensures that any node can discover with fairly high convergence of the current full list of peers on the network if desired. This is a separate process that works a little bit like "shout-outs" in social media. +The p2p layer ensures that any node can discover with fairly high convergence of +the current full list of peers on the network if desired. This is a separate +process that works a little bit like "shout-outs" in social media. -In order to prevent the correlation of esteemed peers to a client's currently active traffic, the most recent activity and volumes of traffic over their circuits is weighed to not leak current connection data in close time proximity to this chatter. +In order to prevent the correlation of esteemed peers to a client's currently +active traffic, the most recent activity and volumes of traffic over their +circuits is weighed to not leak current connection data in close time proximity +to this chatter. -The messages are only sent out once a day, and in proportion with the time known and the amount of first/last hop connection traffic sending these "shout-outs" are then combined with the frequencies of clients' recommendations lists allows long serving customers running honest clients to evaluate their peers in a way that doesn't either unmask them or enable spammy advertisement from constantly new clients attempting to poison these scores. +The messages are only sent out once a day, and in proportion with the time known +and the amount of first/last hop connection traffic sending these "shout-outs" +are then combined with the frequencies of clients' recommendations lists allows +long serving customers running honest clients to evaluate their peers in a way +that doesn't either unmask them or enable spammy advertisement from constantly +new clients attempting to poison these scores. -> With the combination of a form of lightweight bonding, timestamp anchoring to Bitcoin with proof of ownership, subjective histories of fidelity and a probabilistic feedback mechanism, it will be difficult to find a way to make income from Indranet without actually delivering service. +> With the combination of a form of lightweight bonding, timestamp anchoring to +> Bitcoin with proof of ownership, subjective histories of fidelity and a +> probabilistic feedback mechanism, it will be difficult to find a way to make +> income from Indranet without actually delivering service. ### Rate Limiting -Because inherently anonymous, and especially source routed traffic volumes cannot be controlled by the use of client identifiers, Indranet needs a mechanism to enforce bandwidth limits and prevent congestion caused by the coincidence of many clients selecting a relay at the same time randomly. +Because inherently anonymous, and especially source routed traffic volumes +cannot be controlled by the use of client identifiers, Indranet needs a +mechanism to enforce bandwidth limits and prevent congestion caused by the +coincidence of many clients selecting a relay at the same time randomly. -When a relay is exceeding its momentary traffic volume limit, as set by the relay operator, it will delay processing of message, and if the volume continues to flood, it will start to drop packets. This is obviously a potential vector for attacking a specific relay, but because although clients are not identifiable, sessions are, the sessions with the highest volumes will be dropped before lesser volume messages. +When a relay is exceeding its momentary traffic volume limit, as set by the +relay operator, it will delay processing of message, and if the volume continues +to flood, it will start to drop packets. This is obviously a potential vector +for attacking a specific relay, but because although clients are not +identifiable, sessions are, the sessions with the highest volumes will be +dropped before lesser volume messages. -Such sessions are clearly being used by attackers, and rightly can be denied, even if the session has allocation remaining. Thus, the messages are not only dropped, but the sessions balances are decremented as though they were relayed, as further punishment. +Such sessions are clearly being used by attackers, and rightly can be denied, +even if the session has allocation remaining. Thus, the messages are not only +dropped, but the sessions balances are decremented as though they were relayed, +as further punishment. -In order to help smooth out the naturally fluctuating traffic levels, every exit session the relay processes, in the reply header of the response there is a single byte value that informs the client of the current utilisation rate as a percentage represented as a value between 0 and 255. Clients will then record this information in the session database and nodes with high utilisation will be reduced in their odds of selection for a path. Updating this data will be client-driven, and be based on the existing probabilities of selection as used to pick hops for paths. +In order to help smooth out the naturally fluctuating traffic levels, every exit +session the relay processes, in the reply header of the response there is a +single byte value that informs the client of the current utilisation rate as a +percentage represented as a value between 0 and 255. Clients will then record +this information in the session database and nodes with high utilisation will be +reduced in their odds of selection for a path. Updating this data will be +client-driven, and be based on the existing probabilities of selection as used +to pick hops for paths. -Non-exit nodes do not have the ability to pass such messages through on the path to and from the exit, so in addition, a longer period EMA of utilisation rate (traffic vs configured bandwidth capacity) is published to the p2p DHT and propagates to clients at lower time precision. Since this specific update is quite important to the network consensus, the client will try to also shuffle paths around so that circuits using a relay for an intermediate hop also is used as an exit point where possible. +Non-exit nodes do not have the ability to pass such messages through on the path +to and from the exit, so in addition, a longer period EMA of utilisation rate ( +traffic vs configured bandwidth capacity) is published to the p2p DHT and +propagates to clients at lower time precision. Since this specific update is +quite important to the network consensus, the client will try to also shuffle +paths around so that circuits using a relay for an intermediate hop also is used +as an exit point where possible. -Requesting this information to peers would leak client's current running circuits to peers, so relays will interpret an exit message with no request data to be a utilisation state request and return this byte in a reply with no body. Clients will send out these empty requests randomly in the same way as the relays are chosen for paths to gather advance intelligence about potential congestion, and avoid a given relay for a while until a later request reveals the relay's traffic has returned to nominal levels. +Requesting this information to peers would leak client's current running +circuits to peers, so relays will interpret an exit message with no request data +to be a utilisation state request and return this byte in a reply with no body. +Clients will send out these empty requests randomly in the same way as the +relays are chosen for paths to gather advance intelligence about potential +congestion, and avoid a given relay for a while until a later request reveals +the relay's traffic has returned to nominal levels. -With the combination of periodic updating of longer time windows of recent activity from the p2p network, and the direct queries via empty exit onions, clients will avoid overloading relays. Users will even be able to define a threshold for "too busy" for a peer at a lower level in order to get a better latency guarantee, indeed, some service ports relate to applications with high interactivity and these can be automatically evaluated using this different threshold and achieve low latency as well as preventing network congestion. +With the combination of periodic updating of longer time windows of recent +activity from the p2p network, and the direct queries via empty exit onions, +clients will avoid overloading relays. Users will even be able to define a +threshold for "too busy" for a peer at a lower level in order to get a better +latency guarantee, indeed, some service ports relate to applications with high +interactivity and these can be automatically evaluated using this different +threshold and achieve low latency as well as preventing network congestion. ## Relay to Relay Traffic -Messages are segmented into 1382 byte segments and reassembled by relays when they receive them. The relays return an acknowledgement being a signature on the hash of the packet data (which includes the checksum prefix), and these are dispatched in a stream after shuffling already queued messages by the sending relay, as well as interleaving messages passing to the common next hop when this happens. +Messages are segmented into 1382 byte segments and reassembled by relays when +they receive them. The relays return an acknowledgement being a signature on the +hash of the packet data (which includes the checksum prefix), and these are +dispatched in a stream after shuffling already queued messages by the sending +relay, as well as interleaving messages passing to the common next hop when this +happens. -If a message fails to be received on the other end, the relay will retry the send a few times before giving up. Unfortunately it is not possible, while minimising packet overhead, to allow intermediate hops to return replies (it is around 250 bytes), and so from the client's perspective, the message has failed to be delivered as it does not receive the expected return trip confirmation or response, and it is impractical to then return a failure response back to the client. +If a message fails to be received on the other end, the relay will retry the +send a few times before giving up. Unfortunately it is not possible, while +minimising packet overhead, to allow intermediate hops to return replies (it is +around 250 bytes), and so from the client's perspective, the message has failed +to be delivered as it does not receive the expected return trip confirmation or +response, and it is impractical to then return a failure response back to the +client. -By not allowing such back-propagation in the protocol, attempting to attack the network by disrupting protocol packets with delays, corruption and dropping does not prompt a reverse path chain of messages that lead from the intermediate hop to the client. +By not allowing such back-propagation in the protocol, attempting to attack the +network by disrupting protocol packets with delays, corruption and dropping does +not prompt a reverse path chain of messages that lead from the intermediate hop +to the client. -Because it is not possible, due to protecting anonymity, to connect an intermediate hop with an exit point's sessions, clients are simply in the dark when a message fails to travel forwards, and by the use of a time to live on the return path (which clients open after sending out a message to enable the last hop to send back the reply) for a given service type, the client caches the forward message payload and exit point after sending them, and then flushes them from the cache once they receive the confirmation/response. +Because it is not possible, due to protecting anonymity, to connect an +intermediate hop with an exit point's sessions, clients are simply in the dark +when a message fails to travel forwards, and by the use of a time to live on the +return path (which clients open after sending out a message to enable the last +hop to send back the reply) for a given service type, the client caches the +forward message payload and exit point after sending them, and then flushes them +from the cache once they receive the confirmation/response. -If it does not arrive before the TTL for the service type, the client will then construct a new path to deliver the message to the same exit a few more times before giving up. This value will also have the requested packet delay total added to it when there is delay messages in the onions. Failed messages may turn out to be failing on the return path, so on the other side, when a request is received, it is cached for a little while in case the request is sent again with a different return path, though it only needs to store the request message hash to achieve this. +If it does not arrive before the TTL for the service type, the client will then +construct a new path to deliver the message to the same exit a few more times +before giving up. This value will also have the requested packet delay total +added to it when there is delay messages in the onions. Failed messages may turn +out to be failing on the return path, so on the other side, when a request is +received, it is cached for a little while in case the request is sent again with +a different return path, though it only needs to store the request message hash +to achieve this. ### Relay to Relay Encryption -In order to further secure traffic, relays in their chatter with each other provide private relay-to-relay keys to use for message encryption. These are rolled over in accordance with the traffic volume between the peers. +In order to further secure traffic, relays in their chatter with each other +provide private relay-to-relay keys to use for message encryption. These are +rolled over in accordance with the traffic volume between the peers. ### Dynamic error correction adjustment for Re-transmit Avoidance -Based on the conditions of the paths between two relays, by the ratio of packet loss the nodes adjust the error correction to use in order to maintain a margin above the current loss rate, built using a moving average of successful deliveries versus failed between the two relays. +Based on the conditions of the paths between two relays, by the ratio of packet +loss the nodes adjust the error correction to use in order to maintain a margin +above the current loss rate, built using a moving average of successful +deliveries versus failed between the two relays. ## Client Path Generation Configuration -A flexible configuration system for selecting paths and exit points is required to cover several different types of use case of obfuscated traffic paths. +A flexible configuration system for selecting paths and exit points is required +to cover several different types of use case of obfuscated traffic paths. - Geo-location based exit and route hop selection: - - Users may need to avoid using exits within their own or some specified outside jurisdiction. - - Users may specifically want their exits to emerge in a specified geographical region. - - Users may want to specify, or avoid selecting intermediate paths in a list of specified geographical regions. + - Users may need to avoid using exits within their own or some specified + outside jurisdiction. + - Users may specifically want their exits to emerge in a specified + geographical region. + - Users may want to specify, or avoid selecting intermediate paths in a list + of specified geographical regions. - Selection of specific routers for exits for a given protocol: - - Using a user's own servers, this can be generalised to allow remote access to a server controlled by the user. - - A company may provide specific services that users can access at a given set of addresses, whether IP based or domain based. + - Using a user's own servers, this can be generalised to allow remote access + to a server controlled by the user. + - A company may provide specific services that users can access at a given + set of addresses, whether IP based or domain based. -Simply providing the IP or domain name of the endpoint to the built in Socks5 proxy will also pick the exit if it is an Indra peer, which makes servers like Bitcoin and LN nodes transparently reachable over Indranet without any user intervention, and makes Indranet the default path for all of the Indra relays when they connect to Indra peers. +Simply providing the IP or domain name of the endpoint to the built in Socks5 +proxy will also pick the exit if it is an Indra peer, which makes servers like +Bitcoin and LN nodes transparently reachable over Indranet without any user +intervention, and makes Indranet the default path for all of the Indra relays +when they connect to Indra peers. -Since the net effect will be that relays will spend the same amount on tunnelling to other Indranet LN and Bitcoin nodes as others do to their own, the relays will be able to use this to prompt usage of nodes with imbalanced channels to correct their inbound liquidity. And of course this increases the amount of traffic and thus the anonymity set to include not just client initiated traffic but relay initiated as well. +Since the net effect will be that relays will spend the same amount on +tunnelling to other Indranet LN and Bitcoin nodes as others do to their own, the +relays will be able to use this to prompt usage of nodes with imbalanced +channels to correct their inbound liquidity. And of course this increases the +amount of traffic and thus the anonymity set to include not just client +initiated traffic but relay initiated as well. ## Hidden Services -Because Indranet is source routed, unlike the connection oriented onion paths that Tor uses, it can avoid the potential problem of all of its rendezvous points becoming congested and impacting user experience via increased latency and dropped packets. It requires more than 6 hops but these are changed at every message cycle. +Because Indranet is source routed, unlike the connection oriented onion paths +that Tor uses, it can avoid the potential problem of all of its rendezvous +points becoming congested and impacting user experience via increased latency +and dropped packets. It requires more than 6 hops but these are changed at every +message cycle. -1. The hidden service selects a set of 6 randomly selected relays to act as introducers. It sends out a few messages to the relay so that it can handle several requests and a reply header to request more. -2. The client contacts the introducer and requests one of there routing headers, and the introducer delivers one and also sends back a reply to the hidden service in its pending reply packet provided precisely for requesting new routing headers, and the hidden service duly dispatches a new one on a new path. -3. The client then creates a 2 hop, but 3 layer "reverse" header, then puts the routing header provided by the introducer inside it, so the second hop unwraps it and continues to forward the rest of the onion. And then at the end of the message just before the request payload, it provides a 3 hop reply path, two intermediaries and its own randomly selected session keyset. -4. The packet arrives at the hidden service after the path defined by its 3 hop header, it dispatches the payload out to the connected service for the message, and then creates another two hop, but 3 layer "reverse" header, which it then wraps the client's routing header, and attaches the response payload to it, with the hash of the request payload to identify what request the response relates to, and lastly, the next 3 hop routing header like the one the introducer provides, enabling the next message cycle. -5. Embedded in the payload part of the routing headers on both sides is a 64 bit nonce which acts as the connection cookie to identify quickly what connection a message relates to, which is translated into a fake hexadecimal pretend domain name, enabling a web service to block this connection if the app determines the client is up to mischief. -6. To minimise DoS attack potential in repeatedly requesting routing headers and then not using them, the introducer node charges a substantially larger fee for delivering the routing headers, raising the barrier against such attempts to congest the introducer's message stream back to the hidden service. After a message session has gone quiet for a timeout period, maybe around 1 hour, the last delivered routing header is expired and cannot be used to continue the hidden service connection. +1. The hidden service selects a set of 6 randomly selected relays to act as + introducers. It sends out a few messages to the relay so that it can handle + several requests and a reply header to request more. +2. The client contacts the introducer and requests one of there routing headers, + and the introducer delivers one and also sends back a reply to the hidden + service in its pending reply packet provided precisely for requesting new + routing headers, and the hidden service duly dispatches a new one on a new + path. +3. The client then creates a 2 hop, but 3 layer "reverse" header, then puts the + routing header provided by the introducer inside it, so the second hop + unwraps it and continues to forward the rest of the onion. And then at the + end of the message just before the request payload, it provides a 3 hop reply + path, two intermediaries and its own randomly selected session keyset. +4. The packet arrives at the hidden service after the path defined by its 3 hop + header, it dispatches the payload out to the connected service for the + message, and then creates another two hop, but 3 layer "reverse" header, + which it then wraps the client's routing header, and attaches the response + payload to it, with the hash of the request payload to identify what request + the response relates to, and lastly, the next 3 hop routing header like the + one the introducer provides, enabling the next message cycle. +5. Embedded in the payload part of the routing headers on both sides is a 64 bit + nonce which acts as the connection cookie to identify quickly what connection + a message relates to, which is translated into a fake hexadecimal pretend + domain name, enabling a web service to block this connection if the app + determines the client is up to mischief. +6. To minimise DoS attack potential in repeatedly requesting routing headers and + then not using them, the introducer node charges a substantially larger fee + for delivering the routing headers, raising the barrier against such attempts + to congest the introducer's message stream back to the hidden service. After + a message session has gone quiet for a timeout period, maybe around 1 hour, + the last delivered routing header is expired and cannot be used to continue + the hidden service connection. -With this scheme, the anonymity of both sides of the connection is maintained, at the cost of the extra 4 hops versus the potentially small channel created by a set of 6 rendezvous nodes in Tor. Because all the processing is done at the endpoints, the trip time is a little longer, but because it is dynamically generated, peers can minimise bottlenecks along the path through their constant updating of reported utilisation levels for relays both on the p2p network as well as via the replies to exit onion messages. +With this scheme, the anonymity of both sides of the connection is maintained, +at the cost of the extra 4 hops versus the potentially small channel created by +a set of 6 rendezvous nodes in Tor. Because all the processing is done at the +endpoints, the trip time is a little longer, but because it is dynamically +generated, peers can minimise bottlenecks along the path through their constant +updating of reported utilisation levels for relays both on the p2p network as +well as via the replies to exit onion messages. -And of course, in stark contrast to Tor, the paths change every time meaning that timing patterns require a far larger number of evil nodes to capture meaningful patterns, a difficulty level that rises asymptotically with the number of relays and clients on the network, as well as the advantage of source routing eliminating the ability for evil nodes to reroute traffic. +And of course, in stark contrast to Tor, the paths change every time meaning +that timing patterns require a far larger number of evil nodes to capture +meaningful patterns, a difficulty level that rises asymptotically with the +number of relays and clients on the network, as well as the advantage of source +routing eliminating the ability for evil nodes to reroute traffic. ### Fully Anonymous VPS Hosting -With the use of Indranet's hidden services protocol, in theory a user can establish an account with a remote VPS rental provider that uses Indranet, with an package that includes a pre-installed instance of Indranet (not providing relay service, but appearing in the peer DHT), running a certificate authenticated SSH endpoint, and then install whatever applications they want, hook them up to the server's service configuration, and thus remain completely anonymous and untraceable to the public IP of the VPS. In this plain configuration the user knows the IP address of the server's Indra node. +With the use of Indranet's hidden services protocol, in theory a user can +establish an account with a remote VPS rental provider that uses Indranet, with +an package that includes a pre-installed instance of Indranet (not providing +relay service, but appearing in the peer DHT), running a certificate +authenticated SSH endpoint, and then install whatever applications they want, +hook them up to the server's service configuration, and thus remain completely +anonymous and untraceable to the public IP of the VPS. In this plain +configuration the user knows the IP address of the server's Indra node. -Or it can even go one step further, where even the server IP address is hidden, connected by a point to point connection to the provider's network infrastructure, which further increases security against an application breach leaking the IP address of the hidden services running on it. Neither the provider, or the client know anything about each other, and thus cannot be connected together, and likewise, none of the clients of the hidden service will reveal any location data by default to the applications on the server. +Or it can even go one step further, where even the server IP address is hidden, +connected by a point to point connection to the provider's network +infrastructure, which further increases security against an application breach +leaking the IP address of the hidden services running on it. Neither the +provider, or the client know anything about each other, and thus cannot be +connected together, and likewise, none of the clients of the hidden service will +reveal any location data by default to the applications on the server. ## Proxy Service -The client will run a Socks5 proxy, which users then set up as their web browser/other proxy for connections. This proxy will make DNS requests via Indranet for the names in the requests, whether Indra hidden service addresses or clearnet addresses, forwarding the name resolution request out to random Indranet relays, who send back the IP address replies. +The client will run a Socks5 proxy, which users then set up as their web +browser/other proxy for connections. This proxy will make DNS requests via +Indranet for the names in the requests, whether Indra hidden service addresses +or clearnet addresses, forwarding the name resolution request out to random +Indranet relays, who send back the IP address replies. -Requests for forwarding to a specific Indra relay can be specified by an address matching the relay's IP address, or the zero address, meaning randomly select the exit, or a regular domain name for the case of relays that provide tunnel exit services. As mentioned previously, relays automatically route Bitcoin and LN traffic over Indranet to Indranet Bitcoin and LN nodes. +Requests for forwarding to a specific Indra relay can be specified by an address +matching the relay's IP address, or the zero address, meaning randomly select +the exit, or a regular domain name for the case of relays that provide tunnel +exit services. As mentioned previously, relays automatically route Bitcoin and +LN traffic over Indranet to Indranet Bitcoin and LN nodes. -Relays that wish to provide tunnel exit service simply place a Socks5 proxy listening on their localhost service ports, inbound connections for these services are then forwarded through the proxy which then resolves names via Indra to dissociate this request from the exit, and forwards the messages and routes the replies back to the clients using the exit header reply segment. +Relays that wish to provide tunnel exit service simply place a Socks5 proxy +listening on their localhost service ports, inbound connections for these +services are then forwarded through the proxy which then resolves names via +Indra to dissociate this request from the exit, and forwards the messages and +routes the replies back to the clients using the exit header reply segment. -In this way, a user can run a bitcoin or lightning wallet or other client application, and set its proxy to the Indra client's proxy and they will then be able to tunnel out to the endpoint, in the case of Indra nodes offering this service, or via tunnel exit services for addresses not part of the Indranet swarm, no modification required except to add the proxy configuration to the server, or even to the operating system settings to enable proxying automatically for any application that knows how to use the OS proxy setting. Or indeed, just providing this outbound routing service to only a specific set of ports. +In this way, a user can run a bitcoin or lightning wallet or other client +application, and set its proxy to the Indra client's proxy and they will then be +able to tunnel out to the endpoint, in the case of Indra nodes offering this +service, or via tunnel exit services for addresses not part of the Indranet +swarm, no modification required except to add the proxy configuration to the +server, or even to the operating system settings to enable proxying +automatically for any application that knows how to use the OS proxy setting. Or +indeed, just providing this outbound routing service to only a specific set of +ports. -For software that does not have the ability to use a proxy, the Indra client also opens listeners on localhost addresses for configured port numbers, and then using server configurations' "connect only" type setting, establish a path to a single, randomly chosen Indra peer that provides this service, and of course many of them can be set up as needed. The caveat to this is that during a session, if it were desired to change the endpoint the path leads to, this has to be tolerated by the protocol, that it be ok for an endpoint change to occur periodically, or on every request. +For software that does not have the ability to use a proxy, the Indra client +also opens listeners on localhost addresses for configured port numbers, and +then using server configurations' "connect only" type setting, establish a path +to a single, randomly chosen Indra peer that provides this service, and of +course many of them can be set up as needed. The caveat to this is that during a +session, if it were desired to change the endpoint the path leads to, this has +to be tolerated by the protocol, that it be ok for an endpoint change to occur +periodically, or on every request. -Peer to peer applications may or may not tolerate the apparent change. For services that have no concept of association, like a Bitcoin or other distributed application service RPC API, it is fine for each new request to take a different path and go to a different endpoint. Configuration will allow fixed endpoints (that don't change during a run), a rotating change of endpoint, and a period in which the endpoint is rotated if it is set to rotate, or a new endpoint each time. +Peer to peer applications may or may not tolerate the apparent change. For +services that have no concept of association, like a Bitcoin or other +distributed application service RPC API, it is fine for each new request to take +a different path and go to a different endpoint. Configuration will allow fixed +endpoints (that don't change during a run), a rotating change of endpoint, and a +period in which the endpoint is rotated if it is set to rotate, or a new +endpoint each time. -However, since most p2p applications understand the use of Socks proxies, this won't be a frequent requirement, and is of lower priority for implementation than the straight Socks5 proxy. +However, since most p2p applications understand the use of Socks proxies, this +won't be a frequent requirement, and is of lower priority for implementation +than the straight Socks5 proxy. ## Private Relay Services -To enable users to use Indranet as a transport for accessing private servers, deployments using the `Neutrino` SPV bitcoin node and `lnd` can configure a public key certificate that they can use with a private key, in a similar way to SSH certificate encryption, to enable routing from any client to their specified node identified with its IP address, where there is a relay running at that IP address with the public key registered as enabling access to forward connections to a defined loopback address where the hidden service is running. +To enable users to use Indranet as a transport for accessing private servers, +deployments using the `Neutrino` SPV bitcoin node and `lnd` can configure a +public key certificate that they can use with a private key, in a similar way to +SSH certificate encryption, to enable routing from any client to their specified +node identified with its IP address, where there is a relay running at that IP +address with the public key registered as enabling access to forward connections +to a defined loopback address where the hidden service is running. -This will enable SSH, FTP, and similar services for users to be accessed via Indra, while preventing third parties from identifying the origin of access to the server. This will also enable things like remote desktop access, but it does not include rendezvous routing. It can also, as previously mentioned, to hide the destination point as well using hidden services. +This will enable SSH, FTP, and similar services for users to be accessed via +Indra, while preventing third parties from identifying the origin of access to +the server. This will also enable things like remote desktop access, but it does +not include rendezvous routing. It can also, as previously mentioned, to hide +the destination point as well using hidden services. ## The Indra Tax -Here at Indra Labs we like to call a spade a spade, and we will be establishing in our distribution of the Indranet clients and relays the default establishment of Lightning channels through our peer to peer network seed nodes, which will charge market-typical routing fees to connect clients to the Indranet swarm's Lightning nodes and enable payments. Because we make this the default, and by default relays only connect to other relays and the seed nodes, effectively we can levy a kind of toll for the delivery of payments. +Here at Indra Labs we like to call a spade a spade, and we will be establishing +in our distribution of the Indranet clients and relays the default establishment +of Lightning channels through our peer to peer network seed nodes, which will +charge market-typical routing fees to connect clients to the Indranet swarm's +Lightning nodes and enable payments. Because we make this the default, and by +default relays only connect to other relays and the seed nodes, effectively we +can levy a kind of toll for the delivery of payments. -We intend to also offer the option for investors to, preferably independently, run seed nodes, under the conditions they request, which can include our promotion of them on our website and communications. It is better that there be several independent entities involved in this, and of course the fees their seed nodes collect will be part of their reward for this service. +We intend to also offer the option for investors to, preferably independently, +run seed nodes, under the conditions they request, which can include our +promotion of them on our website and communications. It is better that there be +several independent entities involved in this, and of course the fees their seed +nodes collect will be part of their reward for this service. -Session payments are always routed through long paths, as permitted by the Lightning Network protocol, selecting a seed node first and then to several intermediary nodes in the swarm before reaching the destination. Users will be able to verify by enabling payment logging, and see a recent history of payments and the hops that the Lighting node in the client used to construct the payment message. +Session payments are always routed through long paths, as permitted by the +Lightning Network protocol, selecting a seed node first and then to several +intermediary nodes in the swarm before reaching the destination. Users will be +able to verify by enabling payment logging, and see a recent history of payments +and the hops that the Lighting node in the client used to construct the payment +message. -The channels between clients and seed nodes are bidirectional, and users can then add to their client balance by sending payments to an invoice their client creates for adding funds to it. The amount of satoshis in their channels will be at their discretion, though the client will alert them that their channel is empty in order to prompt the user to fill it back up. Seed nodes will collect routing fees on both directions, of course. +The channels between clients and seed nodes are bidirectional, and users can +then add to their client balance by sending payments to an invoice their client +creates for adding funds to it. The amount of satoshis in their channels will be +at their discretion, though the client will alert them that their channel is +empty in order to prompt the user to fill it back up. Seed nodes will collect +routing fees on both directions, of course. -The size of Indra client Lightning wallet balances is dictated by the user, but in general it does not need to be very large, as it will likely process maybe several US dollars worth of traffic a day for most users, it is just a matter of convenience to make them larger. Rather than close them and establish new ones, when users want to increase their wallet balance they can simply establish more channels to the seeds, and avoid closure fees. Since a user is not going to pay into their wallet unless the client is running, these channels are ok to be offline the rest of the time. +The size of Indra client Lightning wallet balances is dictated by the user, but +in general it does not need to be very large, as it will likely process maybe +several US dollars worth of traffic a day for most users, it is just a matter of +convenience to make them larger. Rather than close them and establish new ones, +when users want to increase their wallet balance they can simply establish more +channels to the seeds, and avoid closure fees. Since a user is not going to pay +into their wallet unless the client is running, these channels are ok to be +offline the rest of the time. -Users can also configure their client's Lightning node to open other channels to the Lightning Network as they wish, by pointing their wallet application to the IP address of their client, or if they are technically inclined, via `lncli`, as we are using `lnd` as our primary LN node due to it being written in the same language as Indranet itself. Payments into their Indra LN wallets can thus also pass through other channels than the seed nodes' channels, evading the inbound transaction fee to our seeds, although in general that just means paying someone else. +Users can also configure their client's Lightning node to open other channels to +the Lightning Network as they wish, by pointing their wallet application to the +IP address of their client, or if they are technically inclined, via `lncli`, as +we are using `lnd` as our primary LN node due to it being written in the same +language as Indranet itself. Payments into their Indra LN wallets can thus also +pass through other channels than the seed nodes' channels, evading the inbound +transaction fee to our seeds, although in general that just means paying someone +else. -Indra's channel management system will automate most of the task of balancing channels, shifting balance from depleted paths into ones that are not yet depleted, to ensure there is as many routes as possible in case seed nodes happen to be offline at the time of a payment. +Indra's channel management system will automate most of the task of balancing +channels, shifting balance from depleted paths into ones that are not yet +depleted, to ensure there is as many routes as possible in case seed nodes +happen to be offline at the time of a payment. ----- diff --git a/go.mod b/go.mod index e04506c2..0605da1d 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,9 @@ -module github.com/indra-labs/indra +module git-indra.lan/indra-labs/indra -go 1.18 +go 1.19 require ( + git-indra.lan/indra-labs/lnd v0.15.5-beta github.com/btcsuite/btcd/btcutil v1.1.3 github.com/cybriq/qu v0.1.2 github.com/davecgh/go-spew v1.1.1 @@ -10,7 +11,6 @@ require ( github.com/docker/cli v20.10.22+incompatible github.com/docker/docker v20.10.22+incompatible github.com/gookit/color v1.5.2 - github.com/indra-labs/lnd v0.15.5-beta github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/libp2p/go-libp2p v0.24.2 github.com/libp2p/go-libp2p-kad-dht v0.20.0 @@ -73,6 +73,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/huin/goupnp v1.0.3 // indirect + github.com/indra-labs/lnd v0.15.5-beta // indirect github.com/ipfs/go-cid v0.3.2 // indirect github.com/ipfs/go-datastore v0.6.0 // indirect github.com/ipfs/go-ipfs-util v0.0.2 // indirect @@ -181,4 +182,12 @@ replace crypto/sha256 => github.com/minio/sha256-simd v1.0.0 replace math/rand => github.com/lukechampine/frand v1.4.2 -replace github.com/indra-labs/indra => ./ +//replace git-indra.lan/indra-labs/indra => ./ + +//replace git-indra.lan/indra-labs/lnd => ../lnd + +exclude github.com/indra-labs/lnd v0.15.10001 + +exclude github.com/indra-labs/lnd v0.15.101 + +exclude github.com/indra-labs/lnd v0.15.1 diff --git a/go.sum b/go.sum index 6d630ff3..56ce5669 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= +git-indra.lan/indra-labs/lnd v0.15.5-beta h1:pBqv0pUdOG2W8qhl8hJWZWwll/ejKEKIDHvjscN8dPc= +git-indra.lan/indra-labs/lnd v0.15.5-beta/go.mod h1:YSjOskYVjP2Lm4WW+ldH2uGSHqYjmBnUGzrrWc0OSuw= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= diff --git a/pkg/cfg/params.go b/pkg/cfg/params.go index 2a543196..3b5ab8b4 100644 --- a/pkg/cfg/params.go +++ b/pkg/cfg/params.go @@ -1,10 +1,11 @@ package cfg import ( - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/node" - log2 "github.com/indra-labs/indra/pkg/proc/log" "github.com/multiformats/go-multiaddr" + + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/node" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/client/client.go b/pkg/client/client.go deleted file mode 100644 index fb2e20e9..00000000 --- a/pkg/client/client.go +++ /dev/null @@ -1,99 +0,0 @@ -package client - -import ( - "sync" - "time" - - "github.com/cybriq/qu" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/key/signer" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/node" - "github.com/indra-labs/indra/pkg/onion/layers/confirm" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/traffic" - "github.com/indra-labs/indra/pkg/types" - "go.uber.org/atomic" -) - -var ( - log = log2.GetLogger(indra.PathBase) - check = log.E.Chk -) - -type Client struct { - *node.Node - node.Nodes - sync.Mutex - Load byte - *confirm.Confirms - PendingResponses - *signer.KeySet - ShuttingDown atomic.Bool - qu.C -} - -func NewClient(tpt types.Transport, hdrPrv *prv.Key, no *node.Node, - nodes node.Nodes) (c *Client, e error) { - - no.Transport = tpt - no.IdentityPrv = hdrPrv - no.IdentityPub = pub.Derive(hdrPrv) - var ks *signer.KeySet - if _, ks, e = signer.New(); check(e) { - return - } - // Add our first return session. - no.AddSession(traffic.NewSession(nonce.NewID(), no.Peer, 0, nil, nil, 5)) - c = &Client{ - Confirms: confirm.NewConfirms(), - Node: no, - Nodes: nodes, - KeySet: ks, - C: qu.T(), - } - return -} - -// Start a single thread of the Client. -func (cl *Client) Start() { - for { - if cl.handler() { - break - } - } -} - -func (cl *Client) RegisterConfirmation(hook confirm.Hook, - cnf nonce.ID) { - - if hook == nil { - return - } - cl.Confirms.Add(&confirm.Callback{ - ID: cnf, - Time: time.Now(), - Hook: hook, - }) -} - -// Cleanup closes and flushes any resources the client opened that require sync -// in order to reopen correctly. -func (cl *Client) Cleanup() { - // Do cleanup stuff before shutdown. -} - -// Shutdown triggers the shutdown of the client and the Cleanup before -// finishing. -func (cl *Client) Shutdown() { - if cl.ShuttingDown.Load() { - return - } - log.T.C(func() string { - return "shutting down client " + cl.Node.AddrPort.String() - }) - cl.ShuttingDown.Store(true) - cl.C.Q() -} diff --git a/pkg/client/handler-balance.go b/pkg/client/handler-balance.go deleted file mode 100644 index 6982de58..00000000 --- a/pkg/client/handler-balance.go +++ /dev/null @@ -1,46 +0,0 @@ -package client - -import ( - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/onion/layers/balance" - "github.com/indra-labs/indra/pkg/traffic" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/lnd/lnd/lnwire" -) - -func (cl *Client) balance(on *balance.Layer, - b slice.Bytes, c *slice.Cursor, prev types.Onion) { - - log.T.S(on.ConfID) - - cl.IterateSessions(func(s *traffic.Session) bool { - if s.ID == on.ID { - log.D.F("received balance %x for session %x", - on.MilliSatoshi, on.ID) - // todo: check for close match client's running estimate - // based on the outbound packet send volume. - s.Remaining = on.MilliSatoshi - return true - } - return false - }) - pending := cl.PendingResponses.Find(sha256.Single(on.ID[:])) - if pending != nil { - for i := range pending.Billable { - s := cl.FindSession(pending.Billable[i]) - if s != nil { - log.D.Ln(cl.AddrPort.String(), "post acct") - if i == 0 { - cl.DecSession(s.ID, - s.RelayRate*lnwire.MilliSatoshi(len(b)/2)/1024/1024) - } else { - cl.DecSession(s.ID, - s.RelayRate*lnwire.MilliSatoshi(len(b))/1024/1024) - } - } - } - cl.PendingResponses.Delete(pending.Hash) - } - cl.Confirms.Confirm(on.ConfID) -} diff --git a/pkg/client/handler-confirm.go b/pkg/client/handler-confirm.go deleted file mode 100644 index 8692e2d6..00000000 --- a/pkg/client/handler-confirm.go +++ /dev/null @@ -1,16 +0,0 @@ -package client - -import ( - "github.com/indra-labs/indra/pkg/onion/layers/confirm" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" -) - -func (cl *Client) confirm(on *confirm.Layer, - b slice.Bytes, c *slice.Cursor, prev types.Onion) { - - // When a confirm arrives check if it is registered for and run - // the hook that was registered with it. - log.T.S(cl.Confirms) - cl.Confirms.Confirm(on.ID) -} diff --git a/pkg/client/handler-crypt.go b/pkg/client/handler-crypt.go deleted file mode 100644 index d315e806..00000000 --- a/pkg/client/handler-crypt.go +++ /dev/null @@ -1,81 +0,0 @@ -package client - -import ( - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/onion" - "github.com/indra-labs/indra/pkg/onion/layers/crypt" - "github.com/indra-labs/indra/pkg/onion/layers/directbalance" - "github.com/indra-labs/indra/pkg/onion/layers/forward" - "github.com/indra-labs/indra/pkg/onion/layers/magicbytes" - "github.com/indra-labs/indra/pkg/onion/layers/session" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/lnd/lnd/lnwire" -) - -func (cl *Client) crypt(on *crypt.Layer, b slice.Bytes, - c *slice.Cursor, prev types.Onion) { - - // this is probably an encrypted crypt for us. - hdr, _, sess, identity := cl.FindCloaked(on.Cloak) - if hdr == nil { - log.T.Ln("no matching key found from cloaked key") - return - } - on.ToPriv = hdr - on.Decrypt(hdr, b, c) - if identity { - log.T.F("identity") - if string(b[*c:][:magicbytes.Len]) != session.MagicString { - log.T.Ln("dropping message due to identity key with" + - " no following session") - return - } - - cl.handleMessage(BudgeUp(b, *c), on) - return - } - if string(b[*c:][:magicbytes.Len]) == directbalance.MagicString { - log.D.Ln("directbalance") - var on1, on2 types.Onion - var e error - if on1, e = onion.Peel(b, c); check(e) { - return - } - var balID, confID nonce.ID - switch db := on1.(type) { - case *directbalance.Layer: - log.T.S(cl.AddrPort.String(), db, b[*c:].ToBytes()) - balID = db.ID - confID = db.ConfID - default: - log.T.Ln("malformed/truncated onion") - return - } - if on2, e = onion.Peel(b, c); check(e) { - return - } - switch fwd := on2.(type) { - case *forward.Layer: - log.T.S(cl.AddrPort.String(), fwd) - o := (&onion.Skins{}). - Forward(fwd.AddrPort). - Crypt(pub.Derive(hdr), nil, cl.KeySet.Next(), nonce.New(), 0). - Balance(balID, confID, sess.Remaining) - rb := onion.Encode(o.Assemble()) - cl.Send(fwd.AddrPort, rb) - // cl.SendOnion(fwd.AddrPort, o) - log.D.Ln(cl.AddrPort.String(), "directbalance reply") - cl.DecSession(sess.ID, - cl.RelayRate*lnwire.MilliSatoshi(len(b)/2+len(rb)/2)/1024/1024) - return - default: - log.T.Ln("dropping directbalance without following " + - "forward") - return - } - return - } - cl.handleMessage(BudgeUp(b, *c), on) -} diff --git a/pkg/client/handler-exit.go b/pkg/client/handler-exit.go deleted file mode 100644 index 325b21b4..00000000 --- a/pkg/client/handler-exit.go +++ /dev/null @@ -1,64 +0,0 @@ -package client - -import ( - "time" - - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/onion" - "github.com/indra-labs/indra/pkg/onion/layers/crypt" - "github.com/indra-labs/indra/pkg/onion/layers/exit" - "github.com/indra-labs/indra/pkg/onion/layers/response" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/lnd/lnd/lnwire" -) - -func (cl *Client) exit(ex *exit.Layer, b slice.Bytes, - c *slice.Cursor, prev types.Onion) { - - // payload is forwarded to a local port and the result is forwarded - // back with a reverse header. - var e error - var result slice.Bytes - h := sha256.Single(ex.Bytes) - log.T.S(h) - if e = cl.SendTo(ex.Port, ex.Bytes); check(e) { - return - } - timer := time.NewTicker(time.Second * 5) // todo: timeout/retries etc - select { - case result = <-cl.ReceiveFrom(ex.Port): - case <-timer.C: - } - // We need to wrap the result in a message crypt. The client recognises - // the context of the response by the hash of the request message. - cl.Lock() - res := onion.Encode(&response.Layer{ - Hash: h, - Load: cl.Load, - Bytes: result, - }) - cl.Unlock() - rb := FormatReply(b[*c:c.Inc(crypt.ReverseHeaderLen)], - res, ex.Ciphers, ex.Nonces) - switch on := prev.(type) { - case *crypt.Layer: - sess := cl.FindSessionByHeader(on.ToPriv) - if sess == nil { - break - } - for i := range sess.Services { - if ex.Port != sess.Services[i].Port { - continue - } - in := sess.Services[i].RelayRate * - lnwire.MilliSatoshi(len(b)) / 2 / 1024 / 1024 - out := sess.Services[i].RelayRate * - lnwire.MilliSatoshi(len(rb)) / 2 / 1024 / 1024 - log.D.Ln(sess.AddrPort.String(), "exit send") - cl.DecSession(sess.ID, in+out) - break - } - } - cl.handleMessage(rb, ex) -} diff --git a/pkg/client/handler-forward.go b/pkg/client/handler-forward.go deleted file mode 100644 index 2c3eb532..00000000 --- a/pkg/client/handler-forward.go +++ /dev/null @@ -1,32 +0,0 @@ -package client - -import ( - "github.com/indra-labs/indra/pkg/onion/layers/crypt" - "github.com/indra-labs/indra/pkg/onion/layers/forward" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/lnd/lnd/lnwire" -) - -func (cl *Client) forward(on *forward.Layer, b slice.Bytes, - c *slice.Cursor, prev types.Onion) { - - // forward the whole buffer received onwards. Usually there will be a - // crypt.Layer under this which will be unwrapped by the receiver. - if on.AddrPort.String() == cl.Node.AddrPort.String() { - // it is for us, we want to unwrap the next part. - cl.handleMessage(BudgeUp(b, *c), on) - } else { - switch on1 := prev.(type) { - case *crypt.Layer: - sess := cl.FindSessionByHeader(on1.ToPriv) - if sess != nil { - log.D.Ln(on.AddrPort.String(), "forward forward") - cl.DecSession(sess.ID, - cl.RelayRate*lnwire.MilliSatoshi(len(b))/1024/1024) - } - } - // we need to forward this message onion. - cl.Send(on.AddrPort, b) - } -} diff --git a/pkg/client/handler-response.go b/pkg/client/handler-response.go deleted file mode 100644 index 7e6ccb81..00000000 --- a/pkg/client/handler-response.go +++ /dev/null @@ -1,44 +0,0 @@ -package client - -import ( - "github.com/indra-labs/indra/pkg/onion/layers/response" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/lnd/lnd/lnwire" -) - -// response is a payload from an exit message. -func (cl *Client) response(on *response.Layer, b slice.Bytes, - cur *slice.Cursor, prev types.Onion) { - - pending := cl.PendingResponses.Find(on.Hash) - first := true - var rr lnwire.MilliSatoshi - if pending != nil { - for i := range pending.Billable { - if first { - first = false - s := cl.FindSession(pending.Billable[i]) - for i := range s.Services { - if s.Services[i].Port == pending.Port { - rr = s.Services[i].RelayRate - } - } - if s != nil { - log.D.Ln(cl.AddrPort.String(), "exit send", i) - cl.DecSession(s.ID, rr*lnwire. - MilliSatoshi(len(b)/2)/1024/1024) - } - continue - } - s := cl.FindSession(pending.Billable[i]) - if s != nil { - log.D.Ln(cl.AddrPort.String(), "reverse") - cl.DecSession(s.ID, s.RelayRate*lnwire. - MilliSatoshi(len(b))/1024/1024) - } - } - pending.Callback(on.Bytes) - cl.PendingResponses.Delete(on.Hash) - } -} diff --git a/pkg/client/handler-session.go b/pkg/client/handler-session.go deleted file mode 100644 index 57bc7c73..00000000 --- a/pkg/client/handler-session.go +++ /dev/null @@ -1,33 +0,0 @@ -package client - -import ( - "fmt" - - "github.com/davecgh/go-spew/spew" - "github.com/indra-labs/indra/pkg/onion/layers/session" - "github.com/indra-labs/indra/pkg/traffic" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" -) - -func (cl *Client) session(on *session.Layer, b slice.Bytes, - c *slice.Cursor, prev types.Onion) { - - log.T.C(func() string { - return fmt.Sprint("incoming session", - spew.Sdump(on.PreimageHash())) - }) - pi := cl.FindPendingPreimage(on.PreimageHash()) - if pi != nil { - // We need to delete this first in case somehow two such - // messages arrive at the same time, and we end up with - // duplicate sessions. - cl.DeletePendingPayment(pi.Preimage) - log.T.F("Adding session %x\n", pi.ID) - cl.AddSession(traffic.NewSession(pi.ID, - cl.Node.Peer, pi.Amount, on.Header, on.Payload, on.Hop)) - cl.handleMessage(BudgeUp(b, *c), on) - } else { - log.T.Ln("dropping session message without payment") - } -} diff --git a/pkg/client/handler.go b/pkg/client/handler.go deleted file mode 100644 index a00de68b..00000000 --- a/pkg/client/handler.go +++ /dev/null @@ -1,112 +0,0 @@ -package client - -import ( - "fmt" - "reflect" - - "github.com/davecgh/go-spew/spew" - "github.com/indra-labs/indra/pkg/onion" - "github.com/indra-labs/indra/pkg/onion/layers/balance" - "github.com/indra-labs/indra/pkg/onion/layers/confirm" - "github.com/indra-labs/indra/pkg/onion/layers/crypt" - "github.com/indra-labs/indra/pkg/onion/layers/delay" - "github.com/indra-labs/indra/pkg/onion/layers/exit" - "github.com/indra-labs/indra/pkg/onion/layers/forward" - "github.com/indra-labs/indra/pkg/onion/layers/getbalance" - "github.com/indra-labs/indra/pkg/onion/layers/response" - "github.com/indra-labs/indra/pkg/onion/layers/reverse" - "github.com/indra-labs/indra/pkg/onion/layers/session" - "github.com/indra-labs/indra/pkg/traffic" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" -) - -func (cl *Client) handler() (out bool) { - log.T.C(func() string { - return cl.AddrPort.String() + - " awaiting message" - }) - var prev types.Onion - select { - case <-cl.C.Wait(): - cl.Cleanup() - out = true - break - case b := <-cl.Node.Receive(): - cl.handleMessage(b, prev) - case p := <-cl.PaymentChan: - log.T.S("incoming payment", cl.AddrPort.String(), p) - topUp := false - cl.IterateSessions(func(s *traffic.Session) bool { - if s.Preimage == p.Preimage { - s.IncSats(p.Amount) - topUp = true - log.T.F("topping up %x with %d mSat", - s.ID, p.Amount) - return true - } - return false - }) - if !topUp { - cl.AddPendingPayment(p) - log.T.F("awaiting session keys for preimage %x", - p.Preimage) - } - } - return -} - -func (cl *Client) handleMessage(b slice.Bytes, prev types.Onion) { - // process received message - var on types.Onion - var e error - c := slice.NewCursor() - if on, e = onion.Peel(b, c); check(e) { - return - } - switch on := on.(type) { - case *balance.Layer: - log.T.C(recLog(on, b, cl)) - cl.balance(on, b, c, prev) - case *confirm.Layer: - log.T.C(recLog(on, b, cl)) - cl.confirm(on, b, c, prev) - case *crypt.Layer: - log.T.C(recLog(on, b, cl)) - cl.crypt(on, b, c, prev) - case *delay.Layer: - log.T.C(recLog(on, b, cl)) - cl.delay(on, b, c, prev) - case *exit.Layer: - log.T.C(recLog(on, b, cl)) - cl.exit(on, b, c, prev) - case *forward.Layer: - log.T.C(recLog(on, b, cl)) - cl.forward(on, b, c, prev) - case *getbalance.Layer: - log.T.C(recLog(on, b, cl)) - cl.getBalance(on, b, c, prev) - case *reverse.Layer: - log.T.C(recLog(on, b, cl)) - cl.reverse(on, b, c, prev) - case *response.Layer: - log.T.C(recLog(on, b, cl)) - cl.response(on, b, c, prev) - case *session.Layer: - log.T.C(recLog(on, b, cl)) - cl.session(on, b, c, prev) - default: - log.I.S("unrecognised packet", b) - } -} - -// utility functions - -func recLog(on types.Onion, b slice.Bytes, cl *Client) func() string { - return func() string { - return cl.AddrPort.String() + - " received " + - fmt.Sprint(reflect.TypeOf(on)) + "\n" + - spew.Sdump(b.ToBytes()) - } -} diff --git a/pkg/client/helper-buysessions.go b/pkg/client/helper-buysessions.go deleted file mode 100644 index 5543ebf7..00000000 --- a/pkg/client/helper-buysessions.go +++ /dev/null @@ -1,32 +0,0 @@ -package client - -import ( - "github.com/indra-labs/indra/pkg/node" - "github.com/indra-labs/indra/pkg/onion/layers/session" - "github.com/indra-labs/indra/pkg/payment" - "github.com/indra-labs/lnd/lnd/lnwire" -) - -func (cl *Client) BuySessions( - s ...*SessionBuy) (sess []*session.Layer, - pmt []*payment.Payment) { - - for i := range s { - // Create a new payment and drop on the payment channel. - sess = append(sess, session.New(s[i].Hop)) - pmt = append(pmt, sess[i].ToPayment(s[i].Amount)) - s[i].PaymentChan <- pmt[i] - log.T.Ln("sent out payment", i) - } - return -} - -func BuySession(n *node.Node, amt lnwire.MilliSatoshi, hop byte) (o *SessionBuy) { - return &SessionBuy{Hop: hop, Amount: amt, Node: n} -} - -type SessionBuy struct { - Hop byte - Amount lnwire.MilliSatoshi - *node.Node -} diff --git a/pkg/client/helper-sendexit.go b/pkg/client/helper-sendexit.go deleted file mode 100644 index 412e7891..00000000 --- a/pkg/client/helper-sendexit.go +++ /dev/null @@ -1,20 +0,0 @@ -package client - -import ( - "github.com/indra-labs/indra/pkg/onion" - "github.com/indra-labs/indra/pkg/traffic" - "github.com/indra-labs/indra/pkg/util/slice" -) - -func (cl *Client) SendExit(port uint16, message slice.Bytes, - target *traffic.Session, hook func(b slice.Bytes)) { - - hops := []byte{0, 1, 2, 3, 4, 5} - s := make(traffic.Sessions, len(hops)) - s[2] = target - se := cl.Select(hops, s) - var c traffic.Circuit - copy(c[:], se) - o := onion.SendExit(port, message, se[len(se)-1], c, cl.KeySet) - cl.SendOnion(c[0].AddrPort, o, hook) -} diff --git a/pkg/client/helper-sendkeys.go b/pkg/client/helper-sendkeys.go deleted file mode 100644 index 5b08748c..00000000 --- a/pkg/client/helper-sendkeys.go +++ /dev/null @@ -1,70 +0,0 @@ -package client - -import ( - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/node" - "github.com/indra-labs/indra/pkg/onion" - "github.com/indra-labs/indra/pkg/onion/layers/session" - "github.com/indra-labs/indra/pkg/payment" - "github.com/indra-labs/indra/pkg/traffic" -) - -func (cl *Client) SendKeys(sb []*SessionBuy, sess []*session.Layer, - pmt []*payment.Payment, hook func(hops []*traffic.Session)) { - - if len(sb) != len(sess) || len(sess) != len(pmt) { - log.E.Ln("all sendkeys parameters must be same length") - return - } - var buys [][]*SessionBuy - var s [][]*session.Layer - var p [][]*payment.Payment - n := len(sb) / 5 - nMod := len(sb) % 5 - if nMod != 0 { - n++ - } - for i := 0; i < n; i++ { - buys = append(buys, sb[i*5:][:5]) - s = append(s, sess[i*5:][:5]) - p = append(p, pmt[i*5:][:5]) - } - for bu := range buys { - hops := []byte{0, 1, 2, 3, 4, 5} - sessions := make(traffic.Sessions, len(hops)) - // Put the sessions in the middle if there is less than 5. - for i := range s { - sessions[i] = traffic.NewSession(nonce.NewID(), - buys[bu][i].Peer, buys[bu][i].Amount, - s[bu][i].Header, s[bu][i].Payload, byte(i)) - } - // Fill the gaps. - se := cl.Select([]byte{5}, make(traffic.Sessions, 1)) - cnf := nonce.NewID() - // Send the keys. - var circuit node.Nodes - for i := range buys[bu] { - circuit = append(circuit, buys[bu][i].Node) - } - // Build the session layer parameter. - var ss [5]*session.Layer - for i := range s[bu] { - ss[i] = s[bu][i] - } - // FIRE! - sk := onion.SendKeys(cnf, ss, se[0], - circuit, cl.KeySet) - cl.RegisterConfirmation(func(cf nonce.ID) { - log.T.F("confirmed sendkeys id %x", cf) - var h []*traffic.Session - for i := range circuit { - if circuit[i] != nil { - h = append(h, sessions[i]) - } - } - hook(h) - }, cnf) - log.T.F("sending out %d session keys", len(buys[bu])) - cl.SendOnion(circuit[0].AddrPort, sk, nil) - } -} diff --git a/pkg/client/helper-sendonion.go b/pkg/client/helper-sendonion.go deleted file mode 100644 index 17eb1318..00000000 --- a/pkg/client/helper-sendonion.go +++ /dev/null @@ -1,99 +0,0 @@ -package client - -import ( - "net/netip" - - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/onion" - "github.com/indra-labs/indra/pkg/onion/layers/crypt" - "github.com/indra-labs/indra/pkg/onion/layers/directbalance" - "github.com/indra-labs/indra/pkg/onion/layers/exit" - "github.com/indra-labs/indra/pkg/onion/layers/forward" - "github.com/indra-labs/indra/pkg/onion/layers/getbalance" - "github.com/indra-labs/indra/pkg/onion/layers/reverse" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/lnd/lnd/lnwire" -) - -func (cl *Client) SendOnion(ap *netip.AddrPort, o onion.Skins, - responseHook func(b slice.Bytes)) { - b := onion.Encode(o.Assemble()) - var billable, accounted []nonce.ID - var ret nonce.ID - var last sha256.Hash - var port uint16 - // do client accounting - skip := false - for i := range o { - if skip { - skip = false - continue - } - switch on := o[i].(type) { - case *crypt.Layer: - s := cl.FindSessionByHeaderPub(on.ToHeaderPub) - // The last hop needs no accounting as it's us! - if i == len(o)-1 { - // The session used for the last hop is stored, however. - ret = s.ID - break - } - if s == nil { - continue - } - switch on2 := o[i+1].(type) { - case *forward.Layer: - log.D.Ln("sender:", - cl.AddrPort.String(), "send forward") - cl.DecSession(s.ID, - s.RelayRate*lnwire.MilliSatoshi(len(b))/1024/1024) - accounted = append(accounted, s.ID) - case *reverse.Layer: - billable = append(billable, s.ID) - case *exit.Layer: - for i := range s.Services { - if s.Services[i].Port != on2.Port { - continue - } - port = on2.Port - log.D.Ln("sender:", - s.AddrPort.String(), "exit receive") - cl.DecSession(s.ID, - s.Services[i].RelayRate*lnwire.MilliSatoshi(len(b)/2)/1024/1024) - accounted = append(accounted, s.ID) - break - } - billable = append(billable, s.ID) - last = sha256.Single(on2.Bytes) - skip = true - case *getbalance.Layer: - log.D.Ln("sender: getbalance layer") - cl.DecSession(s.ID, - s.RelayRate*lnwire.MilliSatoshi(len(b)/2)/1024/1024) - last = sha256.Single(s.ID[:]) - billable = append(billable, s.ID) - skip = true - } - case *directbalance.Layer: - // the immediate previous layer session needs to be accounted. - switch on3 := o[i-1].(type) { - case *crypt.Layer: - s := cl.FindSessionByHeaderPub(on3.ToHeaderPub) - if s == nil { - return - } - log.D.Ln("sender: directbalance layer") - cl.DecSession(s.ID, - s.RelayRate*lnwire.MilliSatoshi(len(b))/1024/1024) - } - } - } - if responseHook == nil { - responseHook = func(_ slice.Bytes) {} - } - cl.PendingResponses.Add(last, billable, accounted, ret, port, responseHook) - log.T.Ln("sending out onion") - cl.Send(ap, b) - -} diff --git a/pkg/client/helper-sendping.go b/pkg/client/helper-sendping.go deleted file mode 100644 index dd294631..00000000 --- a/pkg/client/helper-sendping.go +++ /dev/null @@ -1,20 +0,0 @@ -package client - -import ( - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/onion" - "github.com/indra-labs/indra/pkg/traffic" -) - -func (cl *Client) SendPing(c traffic.Circuit, conf func(cf nonce.ID)) { - - hops := []byte{0, 1, 2, 3, 4, 5} - s := make(traffic.Sessions, len(hops)) - copy(s, c[:]) - se := cl.Select(hops, s) - copy(c[:], se) - confID := nonce.NewID() - cl.RegisterConfirmation(conf, confID) - o := onion.Ping(confID, se[len(se)-1], c, cl.KeySet) - cl.SendOnion(c[0].AddrPort, o, nil) -} diff --git a/pkg/client/oniontypes_test.go b/pkg/client/oniontypes_test.go deleted file mode 100644 index f06d89d1..00000000 --- a/pkg/client/oniontypes_test.go +++ /dev/null @@ -1,263 +0,0 @@ -package client - -import ( - "sync" - "testing" - "time" - - "github.com/cybriq/qu" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/node" - "github.com/indra-labs/indra/pkg/onion" - "github.com/indra-labs/indra/pkg/onion/layers/confirm" - "github.com/indra-labs/indra/pkg/onion/layers/session" - "github.com/indra-labs/indra/pkg/payment" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/service" - "github.com/indra-labs/indra/pkg/traffic" - "github.com/indra-labs/indra/pkg/transport" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/indra/pkg/util/tests" -) - -func TestPing(t *testing.T) { - log2.SetLogLevel(log2.Trace) - const nTotal = 6 - clients := make([]*Client, nTotal) - var e error - if clients, e = CreateNMockCircuits(true, 1); check(e) { - t.Error(e) - t.FailNow() - } - // Start up the clients. - for _, v := range clients { - go v.Start() - } - conf := nonce.NewID() - var circuit traffic.Circuit - for i := range circuit { - circuit[i] = clients[i+1].GetSessionByIndex(1) - } - os := onion.Ping(conf, clients[0].GetSessionByIndex(0), - circuit, clients[0].KeySet) - quit := qu.T() - clients[0].RegisterConfirmation(func(cf nonce.ID) { - if cf == conf { - log.T.S("received ping confirmation ID", cf) - quit.Q() - } - }, os[len(os)-1].(*confirm.Layer).ID) - log.T.S("sending ping with ID", os[len(os)-1].(*confirm.Layer)) - clients[0].SendOnion(clients[0].Nodes[0].AddrPort, os, nil) - go func() { - select { - case <-time.After(time.Second): - t.Error("ping got stuck") - quit.Q() - case <-quit: - } - }() - <-quit.Wait() - for _, v := range clients { - v.Shutdown() - } -} - -func TestSendExit(t *testing.T) { - log2.SetLogLevel(log2.Trace) - const nTotal = 6 - clients := make([]*Client, nTotal) - var e error - if clients, e = CreateNMockCircuits(true, 1); check(e) { - t.Error(e) - t.FailNow() - } - // set up forwarding port service - const port = 3455 - clients[3].Services = append(clients[3].Services, &service.Service{ - Port: port, - Transport: transport.NewSim(0), - RelayRate: 18000 * 4, - }) - // Start up the clients. - for _, v := range clients { - go v.Start() - } - var circuit traffic.Circuit - for i := range circuit { - circuit[i] = clients[0].GetSessionByIndex(i + 1) - } - var msg slice.Bytes - if msg, _, e = tests.GenMessage(32, "request"); check(e) { - t.Error(e) - t.FailNow() - } - var respMsg slice.Bytes - var respHash sha256.Hash - if respMsg, respHash, e = tests.GenMessage(32, "response"); check(e) { - t.Error(e) - t.FailNow() - } - quit := qu.T() - os := onion.SendExit(port, msg, clients[0].GetSessionByIndex(0), circuit, - clients[0].KeySet) - - hook := func(b slice.Bytes) { - log.T.S(b.ToBytes()) - if sha256.Single(b) != respHash { - t.Error("failed to receive expected message") - } - quit.Q() - } - clients[0].SendOnion(clients[0].Nodes[0].AddrPort, os, hook) - go func() { - select { - case <-time.After(time.Second): - t.Error("sendexit got stuck") - quit.Q() - case <-quit: - } - }() - bb := <-clients[3].Services[0].Receive() - log.T.S(bb.ToBytes()) - if e = clients[3].SendTo(port, respMsg); check(e) { - t.Error("fail send") - } - log.T.Ln("response sent") - <-quit.Wait() - for _, v := range clients { - v.Shutdown() - } -} - -func TestSendKeys(t *testing.T) { - log2.SetLogLevel(log2.Trace) - var clients []*Client - var e error - if clients, e = CreateNMockCircuits(false, 1); check(e) { - t.Error(e) - t.FailNow() - } - // Start up the clients. - for _, v := range clients { - go v.Start() - } - quit := qu.T() - go func() { - select { - case <-time.After(time.Second): - t.Error("sendkeys got stuck") - quit.Q() - case <-quit: - } - }() - cnf := nonce.NewID() - var sess [5]*session.Layer - var pmt [5]*payment.Payment - for i := range clients[1:] { - // Create a new payment and drop on the payment channel. - sess[i] = session.New(byte(i)) - pmt[i] = sess[i].ToPayment(1000000) - clients[i+1].Peer.PaymentChan <- pmt[i] - } - // Send the keys. - circuit := make(node.Nodes, 5) - for i := range circuit { - circuit[i] = clients[i+1].Node - } - sk := onion.SendKeys(cnf, sess, clients[0].GetSessionByIndex(0), - circuit, clients[0].KeySet) - clients[0].RegisterConfirmation(func(cf nonce.ID) { - log.T.S("received payment confirmation ID", cf) - if cf != cnf { - t.Errorf("did not receive expected confirmation, got:"+ - " %x expected: %x", cf, cnf) - t.FailNow() - } - quit.Q() - }, cnf) - b := onion.Encode(sk.Assemble()) - clients[0].Send(clients[0].Nodes[0].AddrPort, b) - <-quit.Wait() - for _, v := range clients { - v.Shutdown() - } -} - -func TestGetBalance(t *testing.T) { - log2.SetLogLevel(log2.Trace) - var clients []*Client - var e error - if clients, e = CreateNMockCircuits(true, 2); check(e) { - t.Error(e) - t.FailNow() - } - // Start up the clients. - for _, v := range clients { - go v.Start() - } - quit := qu.T() - var wg sync.WaitGroup - go func() { - select { - case <-time.After(time.Second): - case <-quit: - return - } - quit.Q() - t.Error("test failed") - }() - // Sessions are generated by the function above as in sequential order - // of session hop position such that it goes 0,0,1,1,2,2 and so on, - // repeating the hop position N times as the number parameter in - // CreateNMockCircuits(). So we iterate odd and even in this case and - // then multiply the iterator by the circuit hop position to generate - // the index for it. - var circuit traffic.Circuit - _ = circuit -out: - for odd := 0; odd < 2; odd++ { - for circ := 0; circ < 5; circ++ { - wg.Add(1) - // one is added to skip the return hop. - first := odd + 1 - // fill the hops until the target in the circuit - the - // remainder will not be accessed anyway, and last hop - // actually we can just put the last hop, but for - // testing this doesn't matter, just fill to the point. - for i := 0; i < circ+1; i++ { - circuit[circ] = clients[0]. - GetSessionByIndex(first) - first += 2 - } - // Now form the return hops from the last two of the - // complementary set. - firstR := 10 - odd - 2 - secondR := firstR + 2 - returns := [3]*traffic.Session{ - clients[0].GetSessionByIndex(firstR), - clients[0].GetSessionByIndex(secondR), - clients[0].GetSessionByIndex(0), - } - nn := nonce.NewID() - o := onion.GetBalance(circuit, circ, returns, clients[0].KeySet, - nn) - clients[0].SendOnion(circuit[0].AddrPort, o, nil) - clients[0].RegisterConfirmation(func(cf nonce.ID) { - log.I.Ln("success") - wg.Done() - }, nn) - select { - case <-quit: - break out - default: - } - wg.Wait() - } - quit.Q() - } - for _, v := range clients { - v.Shutdown() - } -} diff --git a/pkg/client/utils_test.go b/pkg/client/utils_test.go deleted file mode 100644 index d018e72f..00000000 --- a/pkg/client/utils_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package client - -import ( - "math" - - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/node" - "github.com/indra-labs/indra/pkg/traffic" - "github.com/indra-labs/indra/pkg/transport" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" -) - -func CreateNMockCircuits(inclSessions bool, - nCircuits int) (cl []*Client, e error) { - - nTotal := 1 + nCircuits*5 - cl = make([]*Client, nTotal) - nodes := make([]*node.Node, nTotal) - transports := make([]types.Transport, nTotal) - sessions := make(traffic.Sessions, nTotal-1) - for i := range transports { - transports[i] = transport.NewSim(nTotal) - } - for i := range nodes { - var idPrv *prv.Key - if idPrv, e = prv.GenerateKey(); check(e) { - return - } - idPub := pub.Derive(idPrv) - addr := slice.GenerateRandomAddrPortIPv4() - var local bool - if i == 0 { - local = true - } - nodes[i], _ = node.New(addr, idPub, idPrv, transports[i], 18000, local) - if cl[i], e = NewClient(transports[i], idPrv, nodes[i], - nil); check(e) { - return - } - cl[i].AddrPort = nodes[i].AddrPort - cl[i].Node = nodes[i] - if inclSessions { - // create a session for all but the first - if i > 0 { - sessions[i-1] = traffic.NewSession( - nonce.NewID(), nodes[i].Peer, - math.MaxUint64, nil, nil, - byte((i-1)/nCircuits)) - // Add session to node, so it will be able to - // relay if it gets a message with the key. - nodes[i].AddSession(sessions[i-1]) - // we need a copy for the node so the balance - // adjustments don't double up. - s := *sessions[i-1] - nodes[0].AddSession(&s) - } - } - } - for i := range cl { - for j := range nodes { - if i == j { - continue - } - cl[i].Nodes = append(cl[i].Nodes, nodes[j]) - } - } - return -} diff --git a/pkg/crypto/ciph/cipher.go b/pkg/crypto/ciph/cipher.go index e4e6c6c6..a739053e 100644 --- a/pkg/crypto/ciph/cipher.go +++ b/pkg/crypto/ciph/cipher.go @@ -8,13 +8,13 @@ import ( "crypto/aes" "crypto/cipher" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/key/ecdh" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - log2 "github.com/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/ecdh" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/crypto/key/cloak/cloaked.go b/pkg/crypto/key/cloak/cloaked.go index 53ee7ef7..323f2b1c 100644 --- a/pkg/crypto/key/cloak/cloaked.go +++ b/pkg/crypto/key/cloak/cloaked.go @@ -5,10 +5,10 @@ package cloak import ( "crypto/rand" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/sha256" - log2 "github.com/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/crypto/key/cloak/cloaked_test.go b/pkg/crypto/key/cloak/cloaked_test.go index 9ad96240..311fc12f 100644 --- a/pkg/crypto/key/cloak/cloaked_test.go +++ b/pkg/crypto/key/cloak/cloaked_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" ) func TestAddress(t *testing.T) { diff --git a/pkg/crypto/key/ecdh/ecdh.go b/pkg/crypto/key/ecdh/ecdh.go index 499eb4f3..285660a8 100644 --- a/pkg/crypto/key/ecdh/ecdh.go +++ b/pkg/crypto/key/ecdh/ecdh.go @@ -4,10 +4,10 @@ package ecdh import ( + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" "github.com/decred/dcrd/dcrec/secp256k1/v4" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/sha256" ) // Compute computes an Elliptic Curve Diffie-Hellman shared secret that can be diff --git a/pkg/crypto/key/prv/private.go b/pkg/crypto/key/prv/private.go index 5ff9d71f..82c5edcb 100644 --- a/pkg/crypto/key/prv/private.go +++ b/pkg/crypto/key/prv/private.go @@ -4,8 +4,9 @@ package prv import ( "github.com/decred/dcrd/dcrec/secp256k1/v4" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" + + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/crypto/key/pub/public.go b/pkg/crypto/key/pub/public.go index a07badd5..ae31a33b 100644 --- a/pkg/crypto/key/pub/public.go +++ b/pkg/crypto/key/pub/public.go @@ -7,9 +7,10 @@ import ( "encoding/hex" "github.com/decred/dcrd/dcrec/secp256k1/v4" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - log2 "github.com/indra-labs/indra/pkg/proc/log" + + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/crypto/key/sig/signature.go b/pkg/crypto/key/sig/signature.go index a3d6b000..0a75fff9 100644 --- a/pkg/crypto/key/sig/signature.go +++ b/pkg/crypto/key/sig/signature.go @@ -7,11 +7,12 @@ package sig import ( "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/sha256" - log2 "github.com/indra-labs/indra/pkg/proc/log" + + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/crypto/key/sig/signature_test.go b/pkg/crypto/key/sig/signature_test.go index da282c36..63e83de4 100644 --- a/pkg/crypto/key/sig/signature_test.go +++ b/pkg/crypto/key/sig/signature_test.go @@ -6,9 +6,9 @@ import ( mrand "math/rand" "testing" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" ) func TestSignRecover(t *testing.T) { diff --git a/pkg/crypto/key/signer/signer.go b/pkg/crypto/key/signer/signer.go index 8a6616a2..4116298f 100644 --- a/pkg/crypto/key/signer/signer.go +++ b/pkg/crypto/key/signer/signer.go @@ -6,9 +6,9 @@ package signer import ( "sync" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - log2 "github.com/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/crypto/key/signer/signer_test.go b/pkg/crypto/key/signer/signer_test.go index e110a6c4..9120821a 100644 --- a/pkg/crypto/key/signer/signer_test.go +++ b/pkg/crypto/key/signer/signer_test.go @@ -4,9 +4,9 @@ import ( "crypto/rand" "testing" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/key/sig" - "github.com/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/sig" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" ) // this just really demonstrates how the keys generated are not linkable. diff --git a/pkg/crypto/nonce/id.go b/pkg/crypto/nonce/id.go index a53e0dbd..a9b50fdc 100644 --- a/pkg/crypto/nonce/id.go +++ b/pkg/crypto/nonce/id.go @@ -3,17 +3,17 @@ package nonce import ( "crypto/rand" - "github.com/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" ) const IDLen = 8 // ID is a value generated by a hash chain that renews at first use and every -// time it generates 65536 new ID's. +// time it generates 2^32 new ID's. type ID [IDLen]byte var seed sha256.Hash -var counter uint16 +var counter uint32 func reseed() { if c, e := rand.Read(seed[:]); check(e) && c != IDLen { diff --git a/pkg/crypto/nonce/nonce.go b/pkg/crypto/nonce/nonce.go index 06cc804e..f5df6c13 100644 --- a/pkg/crypto/nonce/nonce.go +++ b/pkg/crypto/nonce/nonce.go @@ -7,8 +7,8 @@ import ( "crypto/aes" "crypto/rand" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/crypto/sha256/sha256.go b/pkg/crypto/sha256/sha256.go index fb5b5add..aeb132ce 100644 --- a/pkg/crypto/sha256/sha256.go +++ b/pkg/crypto/sha256/sha256.go @@ -4,9 +4,10 @@ package sha256 import ( - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" "github.com/minio/sha256-simd" + + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) const ( diff --git a/pkg/docker/builder.go b/pkg/docker/builder.go index 3e67db9a..dff429a8 100644 --- a/pkg/docker/builder.go +++ b/pkg/docker/builder.go @@ -15,9 +15,10 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/jsonmessage" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" "github.com/moby/term" + + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/interrupt/interrupt.go b/pkg/interrupt/interrupt.go index 6f8b4372..e8accb39 100644 --- a/pkg/interrupt/interrupt.go +++ b/pkg/interrupt/interrupt.go @@ -9,10 +9,11 @@ import ( "strings" "syscall" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" "go.uber.org/atomic" + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "github.com/kardianos/osext" ) diff --git a/pkg/node/node.go b/pkg/node/node.go deleted file mode 100644 index 15e6c966..00000000 --- a/pkg/node/node.go +++ /dev/null @@ -1,94 +0,0 @@ -// Package node maintains information about peers on the network and associated -// connection sessions. -package node - -import ( - "fmt" - "net/netip" - "time" - - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/nonce" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/ring" - "github.com/indra-labs/indra/pkg/traffic" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/lnd/lnd/lnwire" -) - -var ( - log = log2.GetLogger(indra.PathBase) - check = log.E.Chk -) - -// Node is a representation of a messaging counterparty. The netip.AddrPort can -// be nil for the case of a client node that is not in a direct open connection. -// For this reason all nodes are assigned an ID and will normally be handled by -// this except when the netip.AddrPort is known via the packet sender address. -type Node struct { - nonce.ID - *traffic.Peer - PingCount int - LastSeen time.Time -} - -const DefaultSampleBufferSize = 64 - -// New creates a new Node. A netip.AddrPort is optional if the counterparty is -// not in direct connection. Also, the idPrv node private key can be nil, as -// only the node embedded in a client and not the peer node list has one -// available. The Node for a client's self should use true in the local -// parameter to not initialise the peer state ring buffers as it won't use them. -func New(addr *netip.AddrPort, idPub *pub.Key, idPrv *prv.Key, - tpt types.Transport, relayRate lnwire.MilliSatoshi, - local bool) (n *Node, id nonce.ID) { - - id = nonce.NewID() - n = &Node{ - ID: id, - Peer: &traffic.Peer{ - ID: id, - AddrPort: addr, - IdentityPub: idPub, - IdentityBytes: idPub.ToBytes(), - IdentityPrv: idPrv, - RelayRate: relayRate, - Transport: tpt, - Payments: traffic.NewPayments(), - }, - } - if !local { - n.Load = ring.NewBufferLoad(DefaultSampleBufferSize) - n.Latency = ring.NewBufferLatency(DefaultSampleBufferSize) - n.Failure = ring.NewBufferFailure(DefaultSampleBufferSize) - } - return -} - -// SendTo delivers a message to a service identified by its port. -func (n *Node) SendTo(port uint16, b slice.Bytes) (e error) { - e = fmt.Errorf("port not registered %d", port) - for i := range n.Services { - if n.Services[i].Port == port { - n.Services[i].Send(b) - e = nil - return - } - } - return -} - -// ReceiveFrom returns the channel that receives messages for a given port. -func (n *Node) ReceiveFrom(port uint16) (b <-chan slice.Bytes) { - for i := range n.Services { - if n.Services[i].Port == port { - log.T.Ln("receive from") - b = n.Services[i].Receive() - return - } - } - return -} diff --git a/pkg/node/nodes.go b/pkg/node/nodes.go deleted file mode 100644 index a104b1d2..00000000 --- a/pkg/node/nodes.go +++ /dev/null @@ -1,65 +0,0 @@ -package node - -import ( - "fmt" - "net/netip" - - "github.com/indra-labs/indra/pkg/crypto/nonce" -) - -type Nodes []*Node - -// NewNodes creates an empty Nodes -func NewNodes() (n Nodes) { return Nodes{} } - -// Len returns the length of a Nodes. -func (n Nodes) Len() int { return len(n) } - -// Add a Node to a Nodes. -func (n Nodes) Add(nn *Node) Nodes { return append(n, nn) } - -// FindByID searches for a Node by ID. -func (n Nodes) FindByID(i nonce.ID) (no *Node) { - for _, nn := range n { - if nn.ID == i { - no = nn - break - } - } - return -} - -// FindByAddrPort searches for a Node by netip.AddrPort. -func (n Nodes) FindByAddrPort(id *netip.AddrPort) (no *Node) { - for _, nn := range n { - if nn.AddrPort.String() == id.String() { - no = nn - break - } - } - return -} - -// DeleteByID deletes a node identified by an ID. -func (n Nodes) DeleteByID(ii nonce.ID) (nn Nodes, e error) { - e, nn = fmt.Errorf("id %x not found", ii), n - for i := range n { - if n[i].ID == ii { - return append(n[:i], n[i+1:]...), nil - } - } - return -} - -// DeleteByAddrPort deletes a node identified by a netip.AddrPort. -func (n Nodes) DeleteByAddrPort(ip *netip.AddrPort) (nn Nodes, e error) { - e, nn = fmt.Errorf("node with ip %v not found", ip), n - for i := range n { - if n[i].AddrPort.String() == ip.String() { - nn = append(n[:i], n[i+1:]...) - e = nil - break - } - } - return -} diff --git a/pkg/node/nodes_test.go b/pkg/node/nodes_test.go deleted file mode 100644 index 4b0b5fd2..00000000 --- a/pkg/node/nodes_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package node - -import ( - "fmt" - "net/netip" - "testing" - - "github.com/indra-labs/indra/pkg/transport" - "github.com/indra-labs/indra/pkg/util/tests" -) - -var testAddrPort, _ = netip.ParseAddrPort("1.1.1.1:20000") - -func TestNodes_Add(t *testing.T) { - n := NewNodes() - pubKey, prvKey, e := tests.GenerateTestKeyPair() - if check(e) { - t.Error(e) - t.FailNow() - } - const nNodes = 10000 - for i := 0; i < nNodes; i++ { - var nn *Node - nn, _ = New(&testAddrPort, prvKey, pubKey, transport.NewSim(0), 0, false) - n = n.Add(nn) - } - if n.Len() != nNodes { - t.Error("did not create expected number of nodes") - } -} - -func TestNodes_DeleteByID(t *testing.T) { - n := NewNodes() - const nNodes = 10000 - pubKey, prvKey, e := tests.GenerateTestKeyPair() - if check(e) { - t.Error(e) - t.FailNow() - } - for i := 0; i < nNodes; i++ { - var nn *Node - nn, _ = New(&testAddrPort, prvKey, pubKey, transport.NewSim(0), 0, false) - n.Add(nn) - } - for i := range n { - if n, e = n.DeleteByID(n[nNodes-i-1].ID); check(e) { - t.Error(e) - } - } -} - -func TestNodes_DeleteByAddrPort(t *testing.T) { - n := NewNodes() - const nNodes = 10000 - pubKey, prvKey, e := tests.GenerateTestKeyPair() - if check(e) { - t.Error(e) - t.FailNow() - } - for i := 0; i < nNodes; i++ { - var nn *Node - nn, _ = New(&testAddrPort, prvKey, pubKey, transport.NewSim(0), 0, false) - n.Add(nn) - } - for i := range n { - if n, e = n.DeleteByAddrPort(n[nNodes-i-1].AddrPort); check(e) { - t.Error(e) - } - } -} - -func TestNodes_FindByID(t *testing.T) { - n := NewNodes() - const nNodes = 10000 - pubKey, prvKey, e := tests.GenerateTestKeyPair() - if check(e) { - t.Error(e) - t.FailNow() - } - for i := 0; i < nNodes; i++ { - var nn *Node - nn, _ = New(&testAddrPort, prvKey, pubKey, transport.NewSim(0), 0, false) - n.Add(nn) - } - for i := range n { - if n.FindByID(n[nNodes-i-1].ID) == nil { - t.Error(fmt.Errorf("id %v not found", - n[nNodes-i-1].ID)) - } - } -} - -func TestNodes_FindByAddrPort(t *testing.T) { - n := NewNodes() - const nNodes = 10000 - pubKey, prvKey, e := tests.GenerateTestKeyPair() - if check(e) { - t.Error(e) - t.FailNow() - } - for i := 0; i < nNodes; i++ { - var nn *Node - nn, _ = New(&testAddrPort, prvKey, pubKey, transport.NewSim(0), 0, false) - n.Add(nn) - } - for i := range n { - if n.FindByAddrPort(n[nNodes-i-1].AddrPort) == nil { - t.Error(fmt.Errorf("id %v not found", - n[nNodes-i-1].AddrPort)) - } - } -} diff --git a/pkg/onion/codec.go b/pkg/onion/codec.go index 6ecf204b..0137ac2e 100644 --- a/pkg/onion/codec.go +++ b/pkg/onion/codec.go @@ -4,27 +4,28 @@ import ( "fmt" "github.com/davecgh/go-spew/spew" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/key/ecdh" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/onion/layers/balance" - "github.com/indra-labs/indra/pkg/onion/layers/confirm" - "github.com/indra-labs/indra/pkg/onion/layers/crypt" - "github.com/indra-labs/indra/pkg/onion/layers/delay" - "github.com/indra-labs/indra/pkg/onion/layers/directbalance" - "github.com/indra-labs/indra/pkg/onion/layers/exit" - "github.com/indra-labs/indra/pkg/onion/layers/forward" - "github.com/indra-labs/indra/pkg/onion/layers/getbalance" - "github.com/indra-labs/indra/pkg/onion/layers/magicbytes" - "github.com/indra-labs/indra/pkg/onion/layers/response" - "github.com/indra-labs/indra/pkg/onion/layers/reverse" - "github.com/indra-labs/indra/pkg/onion/layers/session" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" + + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/ecdh" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/balance" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/confirm" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/crypt" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/delay" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/directbalance" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/exit" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/forward" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/getbalance" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/magicbytes" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/response" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/reverse" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/session" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) var ( diff --git a/pkg/onion/codec_test.go b/pkg/onion/codec_test.go index c8835ec9..0331893d 100644 --- a/pkg/onion/codec_test.go +++ b/pkg/onion/codec_test.go @@ -8,21 +8,21 @@ import ( "testing" "time" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/onion/layers/confirm" - "github.com/indra-labs/indra/pkg/onion/layers/crypt" - "github.com/indra-labs/indra/pkg/onion/layers/delay" - "github.com/indra-labs/indra/pkg/onion/layers/exit" - "github.com/indra-labs/indra/pkg/onion/layers/forward" - "github.com/indra-labs/indra/pkg/onion/layers/response" - "github.com/indra-labs/indra/pkg/onion/layers/reverse" - "github.com/indra-labs/indra/pkg/onion/layers/session" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/indra/pkg/util/tests" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/confirm" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/crypt" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/delay" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/exit" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/forward" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/response" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/reverse" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/session" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra/pkg/util/tests" ) func TestOnionSkins_Cipher(t *testing.T) { @@ -117,8 +117,9 @@ func TestOnionSkins_Exit(t *testing.T) { } n3 := Gen3Nonces() p := uint16(rand.Uint32()) + id := nonce.NewID() on := Skins{}. - Exit(p, prvs, pubs, n3, msg). + Exit(p, prvs, pubs, n3, id, msg). Assemble() onb := Encode(on) c := slice.NewCursor() @@ -148,6 +149,11 @@ func TestOnionSkins_Exit(t *testing.T) { t.FailNow() } } + if ex.ID != id { + t.Errorf("exit message ID did not unwrap correctly, got %x expected %x", + ex.ID, id) + t.FailNow() + } plH := sha256.Single(ex.Bytes) if plH != hash { t.Errorf("exit message did not unwrap correctly") @@ -286,13 +292,14 @@ func TestOnionSkins_Response(t *testing.T) { var e error var msg slice.Bytes + var id nonce.ID var hash sha256.Hash if msg, hash, e = tests.GenMessage(10000, ""); check(e) { t.Error(e) t.FailNow() } on := Skins{}. - Response(hash, msg). + Response(id, msg, 0). Assemble() onb := Encode(on) c := slice.NewCursor() diff --git a/pkg/onion/layers/balance/balance.go b/pkg/onion/layers/balance/balance.go index bfa9fdfc..6b3cfbd1 100644 --- a/pkg/onion/layers/balance/balance.go +++ b/pkg/onion/layers/balance/balance.go @@ -1,13 +1,14 @@ package balance import ( - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/onion/layers/magicbytes" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/lnd/lnd/lnwire" + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/magicbytes" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) const ( diff --git a/pkg/onion/layers/confirm/confirmation.go b/pkg/onion/layers/confirm/confirmation.go index c8269796..0071807e 100644 --- a/pkg/onion/layers/confirm/confirmation.go +++ b/pkg/onion/layers/confirm/confirmation.go @@ -3,12 +3,12 @@ package confirm import ( "fmt" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/onion/layers/magicbytes" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/magicbytes" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) const ( diff --git a/pkg/onion/layers/confirm/confirms.go b/pkg/onion/layers/confirm/confirms.go deleted file mode 100644 index 55c7d674..00000000 --- a/pkg/onion/layers/confirm/confirms.go +++ /dev/null @@ -1,72 +0,0 @@ -package confirm - -import ( - "sync" - "time" - - "github.com/indra-labs/indra/pkg/crypto/nonce" -) - -type Hook func(cf nonce.ID) - -type Callback struct { - nonce.ID - time.Time - Onion *Layer - Hook -} - -type Confirms struct { - sync.Mutex - Cnf []Callback -} - -func NewConfirms() *Confirms { return &Confirms{Cnf: make([]Callback, 0)} } - -func (cn *Confirms) Add(cb *Callback) { - cn.Lock() - (*cn).Cnf = append((*cn).Cnf, *cb) - cn.Unlock() -} - -func (cn *Confirms) Confirm(id nonce.ID) bool { - cn.Lock() - defer cn.Unlock() - for i := range (*cn).Cnf { - if id == (*cn).Cnf[i].ID { - (*cn).Cnf[i].Hook(id) - // delete Callback. - end := i + 1 - // if this is the last one, the end is the last one - // also. - if end > len((*cn).Cnf)-1 { - end = len((*cn).Cnf) - 1 - } - (*cn).Cnf = append((*cn).Cnf[:i], (*cn).Cnf[end:]...) - return true - } - } - return false -} - -// Flush clears out entries older than timePast. -func (cn *Confirms) Flush(timePast time.Time) { - cn.Lock() - defer cn.Unlock() - var foundCount int - for i := range (*cn).Cnf { - if (*cn).Cnf[i].Time.Before(timePast) { - foundCount++ - } - } - if foundCount > 0 { - cnNew := NewConfirms() - for i := range (*cn).Cnf { - if !(*cn).Cnf[i].Time.Before(timePast) { - (*cnNew).Cnf = append((*cnNew).Cnf, - (*cn).Cnf[i]) - } - } - (*cn).Cnf = (*cnNew).Cnf - } -} diff --git a/pkg/onion/layers/crypt/crypt.go b/pkg/onion/layers/crypt/crypt.go index 0b7e8d0d..b5e5a068 100644 --- a/pkg/onion/layers/crypt/crypt.go +++ b/pkg/onion/layers/crypt/crypt.go @@ -4,17 +4,17 @@ import ( "crypto/cipher" "fmt" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/ciph" - "github.com/indra-labs/indra/pkg/crypto/key/cloak" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/onion/layers/magicbytes" - "github.com/indra-labs/indra/pkg/onion/layers/reverse" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/ciph" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/cloak" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/magicbytes" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/reverse" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) const ( diff --git a/pkg/onion/layers/delay/delay.go b/pkg/onion/layers/delay/delay.go index 172db926..f5ef3da8 100644 --- a/pkg/onion/layers/delay/delay.go +++ b/pkg/onion/layers/delay/delay.go @@ -3,11 +3,11 @@ package delay import ( "time" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/onion/layers/magicbytes" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/magicbytes" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) const ( diff --git a/pkg/onion/layers/directbalance/directbalance.go b/pkg/onion/layers/directbalance/directbalance.go index f2f33020..a0d28033 100644 --- a/pkg/onion/layers/directbalance/directbalance.go +++ b/pkg/onion/layers/directbalance/directbalance.go @@ -1,12 +1,12 @@ package directbalance import ( - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/onion/layers/magicbytes" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/magicbytes" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) const ( diff --git a/pkg/onion/layers/exit/exit.go b/pkg/onion/layers/exit/exit.go index 06cdc037..4302c579 100644 --- a/pkg/onion/layers/exit/exit.go +++ b/pkg/onion/layers/exit/exit.go @@ -2,19 +2,20 @@ package exit import ( "github.com/davecgh/go-spew/spew" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/onion/layers/magicbytes" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" + + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/magicbytes" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) const ( MagicString = "ex" Len = magicbytes.Len + slice.Uint16Len + 3*sha256.Len + - slice.Uint32Len + nonce.IVLen*3 + slice.Uint32Len + nonce.IVLen*3 + nonce.IDLen ) var ( @@ -41,6 +42,7 @@ type Layer struct { // Nonces are the nonces to use with the cipher when creating the // encryption for the reply message. Nonces [3]nonce.IV + nonce.ID // Bytes are the message to be passed to the exit service. slice.Bytes types.Onion @@ -67,6 +69,7 @@ func (x *Layer) Encode(b slice.Bytes, c *slice.Cursor) { copy(b[*c:c.Inc(nonce.IVLen)], x.Nonces[0][:]) copy(b[*c:c.Inc(nonce.IVLen)], x.Nonces[1][:]) copy(b[*c:c.Inc(nonce.IVLen)], x.Nonces[2][:]) + copy(b[*c:c.Inc(nonce.IDLen)], x.ID[:]) bytesLen := slice.NewUint32() slice.EncodeUint32(bytesLen, len(x.Bytes)) copy(b[*c:c.Inc(slice.Uint32Len)], bytesLen) @@ -89,6 +92,7 @@ func (x *Layer) Decode(b slice.Bytes, c *slice.Cursor) (e error) { copy(x.Nonces[i][:], bytes) bytes.Zero() } + copy(x.ID[:], b[*c:c.Inc(nonce.IDLen)]) bytesLen := slice.DecodeUint32(b[*c:c.Inc(slice.Uint32Len)]) x.Bytes = b[*c:c.Inc(bytesLen)] return diff --git a/pkg/onion/layers/forward/forward.go b/pkg/onion/layers/forward/forward.go index 065ad4d5..85870656 100644 --- a/pkg/onion/layers/forward/forward.go +++ b/pkg/onion/layers/forward/forward.go @@ -5,11 +5,11 @@ import ( "net" "net/netip" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/onion/layers/magicbytes" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/magicbytes" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) const ( diff --git a/pkg/onion/layers/getbalance/getbalance.go b/pkg/onion/layers/getbalance/getbalance.go index 2734c995..074eb44d 100644 --- a/pkg/onion/layers/getbalance/getbalance.go +++ b/pkg/onion/layers/getbalance/getbalance.go @@ -2,13 +2,14 @@ package getbalance import ( "github.com/davecgh/go-spew/spew" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/onion/layers/magicbytes" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" + + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/magicbytes" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) const ( diff --git a/pkg/onion/layers/magicbytes/magic.go b/pkg/onion/layers/magicbytes/magic.go index 436346a2..deabd3ad 100644 --- a/pkg/onion/layers/magicbytes/magic.go +++ b/pkg/onion/layers/magicbytes/magic.go @@ -3,8 +3,8 @@ package magicbytes import ( "fmt" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/onion/layers/noop/noop.go b/pkg/onion/layers/noop/noop.go index 90f8b30b..dff9cefb 100644 --- a/pkg/onion/layers/noop/noop.go +++ b/pkg/onion/layers/noop/noop.go @@ -1,8 +1,8 @@ package noop import ( - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) type Layer struct { diff --git a/pkg/onion/layers/response/response.go b/pkg/onion/layers/response/response.go index 8b26221b..95685dd7 100644 --- a/pkg/onion/layers/response/response.go +++ b/pkg/onion/layers/response/response.go @@ -1,17 +1,18 @@ package response import ( - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/onion/layers/magicbytes" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/magicbytes" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) const ( MagicString = "rs" - Len = magicbytes.Len + slice.Uint32Len + sha256.Len + 1 + Len = magicbytes.Len + slice.Uint32Len + slice.Uint16Len + + nonce.IDLen + 1 ) var ( @@ -23,7 +24,8 @@ var ( // Layer messages are what are carried back via Reverse messages from an Exit. type Layer struct { - sha256.Hash + nonce.ID + Port uint16 Load byte slice.Bytes } @@ -38,7 +40,10 @@ func (x *Layer) Insert(_ types.Onion) {} func (x *Layer) Len() int { return Len + len(x.Bytes) } func (x *Layer) Encode(b slice.Bytes, c *slice.Cursor) { copy(b[*c:c.Inc(magicbytes.Len)], Magic) - copy(b[*c:c.Inc(sha256.Len)], x.Hash[:]) + copy(b[*c:c.Inc(nonce.IDLen)], x.ID[:]) + port := slice.NewUint16() + slice.EncodeUint16(port, int(x.Port)) + copy(b[*c:c.Inc(slice.Uint16Len)], port) b[*c] = x.Load c.Inc(1) bytesLen := slice.NewUint32() @@ -51,7 +56,9 @@ func (x *Layer) Decode(b slice.Bytes, c *slice.Cursor) (e error) { return magicbytes.TooShort(len(b[*c:]), Len-magicbytes.Len, string(Magic)) } - copy(x.Hash[:], b[*c:c.Inc(sha256.Len)]) + copy(x.ID[:], b[*c:c.Inc(nonce.IDLen)]) + port := slice.DecodeUint16(b[*c:c.Inc(slice.Uint16Len)]) + x.Port = uint16(port) x.Load = b[*c] c.Inc(1) responseLen := slice.DecodeUint32(b[*c:c.Inc(slice.Uint32Len)]) diff --git a/pkg/onion/layers/reverse/reverse.go b/pkg/onion/layers/reverse/reverse.go index 0ff3f6d0..3105d3c7 100644 --- a/pkg/onion/layers/reverse/reverse.go +++ b/pkg/onion/layers/reverse/reverse.go @@ -4,11 +4,11 @@ import ( "net" "net/netip" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/onion/layers/magicbytes" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/magicbytes" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) const ( diff --git a/pkg/onion/layers/session/session.go b/pkg/onion/layers/session/session.go index ebe81a49..f1378b52 100644 --- a/pkg/onion/layers/session/session.go +++ b/pkg/onion/layers/session/session.go @@ -1,17 +1,18 @@ package session import ( - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/onion/layers/magicbytes" - "github.com/indra-labs/indra/pkg/onion/layers/noop" - "github.com/indra-labs/indra/pkg/payment" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/lnd/lnd/lnwire" + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/magicbytes" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/noop" + "git-indra.lan/indra-labs/indra/pkg/payment" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) const ( diff --git a/pkg/onion/onion.go b/pkg/onion/onion.go index 637dbaf6..9f7b9d60 100644 --- a/pkg/onion/onion.go +++ b/pkg/onion/onion.go @@ -1,14 +1,13 @@ package onion import ( - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/key/signer" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/node" - "github.com/indra-labs/indra/pkg/onion/layers/session" - "github.com/indra-labs/indra/pkg/traffic" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/signer" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/session" + "git-indra.lan/indra-labs/indra/pkg/traffic" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) // Ping is a message which checks the liveness of relays by ensuring they are @@ -50,7 +49,7 @@ func Ping(id nonce.ID, client *traffic.Session, s traffic.Circuit, // The header remains a constant size and each node in the Reverse trims off // their section at the top, moves the next crypt header to the top and pads the // remainder with noise, so it always looks like the first hop. -func SendExit(port uint16, payload slice.Bytes, +func SendExit(port uint16, payload slice.Bytes, id nonce.ID, client *traffic.Session, s traffic.Circuit, ks *signer.KeySet) Skins { var prvs [3]*prv.Key @@ -68,7 +67,7 @@ func SendExit(port uint16, payload slice.Bytes, ReverseCrypt(s[0], ks.Next(), n[0], 3). ReverseCrypt(s[1], ks.Next(), n[1], 2). ReverseCrypt(s[2], ks.Next(), n[2], 1). - Exit(port, prvs, pubs, returnNonces, payload). + Exit(port, prvs, pubs, returnNonces, id, payload). ReverseCrypt(s[3], prvs[0], n[3], 0). ReverseCrypt(s[4], prvs[1], n[4], 0). ReverseCrypt(client, prvs[2], n[5], 0) @@ -97,7 +96,7 @@ func SendExit(port uint16, payload slice.Bytes, // set of sessions. This is by way of indicating to not use the IdentityPub but // the HeaderPub instead. Not allowing free relay at all prevents spam attacks. func SendKeys(id nonce.ID, s [5]*session.Layer, - client *traffic.Session, hop node.Nodes, ks *signer.KeySet) Skins { + client *traffic.Session, hop []*traffic.Node, ks *signer.KeySet) Skins { n := GenNonces(6) sk := Skins{} @@ -128,7 +127,6 @@ func GetBalance(s traffic.Circuit, target int, returns [3]*traffic.Session, o = o.ForwardCrypt(s[target], ks.Next(), n[0]). DirectBalance(s[target].ID, id). Forward(returns[2].AddrPort) - log.T.S(o) return } n := GenNonces(target + 1 + 3) diff --git a/pkg/onion/skins.go b/pkg/onion/skins.go index 849a3c0b..0a3cac08 100644 --- a/pkg/onion/skins.go +++ b/pkg/onion/skins.go @@ -4,27 +4,26 @@ import ( "net/netip" "time" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/node" - "github.com/indra-labs/indra/pkg/onion/layers/balance" - "github.com/indra-labs/indra/pkg/onion/layers/confirm" - "github.com/indra-labs/indra/pkg/onion/layers/crypt" - "github.com/indra-labs/indra/pkg/onion/layers/delay" - "github.com/indra-labs/indra/pkg/onion/layers/directbalance" - "github.com/indra-labs/indra/pkg/onion/layers/exit" - "github.com/indra-labs/indra/pkg/onion/layers/forward" - "github.com/indra-labs/indra/pkg/onion/layers/getbalance" - "github.com/indra-labs/indra/pkg/onion/layers/noop" - "github.com/indra-labs/indra/pkg/onion/layers/response" - "github.com/indra-labs/indra/pkg/onion/layers/reverse" - "github.com/indra-labs/indra/pkg/onion/layers/session" - "github.com/indra-labs/indra/pkg/traffic" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/lnd/lnd/lnwire" + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/balance" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/confirm" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/crypt" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/delay" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/directbalance" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/exit" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/forward" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/getbalance" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/noop" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/response" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/reverse" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/session" + "git-indra.lan/indra-labs/indra/pkg/traffic" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) type Skins []types.Onion @@ -34,19 +33,19 @@ var os = &noop.Layer{} func (o Skins) ForwardCrypt(s *traffic.Session, k *prv.Key, n nonce.IV) Skins { - return o.Forward(s.Peer.AddrPort).Crypt(s.HeaderPub, s.PayloadPub, k, n, 0) + return o.Forward(s.AddrPort).Crypt(s.HeaderPub, s.PayloadPub, k, n, 0) } func (o Skins) ReverseCrypt(s *traffic.Session, k *prv.Key, n nonce.IV, seq int) Skins { - return o.Reverse(s.Peer.AddrPort).Crypt(s.HeaderPub, s.PayloadPub, k, n, seq) + return o.Reverse(s.AddrPort).Crypt(s.HeaderPub, s.PayloadPub, k, n, seq) } -func (o Skins) ForwardSession(s *node.Node, +func (o Skins) ForwardSession(s *traffic.Node, k *prv.Key, n nonce.IV, sess *session.Layer) Skins { - return o.Forward(s.Peer.AddrPort). - Crypt(s.Peer.IdentityPub, nil, k, n, 0). + return o.Forward(s.AddrPort). + Crypt(s.IdentityPub, nil, k, n, 0). Session(sess) } @@ -73,12 +72,13 @@ func (o Skins) DirectBalance(id, confID nonce.ID) Skins { } func (o Skins) Exit(port uint16, prvs [3]*prv.Key, pubs [3]*pub.Key, - nonces [3]nonce.IV, payload slice.Bytes) Skins { + nonces [3]nonce.IV, id nonce.ID, payload slice.Bytes) Skins { return append(o, &exit.Layer{ Port: port, Ciphers: GenCiphers(prvs, pubs), Nonces: nonces, + ID: id, Bytes: payload, Onion: os, }) @@ -120,8 +120,8 @@ func (o Skins) Reverse(ip *netip.AddrPort) Skins { return append(o, &reverse.Layer{AddrPort: ip, Onion: os}) } -func (o Skins) Response(hash sha256.Hash, res slice.Bytes) Skins { - rs := response.Layer{Hash: hash, Bytes: res} +func (o Skins) Response(id nonce.ID, res slice.Bytes, port uint16) Skins { + rs := response.Layer{ID: id, Port: port, Bytes: res} return append(o, &rs) } diff --git a/pkg/p2p/introducer/bootstrap.go b/pkg/p2p/introducer/bootstrap.go index 006e36da..62b147fe 100644 --- a/pkg/p2p/introducer/bootstrap.go +++ b/pkg/p2p/introducer/bootstrap.go @@ -5,13 +5,14 @@ import ( "errors" "sync" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" dht "github.com/libp2p/go-libp2p-kad-dht" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/protocol" "github.com/multiformats/go-multiaddr" + + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/packet/packet.go b/pkg/packet/packet.go index 5ed426e9..5446eb70 100644 --- a/pkg/packet/packet.go +++ b/pkg/packet/packet.go @@ -9,14 +9,14 @@ import ( "fmt" "time" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/ciph" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/ciph" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) var ( diff --git a/pkg/packet/packet_test.go b/pkg/packet/packet_test.go index efefd3d8..6184dda6 100644 --- a/pkg/packet/packet_test.go +++ b/pkg/packet/packet_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/util/tests" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/util/tests" ) func TestEncode_Decode(t *testing.T) { diff --git a/pkg/packet/segment.go b/pkg/packet/segment.go index 1d4cdace..e4490b67 100644 --- a/pkg/packet/segment.go +++ b/pkg/packet/segment.go @@ -5,8 +5,8 @@ import ( "fmt" "sort" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/util/slice" "github.com/templexxx/reedsolomon" ) diff --git a/pkg/packet/segment_test.go b/pkg/packet/segment_test.go index 83e4716e..c55f7ebb 100644 --- a/pkg/packet/segment_test.go +++ b/pkg/packet/segment_test.go @@ -5,10 +5,10 @@ import ( "math/rand" "testing" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/util/tests" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/util/tests" ) func TestSplitJoin(t *testing.T) { @@ -104,7 +104,7 @@ func BenchmarkSplit(b *testing.B) { // // goos: linux // goarch: amd64 - // pkg: github.com/indra-labs/indra/pkg/packet + // pkg: git-indra.lan/indra-labs/indra/pkg/packet // cpu: AMD Ryzen 7 5800H with Radeon Graphics // BenchmarkSplit // BenchmarkSplit-16 157 7670080 ns/op diff --git a/pkg/payment/payment.go b/pkg/payment/payment.go index 88cface5..8d616b24 100644 --- a/pkg/payment/payment.go +++ b/pkg/payment/payment.go @@ -1,9 +1,10 @@ package payment import ( - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/lnd/lnd/lnwire" + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" ) type Payment struct { diff --git a/pkg/proc/app/app.go b/pkg/proc/app/app.go index 61089f05..c813d66e 100644 --- a/pkg/proc/app/app.go +++ b/pkg/proc/app/app.go @@ -1,9 +1,9 @@ package app import ( - "github.com/indra-labs/indra" - cmds2 "github.com/indra-labs/indra/pkg/proc/cmds" - log2 "github.com/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra" + cmds2 "git-indra.lan/indra-labs/indra/pkg/proc/cmds" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/proc/app/app_test.go b/pkg/proc/app/app_test.go index 74f073ca..c2192b48 100644 --- a/pkg/proc/app/app_test.go +++ b/pkg/proc/app/app_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - "github.com/indra-labs/indra/pkg/proc/cmds" - log2 "github.com/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/proc/cmds" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) func TestNew(t *testing.T) { diff --git a/pkg/proc/cmds/args.go b/pkg/proc/cmds/args.go index c07a9bc1..dede22c6 100644 --- a/pkg/proc/cmds/args.go +++ b/pkg/proc/cmds/args.go @@ -4,10 +4,10 @@ import ( "fmt" "strings" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/proc/opts/meta" - "github.com/indra-labs/indra/pkg/util/norm" + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/meta" + "git-indra.lan/indra-labs/indra/pkg/util/norm" ) var ( diff --git a/pkg/proc/cmds/args_test.go b/pkg/proc/cmds/args_test.go index ca445ce7..5b6c356a 100644 --- a/pkg/proc/cmds/args_test.go +++ b/pkg/proc/cmds/args_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - log2 "github.com/indra-labs/indra/pkg/proc/log" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) func TestCommand_ParseCLIArgs(t *testing.T) { diff --git a/pkg/proc/cmds/commands.go b/pkg/proc/cmds/commands.go index e67bd475..a67b9a84 100644 --- a/pkg/proc/cmds/commands.go +++ b/pkg/proc/cmds/commands.go @@ -7,13 +7,13 @@ import ( "strings" "sync" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/proc/opts/config" - "github.com/indra-labs/indra/pkg/proc/opts/meta" - "github.com/indra-labs/indra/pkg/proc/opts/text" - "github.com/indra-labs/indra/pkg/proc/opts/toggle" - "github.com/indra-labs/indra/pkg/util/norm" - "github.com/indra-labs/indra/pkg/util/path/path" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/config" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/meta" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/text" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/toggle" + "git-indra.lan/indra-labs/indra/pkg/util/norm" + "git-indra.lan/indra-labs/indra/pkg/util/path/path" ) type Op func(c *Command, args []string) error diff --git a/pkg/proc/cmds/commands_test.go b/pkg/proc/cmds/commands_test.go index 40ee11ee..d3b1d055 100644 --- a/pkg/proc/cmds/commands_test.go +++ b/pkg/proc/cmds/commands_test.go @@ -6,8 +6,8 @@ import ( "strings" "testing" - "github.com/indra-labs/indra/pkg/proc/opts/config" - "github.com/indra-labs/indra/pkg/util/path/path" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/config" + "git-indra.lan/indra-labs/indra/pkg/util/path/path" ) func TestCommand_Foreach(t *testing.T) { diff --git a/pkg/proc/cmds/env.go b/pkg/proc/cmds/env.go index a6c47435..8c1c6953 100644 --- a/pkg/proc/cmds/env.go +++ b/pkg/proc/cmds/env.go @@ -5,8 +5,8 @@ import ( "sort" "strings" - "github.com/indra-labs/indra/pkg/proc/opts/config" - "github.com/indra-labs/indra/pkg/util/path/path" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/config" + "git-indra.lan/indra-labs/indra/pkg/util/path/path" ) type Env struct { diff --git a/pkg/proc/cmds/example.go b/pkg/proc/cmds/example.go index 4c92c6a1..bcd8b925 100644 --- a/pkg/proc/cmds/example.go +++ b/pkg/proc/cmds/example.go @@ -6,14 +6,14 @@ import ( "fmt" "runtime" - "github.com/indra-labs/indra/pkg/proc/opts/config" - "github.com/indra-labs/indra/pkg/proc/opts/duration" - "github.com/indra-labs/indra/pkg/proc/opts/float" - "github.com/indra-labs/indra/pkg/proc/opts/integer" - "github.com/indra-labs/indra/pkg/proc/opts/list" - "github.com/indra-labs/indra/pkg/proc/opts/meta" - "github.com/indra-labs/indra/pkg/proc/opts/text" - "github.com/indra-labs/indra/pkg/proc/opts/toggle" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/config" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/duration" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/float" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/integer" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/list" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/meta" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/text" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/toggle" ) const lorem = ` diff --git a/pkg/proc/cmds/help.go b/pkg/proc/cmds/help.go index 0c743e5a..75e37a1c 100644 --- a/pkg/proc/cmds/help.go +++ b/pkg/proc/cmds/help.go @@ -9,8 +9,8 @@ import ( "sync" "text/tabwriter" - "github.com/indra-labs/indra/pkg/proc/opts/config" - "github.com/indra-labs/indra/pkg/util/norm" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/config" + "git-indra.lan/indra-labs/indra/pkg/util/norm" ) // Help is a default top level command that is injected into a Command at the diff --git a/pkg/proc/cmds/marshal.go b/pkg/proc/cmds/marshal.go index da77add5..c658c3e8 100644 --- a/pkg/proc/cmds/marshal.go +++ b/pkg/proc/cmds/marshal.go @@ -9,14 +9,14 @@ import ( "strings" "time" - "github.com/indra-labs/indra/pkg/proc/opts/duration" - "github.com/indra-labs/indra/pkg/proc/opts/float" - "github.com/indra-labs/indra/pkg/proc/opts/integer" - "github.com/indra-labs/indra/pkg/proc/opts/list" - "github.com/indra-labs/indra/pkg/proc/opts/meta" - "github.com/indra-labs/indra/pkg/proc/opts/text" - "github.com/indra-labs/indra/pkg/proc/opts/toggle" - path2 "github.com/indra-labs/indra/pkg/util/path/path" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/duration" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/float" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/integer" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/list" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/meta" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/text" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/toggle" + path2 "git-indra.lan/indra-labs/indra/pkg/util/path/path" "github.com/naoina/toml" ) diff --git a/pkg/proc/log/log.go b/pkg/proc/log/log.go index 64017593..133899f0 100644 --- a/pkg/proc/log/log.go +++ b/pkg/proc/log/log.go @@ -13,7 +13,8 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/gookit/color" - "github.com/indra-labs/indra" + + "git-indra.lan/indra-labs/indra" ) // log is your generic Logger creation invocation that uses the version data diff --git a/pkg/proc/opts/config/interface.go b/pkg/proc/opts/config/interface.go index a8b004d3..ebd7c0bf 100644 --- a/pkg/proc/opts/config/interface.go +++ b/pkg/proc/opts/config/interface.go @@ -3,8 +3,8 @@ package config import ( "time" - "github.com/indra-labs/indra/pkg/proc/opts/meta" - "github.com/indra-labs/indra/pkg/util/path/path" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/meta" + "git-indra.lan/indra-labs/indra/pkg/util/path/path" ) // Concrete is a struct of functions that return the concrete values. Only the diff --git a/pkg/proc/opts/duration/spec.go b/pkg/proc/opts/duration/spec.go index 4c8103f4..1dd9e652 100644 --- a/pkg/proc/opts/duration/spec.go +++ b/pkg/proc/opts/duration/spec.go @@ -5,12 +5,13 @@ import ( "strings" "time" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/proc/opts/config" - "github.com/indra-labs/indra/pkg/proc/opts/meta" - "github.com/indra-labs/indra/pkg/util/path/path" "go.uber.org/atomic" + + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/config" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/meta" + "git-indra.lan/indra-labs/indra/pkg/util/path/path" ) var ( diff --git a/pkg/proc/opts/float/spec.go b/pkg/proc/opts/float/spec.go index 894bc224..51e75828 100644 --- a/pkg/proc/opts/float/spec.go +++ b/pkg/proc/opts/float/spec.go @@ -4,12 +4,13 @@ import ( "strconv" "strings" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/proc/opts/config" - "github.com/indra-labs/indra/pkg/proc/opts/meta" - "github.com/indra-labs/indra/pkg/util/path/path" "go.uber.org/atomic" + + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/config" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/meta" + "git-indra.lan/indra-labs/indra/pkg/util/path/path" ) var ( diff --git a/pkg/proc/opts/integer/spec.go b/pkg/proc/opts/integer/spec.go index 214320b2..42bc4a34 100644 --- a/pkg/proc/opts/integer/spec.go +++ b/pkg/proc/opts/integer/spec.go @@ -4,12 +4,13 @@ import ( "strconv" "strings" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/proc/opts/config" - "github.com/indra-labs/indra/pkg/proc/opts/meta" - "github.com/indra-labs/indra/pkg/util/path/path" "go.uber.org/atomic" + + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/config" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/meta" + "git-indra.lan/indra-labs/indra/pkg/util/path/path" ) var ( diff --git a/pkg/proc/opts/list/spec.go b/pkg/proc/opts/list/spec.go index 42f97d69..74ff1c26 100644 --- a/pkg/proc/opts/list/spec.go +++ b/pkg/proc/opts/list/spec.go @@ -3,13 +3,14 @@ package list import ( "strings" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/proc/opts/config" - "github.com/indra-labs/indra/pkg/proc/opts/meta" - "github.com/indra-labs/indra/pkg/proc/opts/normalize" - "github.com/indra-labs/indra/pkg/util/path/path" "go.uber.org/atomic" + + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/config" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/meta" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/normalize" + "git-indra.lan/indra-labs/indra/pkg/util/path/path" ) var ( diff --git a/pkg/proc/opts/normalize/addresses.go b/pkg/proc/opts/normalize/addresses.go index fccd787b..b78af33c 100644 --- a/pkg/proc/opts/normalize/addresses.go +++ b/pkg/proc/opts/normalize/addresses.go @@ -4,8 +4,8 @@ import ( "net" "strconv" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/proc/opts/normalize/paths.go b/pkg/proc/opts/normalize/paths.go index 2b7c8d70..1772dd7f 100644 --- a/pkg/proc/opts/normalize/paths.go +++ b/pkg/proc/opts/normalize/paths.go @@ -6,7 +6,7 @@ import ( "path/filepath" "strings" - "github.com/indra-labs/indra/pkg/util/appdata" + "git-indra.lan/indra-labs/indra/pkg/util/appdata" ) func ResolvePath(input, appName string, abs bool) (cleaned string, e error) { diff --git a/pkg/proc/opts/text/spec.go b/pkg/proc/opts/text/spec.go index 571023ab..25b50975 100644 --- a/pkg/proc/opts/text/spec.go +++ b/pkg/proc/opts/text/spec.go @@ -3,13 +3,14 @@ package text import ( "strings" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/proc/opts/config" - "github.com/indra-labs/indra/pkg/proc/opts/meta" - "github.com/indra-labs/indra/pkg/proc/opts/normalize" - "github.com/indra-labs/indra/pkg/util/path/path" "go.uber.org/atomic" + + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/config" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/meta" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/normalize" + "git-indra.lan/indra-labs/indra/pkg/util/path/path" ) var ( diff --git a/pkg/proc/opts/toggle/spec.go b/pkg/proc/opts/toggle/spec.go index bd62d9b7..e5b4a9be 100644 --- a/pkg/proc/opts/toggle/spec.go +++ b/pkg/proc/opts/toggle/spec.go @@ -5,12 +5,13 @@ import ( "strconv" "strings" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/proc/opts/config" - "github.com/indra-labs/indra/pkg/proc/opts/meta" - "github.com/indra-labs/indra/pkg/util/path/path" "go.uber.org/atomic" + + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/config" + "git-indra.lan/indra-labs/indra/pkg/proc/opts/meta" + "git-indra.lan/indra-labs/indra/pkg/util/path/path" ) var ( diff --git a/pkg/client/accounting.go b/pkg/relay/accounting.go similarity index 52% rename from pkg/client/accounting.go rename to pkg/relay/accounting.go index 3fb75e74..ce1d637a 100644 --- a/pkg/client/accounting.go +++ b/pkg/relay/accounting.go @@ -1,45 +1,52 @@ -package client +package relay import ( "sync" "time" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) -type Hook func(cf sha256.Hash) +type Callback func(id nonce.ID, b slice.Bytes) type PendingResponse struct { - sha256.Hash + nonce.ID + SentSize int Port uint16 Billable, Accounted []nonce.ID Return nonce.ID - Callback func(b slice.Bytes) + Callback time.Time + Timeout time.Duration } type PendingResponses struct { sync.Mutex - responses []*PendingResponse - oldestPending *PendingResponse + responses []*PendingResponse } func (p *PendingResponses) GetOldestPending() (pr *PendingResponse) { p.Lock() defer p.Unlock() - return p.oldestPending + if len(p.responses) > 0 { + // Pending responses are added in chronological order to the end so the + // first one in the slice is the oldest. + return p.responses[0] + } + return } -func (p *PendingResponses) Add(h sha256.Hash, billable, accounted []nonce.ID, - ret nonce.ID, port uint16, callback func(b slice.Bytes)) { +func (p *PendingResponses) Add(id nonce.ID, sentSize int, billable, + accounted []nonce.ID, ret nonce.ID, port uint16, + callback func(id nonce.ID, b slice.Bytes)) { p.Lock() defer p.Unlock() - log.T.F("adding response hook %x", h) + log.T.F("adding response hook %x", id) p.responses = append(p.responses, &PendingResponse{ - Hash: h, + ID: id, + SentSize: sentSize, Time: time.Now(), Billable: billable, Accounted: accounted, @@ -60,41 +67,32 @@ func (p *PendingResponses) FindOlder(t time.Time) (r []*PendingResponse) { return } -func (p *PendingResponses) Find(h sha256.Hash) (pr *PendingResponse) { +func (p *PendingResponses) Find(id nonce.ID) (pr *PendingResponse) { p.Lock() defer p.Unlock() for i := range p.responses { - if p.responses[i].Hash == h { - + if p.responses[i].ID == id { return p.responses[i] } } return } -func (p *PendingResponses) Delete(h sha256.Hash) { +func (p *PendingResponses) Delete(id nonce.ID, b slice.Bytes) { p.Lock() defer p.Unlock() + log.T.F("deleting response %x", id) for i := range p.responses { - if p.responses[i].Hash == h { + if p.responses[i].ID == id { + p.responses[i].Callback(id, b) if i < 1 { p.responses = p.responses[1:] } else { p.responses = append(p.responses[:i], p.responses[i+1:]...) } + break } - } - // Update the oldest pending response entry. - if len(p.responses) > 0 { - oldest := time.Now() - for i := range p.responses { - if p.responses[i].Time.Before(oldest) { - oldest = p.responses[i].Time - p.oldestPending = p.responses[i] - } - } - } else { - p.oldestPending = nil + // Add handler to trigger after timeout. } } diff --git a/pkg/relay/engine.go b/pkg/relay/engine.go new file mode 100644 index 00000000..1debad00 --- /dev/null +++ b/pkg/relay/engine.go @@ -0,0 +1,83 @@ +package relay + +import ( + "sync" + + "github.com/cybriq/qu" + "go.uber.org/atomic" + + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/signer" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/traffic" + "git-indra.lan/indra-labs/indra/pkg/types" +) + +var ( + log = log2.GetLogger(indra.PathBase) + check = log.E.Chk +) + +type Engine struct { + sync.Mutex + *PendingResponses + *traffic.SessionManager + *signer.KeySet + Load byte + ShuttingDown atomic.Bool + qu.C +} + +func NewEngine(tpt types.Transport, hdrPrv *prv.Key, no *traffic.Node, + nodes []*traffic.Node) (c *Engine, e error) { + + no.Transport = tpt + no.IdentityPrv = hdrPrv + no.IdentityPub = pub.Derive(hdrPrv) + var ks *signer.KeySet + if _, ks, e = signer.New(); check(e) { + return + } + c = &Engine{ + PendingResponses: &PendingResponses{}, + KeySet: ks, + SessionManager: traffic.NewSessionManager(), + C: qu.T(), + } + c.AddNodes(append([]*traffic.Node{no}, nodes...)...) + // Add a return session for receiving responses, ideally more of these will + // be generated during operation and rotated out over time. + c.AddSession(traffic.NewSession(nonce.NewID(), no, 0, nil, nil, 5)) + return +} + +// Start a single thread of the Engine. +func (eng *Engine) Start() { + for { + if eng.handler() { + break + } + } +} + +// Cleanup closes and flushes any resources the client opened that require sync +// in order to reopen correctly. +func (eng *Engine) Cleanup() { + // Do cleanup stuff before shutdown. +} + +// Shutdown triggers the shutdown of the client and the Cleanup before +// finishing. +func (eng *Engine) Shutdown() { + if eng.ShuttingDown.Load() { + return + } + log.T.C(func() string { + return "shutting down client " + eng.GetLocalNodeAddress().String() + }) + eng.ShuttingDown.Store(true) + eng.C.Q() +} diff --git a/pkg/client/client_test.go b/pkg/relay/engine_test.go similarity index 70% rename from pkg/client/client_test.go rename to pkg/relay/engine_test.go index d653d200..adca9b82 100644 --- a/pkg/client/client_test.go +++ b/pkg/relay/engine_test.go @@ -1,4 +1,4 @@ -package client +package relay import ( "os" @@ -7,20 +7,24 @@ import ( "time" "github.com/cybriq/qu" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/service" - "github.com/indra-labs/indra/pkg/traffic" - "github.com/indra-labs/indra/pkg/transport" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/indra/pkg/util/tests" "go.uber.org/atomic" + + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/service" + "git-indra.lan/indra-labs/indra/pkg/traffic" + "git-indra.lan/indra-labs/indra/pkg/transport" + "git-indra.lan/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra/pkg/util/tests" ) -func TestClient_SendKeys(t *testing.T) { +// todo: add accounting validation to these tests where relevant +// (check relay and client see the same balance after the operations) + +func TestClient_SendSessionKeys(t *testing.T) { log2.SetLogLevel(log2.Trace) - var clients []*Client + var clients []*Engine var e error if clients, e = CreateNMockCircuits(false, 2); check(e) { t.Error(e) @@ -42,22 +46,7 @@ func TestClient_SendKeys(t *testing.T) { t.Error("SendExit test failed") os.Exit(1) }() - cl := clients[0] - sb := make([]*SessionBuy, len(cl.Nodes)) - for i := range cl.Nodes { - sb[i] = BuySession(cl.Nodes[i], 1000000, byte(i/2)) - counter.Inc() - wg.Add(1) - } - sess, pmt := cl.BuySessions(sb...) - time.Sleep(time.Second / 4) - log.T.Ln("sending out sessions") - cl.SendKeys(sb, sess, pmt, func(hops []*traffic.Session) { - for _ = range hops { - counter.Dec() - wg.Done() - } - }) + wg.Wait() for _, v := range clients { v.Shutdown() @@ -66,7 +55,7 @@ func TestClient_SendKeys(t *testing.T) { func TestClient_SendPing(t *testing.T) { log2.SetLogLevel(log2.Debug) - var clients []*Client + var clients []*Engine var e error if clients, e = CreateNMockCircuits(true, 2); check(e) { t.Error(e) @@ -94,7 +83,7 @@ out: sess := clients[0].Sessions[i] c[sess.Hop] = clients[0].Sessions[i] clients[0].SendPing(c, - func(b nonce.ID) { + func(id nonce.ID, b slice.Bytes) { log.I.Ln("success") wg.Done() }) @@ -113,7 +102,7 @@ out: func TestClient_SendExit(t *testing.T) { log2.SetLogLevel(log2.Debug) - var clients []*Client + var clients []*Engine var e error if clients, e = CreateNMockCircuits(true, 2); check(e) { t.Error(e) @@ -126,7 +115,7 @@ func TestClient_SendExit(t *testing.T) { if i == 0 { continue } - clients[i].Services = append(clients[i].Services, &service.Service{ + _ = clients[i].AddServiceToLocalNode(&service.Service{ Port: port, Transport: sim, RelayRate: 18000 * 4, @@ -152,7 +141,7 @@ out: wg.Add(1) var c traffic.Circuit var msg slice.Bytes - if msg, _, e = tests.GenMessage(32, "request"); check(e) { + if msg, _, e = tests.GenMessage(64, "request"); check(e) { t.Error(e) t.FailNow() } @@ -164,17 +153,21 @@ out: } sess := clients[0].Sessions[i] c[sess.Hop] = clients[0].Sessions[i] - clients[0].SendExit(port, msg, clients[0].Sessions[i], - func(b slice.Bytes) { + id := nonce.NewID() + clients[0].SendExit(port, msg, id, clients[0].Sessions[i], + func(idd nonce.ID, b slice.Bytes) { if sha256.Single(b) != respHash { t.Error("failed to receive expected message") } - log.I.Ln("success") + if id != idd { + t.Error("failed to receive expected message ID") + } + log.I.F("success\n\n") wg.Done() }) - bb := <-clients[3].Services[0].Receive() + bb := <-clients[3].ReceiveToLocalNode(port) log.T.S(bb.ToBytes()) - if e = clients[3].SendTo(port, respMsg); check(e) { + if e = clients[3].SendFromLocalNode(port, respMsg); check(e) { t.Error("fail send") } log.T.Ln("response sent") @@ -193,7 +186,7 @@ out: func TestClient_SendGetBalance(t *testing.T) { log2.SetLogLevel(log2.Debug) - var clients []*Client + var clients []*Engine var e error if clients, e = CreateNMockCircuits(true, 2); check(e) { t.Error(e) @@ -217,10 +210,11 @@ func TestClient_SendGetBalance(t *testing.T) { out: for i := 1; i < len(clients[0].Sessions)-1; i++ { wg.Add(1) - clients[0].SendGetBalance(clients[0].Sessions[i], func(cf nonce.ID) { - log.I.Ln("success") - wg.Done() - }) + clients[0].SendGetBalance(clients[0].Sessions[i], + func(cf nonce.ID, b slice.Bytes) { + log.I.F("success\n\n") + wg.Done() + }) select { case <-quit: break out diff --git a/pkg/relay/handler-balance.go b/pkg/relay/handler-balance.go new file mode 100644 index 00000000..c7a12b3e --- /dev/null +++ b/pkg/relay/handler-balance.go @@ -0,0 +1,42 @@ +package relay + +import ( + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra/pkg/onion/layers/balance" + "git-indra.lan/indra-labs/indra/pkg/traffic" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +func (eng *Engine) balance(on *balance.Layer, + b slice.Bytes, c *slice.Cursor, prev types.Onion) { + + eng.IterateSessions(func(s *traffic.Session) bool { + if s.ID == on.ID { + log.D.F("received balance %x for session %x", + on.MilliSatoshi, on.ID) + s.Remaining = on.MilliSatoshi + return true + } + return false + }) + pending := eng.PendingResponses.Find(on.ID) + if pending != nil { + for i := range pending.Billable { + s := eng.FindSession(pending.Billable[i]) + if s != nil { + if i == 0 { + eng.DecSession(s.ID, + s.RelayRate*lnwire.MilliSatoshi(len(b)/2)/1024/1024, + true, "balance") + } else { + eng.DecSession(s.ID, + s.RelayRate*lnwire.MilliSatoshi(len(b))/1024/1024, + true, "balance") + } + } + } + eng.PendingResponses.Delete(pending.ID, nil) + } +} diff --git a/pkg/relay/handler-confirm.go b/pkg/relay/handler-confirm.go new file mode 100644 index 00000000..96d9d3fb --- /dev/null +++ b/pkg/relay/handler-confirm.go @@ -0,0 +1,16 @@ +package relay + +import ( + "git-indra.lan/indra-labs/indra/pkg/onion/layers/confirm" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +func (eng *Engine) confirm(on *confirm.Layer, + b slice.Bytes, c *slice.Cursor, prev types.Onion) { + + log.T.F("processing confirmation %x", on.ID) + // When a confirmation arrives check if it is registered for and run the + // hook that was registered with it. + eng.PendingResponses.Delete(on.ID, nil) +} diff --git a/pkg/relay/handler-crypt.go b/pkg/relay/handler-crypt.go new file mode 100644 index 00000000..3e5f5bc6 --- /dev/null +++ b/pkg/relay/handler-crypt.go @@ -0,0 +1,85 @@ +package relay + +import ( + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/onion" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/balance" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/crypt" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/directbalance" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/forward" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/magicbytes" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/session" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +func (eng *Engine) crypt(on *crypt.Layer, b slice.Bytes, + c *slice.Cursor, prev types.Onion) { + + // this is probably an encrypted crypt for us. + hdr, _, sess, identity := eng.FindCloaked(on.Cloak) + if hdr == nil { + log.T.Ln("no matching key found from cloaked key") + return + } + on.ToPriv = hdr + on.Decrypt(hdr, b, c) + if identity { + log.T.F("identity") + if string(b[*c:][:magicbytes.Len]) != session.MagicString { + log.T.Ln("dropping message due to identity key with" + + " no following session") + return + } + + eng.handleMessage(BudgeUp(b, *c), on) + return + } + if string(b[*c:][:magicbytes.Len]) == directbalance.MagicString { + var on1, on2 types.Onion + var e error + if on1, e = onion.Peel(b, c); check(e) { + return + } + var balID, confID nonce.ID + switch db := on1.(type) { + case *directbalance.Layer: + balID = db.ID + confID = db.ConfID + default: + log.T.Ln("malformed/truncated onion") + return + } + if on2, e = onion.Peel(b, c); check(e) { + return + } + switch fwd := on2.(type) { + case *forward.Layer: + o := (&onion.Skins{}). + Forward(fwd.AddrPort). + Crypt(pub.Derive(hdr), nil, eng.KeySet.Next(), nonce.New(), 0). + Balance(balID, confID, sess.Remaining) + oo := o.Assemble() + // This is a little more complicated as we need to decrement the + // amount before sending out the balance. + eng.DecSession(sess.ID, + (eng.GetLocalNodeRelayRate()*lnwire.MilliSatoshi(len(b)+oo. + Len())/2)/1024/1024, + false, "directbalance") + o[2].(*balance.Layer).MilliSatoshi = sess.Remaining + rb := onion.Encode(oo) + eng.Send(fwd.AddrPort, rb) + // eng.SendOnion(fwd.AddrPort, o) + return + default: + log.T.Ln("dropping directbalance without following " + + "forward") + return + } + return + } + eng.handleMessage(BudgeUp(b, *c), on) +} diff --git a/pkg/client/handler-delay.go b/pkg/relay/handler-delay.go similarity index 58% rename from pkg/client/handler-delay.go rename to pkg/relay/handler-delay.go index 21aca036..1510d8d3 100644 --- a/pkg/client/handler-delay.go +++ b/pkg/relay/handler-delay.go @@ -1,14 +1,14 @@ -package client +package relay import ( "time" - "github.com/indra-labs/indra/pkg/onion/layers/delay" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/delay" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) -func (cl *Client) delay(on *delay.Layer, b slice.Bytes, +func (eng *Engine) delay(on *delay.Layer, b slice.Bytes, c *slice.Cursor, prev types.Onion) { // this is a message to hold the message in the buffer until a duration @@ -19,5 +19,5 @@ func (cl *Client) delay(on *delay.Layer, b slice.Bytes, select { case <-time.After(on.Duration): } - cl.handleMessage(BudgeUp(b, *c), on) + eng.handleMessage(BudgeUp(b, *c), on) } diff --git a/pkg/relay/handler-exit.go b/pkg/relay/handler-exit.go new file mode 100644 index 00000000..cc48d7eb --- /dev/null +++ b/pkg/relay/handler-exit.go @@ -0,0 +1,65 @@ +package relay + +import ( + "time" + + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/onion" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/crypt" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/exit" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/response" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +func (eng *Engine) exit(ex *exit.Layer, b slice.Bytes, + c *slice.Cursor, prev types.Onion) { + + // payload is forwarded to a local port and the result is forwarded + // back with a reverse header. + var e error + var result slice.Bytes + h := sha256.Single(ex.Bytes) + log.T.S(h) + log.T.F("received exit id %x", ex.ID) + if e = eng.SendFromLocalNode(ex.Port, ex.Bytes); check(e) { + return + } + timer := time.NewTicker(time.Second * 5) // todo: timeout/retries etc + select { + case result = <-eng.ReceiveToLocalNode(ex.Port): + case <-timer.C: + } + // We need to wrap the result in a message crypt. + eng.Lock() + res := onion.Encode(&response.Layer{ + ID: ex.ID, + Port: ex.Port, + Load: eng.Load, + Bytes: result, + }) + eng.Unlock() + rb := FormatReply(b[*c:c.Inc(crypt.ReverseHeaderLen)], + res, ex.Ciphers, ex.Nonces) + switch on := prev.(type) { + case *crypt.Layer: + sess := eng.FindSessionByHeader(on.ToPriv) + if sess == nil { + break + } + for i := range sess.Services { + if ex.Port != sess.Services[i].Port { + continue + } + in := sess.Services[i].RelayRate * + lnwire.MilliSatoshi(len(b)) / 2 / 1024 / 1024 + out := sess.Services[i].RelayRate * + lnwire.MilliSatoshi(len(rb)) / 2 / 1024 / 1024 + eng.DecSession(sess.ID, in+out, false, "exit") + break + } + } + eng.handleMessage(rb, ex) +} diff --git a/pkg/relay/handler-forward.go b/pkg/relay/handler-forward.go new file mode 100644 index 00000000..3ad188a1 --- /dev/null +++ b/pkg/relay/handler-forward.go @@ -0,0 +1,33 @@ +package relay + +import ( + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra/pkg/onion/layers/crypt" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/forward" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +func (eng *Engine) forward(on *forward.Layer, b slice.Bytes, + c *slice.Cursor, prev types.Onion) { + + // forward the whole buffer received onwards. Usually there will be a + // crypt.Layer under this which will be unwrapped by the receiver. + if on.AddrPort.String() == eng.GetLocalNodeAddress().String() { + // it is for us, we want to unwrap the next part. + eng.handleMessage(BudgeUp(b, *c), on) + } else { + switch on1 := prev.(type) { + case *crypt.Layer: + sess := eng.FindSessionByHeader(on1.ToPriv) + if sess != nil { + eng.DecSession(sess.ID, + eng.GetLocalNodeRelayRate()*lnwire.MilliSatoshi(len(b))/1024/1024, + false, "forward") + } + } + // we need to forward this message onion. + eng.Send(on.AddrPort, b) + } +} diff --git a/pkg/client/handler-getbalance.go b/pkg/relay/handler-getbalance.go similarity index 53% rename from pkg/client/handler-getbalance.go rename to pkg/relay/handler-getbalance.go index adf8c8cd..a75a0848 100644 --- a/pkg/client/handler-getbalance.go +++ b/pkg/relay/handler-getbalance.go @@ -1,25 +1,26 @@ -package client +package relay import ( "fmt" - "github.com/indra-labs/indra/pkg/onion" - "github.com/indra-labs/indra/pkg/onion/layers/balance" - "github.com/indra-labs/indra/pkg/onion/layers/crypt" - "github.com/indra-labs/indra/pkg/onion/layers/getbalance" - "github.com/indra-labs/indra/pkg/traffic" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/lnd/lnd/lnwire" + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra/pkg/onion" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/balance" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/crypt" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/getbalance" + "git-indra.lan/indra-labs/indra/pkg/traffic" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) -func (cl *Client) getBalance(on *getbalance.Layer, +func (eng *Engine) getBalance(on *getbalance.Layer, b slice.Bytes, c *slice.Cursor, prev types.Onion) { log.T.S(on) var found bool var bal *balance.Layer - cl.IterateSessions(func(s *traffic.Session) bool { + eng.IterateSessions(func(s *traffic.Session) bool { if s.ID == on.ID { bal = &balance.Layer{ ID: on.ID, @@ -40,15 +41,14 @@ func (cl *Client) getBalance(on *getbalance.Layer, rb = append(rb, slice.NoisePad(714-len(rb))...) switch on1 := prev.(type) { case *crypt.Layer: - sess := cl.FindSessionByHeader(on1.ToPriv) + sess := eng.FindSessionByHeader(on1.ToPriv) if sess != nil { - log.D.Ln("getbalance reply") in := sess.RelayRate * lnwire.MilliSatoshi(len(b)) / 2 / 1024 / 1024 out := sess.RelayRate * lnwire.MilliSatoshi(len(rb)) / 2 / 1024 / 1024 - cl.DecSession(sess.ID, in+out) + eng.DecSession(sess.ID, in+out, false, "getbalance") } } - cl.handleMessage(rb, on) + eng.handleMessage(rb, on) } diff --git a/pkg/relay/handler-response.go b/pkg/relay/handler-response.go new file mode 100644 index 00000000..a76f955f --- /dev/null +++ b/pkg/relay/handler-response.go @@ -0,0 +1,41 @@ +package relay + +import ( + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra/pkg/onion/layers/response" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +// response is a payload from an exit message. +func (eng *Engine) response(on *response.Layer, b slice.Bytes, + cur *slice.Cursor, prev types.Onion) { + + pending := eng.PendingResponses.Find(on.ID) + log.T.F("searching for pending ID %x", on.ID) + if pending != nil { + for i := range pending.Billable { + s := eng.FindSession(pending.Billable[i]) + if s != nil { + typ := "response" + relayRate := s.RelayRate + dataSize := len(b) + switch i { + case 0, 1: + dataSize = pending.SentSize + case 2: + for j := range s.Services { + if s.Services[j].Port == on.Port { + relayRate = s.Services[j].RelayRate / 2 + typ = "exit" + } + } + } + eng.DecSession(s.ID, relayRate*lnwire. + MilliSatoshi(dataSize)/1024/1024, true, typ) + } + } + eng.PendingResponses.Delete(on.ID, on.Bytes) + } +} diff --git a/pkg/client/handler-reverse.go b/pkg/relay/handler-reverse.go similarity index 53% rename from pkg/client/handler-reverse.go rename to pkg/relay/handler-reverse.go index 57eaace8..8927df12 100644 --- a/pkg/client/handler-reverse.go +++ b/pkg/relay/handler-reverse.go @@ -1,21 +1,22 @@ -package client +package relay import ( - "github.com/indra-labs/indra/pkg/crypto/ciph" - "github.com/indra-labs/indra/pkg/onion" - "github.com/indra-labs/indra/pkg/onion/layers/crypt" - "github.com/indra-labs/indra/pkg/onion/layers/reverse" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/indra/pkg/util/slice" - "github.com/indra-labs/lnd/lnd/lnwire" + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra/pkg/crypto/ciph" + "git-indra.lan/indra-labs/indra/pkg/onion" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/crypt" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/reverse" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) -func (cl *Client) reverse(on *reverse.Layer, b slice.Bytes, +func (eng *Engine) reverse(on *reverse.Layer, b slice.Bytes, c *slice.Cursor, prev types.Onion) { var e error var on2 types.Onion - if on.AddrPort.String() == cl.Node.AddrPort.String() { + if on.AddrPort.String() == eng.GetLocalNodeAddress().String() { if on2, e = onion.Peel(b, c); check(e) { return } @@ -26,20 +27,19 @@ func (cl *Client) reverse(on *reverse.Layer, b slice.Bytes, second := first + crypt.ReverseLayerLen last := second + crypt.ReverseLayerLen log.T.Ln("searching for reverse crypt keys") - hdr, pld, _, _ := cl.FindCloaked(on1.Cloak) + hdr, pld, _, _ := eng.FindCloaked(on1.Cloak) if hdr == nil || pld == nil { log.E.F("failed to find key for %s", - cl.Node.AddrPort.String()) + eng.GetLocalNodeAddress().String()) return } // We need to find the PayloadPub to match. - hdrPrv := hdr - hdrPub := on1.FromPub - blk := ciph.GetBlock(hdrPrv, hdrPub) + on1.ToPriv = hdr + blk := ciph.GetBlock(on1.ToPriv, on1.FromPub) // Decrypt using the Payload key and header nonce. ciph.Encipher(blk, on1.Nonce, b[*c:c.Inc(2*crypt.ReverseLayerLen)]) - blk = ciph.GetBlock(pld, hdrPub) + blk = ciph.GetBlock(pld, on1.FromPub) ciph.Encipher(blk, on1.Nonce, b[*c:]) // shift the header segment upwards and pad the // remainder. @@ -48,17 +48,17 @@ func (cl *Client) reverse(on *reverse.Layer, b slice.Bytes, copy(b[second:last], slice.NoisePad(crypt.ReverseLayerLen)) if b[start:start+2].String() != reverse.MagicString { // It's for us! - log.D.Ln("handling response") - cl.handleMessage(BudgeUp(b, last), on) + log.T.Ln("handling response") + eng.handleMessage(BudgeUp(b, last), on1) break } - sess := cl.FindSessionByHeader(hdr) + sess := eng.FindSessionByHeader(hdr) if sess != nil { - log.D.Ln(on.AddrPort.String(), "reverse receive") - cl.DecSession(sess.ID, - cl.RelayRate*lnwire.MilliSatoshi(len(b))/1024/1024) + eng.DecSession(sess.ID, + eng.GetLocalNodeRelayRate()*lnwire. + MilliSatoshi(len(b))/1024/1024, false, "reverse") + eng.handleMessage(BudgeUp(b, start), on1) } - cl.handleMessage(BudgeUp(b, start), on) default: // If a reverse is not followed by an onion crypt the // message is incorrectly formed, just drop it. @@ -66,8 +66,8 @@ func (cl *Client) reverse(on *reverse.Layer, b slice.Bytes, } } else { // we need to forward this message onion. - log.D.Ln("forwarding reverse") - cl.Send(on.AddrPort, b) + log.T.Ln("forwarding reverse") + eng.Send(on.AddrPort, b) } } diff --git a/pkg/relay/handler-session.go b/pkg/relay/handler-session.go new file mode 100644 index 00000000..92441f05 --- /dev/null +++ b/pkg/relay/handler-session.go @@ -0,0 +1,34 @@ +package relay + +import ( + "fmt" + + "github.com/davecgh/go-spew/spew" + + "git-indra.lan/indra-labs/indra/pkg/onion/layers/session" + "git-indra.lan/indra-labs/indra/pkg/traffic" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +func (eng *Engine) session(on *session.Layer, b slice.Bytes, + c *slice.Cursor, prev types.Onion) { + + log.T.C(func() string { + return fmt.Sprint("incoming session", + spew.Sdump(on.PreimageHash())) + }) + pi := eng.FindPendingPreimage(on.PreimageHash()) + if pi != nil { + // We need to delete this first in case somehow two such + // messages arrive at the same time, and we end up with + // duplicate sessions. + eng.DeletePendingPayment(pi.Preimage) + log.T.F("Adding session %x\n", pi.ID) + eng.AddSession(traffic.NewSession(pi.ID, + eng.GetLocalNode(), pi.Amount, on.Header, on.Payload, on.Hop)) + eng.handleMessage(BudgeUp(b, *c), on) + } else { + log.T.Ln("dropping session message without payment") + } +} diff --git a/pkg/relay/handler.go b/pkg/relay/handler.go new file mode 100644 index 00000000..4c44dfa0 --- /dev/null +++ b/pkg/relay/handler.go @@ -0,0 +1,115 @@ +package relay + +import ( + "fmt" + "reflect" + + "github.com/davecgh/go-spew/spew" + + "git-indra.lan/indra-labs/indra/pkg/onion" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/balance" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/confirm" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/crypt" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/delay" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/exit" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/forward" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/getbalance" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/response" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/reverse" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/session" + "git-indra.lan/indra-labs/indra/pkg/traffic" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +func (eng *Engine) handler() (out bool) { + log.T.C(func() string { + return eng.GetLocalNodeAddress().String() + + " awaiting message" + }) + var prev types.Onion + select { + case <-eng.C.Wait(): + eng.Cleanup() + out = true + break + case b := <-eng.ReceiveToLocalNode(0): + eng.handleMessage(b, prev) + case p := <-eng.PaymentChan: + log.D.F("incoming payment for %x: %v", p.ID, p.Amount) + topUp := false + eng.IterateSessions(func(s *traffic.Session) bool { + log.D.F("session preimage %x payment preimage %x", s.Preimage, + p.Preimage) + if s.Preimage == p.Preimage { + s.IncSats(p.Amount, false, "top-up") + topUp = true + log.T.F("topping up %x with %d mSat", + s.ID, p.Amount) + return true + } + return false + }) + if !topUp { + eng.AddPendingPayment(p) + log.T.F("awaiting session keys for preimage %x", + p.Preimage) + } + } + return +} + +func (eng *Engine) handleMessage(b slice.Bytes, prev types.Onion) { + log.T.Ln("process received message") + var on types.Onion + var e error + c := slice.NewCursor() + if on, e = onion.Peel(b, c); check(e) { + return + } + switch on := on.(type) { + case *balance.Layer: + log.T.C(recLog(on, b, eng)) + eng.balance(on, b, c, prev) + case *confirm.Layer: + log.T.C(recLog(on, b, eng)) + eng.confirm(on, b, c, prev) + case *crypt.Layer: + log.T.C(recLog(on, b, eng)) + eng.crypt(on, b, c, prev) + case *delay.Layer: + log.T.C(recLog(on, b, eng)) + eng.delay(on, b, c, prev) + case *exit.Layer: + log.T.C(recLog(on, b, eng)) + eng.exit(on, b, c, prev) + case *forward.Layer: + log.T.C(recLog(on, b, eng)) + eng.forward(on, b, c, prev) + case *getbalance.Layer: + log.T.C(recLog(on, b, eng)) + eng.getBalance(on, b, c, prev) + case *reverse.Layer: + log.T.C(recLog(on, b, eng)) + eng.reverse(on, b, c, prev) + case *response.Layer: + log.T.C(recLog(on, b, eng)) + eng.response(on, b, c, prev) + case *session.Layer: + log.T.C(recLog(on, b, eng)) + eng.session(on, b, c, prev) + default: + log.I.S("unrecognised packet", b) + } +} + +// utility functions + +func recLog(on types.Onion, b slice.Bytes, cl *Engine) func() string { + return func() string { + return cl.GetLocalNodeAddress().String() + + " received " + + fmt.Sprint(reflect.TypeOf(on)) + "\n" + + spew.Sdump(b.ToBytes()) + } +} diff --git a/pkg/relay/helper-buysessions.go b/pkg/relay/helper-buysessions.go new file mode 100644 index 00000000..a7ea29b6 --- /dev/null +++ b/pkg/relay/helper-buysessions.go @@ -0,0 +1,7 @@ +package relay + +// BuySessions performs the initial purchase of 5 sessions as well as adding +// different hop numbers to relays with existing sessions. +func (eng *Engine) BuySessions(conf Callback) { + +} diff --git a/pkg/client/helper-findcloaked.go b/pkg/relay/helper-findcloaked.go similarity index 63% rename from pkg/client/helper-findcloaked.go rename to pkg/relay/helper-findcloaked.go index bbe60eb7..a149544c 100644 --- a/pkg/client/helper-findcloaked.go +++ b/pkg/relay/helper-findcloaked.go @@ -1,29 +1,29 @@ -package client +package relay import ( - "github.com/indra-labs/indra/pkg/crypto/key/cloak" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/traffic" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/cloak" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/traffic" ) // FindCloaked searches the client identity key and the sessions for a match. It // returns the session as well, though not all users of this function will need // this. -func (cl *Client) FindCloaked(clk cloak.PubKey) (hdr *prv.Key, +func (eng *Engine) FindCloaked(clk cloak.PubKey) (hdr *prv.Key, pld *prv.Key, sess *traffic.Session, identity bool) { var b cloak.Blinder copy(b[:], clk[:cloak.BlindLen]) - hash := cloak.Cloak(b, cl.Node.IdentityBytes) + hash := cloak.Cloak(b, eng.GetLocalNodeIdentityBytes()) if hash == clk { log.T.F("encrypted to identity key") - hdr = cl.Node.IdentityPrv + hdr = eng.GetLocalNodeIdentityPrv() // there is no payload key for the node, only in sessions. identity = true return } var i int - cl.Node.IterateSessions(func(s *traffic.Session) (stop bool) { + eng.IterateSessions(func(s *traffic.Session) (stop bool) { hash = cloak.Cloak(b, s.HeaderBytes) if hash == clk { log.T.F("found cloaked key in session %d", i) diff --git a/pkg/client/helper-send.go b/pkg/relay/helper-send.go similarity index 61% rename from pkg/client/helper-send.go rename to pkg/relay/helper-send.go index 24cf5130..9398aa6f 100644 --- a/pkg/client/helper-send.go +++ b/pkg/relay/helper-send.go @@ -1,30 +1,33 @@ -package client +package relay import ( "net/netip" "github.com/davecgh/go-spew/spew" - "github.com/indra-labs/indra/pkg/util/slice" + + "git-indra.lan/indra-labs/indra/pkg/traffic" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) // Send a message to a peer via their AddrPort. -func (cl *Client) Send(addr *netip.AddrPort, b slice.Bytes) { +func (eng *Engine) Send(addr *netip.AddrPort, b slice.Bytes) { // first search if we already have the node available with connection // open. as := addr.String() - for i := range cl.Nodes { - if as == cl.Nodes[i].AddrPort.String() { + eng.ForEachNode(func(n *traffic.Node) bool { + if as == n.AddrPort.String() { log.T.C(func() string { - return cl.AddrPort.String() + + return eng.GetLocalNodeAddress().String() + " sending to " + addr.String() + "\n" + spew.Sdump(b.ToBytes()) }) - cl.Nodes[i].Transport.Send(b) - return + n.Transport.Send(b) + return true } - } + return false + }) // If we got to here none of the addresses matched, and we need to // establish a new peer connection to them, if we know of them (this // would usually be the reason this happens). diff --git a/pkg/relay/helper-sendexit.go b/pkg/relay/helper-sendexit.go new file mode 100644 index 00000000..4b4fcc76 --- /dev/null +++ b/pkg/relay/helper-sendexit.go @@ -0,0 +1,21 @@ +package relay + +import ( + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/onion" + "git-indra.lan/indra-labs/indra/pkg/traffic" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +func (eng *Engine) SendExit(port uint16, message slice.Bytes, id nonce.ID, + target *traffic.Session, hook func(id nonce.ID, b slice.Bytes)) { + + hops := []byte{0, 1, 2, 3, 4, 5} + s := make(traffic.Sessions, len(hops)) + s[2] = target + se := eng.SelectHops(hops, s) + var c traffic.Circuit + copy(c[:], se) + o := onion.SendExit(port, message, id, se[len(se)-1], c, eng.KeySet) + eng.SendOnion(c[0].AddrPort, o, hook) +} diff --git a/pkg/client/helper-sendgetbalance.go b/pkg/relay/helper-sendgetbalance.go similarity index 56% rename from pkg/client/helper-sendgetbalance.go rename to pkg/relay/helper-sendgetbalance.go index 89b18d1f..2d81a574 100644 --- a/pkg/client/helper-sendgetbalance.go +++ b/pkg/relay/helper-sendgetbalance.go @@ -1,12 +1,12 @@ -package client +package relay import ( - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/onion" - "github.com/indra-labs/indra/pkg/traffic" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/onion" + "git-indra.lan/indra-labs/indra/pkg/traffic" ) -func (cl *Client) SendGetBalance(s *traffic.Session, conf func(cf nonce.ID)) { +func (eng *Engine) SendGetBalance(s *traffic.Session, conf Callback) { var c traffic.Circuit var returns [3]*traffic.Session hops := make([]byte, 0) @@ -15,12 +15,11 @@ func (cl *Client) SendGetBalance(s *traffic.Session, conf func(cf nonce.ID)) { c[s.Hop] = s hops = append(hops, 5) se := make(traffic.Sessions, len(hops)) - ss := cl.Payments.Select(hops, se) + ss := eng.SessionManager.SelectHops(hops, se) returns[2] = ss[1] confID := nonce.NewID() - o := onion.GetBalance(c, int(s.Hop), returns, cl.KeySet, confID) - cl.RegisterConfirmation(conf, confID) - cl.SendOnion(c[s.Hop].AddrPort, o, nil) + o := onion.GetBalance(c, int(s.Hop), returns, eng.KeySet, confID) + eng.SendOnion(c[s.Hop].AddrPort, o, conf) return } var cur byte @@ -34,7 +33,7 @@ func (cl *Client) SendGetBalance(s *traffic.Session, conf func(cf nonce.ID)) { } se := make(traffic.Sessions, len(hops)) se[s.Hop] = s - ss := cl.Payments.Select(hops, se) + ss := eng.SessionManager.SelectHops(hops, se) // Construct the circuit parameter. for i := range ss { if i > int(s.Hop) { @@ -47,7 +46,6 @@ func (cl *Client) SendGetBalance(s *traffic.Session, conf func(cf nonce.ID)) { returns[i] = ss[lastIndex+i] } confID := nonce.NewID() - o := onion.GetBalance(c, int(s.Hop), returns, cl.KeySet, confID) - cl.RegisterConfirmation(conf, confID) - cl.SendOnion(c[0].AddrPort, o, nil) + o := onion.GetBalance(c, int(s.Hop), returns, eng.KeySet, confID) + eng.SendOnion(c[0].AddrPort, o, conf) } diff --git a/pkg/relay/helper-sendonion.go b/pkg/relay/helper-sendonion.go new file mode 100644 index 00000000..4629ffb6 --- /dev/null +++ b/pkg/relay/helper-sendonion.go @@ -0,0 +1,104 @@ +package relay + +import ( + "net/netip" + + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/onion" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/balance" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/confirm" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/crypt" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/directbalance" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/exit" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/forward" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/getbalance" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/reverse" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +func (eng *Engine) SendOnion(ap *netip.AddrPort, o onion.Skins, + responseHook func(id nonce.ID, b slice.Bytes)) { + + b := onion.Encode(o.Assemble()) + var billable, accounted []nonce.ID + var ret nonce.ID + var last nonce.ID + var port uint16 + // do client accounting + skip := false + for i := range o { + if skip { + skip = false + continue + } + switch on := o[i].(type) { + case *crypt.Layer: + s := eng.FindSessionByHeaderPub(on.ToHeaderPub) + // The last hop needs no accounting as it's us! + if i == len(o)-1 { + // The session used for the last hop is stored, however. + ret = s.ID + break + } + if s == nil { + continue + } + switch on2 := o[i+1].(type) { + case *forward.Layer: + eng.DecSession(s.ID, + s.RelayRate*lnwire.MilliSatoshi(len(b))/1024/1024, true, + "forward") + accounted = append(accounted, s.ID) + case *reverse.Layer: + billable = append(billable, s.ID) + case *exit.Layer: + for j := range s.Services { + if s.Services[j].Port != on2.Port { + continue + } + port = on2.Port + eng.DecSession(s.ID, + s.Services[j].RelayRate*lnwire. + MilliSatoshi(len(b)/2)/1024/1024, true, "exit") + accounted = append(accounted, s.ID) + break + } + billable = append(billable, s.ID) + last = on2.ID + skip = true + case *getbalance.Layer: + eng.DecSession(s.ID, + s.RelayRate*lnwire.MilliSatoshi(len(b)/2)/1024/1024, true, + "getbalance") + last = s.ID + billable = append(billable, s.ID) + skip = true + } + case *directbalance.Layer: + // the immediate previous layer session needs to be accounted. + switch on3 := o[i-1].(type) { + case *crypt.Layer: + s := eng.FindSessionByHeaderPub(on3.ToHeaderPub) + if s == nil { + return + } + last = on.ID + } + case *confirm.Layer: + last = on.ID + case *balance.Layer: + last = on.ID + } + } + if responseHook == nil { + responseHook = func(_ nonce.ID, _ slice.Bytes) { + log.T.Ln("nil response hook") + } + } + eng.PendingResponses.Add(last, len(b), billable, accounted, ret, port, responseHook) + log.T.Ln("sending out onion") + eng.Send(ap, b) + +} diff --git a/pkg/relay/helper-sendping.go b/pkg/relay/helper-sendping.go new file mode 100644 index 00000000..df1d8e82 --- /dev/null +++ b/pkg/relay/helper-sendping.go @@ -0,0 +1,19 @@ +package relay + +import ( + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/onion" + "git-indra.lan/indra-labs/indra/pkg/traffic" +) + +func (eng *Engine) SendPing(c traffic.Circuit, conf Callback) { + + hops := []byte{0, 1, 2, 3, 4, 5} + s := make(traffic.Sessions, len(hops)) + copy(s, c[:]) + se := eng.SelectHops(hops, s) + copy(c[:], se) + confID := nonce.NewID() + o := onion.Ping(confID, se[len(se)-1], c, eng.KeySet) + eng.SendOnion(c[0].AddrPort, o, conf) +} diff --git a/pkg/relay/helper-topupsessions.go b/pkg/relay/helper-topupsessions.go new file mode 100644 index 00000000..8cc4bf3c --- /dev/null +++ b/pkg/relay/helper-topupsessions.go @@ -0,0 +1,7 @@ +package relay + +// TopUpSessions randomly selects from the least funded existing sessions and +// adds credits to them. +func (eng *Engine) TopUpSessions(conf Callback) { + +} diff --git a/pkg/client/util.go b/pkg/relay/util.go similarity index 69% rename from pkg/client/util.go rename to pkg/relay/util.go index f565839b..21b9b201 100644 --- a/pkg/client/util.go +++ b/pkg/relay/util.go @@ -1,11 +1,11 @@ -package client +package relay import ( - "github.com/indra-labs/indra/pkg/crypto/ciph" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/onion/layers/crypt" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra/pkg/crypto/ciph" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/onion/layers/crypt" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) func BudgeUp(b slice.Bytes, start slice.Cursor) (o slice.Bytes) { diff --git a/pkg/relay/utils_test.go b/pkg/relay/utils_test.go new file mode 100644 index 00000000..69744de9 --- /dev/null +++ b/pkg/relay/utils_test.go @@ -0,0 +1,68 @@ +package relay + +import ( + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/traffic" + "git-indra.lan/indra-labs/indra/pkg/transport" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +func CreateNMockCircuits(inclSessions bool, nCircuits int) (cl []*Engine, e error) { + + nTotal := 1 + nCircuits*5 + cl = make([]*Engine, nTotal) + nodes := make([]*traffic.Node, nTotal) + transports := make([]types.Transport, nTotal) + sessions := make(traffic.Sessions, nTotal-1) + for i := range transports { + transports[i] = transport.NewSim(nTotal) + } + for i := range nodes { + var idPrv *prv.Key + if idPrv, e = prv.GenerateKey(); check(e) { + return + } + idPub := pub.Derive(idPrv) + addr := slice.GenerateRandomAddrPortIPv4() + var local bool + if i == 0 { + local = true + } + nodes[i], _ = traffic.New(addr, idPub, idPrv, transports[i], 180000, + local) + if cl[i], e = NewEngine(transports[i], idPrv, nodes[i], nil); check(e) { + return + } + cl[i].SetLocalNodeAddress(nodes[i].AddrPort) + cl[i].SetLocalNode(nodes[i]) + if inclSessions { + // create a session for all but the first + if i > 0 { + sessions[i-1] = traffic.NewSession( + nonce.NewID(), nodes[i], + 1<<16, nil, nil, + byte((i-1)/nCircuits)) + // Add session to node, so it will be able to + // relay if it gets a message with the key. + cl[i].AddSession(sessions[i-1]) + // we need a copy for the node so the balance + // adjustments don't double up. + s := *sessions[i-1] + cl[0].AddSession(&s) + } + } + } + // Add all the nodes to each other so they can pass messages. + for i := range cl { + for j := range nodes { + if i == j { + continue + } + cl[i].AddNodes(nodes[j]) + } + } + return +} diff --git a/pkg/server/metrics/host.go b/pkg/server/metrics/host.go index e7ae96c2..e58c8dac 100644 --- a/pkg/server/metrics/host.go +++ b/pkg/server/metrics/host.go @@ -5,9 +5,10 @@ import ( "sync" "time" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" "github.com/libp2p/go-libp2p/core/host" + + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/server/server.go b/pkg/server/server.go index 7b04c6bb..41aa0ad9 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -4,15 +4,16 @@ import ( "context" "time" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/cfg" - "github.com/indra-labs/indra/pkg/interrupt" - "github.com/indra-labs/indra/pkg/p2p/introducer" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/server/metrics" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/host" "github.com/multiformats/go-multiaddr" + + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/cfg" + "git-indra.lan/indra-labs/indra/pkg/interrupt" + "git-indra.lan/indra-labs/indra/pkg/p2p/introducer" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/server/metrics" ) var ( diff --git a/pkg/service/service.go b/pkg/service/service.go index 2a4cb80a..31dce3e9 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -1,8 +1,9 @@ package service import ( - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/lnd/lnd/lnwire" + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra/pkg/types" ) type Service struct { diff --git a/pkg/traffic/identity.go b/pkg/traffic/identity.go deleted file mode 100644 index 3c17cc12..00000000 --- a/pkg/traffic/identity.go +++ /dev/null @@ -1,28 +0,0 @@ -package traffic - -import ( - "net/netip" - - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/ring" - "github.com/indra-labs/indra/pkg/service" - "github.com/indra-labs/indra/pkg/types" - "github.com/indra-labs/lnd/lnd/lnwire" -) - -type Peer struct { - nonce.ID // matches node ID - AddrPort *netip.AddrPort - IdentityPub *pub.Key - IdentityBytes pub.Bytes - IdentityPrv *prv.Key - RelayRate lnwire.MilliSatoshi // Base relay price/Mb. - Services service.Services // Services offered by this peer. - Load *ring.BufferLoad // Relay load. - Latency *ring.BufferLatency // Latency to peer. - Failure *ring.BufferFailure // Times of tx failure. - types.Transport - *Payments -} diff --git a/pkg/traffic/node.go b/pkg/traffic/node.go new file mode 100644 index 00000000..e2ed3681 --- /dev/null +++ b/pkg/traffic/node.go @@ -0,0 +1,143 @@ +// Package traffic maintains information about peers on the network and +// associated connection sessions. +package traffic + +import ( + "fmt" + "net/netip" + "sync" + + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/ring" + "git-indra.lan/indra-labs/indra/pkg/service" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +var ( + log = log2.GetLogger(indra.PathBase) + check = log.E.Chk +) + +// Node is a representation of a messaging counterparty. +type Node struct { + nonce.ID + sync.Mutex + AddrPort *netip.AddrPort + IdentityPub *pub.Key + IdentityBytes pub.Bytes + IdentityPrv *prv.Key + RelayRate lnwire.MilliSatoshi // Base relay price/Mb. + Services service.Services // Services offered by this peer. + Load *ring.BufferLoad // Relay load. + Latency *ring.BufferLatency // Latency to peer. + Failure *ring.BufferFailure // Times of tx failure. + types.Transport +} + +// DefaultSampleBufferSize defines the number of samples for the Load, Latency +// and Failure ring buffers. +const DefaultSampleBufferSize = 64 + +// New creates a new Node. A netip.AddrPort is optional if the counterparty is +// not in direct connection. Also, the IdentityPrv node private key can be nil, +// as only the node embedded in a client and not the peer node list has one +// available. The Node for a client's self should use true in the local +// parameter to not initialise the peer state ring buffers as it won't use them. +func New(addr *netip.AddrPort, idPub *pub.Key, idPrv *prv.Key, + tpt types.Transport, relayRate lnwire.MilliSatoshi, + local bool) (n *Node, id nonce.ID) { + + id = nonce.NewID() + n = &Node{ + ID: id, + AddrPort: addr, + IdentityPub: idPub, + IdentityBytes: idPub.ToBytes(), + IdentityPrv: idPrv, + RelayRate: relayRate, + Transport: tpt, + } + if !local { + // These ring buffers are needed to evaluate these metrics for remote + // peers only. + n.Load = ring.NewBufferLoad(DefaultSampleBufferSize) + n.Latency = ring.NewBufferLatency(DefaultSampleBufferSize) + n.Failure = ring.NewBufferFailure(DefaultSampleBufferSize) + } + return +} + +func (n *Node) AddService(s *service.Service) (e error) { + n.Lock() + defer n.Unlock() + for i := range n.Services { + if n.Services[i].Port == s.Port { + return fmt.Errorf("service already exists for port %d", s.Port) + } + } + n.Services = append(n.Services, s) + return +} + +func (n *Node) DeleteService(port uint16) { + n.Lock() + defer n.Unlock() + for i := range n.Services { + if n.Services[i].Port == port { + if i < 1 { + n.Services = n.Services[1:] + } else { + n.Services = append(n.Services[:i], + n.Services[i+1:]...) + } + return + } + } +} + +func (n *Node) FindService(port uint16) (service *service.Service) { + n.Lock() + defer n.Unlock() + for i := range n.Services { + if n.Services[i].Port == port { + return n.Services[i] + } + } + return +} + +// SendTo delivers a message to a service identified by its port. +func (n *Node) SendTo(port uint16, b slice.Bytes) (e error) { + n.Lock() + defer n.Unlock() + e = fmt.Errorf("port not registered %d", port) + for i := range n.Services { + if n.Services[i].Port == port { + n.Services[i].Send(b) + e = nil + return + } + } + return +} + +// ReceiveFrom returns the channel that receives messages for a given port. +func (n *Node) ReceiveFrom(port uint16) (b <-chan slice.Bytes) { + n.Lock() + defer n.Unlock() + for i := range n.Services { + if n.Services[i].Port == port { + log.T.Ln("receive from") + b = n.Services[i].Receive() + return + } + } + return +} diff --git a/pkg/traffic/nodes.go b/pkg/traffic/nodes.go new file mode 100644 index 00000000..e879f13f --- /dev/null +++ b/pkg/traffic/nodes.go @@ -0,0 +1,163 @@ +package traffic + +import ( + "fmt" + "net/netip" + + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/service" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +// NodesLen returns the length of a Nodes. +func (sm *SessionManager) NodesLen() int { + sm.Lock() + defer sm.Unlock() + return len(sm.nodes) +} + +// GetLocalNode returns the engine's local Node. +func (sm *SessionManager) GetLocalNode() *Node { return sm.nodes[0] } + +// Concurrent safe accessors. + +func (sm *SessionManager) GetLocalNodeAddress() (addr *netip.AddrPort) { + sm.Lock() + defer sm.Unlock() + return sm.GetLocalNode().AddrPort +} + +func (sm *SessionManager) SetLocalNodeAddress(addr *netip.AddrPort) { + sm.Lock() + defer sm.Unlock() + sm.GetLocalNode().AddrPort = addr +} + +func (sm *SessionManager) SendFromLocalNode(port uint16, + b slice.Bytes) (e error) { + + sm.Lock() + defer sm.Unlock() + return sm.GetLocalNode().SendTo(port, b) +} + +func (sm *SessionManager) ReceiveToLocalNode(port uint16) <-chan slice.Bytes { + sm.Lock() + defer sm.Unlock() + if port == 0 { + return sm.GetLocalNode().Receive() + } + return sm.GetLocalNode().ReceiveFrom(port) +} + +func (sm *SessionManager) AddServiceToLocalNode(s *service.Service) (e error) { + sm.Lock() + defer sm.Unlock() + return sm.GetLocalNode().AddService(s) +} + +func (sm *SessionManager) GetLocalNodeRelayRate() (rate lnwire.MilliSatoshi) { + sm.Lock() + defer sm.Unlock() + return sm.GetLocalNode().RelayRate +} + +func (sm *SessionManager) GetLocalNodeIdentityBytes() (ident pub.Bytes) { + sm.Lock() + defer sm.Unlock() + return sm.GetLocalNode().IdentityBytes +} + +func (sm *SessionManager) GetLocalNodeIdentityPrv() (ident *prv.Key) { + sm.Lock() + defer sm.Unlock() + return sm.GetLocalNode().IdentityPrv +} + +// SetLocalNode sets the engine's local Node. +func (sm *SessionManager) SetLocalNode(n *Node) { + sm.Lock() + defer sm.Unlock() + sm.nodes[0] = n +} + +// AddNodes adds a Node to a Nodes. +func (sm *SessionManager) AddNodes(nn ...*Node) { + sm.Lock() + defer sm.Unlock() + sm.nodes = append(sm.nodes, nn...) +} + +// FindNodeByID searches for a Node by ID. +func (sm *SessionManager) FindNodeByID(i nonce.ID) (no *Node) { + sm.Lock() + defer sm.Unlock() + for _, nn := range sm.nodes { + if nn.ID == i { + no = nn + break + } + } + return +} + +// FindNodeByAddrPort searches for a Node by netip.AddrPort. +func (sm *SessionManager) FindNodeByAddrPort(id *netip.AddrPort) (no *Node) { + sm.Lock() + defer sm.Unlock() + for _, nn := range sm.nodes { + if nn.AddrPort.String() == id.String() { + no = nn + break + } + } + return +} + +// DeleteNodeByID deletes a node identified by an ID. +func (sm *SessionManager) DeleteNodeByID(ii nonce.ID) (e error) { + sm.Lock() + defer sm.Unlock() + e = fmt.Errorf("id %x not found", ii) + for i := range sm.nodes { + if sm.nodes[i].ID == ii { + sm.nodes = append(sm.nodes[:i], sm.nodes[i+1:]...) + return + } + } + return +} + +// DeleteNodeByAddrPort deletes a node identified by a netip.AddrPort. +func (sm *SessionManager) DeleteNodeByAddrPort(ip *netip.AddrPort) (e error) { + sm.Lock() + defer sm.Unlock() + e = fmt.Errorf("node with ip %v not found", ip) + for i := range sm.nodes { + if sm.nodes[i].AddrPort.String() == ip.String() { + sm.nodes = append(sm.nodes[:i], sm.nodes[i+1:]...) + e = nil + break + } + } + return +} + +// ForEachNode runs a function over the slice of nodes with the mutex locked, +// and terminates when the function returns true. +// +// Do not call any SessionManager methods above inside this function or there +// will be a mutex double locking panic, except GetLocalNode. +func (sm *SessionManager) ForEachNode(fn func(n *Node) bool) { + sm.Lock() + defer sm.Unlock() + for i := range sm.nodes { + if fn(sm.nodes[i]) { + return + } + } +} diff --git a/pkg/traffic/paths.go b/pkg/traffic/paths.go index a0117086..38c5f4f7 100644 --- a/pkg/traffic/paths.go +++ b/pkg/traffic/paths.go @@ -3,17 +3,16 @@ package traffic import ( "math/rand" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) -func (pm *Payments) Select(hops []byte, alreadyHave Sessions) (so Sessions) { - pm.Lock() - defer pm.Unlock() +func (sm *SessionManager) SelectHops(hops []byte, alreadyHave Sessions) (so Sessions) { + sm.Lock() + defer sm.Unlock() ws := make(Sessions, 0) - // todo: later on we want to pre-thin this according to configuration. out: - for i := range pm.Sessions { - if pm.Sessions[i] == nil { + for i := range sm.Sessions { + if sm.Sessions[i] == nil { log.D.Ln("nil session", i) continue } @@ -22,18 +21,17 @@ out: if alreadyHave[j] == nil { continue } - if pm.Sessions[i].ID == alreadyHave[j].ID { + if sm.Sessions[i].ID == alreadyHave[j].ID { continue out } } - ws = append(ws, pm.Sessions[i]) + ws = append(ws, sm.Sessions[i]) } // Shuffle the copy of the sessions. rand.Seed(slice.GetCryptoRandSeed()) rand.Shuffle(len(ws), func(i, j int) { ws[i], ws[j] = ws[j], ws[i] }) - // log.T.S(ws) // Iterate the available sessions picking the first matching hop, then // prune it from the temporary slice and advance the cursor, wrapping // around at end. diff --git a/pkg/traffic/pendingpayments.go b/pkg/traffic/pendingpayments.go new file mode 100644 index 00000000..c6e881c2 --- /dev/null +++ b/pkg/traffic/pendingpayments.go @@ -0,0 +1,78 @@ +package traffic + +import ( + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/payment" +) + +type PendingPayments []*payment.Payment + +func (p PendingPayments) Add(np *payment.Payment) (pp PendingPayments) { + return append(p, np) +} + +func (p PendingPayments) Delete(preimage sha256.Hash) (pp PendingPayments) { + pp = p + for i := range p { + if p[i].Preimage == preimage { + if i == len(p)-1 { + pp = p[:i] + } else { + pp = append(p[:i], p[i+1:]...) + } + } + } + return +} + +func (p PendingPayments) Find(id nonce.ID) (pp *payment.Payment) { + for i := range p { + if p[i].ID == id { + return p[i] + } + } + return +} + +func (p PendingPayments) FindPreimage(pi sha256.Hash) (pp *payment.Payment) { + for i := range p { + if p[i].Preimage == pi { + return p[i] + } + } + return +} + +// PendingPayment accessors. For the same reason as the sessions, pending +// payments need to be accessed only with the node's mutex locked. + +func (sm *SessionManager) AddPendingPayment( + np *payment.Payment) { + + sm.Lock() + defer sm.Unlock() + sm.pendingPayments = sm.pendingPayments.Add(np) +} +func (sm *SessionManager) DeletePendingPayment( + preimage sha256.Hash) { + + sm.Lock() + defer sm.Unlock() + sm.pendingPayments = sm.pendingPayments.Delete(preimage) +} +func (sm *SessionManager) FindPendingPayment( + id nonce.ID) (pp *payment.Payment) { + + sm.Lock() + defer sm.Unlock() + return sm.pendingPayments.Find(id) +} +func (sm *SessionManager) FindPendingPreimage( + pi sha256.Hash) (pp *payment.Payment) { + + log.T.F("searching preimage %x", pi) + sm.Lock() + defer sm.Unlock() + return sm.pendingPayments.FindPreimage(pi) +} diff --git a/pkg/traffic/session.go b/pkg/traffic/session.go index 3571609c..03508f78 100644 --- a/pkg/traffic/session.go +++ b/pkg/traffic/session.go @@ -1,21 +1,12 @@ package traffic import ( - "sync" + "git-indra.lan/indra-labs/lnd/lnd/lnwire" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/nonce" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/payment" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/lnd/lnd/lnwire" -) - -var ( - log = log2.GetLogger(indra.PathBase) - check = log.E.Chk + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" ) // A Session keeps track of a connection session. It specifically maintains the @@ -23,7 +14,7 @@ var ( // with new credit, and the current state of the encryption. type Session struct { ID nonce.ID - *Peer + *Node Remaining lnwire.MilliSatoshi HeaderPrv, PayloadPrv *prv.Key HeaderPub, PayloadPub *pub.Key @@ -32,13 +23,11 @@ type Session struct { Hop byte } -// NewSession creates a new Session. -// -// Purchasing a session the seller returns a token, based on a requested data -// allocation. +// NewSession creates a new Session, generating cached public key bytes and +// preimage. func NewSession( id nonce.ID, - node *Peer, + node *Node, rem lnwire.MilliSatoshi, hdrPrv *prv.Key, pldPrv *prv.Key, @@ -57,7 +46,7 @@ func NewSession( h, p := hdrPrv.ToBytes(), pldPrv.ToBytes() s = &Session{ ID: id, - Peer: node, + Node: node, Remaining: rem, HeaderPub: hdrPub, HeaderBytes: hdrPub.ToBytes(), @@ -73,226 +62,37 @@ func NewSession( // IncSats adds to the Remaining counter, used when new data allowance has been // purchased. -func (s *Session) IncSats(b lnwire.MilliSatoshi) { s.Remaining += b } +func (s *Session) IncSats(sats lnwire.MilliSatoshi, + sender bool, typ string) { + who := "relay" + if sender { + who = "client" + } + log.D.F("%s session %d %x current %v incrementing by %v", who, typ, s.ID, + s.Remaining, sats) + s.Remaining += sats +} // DecSats reduces the amount Remaining, if the requested amount would put // the total below zero it returns false, signalling that new data allowance // needs to be purchased before any further messages can be sent. -func (s *Session) DecSats(b lnwire.MilliSatoshi) bool { - if s.Remaining < b { +func (s *Session) DecSats(sats lnwire.MilliSatoshi, + sender bool, typ string) bool { + if s.Remaining < sats { return false } - s.Remaining -= b + who := "relay" + if sender { + who = "client" + } + log.D.F("%s session %s %x current %v decrementing by %v", who, typ, s.ID, + s.Remaining, sats) + s.Remaining -= sats return true } +// A Circuit is the generic fixed length path used for most messages. type Circuit [5]*Session +// Sessions are arbitrary length lists of sessions. type Sessions []*Session - -// Session management functions -// -// In order to enable scaling client processing the session data will be -// accessed by multiple goroutines, and thus we use the node's mutex to prevent -// race conditions on this data. This is the only mutable data. A relay's -// identity is its keys so a different key is a different relay, even if it is -// on the same IP address. Because we use netip.AddrPort as network addresses -// there can be more than one relay at an IP address, though hop selection will -// consider the IP address as the unique identifier and not select more than one -// relay on the same IP address. (todo:) - -type Payments struct { - pendingPayments PendingPayments - Sessions Sessions - PaymentChan - sync.Mutex -} - -func NewPayments() *Payments { - return &Payments{PaymentChan: make(PaymentChan)} -} - -func (pm *Payments) IncSession(id nonce.ID, sats lnwire.MilliSatoshi) { - sess := pm.FindSession(id) - log.I.S(sess) - if sess != nil { - log.D.F("incrementing session %x by %v", sess.ID, sats) - pm.Lock() - defer pm.Unlock() - sess.IncSats(sats) - } -} -func (pm *Payments) DecSession(id nonce.ID, sats lnwire.MilliSatoshi) bool { - sess := pm.FindSession(id) - if sess != nil { - log.D.F("decrementing session %x by %v", sess.ID, sats) - pm.Lock() - defer pm.Unlock() - return sess.DecSats(sats) - } - return false -} - -func (pm *Payments) AddSession(s *Session) { - pm.Lock() - defer pm.Unlock() - // check for dupes - for i := range pm.Sessions { - if pm.Sessions[i].ID == s.ID { - log.D.Ln("refusing to add duplicate session ID") - return - } - } - pm.Sessions = append(pm.Sessions, s) -} -func (pm *Payments) FindSession(id nonce.ID) *Session { - pm.Lock() - defer pm.Unlock() - for i := range pm.Sessions { - if pm.Sessions[i].ID == id { - return pm.Sessions[i] - } - } - return nil -} -func (pm *Payments) FindSessionByHeader(prvKey *prv.Key) *Session { - pm.Lock() - defer pm.Unlock() - for i := range pm.Sessions { - if pm.Sessions[i].HeaderPrv.Key.Equals(&prvKey.Key) { - return pm.Sessions[i] - } - } - return nil -} -func (pm *Payments) FindSessionByHeaderPub(pubKey *pub.Key) *Session { - pm.Lock() - defer pm.Unlock() - for i := range pm.Sessions { - if pm.Sessions[i].HeaderPub.Equals(pubKey) { - return pm.Sessions[i] - } - } - return nil -} -func (pm *Payments) FindSessionPreimage(pr sha256.Hash) *Session { - pm.Lock() - defer pm.Unlock() - for i := range pm.Sessions { - if pm.Sessions[i].Preimage == pr { - return pm.Sessions[i] - } - } - return nil -} -func (pm *Payments) GetSessionsAtHop(hop byte) (s Sessions) { - pm.Lock() - defer pm.Unlock() - for i := range pm.Sessions { - if pm.Sessions[i].Hop == hop { - s = append(s, pm.Sessions[i]) - } - } - return -} -func (pm *Payments) DeleteSession(id nonce.ID) { - pm.Lock() - defer pm.Unlock() - for i := range pm.Sessions { - if pm.Sessions[i].ID == id { - pm.Sessions = append(pm.Sessions[:i], pm.Sessions[i+1:]...) - } - } - -} -func (pm *Payments) IterateSessions(fn func(s *Session) bool) { - pm.Lock() - defer pm.Unlock() - for i := range pm.Sessions { - if fn(pm.Sessions[i]) { - break - } - } -} - -func (pm *Payments) GetSessionByIndex(i int) (s *Session) { - pm.Lock() - defer pm.Unlock() - if len(pm.Sessions) > i { - s = pm.Sessions[i] - } - return -} - -type PaymentChan chan *payment.Payment - -type PendingPayments []*payment.Payment - -func (p PendingPayments) Add(np *payment.Payment) (pp PendingPayments) { - return append(p, np) -} - -func (p PendingPayments) Delete(preimage sha256.Hash) (pp PendingPayments) { - pp = p - for i := range p { - if p[i].Preimage == preimage { - if i == len(p)-1 { - pp = p[:i] - } else { - pp = append(p[:i], p[i+1:]...) - } - } - } - return -} - -func (p PendingPayments) Find(id nonce.ID) (pp *payment.Payment) { - for i := range p { - if p[i].ID == id { - return p[i] - } - } - return -} - -func (p PendingPayments) FindPreimage(pi sha256.Hash) (pp *payment.Payment) { - for i := range p { - if p[i].Preimage == pi { - return p[i] - } - } - return -} - -// PendingPayment accessors. For the same reason as the sessions, pending -// payments need to be accessed only with the node's mutex locked. - -func (pm *Payments) AddPendingPayment( - np *payment.Payment) { - - pm.Lock() - defer pm.Unlock() - pm.pendingPayments = pm.pendingPayments.Add(np) -} -func (pm *Payments) DeletePendingPayment( - preimage sha256.Hash) { - - pm.Lock() - defer pm.Unlock() - pm.pendingPayments = pm.pendingPayments.Delete(preimage) -} -func (pm *Payments) FindPendingPayment( - id nonce.ID) (pp *payment.Payment) { - - pm.Lock() - defer pm.Unlock() - return pm.pendingPayments.Find(id) -} -func (pm *Payments) FindPendingPreimage( - pi sha256.Hash) (pp *payment.Payment) { - - log.T.F("searching preimage %x", pi) - pm.Lock() - defer pm.Unlock() - return pm.pendingPayments.FindPreimage(pi) -} diff --git a/pkg/traffic/sessionmanager.go b/pkg/traffic/sessionmanager.go new file mode 100644 index 00000000..3f580597 --- /dev/null +++ b/pkg/traffic/sessionmanager.go @@ -0,0 +1,163 @@ +package traffic + +import ( + "sync" + + "git-indra.lan/indra-labs/lnd/lnd/lnwire" + + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/payment" +) + +// Session management functions +// +// In order to enable scaling client processing the session data will be +// accessed by multiple goroutines, and thus we use the node's mutex to prevent +// race conditions on this data. This is the only mutable data. A relay's +// identity is its keys so a different key is a different relay, even if it is +// on the same IP address. Because we use netip.AddrPort as network addresses +// there can be more than one relay at an IP address, though hop selection will +// consider the IP address as the unique identifier and not select more than one +// relay on the same IP address. (todo:) + +type PaymentChan chan *payment.Payment + +type SessionCache map[nonce.ID]SessionCacheEntry + +type SessionCacheEntry struct { + Hops [5]*Session +} + +type SessionManager struct { + nodes []*Node + pendingPayments PendingPayments + Sessions + PaymentChan + SessionCache + sync.Mutex +} + +func NewSessionManager() *SessionManager { + return &SessionManager{ + PaymentChan: make(PaymentChan), + SessionCache: make(SessionCache), + } +} + +func (sm *SessionManager) UpdateSessionCache() { + +} + +func (sm *SessionManager) IncSession(id nonce.ID, sats lnwire.MilliSatoshi, + sender bool, typ string) { + sess := sm.FindSession(id) + if sess != nil { + sm.Lock() + defer sm.Unlock() + sess.IncSats(sats, sender, typ) + } +} +func (sm *SessionManager) DecSession(id nonce.ID, sats lnwire.MilliSatoshi, + sender bool, typ string) bool { + sess := sm.FindSession(id) + if sess != nil { + sm.Lock() + defer sm.Unlock() + return sess.DecSats(sats, sender, typ) + } + return false +} + +func (sm *SessionManager) AddSession(s *Session) { + sm.Lock() + defer sm.Unlock() + // check for dupes + for i := range sm.Sessions { + if sm.Sessions[i].ID == s.ID { + log.D.Ln("refusing to add duplicate session ID") + return + } + } + sm.Sessions = append(sm.Sessions, s) +} +func (sm *SessionManager) FindSession(id nonce.ID) *Session { + sm.Lock() + defer sm.Unlock() + for i := range sm.Sessions { + if sm.Sessions[i].ID == id { + return sm.Sessions[i] + } + } + return nil +} +func (sm *SessionManager) FindSessionByHeader(prvKey *prv.Key) *Session { + sm.Lock() + defer sm.Unlock() + for i := range sm.Sessions { + if sm.Sessions[i].HeaderPrv.Key.Equals(&prvKey.Key) { + return sm.Sessions[i] + } + } + return nil +} +func (sm *SessionManager) FindSessionByHeaderPub(pubKey *pub.Key) *Session { + sm.Lock() + defer sm.Unlock() + for i := range sm.Sessions { + if sm.Sessions[i].HeaderPub.Equals(pubKey) { + return sm.Sessions[i] + } + } + return nil +} +func (sm *SessionManager) FindSessionPreimage(pr sha256.Hash) *Session { + sm.Lock() + defer sm.Unlock() + for i := range sm.Sessions { + if sm.Sessions[i].Preimage == pr { + return sm.Sessions[i] + } + } + return nil +} +func (sm *SessionManager) GetSessionsAtHop(hop byte) (s Sessions) { + sm.Lock() + defer sm.Unlock() + for i := range sm.Sessions { + if sm.Sessions[i].Hop == hop { + s = append(s, sm.Sessions[i]) + } + } + return +} +func (sm *SessionManager) DeleteSession(id nonce.ID) { + sm.Lock() + defer sm.Unlock() + for i := range sm.Sessions { + if sm.Sessions[i].ID == id { + sm.Sessions = append(sm.Sessions[:i], sm.Sessions[i+1:]...) + } + } + +} +func (sm *SessionManager) IterateSessions(fn func(s *Session) bool) { + sm.Lock() + defer sm.Unlock() + for i := range sm.Sessions { + if fn(sm.Sessions[i]) { + break + } + } +} + +func (sm *SessionManager) GetSessionByIndex(i int) (s *Session) { + sm.Lock() + defer sm.Unlock() + if len(sm.Sessions) > i { + s = sm.Sessions[i] + } + return +} diff --git a/pkg/transport/sim.go b/pkg/transport/sim.go index f1e8a27b..f561971e 100644 --- a/pkg/transport/sim.go +++ b/pkg/transport/sim.go @@ -1,9 +1,9 @@ package transport import ( - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) var ( diff --git a/pkg/transport/transport.go b/pkg/transport/transport.go index b800acf6..e4138219 100644 --- a/pkg/transport/transport.go +++ b/pkg/transport/transport.go @@ -1,7 +1,7 @@ package transport import ( - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) type Dispatcher chan slice.Bytes diff --git a/pkg/types/types.go b/pkg/types/types.go index 9db676c6..4e8b76a3 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -1,7 +1,7 @@ package types import ( - "github.com/indra-labs/indra/pkg/util/slice" + "git-indra.lan/indra-labs/indra/pkg/util/slice" ) // Onion is an interface for the layers of messages each encrypted inside a diff --git a/pkg/util/appdata/appdata_test.go b/pkg/util/appdata/appdata_test.go index 9b294d22..e3e56cda 100644 --- a/pkg/util/appdata/appdata_test.go +++ b/pkg/util/appdata/appdata_test.go @@ -8,7 +8,7 @@ import ( "testing" "unicode" - "github.com/indra-labs/indra/pkg/util/appdata" + "git-indra.lan/indra-labs/indra/pkg/util/appdata" ) // TestAppDataDir tests the API for Dir to ensure it gives expected results for various operating systems. diff --git a/pkg/util/path/path/path.go b/pkg/util/path/path/path.go index 0908c77c..cf30bea3 100644 --- a/pkg/util/path/path/path.go +++ b/pkg/util/path/path/path.go @@ -3,9 +3,9 @@ package path import ( "strings" - "github.com/indra-labs/indra" - log2 "github.com/indra-labs/indra/pkg/proc/log" - "github.com/indra-labs/indra/pkg/util/norm" + "git-indra.lan/indra-labs/indra" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra/pkg/util/norm" ) var ( diff --git a/pkg/util/slice/interfaces_test.go b/pkg/util/slice/interfaces_test.go index a6d021f6..1b73ce5b 100644 --- a/pkg/util/slice/interfaces_test.go +++ b/pkg/util/slice/interfaces_test.go @@ -4,8 +4,8 @@ import ( "bytes" "testing" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/util/tests" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/util/tests" ) func TestMessage_ToU64Slice(t *testing.T) { diff --git a/pkg/util/slice/slice.go b/pkg/util/slice/slice.go index 7d92cd85..916b4e70 100644 --- a/pkg/util/slice/slice.go +++ b/pkg/util/slice/slice.go @@ -12,9 +12,9 @@ import ( "reflect" "unsafe" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/sha256" - log2 "github.com/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/pkg/util/slice/slice_test.go b/pkg/util/slice/slice_test.go index b6623269..b0301f6d 100644 --- a/pkg/util/slice/slice_test.go +++ b/pkg/util/slice/slice_test.go @@ -4,8 +4,8 @@ import ( "errors" "testing" - "github.com/indra-labs/indra/pkg/crypto/sha256" - "github.com/indra-labs/indra/pkg/util/tests" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + "git-indra.lan/indra-labs/indra/pkg/util/tests" ) func TestSize24(t *testing.T) { diff --git a/pkg/util/tests/testutils.go b/pkg/util/tests/testutils.go index 16326781..8402e41a 100644 --- a/pkg/util/tests/testutils.go +++ b/pkg/util/tests/testutils.go @@ -3,11 +3,11 @@ package tests import ( "crypto/rand" - "github.com/indra-labs/indra" - "github.com/indra-labs/indra/pkg/crypto/key/prv" - "github.com/indra-labs/indra/pkg/crypto/key/pub" - "github.com/indra-labs/indra/pkg/crypto/sha256" - log2 "github.com/indra-labs/indra/pkg/proc/log" + "git-indra.lan/indra-labs/indra" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/prv" + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" + log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" ) var ( diff --git a/version.go b/version.go index f7c1d48a..3e195823 100644 --- a/version.go +++ b/version.go @@ -10,19 +10,19 @@ var ( // GitRef is the gitref, as in refs/heads/branchname. GitRef = "refs/heads/main" // ParentGitCommit is the commit hash of the parent HEAD. - ParentGitCommit = "94f401d5e318c151c073bf81cf7685cbf3867347" + ParentGitCommit = "4c044ca4db090eb38d44d2b42998b1a16e87fd98" // BuildTime stores the time when the current binary was built. - BuildTime = "2023-01-27T15:41:43Z" + BuildTime = "2023-01-29T11:46:42Z" // SemVer lists the (latest) git tag on the release. - SemVer = "v0.1.12" + SemVer = "v0.0.6" // PathBase is the path base returned from runtime caller. PathBase = "/opt/indra-labs/indra/" // Major is the major number from the tag. Major = 0 // Minor is the minor number from the tag. - Minor = 1 + Minor = 0 // Patch is the patch version number from the tag. - Patch = 12 + Patch = 6 ) // Version returns a pretty printed version information string.