Merge branch 'protocol'
This commit is contained in:
@@ -7,7 +7,7 @@ package sig
|
||||
import (
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
|
||||
|
||||
|
||||
"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"
|
||||
@@ -21,13 +21,11 @@ var (
|
||||
)
|
||||
|
||||
// Len is the length of the signatures used in Indra, compact keys that can have
|
||||
// the public key extracted from them, thus eliminating the need to separately
|
||||
// specify it in messages.
|
||||
// the public key extracted from them.
|
||||
const Len = 65
|
||||
|
||||
// Bytes is an ECDSA BIP62 formatted compact signature which allows the recovery
|
||||
// of the public key from the signature. This allows messages to avoid adding
|
||||
// extra bytes to also specify the public key of the signer.
|
||||
// of the public key from the signature.
|
||||
type Bytes [Len]byte
|
||||
|
||||
// Sign produces an ECDSA BIP62 compact signature.
|
||||
|
||||
@@ -2,9 +2,9 @@ package hiddenservice
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/indra"
|
||||
"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/messages/intro"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
|
||||
log2 "git-indra.lan/indra-labs/indra/pkg/proc/log"
|
||||
"git-indra.lan/indra-labs/indra/pkg/splice"
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
const (
|
||||
MagicString = "hs"
|
||||
Len = magicbytes.Len + nonce.IDLen + pub.KeyLen +
|
||||
Len = magicbytes.Len + nonce.IDLen + intro.Len +
|
||||
3*sha256.Len + nonce.IVLen*3
|
||||
)
|
||||
|
||||
@@ -30,11 +30,7 @@ var (
|
||||
// header for any client that requests it.
|
||||
type Layer struct {
|
||||
nonce.ID
|
||||
// Identity is a public key identifying the hidden service. It is encoded
|
||||
// into Bech32 encoding to function like an IP address, with a 2 byte
|
||||
// truncated hash check suffix to eliminate possible human input errors and
|
||||
// ending in ".indra" to indicate it is an indra hidden service.
|
||||
Identity *pub.Key
|
||||
intro.Layer
|
||||
// Ciphers is a set of 3 symmetric ciphers that are to be used in their
|
||||
// given order over the reply message from the service.
|
||||
Ciphers [3]sha256.Hash
|
||||
@@ -56,7 +52,9 @@ func (x *Layer) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
splice.Splice(b, c).
|
||||
Magic(Magic).
|
||||
ID(x.ID).
|
||||
Pubkey(x.Identity).
|
||||
Pubkey(x.Key).
|
||||
AddrPort(x.AddrPort).
|
||||
Signature(x.Bytes).
|
||||
Hash(x.Ciphers[0]).Hash(x.Ciphers[1]).Hash(x.Ciphers[2]).
|
||||
IV(x.Nonces[0]).IV(x.Nonces[1]).IV(x.Nonces[2])
|
||||
}
|
||||
@@ -67,7 +65,9 @@ func (x *Layer) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
||||
}
|
||||
splice.Splice(b, c).
|
||||
ReadID(&x.ID).
|
||||
ReadPubkey(&x.Identity).
|
||||
ReadPubkey(&x.Layer.Key).
|
||||
ReadAddrPort(&x.Layer.AddrPort).
|
||||
ReadSignature(&x.Layer.Bytes).
|
||||
ReadHash(&x.Ciphers[0]).ReadHash(&x.Ciphers[1]).ReadHash(&x.Ciphers[2]).
|
||||
ReadIV(&x.Nonces[0]).ReadIV(&x.Nonces[1]).ReadIV(&x.Nonces[2])
|
||||
return
|
||||
|
||||
@@ -4,17 +4,27 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"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/sig"
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/sha256"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
|
||||
log2 "git-indra.lan/indra-labs/indra/pkg/proc/log"
|
||||
"git-indra.lan/indra-labs/indra/pkg/splice"
|
||||
"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
|
||||
)
|
||||
|
||||
const (
|
||||
MagicString = "in"
|
||||
AddrLen = net.IPv6len + 3
|
||||
Len = magicbytes.Len + pub.KeyLen + AddrLen
|
||||
Len = magicbytes.Len + pub.KeyLen + AddrLen + sig.Len
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -22,19 +32,60 @@ var (
|
||||
)
|
||||
|
||||
type Layer struct {
|
||||
*pub.Key
|
||||
*netip.AddrPort
|
||||
Key *pub.Key
|
||||
AddrPort *netip.AddrPort
|
||||
Bytes sig.Bytes
|
||||
}
|
||||
|
||||
func New(key *prv.Key, ap *netip.AddrPort) (in *Layer) {
|
||||
pk := pub.Derive(key)
|
||||
bap, _ := ap.MarshalBinary()
|
||||
pkb := pk.ToBytes()
|
||||
hash := sha256.Single(append(pkb[:], bap...))
|
||||
var e error
|
||||
var sign sig.Bytes
|
||||
if sign, e = sig.Sign(key, hash); check(e) {
|
||||
return nil
|
||||
}
|
||||
in = &Layer{
|
||||
Key: pk,
|
||||
AddrPort: ap,
|
||||
Bytes: sign,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (im *Layer) Validate() bool {
|
||||
bap, _ := im.AddrPort.MarshalBinary()
|
||||
pkb := im.Key.ToBytes()
|
||||
hash := sha256.Single(append(pkb[:], bap...))
|
||||
key, e := im.Bytes.Recover(hash)
|
||||
if check(e) {
|
||||
return false
|
||||
}
|
||||
kb := key.ToBytes()
|
||||
if kb.Equals(pkb) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (im *Layer) Insert(o types.Onion) {}
|
||||
func (im *Layer) Len() int { return Len }
|
||||
|
||||
func (im *Layer) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
splice.Splice(b, c).Magic(Magic).Pubkey(im.Key).AddrPort(im.AddrPort)
|
||||
splice.Splice(b, c).
|
||||
Magic(Magic).
|
||||
Pubkey(im.Key).
|
||||
AddrPort(im.AddrPort).
|
||||
Signature(im.Bytes)
|
||||
return
|
||||
}
|
||||
|
||||
func (im *Layer) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
||||
splice.Splice(b, c).ReadPubkey(&im.Key).ReadAddrPort(&im.AddrPort)
|
||||
splice.Splice(b, c).
|
||||
ReadPubkey(&im.Key).
|
||||
ReadAddrPort(&im.AddrPort).
|
||||
ReadSignature(&im.Bytes)
|
||||
return
|
||||
}
|
||||
|
||||
22
pkg/messages/intro/intro-message_test.go
Normal file
22
pkg/messages/intro/intro-message_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package intro
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/key/prv"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
func TestLayer_Validate(t *testing.T) {
|
||||
addr := slice.GenerateRandomAddrPortIPv4()
|
||||
var e error
|
||||
var idPrv *prv.Key
|
||||
if idPrv, e = prv.GenerateKey(); check(e) {
|
||||
return
|
||||
}
|
||||
im := New(idPrv, addr)
|
||||
log.I.S(im)
|
||||
if !im.Validate() {
|
||||
t.Error("failed to validate")
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ var (
|
||||
// is concealed to the hops except for the encryption crypt they decrypt using
|
||||
// their Payload key, delivered in this message.
|
||||
type Layer struct {
|
||||
nonce.ID
|
||||
nonce.ID // only used by a node
|
||||
Hop byte // only used by a node
|
||||
Header, Payload *prv.Key
|
||||
types.Onion
|
||||
|
||||
157
pkg/relay/balance.go
Normal file
157
pkg/relay/balance.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"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/key/signer"
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/balance"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/crypt"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/getbalance"
|
||||
"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) {
|
||||
|
||||
local := eng.GetLocalNodeAddress()
|
||||
pending := eng.PendingResponses.Find(on.ID)
|
||||
if pending != nil {
|
||||
for i := range pending.Billable {
|
||||
s := eng.FindSession(pending.Billable[i])
|
||||
if s != nil {
|
||||
switch {
|
||||
case i < 2:
|
||||
in := s.RelayRate * lnwire.MilliSatoshi(
|
||||
pending.SentSize) / 1024 / 1024
|
||||
eng.DecSession(s.ID, in, true, "reverse")
|
||||
case i == 2:
|
||||
in := s.RelayRate * lnwire.MilliSatoshi(
|
||||
pending.SentSize/2) / 1024 / 1024
|
||||
out := s.RelayRate * lnwire.MilliSatoshi(
|
||||
len(b)/2) / 1024 / 1024
|
||||
eng.DecSession(s.ID, in+out, true, "getbalance")
|
||||
case i > 2:
|
||||
out := s.RelayRate * lnwire.MilliSatoshi(
|
||||
len(b)) / 1024 / 1024
|
||||
eng.DecSession(s.ID, out, true, "reverse")
|
||||
}
|
||||
}
|
||||
}
|
||||
var se *Session
|
||||
eng.IterateSessions(func(s *Session) bool {
|
||||
if s.ID == on.ID {
|
||||
log.D.F("%s received balance %s for session %s %s was %s",
|
||||
local,
|
||||
on.MilliSatoshi, on.ID, on.ConfID, s.Remaining)
|
||||
se = s
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
eng.PendingResponses.Delete(pending.ID, nil)
|
||||
if se != nil {
|
||||
log.D.F("got %v, expected %v", se.Remaining, on.MilliSatoshi)
|
||||
se.Remaining = on.MilliSatoshi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetBalance sends out a request in a similar way to SendExit except the node
|
||||
// being queried can be any of the 5.
|
||||
func GetBalance(id, confID nonce.ID, client *Session,
|
||||
s Circuit, ks *signer.KeySet) Skins {
|
||||
|
||||
var prvs [3]*prv.Key
|
||||
for i := range prvs {
|
||||
prvs[i] = ks.Next()
|
||||
}
|
||||
n := GenNonces(6)
|
||||
var retNonces [3]nonce.IV
|
||||
copy(retNonces[:], n[3:])
|
||||
var pubs [3]*pub.Key
|
||||
pubs[0] = s[3].PayloadPub
|
||||
pubs[1] = s[4].PayloadPub
|
||||
pubs[2] = client.PayloadPub
|
||||
return Skins{}.
|
||||
ReverseCrypt(s[0], ks.Next(), n[0], 3).
|
||||
ReverseCrypt(s[1], ks.Next(), n[1], 2).
|
||||
ReverseCrypt(s[2], ks.Next(), n[2], 1).
|
||||
GetBalance(id, confID, prvs, pubs, retNonces).
|
||||
ReverseCrypt(s[3], prvs[0], n[3], 0).
|
||||
ReverseCrypt(s[4], prvs[1], n[4], 0).
|
||||
ReverseCrypt(client, prvs[2], n[5], 0)
|
||||
}
|
||||
|
||||
func (eng *Engine) SendGetBalance(target *Session, hook Callback) {
|
||||
hops := []byte{0, 1, 2, 3, 4, 5}
|
||||
s := make(Sessions, len(hops))
|
||||
s[2] = target
|
||||
se := eng.SelectHops(hops, s)
|
||||
var c Circuit
|
||||
copy(c[:], se)
|
||||
confID := nonce.NewID()
|
||||
o := GetBalance(target.ID, confID, se[5], c, eng.KeySet)
|
||||
log.D.Ln("sending out getbalance onion")
|
||||
res := eng.PostAcctOnion(o)
|
||||
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
||||
}
|
||||
|
||||
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
|
||||
eng.IterateSessions(func(s *Session) bool {
|
||||
if s.ID == on.ID {
|
||||
bal = &balance.Layer{
|
||||
ID: on.ID,
|
||||
ConfID: on.ConfID,
|
||||
MilliSatoshi: s.Remaining,
|
||||
}
|
||||
found = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
if !found {
|
||||
log.E.Ln("session not found", on.ID)
|
||||
log.D.S(eng.Sessions)
|
||||
return
|
||||
}
|
||||
header := b[*c:c.Inc(crypt.ReverseHeaderLen)]
|
||||
rb := FormatReply(header,
|
||||
Encode(bal), on.Ciphers, on.Nonces)
|
||||
rb = append(rb, slice.NoisePad(714-len(rb))...)
|
||||
switch on1 := prev.(type) {
|
||||
case *crypt.Layer:
|
||||
sess := eng.FindSessionByHeader(on1.ToPriv)
|
||||
if sess != nil {
|
||||
in := sess.RelayRate *
|
||||
lnwire.MilliSatoshi(len(b)) / 2 / 1024 / 1024
|
||||
out := sess.RelayRate *
|
||||
lnwire.MilliSatoshi(len(rb)) / 2 / 1024 / 1024
|
||||
eng.DecSession(sess.ID, in+out, false, "getbalance")
|
||||
}
|
||||
}
|
||||
eng.IterateSessions(func(s *Session) bool {
|
||||
if s.ID == on.ID {
|
||||
bal = &balance.Layer{
|
||||
ID: on.ID,
|
||||
ConfID: on.ConfID,
|
||||
MilliSatoshi: s.Remaining,
|
||||
}
|
||||
found = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
rb = FormatReply(header,
|
||||
Encode(bal), on.Ciphers, on.Nonces)
|
||||
rb = append(rb, slice.NoisePad(714-len(rb))...)
|
||||
eng.handleMessage(rb, on)
|
||||
}
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"go.uber.org/atomic"
|
||||
|
||||
"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/messages/intro"
|
||||
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/transport"
|
||||
@@ -290,10 +290,10 @@ func TestClient_HiddenService(t *testing.T) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
identPub := pub.Derive(identPrv)
|
||||
id := nonce.NewID()
|
||||
il := intro.New(identPrv, clients[0].GetLocalNodeAddress())
|
||||
clients[0].SendIntro(id, clients[0].Sessions[i+returns],
|
||||
identPub, func(id nonce.ID, b slice.Bytes) {
|
||||
il, func(id nonce.ID, b slice.Bytes) {
|
||||
log.I.Ln("success")
|
||||
})
|
||||
}
|
||||
@@ -302,8 +302,9 @@ func TestClient_HiddenService(t *testing.T) {
|
||||
v.Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_HiddenServiceBroadcast(t *testing.T) {
|
||||
log2.SetLogLevel(log2.Trace)
|
||||
log2.SetLogLevel(log2.Info)
|
||||
var clients []*Engine
|
||||
var e error
|
||||
const returns = 2
|
||||
@@ -340,10 +341,12 @@ func TestClient_HiddenServiceBroadcast(t *testing.T) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
identPub := pub.Derive(identPrv)
|
||||
log2.SetLogLevel(log2.Trace)
|
||||
// identPub := pub.Derive(identPrv)
|
||||
id := nonce.NewID()
|
||||
il := intro.New(identPrv, clients[0].GetLocalNodeAddress())
|
||||
clients[0].SendIntro(id, clients[0].Sessions[returns],
|
||||
identPub, func(id nonce.ID, b slice.Bytes) {
|
||||
il, func(id nonce.ID, b slice.Bytes) {
|
||||
log.I.Ln("success")
|
||||
})
|
||||
time.Sleep(time.Second * 5)
|
||||
|
||||
140
pkg/relay/exit.go
Normal file
140
pkg/relay/exit.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"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/key/signer"
|
||||
"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/messages/crypt"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/exit"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/response"
|
||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
// SendExit constructs a message containing an arbitrary payload to a node (3rd
|
||||
// hop) with a set of 3 ciphers derived from the hidden PayloadPub of the return
|
||||
// hops that are layered progressively after the Exit message.
|
||||
//
|
||||
// The Exit node forwards the packet it receives to the local port specified in
|
||||
// the Exit message, and then uses the ciphers to encrypt the reply with the
|
||||
// three ciphers provided, which don't enable it to decrypt the header, only to
|
||||
// encrypt the payload.
|
||||
//
|
||||
// The response is encrypted with the given layers, the ciphers are already
|
||||
// given in reverse order, so they are decoded in given order to create the
|
||||
// correct payload encryption to match the PayloadPub combined with the header's
|
||||
// given public From key.
|
||||
//
|
||||
// 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, id nonce.ID,
|
||||
client *Session, s Circuit, ks *signer.KeySet) Skins {
|
||||
|
||||
var prvs [3]*prv.Key
|
||||
for i := range prvs {
|
||||
prvs[i] = ks.Next()
|
||||
}
|
||||
n := GenNonces(6)
|
||||
var returnNonces [3]nonce.IV
|
||||
copy(returnNonces[:], n[3:])
|
||||
var pubs [3]*pub.Key
|
||||
pubs[0] = s[3].PayloadPub
|
||||
pubs[1] = s[4].PayloadPub
|
||||
pubs[2] = client.PayloadPub
|
||||
return Skins{}.
|
||||
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, id, payload).
|
||||
ReverseCrypt(s[3], prvs[0], n[3], 3).
|
||||
ReverseCrypt(s[4], prvs[1], n[4], 2).
|
||||
ReverseCrypt(client, prvs[2], n[5], 1)
|
||||
}
|
||||
|
||||
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("%s received exit id %x", eng.GetLocalNodeAddress(), ex.ID)
|
||||
if e = eng.SendFromLocalNode(ex.Port, ex.Bytes); check(e) {
|
||||
return
|
||||
}
|
||||
timer := time.NewTicker(time.Second)
|
||||
select {
|
||||
case result = <-eng.ReceiveToLocalNode(ex.Port):
|
||||
case <-timer.C:
|
||||
}
|
||||
// We need to wrap the result in a message crypt.
|
||||
res := Encode(&response.Layer{
|
||||
ID: ex.ID,
|
||||
Port: ex.Port,
|
||||
Load: byte(eng.Load.Load()),
|
||||
Bytes: result,
|
||||
})
|
||||
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)
|
||||
}
|
||||
|
||||
func (eng *Engine) SendExit(port uint16, msg slice.Bytes, id nonce.ID,
|
||||
target *Session, hook Callback) {
|
||||
|
||||
hops := []byte{0, 1, 2, 3, 4, 5}
|
||||
s := make(Sessions, len(hops))
|
||||
s[2] = target
|
||||
se := eng.SelectHops(hops, s)
|
||||
var c Circuit
|
||||
copy(c[:], se)
|
||||
o := SendExit(port, msg, id, se[len(se)-1], c, eng.KeySet)
|
||||
log.D.Ln("sending out exit onion")
|
||||
res := eng.PostAcctOnion(o)
|
||||
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
||||
}
|
||||
|
||||
func (eng *Engine) MakeExit(port uint16, msg slice.Bytes, id nonce.ID,
|
||||
exit *Session) (c Circuit, o Skins) {
|
||||
|
||||
hops := []byte{0, 1, 2, 3, 4, 5}
|
||||
s := make(Sessions, len(hops))
|
||||
s[2] = exit
|
||||
se := eng.SelectHops(hops, s)
|
||||
copy(c[:], se)
|
||||
o = SendExit(port, msg, id, se[len(se)-1], c, eng.KeySet)
|
||||
return
|
||||
}
|
||||
|
||||
func (eng *Engine) SendExitNew(c Circuit, o Skins, hook Callback) {
|
||||
log.D.Ln("sending out exit onion")
|
||||
res := eng.PostAcctOnion(o)
|
||||
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
||||
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/balance"
|
||||
"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) {
|
||||
|
||||
local := eng.GetLocalNodeAddress()
|
||||
pending := eng.PendingResponses.Find(on.ID)
|
||||
if pending != nil {
|
||||
for i := range pending.Billable {
|
||||
s := eng.FindSession(pending.Billable[i])
|
||||
if s != nil {
|
||||
switch {
|
||||
case i < 2:
|
||||
in := s.RelayRate * lnwire.MilliSatoshi(
|
||||
pending.SentSize) / 1024 / 1024
|
||||
eng.DecSession(s.ID, in, true, "reverse")
|
||||
case i == 2:
|
||||
in := s.RelayRate * lnwire.MilliSatoshi(
|
||||
pending.SentSize/2) / 1024 / 1024
|
||||
out := s.RelayRate * lnwire.MilliSatoshi(
|
||||
len(b)/2) / 1024 / 1024
|
||||
eng.DecSession(s.ID, in+out, true, "getbalance")
|
||||
case i > 2:
|
||||
out := s.RelayRate * lnwire.MilliSatoshi(
|
||||
len(b)) / 1024 / 1024
|
||||
eng.DecSession(s.ID, out, true, "reverse")
|
||||
}
|
||||
}
|
||||
}
|
||||
var se *Session
|
||||
eng.IterateSessions(func(s *Session) bool {
|
||||
if s.ID == on.ID {
|
||||
log.D.F("%s received balance %s for session %s %s was %s",
|
||||
local,
|
||||
on.MilliSatoshi, on.ID, on.ConfID, s.Remaining)
|
||||
se = s
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
eng.PendingResponses.Delete(pending.ID, nil)
|
||||
if se != nil {
|
||||
log.D.F("got %v, expected %v", se.Remaining, on.MilliSatoshi)
|
||||
se.Remaining = on.MilliSatoshi
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/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) {
|
||||
|
||||
// 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)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/crypt"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/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, _, _, 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 {
|
||||
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
|
||||
}
|
||||
eng.handleMessage(BudgeUp(b, *c), on)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/delay"
|
||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
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
|
||||
// elapses. The accounting for the remainder of the message adds a
|
||||
// factor to the effective byte consumption in accordance with the time
|
||||
// to be stored.
|
||||
// todo: accounting
|
||||
select {
|
||||
case <-time.After(on.Duration):
|
||||
}
|
||||
eng.handleMessage(BudgeUp(b, *c), on)
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
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/messages/crypt"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/exit"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/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("%s received exit id %x", eng.GetLocalNodeAddress(), ex.ID)
|
||||
if e = eng.SendFromLocalNode(ex.Port, ex.Bytes); check(e) {
|
||||
return
|
||||
}
|
||||
timer := time.NewTicker(time.Second)
|
||||
select {
|
||||
case result = <-eng.ReceiveToLocalNode(ex.Port):
|
||||
case <-timer.C:
|
||||
}
|
||||
// We need to wrap the result in a message crypt.
|
||||
res := Encode(&response.Layer{
|
||||
ID: ex.ID,
|
||||
Port: ex.Port,
|
||||
Load: byte(eng.Load.Load()),
|
||||
Bytes: result,
|
||||
})
|
||||
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)
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
||||
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/crypt"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/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, BudgeUp(b, *c))
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
||||
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/balance"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/crypt"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/getbalance"
|
||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
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
|
||||
eng.IterateSessions(func(s *Session) bool {
|
||||
if s.ID == on.ID {
|
||||
bal = &balance.Layer{
|
||||
ID: on.ID,
|
||||
ConfID: on.ConfID,
|
||||
MilliSatoshi: s.Remaining,
|
||||
}
|
||||
found = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
if !found {
|
||||
log.E.Ln("session not found", on.ID)
|
||||
log.D.S(eng.Sessions)
|
||||
return
|
||||
}
|
||||
header := b[*c:c.Inc(crypt.ReverseHeaderLen)]
|
||||
rb := FormatReply(header,
|
||||
Encode(bal), on.Ciphers, on.Nonces)
|
||||
rb = append(rb, slice.NoisePad(714-len(rb))...)
|
||||
switch on1 := prev.(type) {
|
||||
case *crypt.Layer:
|
||||
sess := eng.FindSessionByHeader(on1.ToPriv)
|
||||
if sess != nil {
|
||||
in := sess.RelayRate *
|
||||
lnwire.MilliSatoshi(len(b)) / 2 / 1024 / 1024
|
||||
out := sess.RelayRate *
|
||||
lnwire.MilliSatoshi(len(rb)) / 2 / 1024 / 1024
|
||||
eng.DecSession(sess.ID, in+out, false, "getbalance")
|
||||
}
|
||||
}
|
||||
eng.IterateSessions(func(s *Session) bool {
|
||||
if s.ID == on.ID {
|
||||
bal = &balance.Layer{
|
||||
ID: on.ID,
|
||||
ConfID: on.ConfID,
|
||||
MilliSatoshi: s.Remaining,
|
||||
}
|
||||
found = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
rb = FormatReply(header,
|
||||
Encode(bal), on.Ciphers, on.Nonces)
|
||||
rb = append(rb, slice.NoisePad(714-len(rb))...)
|
||||
eng.handleMessage(rb, on)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/hiddenservice"
|
||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
func (eng *Engine) hiddenservice(hs *hiddenservice.Layer, b slice.Bytes,
|
||||
c *slice.Cursor, prev types.Onion) {
|
||||
|
||||
log.D.F("%s adding introduction for key %s", eng.GetLocalNodeAddress(),
|
||||
hs.Identity.ToBase32())
|
||||
eng.Introductions.AddIntro(hs.Identity, b[*c:])
|
||||
log.I.Ln("stored new introduction, starting broadcast")
|
||||
go eng.hiddenserviceBroadcaster(hs.Identity)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
|
||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
func (eng *Engine) intro(intr *intro.Layer, b slice.Bytes,
|
||||
c *slice.Cursor, prev types.Onion) {
|
||||
|
||||
log.D.F("sending out intro to %s at %s to all known peers",
|
||||
intr.Key.ToBase32(), intr.AddrPort.String())
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
||||
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/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)
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
||||
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/ciph"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/crypt"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/reverse"
|
||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
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() == eng.GetLocalNodeAddress().String() {
|
||||
if on2, e = Peel(b, c); check(e) {
|
||||
return
|
||||
}
|
||||
switch on1 := on2.(type) {
|
||||
case *crypt.Layer:
|
||||
start := *c - crypt.ReverseLayerLen
|
||||
first := *c
|
||||
second := first + crypt.ReverseLayerLen
|
||||
last := second + crypt.ReverseLayerLen
|
||||
log.T.Ln("searching for reverse crypt keys")
|
||||
hdr, pld, _, _ := eng.FindCloaked(on1.Cloak)
|
||||
if hdr == nil || pld == nil {
|
||||
log.E.F("failed to find key for %s",
|
||||
eng.GetLocalNodeAddress().String())
|
||||
return
|
||||
}
|
||||
// We need to find the PayloadPub to match.
|
||||
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, on1.FromPub)
|
||||
ciph.Encipher(blk, on1.Nonce, b[*c:])
|
||||
// shift the header segment upwards and pad the
|
||||
// remainder.
|
||||
copy(b[start:first], b[first:second])
|
||||
copy(b[first:second], b[second:last])
|
||||
copy(b[second:last], slice.NoisePad(crypt.ReverseLayerLen))
|
||||
if b[start:start+2].String() != reverse.MagicString {
|
||||
// It's for us!
|
||||
log.T.Ln("handling response")
|
||||
eng.handleMessage(BudgeUp(b, last), on1)
|
||||
break
|
||||
}
|
||||
sess := eng.FindSessionByHeader(hdr)
|
||||
if sess != nil {
|
||||
eng.DecSession(sess.ID,
|
||||
eng.GetLocalNodeRelayRate()*lnwire.
|
||||
MilliSatoshi(len(b))/1024/1024, false, "reverse")
|
||||
eng.handleMessage(BudgeUp(b, start), on1)
|
||||
}
|
||||
default:
|
||||
// If a reverse is not followed by an onion crypt the
|
||||
// message is incorrectly formed, just drop it.
|
||||
return
|
||||
}
|
||||
} else if prev != nil {
|
||||
// we need to forward this message onion.
|
||||
log.T.Ln("forwarding reverse")
|
||||
eng.Send(on.AddrPort, b)
|
||||
} else {
|
||||
log.E.Ln("we do not forward nonsense! scoff! snort!")
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/session"
|
||||
"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.D.Ln(prev == nil)
|
||||
log.T.F("incoming session %s", on.ID)
|
||||
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.D.F("Adding session %s to %s", pi.ID, eng.GetLocalNodeAddress())
|
||||
eng.AddSession(NewSession(pi.ID,
|
||||
eng.GetLocalNode(), pi.Amount, on.Header, on.Payload, on.Hop))
|
||||
eng.handleMessage(BudgeUp(b, *c), on)
|
||||
} else {
|
||||
log.E.Ln("dropping session message without payment")
|
||||
}
|
||||
}
|
||||
179
pkg/relay/handlers.go
Normal file
179
pkg/relay/handlers.go
Normal file
@@ -0,0 +1,179 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
||||
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/ciph"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/confirm"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/crypt"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/delay"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/forward"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/response"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/reverse"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/session"
|
||||
"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) {
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
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, _, _, 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 {
|
||||
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
|
||||
}
|
||||
eng.handleMessage(BudgeUp(b, *c), on)
|
||||
}
|
||||
|
||||
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
|
||||
// elapses. The accounting for the remainder of the message adds a
|
||||
// factor to the effective byte consumption in accordance with the time
|
||||
// to be stored.
|
||||
// todo: accounting
|
||||
select {
|
||||
case <-time.After(on.Duration):
|
||||
}
|
||||
eng.handleMessage(BudgeUp(b, *c), on)
|
||||
}
|
||||
|
||||
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, BudgeUp(b, *c))
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
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() == eng.GetLocalNodeAddress().String() {
|
||||
if on2, e = Peel(b, c); check(e) {
|
||||
return
|
||||
}
|
||||
switch on1 := on2.(type) {
|
||||
case *crypt.Layer:
|
||||
start := *c - crypt.ReverseLayerLen
|
||||
first := *c
|
||||
second := first + crypt.ReverseLayerLen
|
||||
last := second + crypt.ReverseLayerLen
|
||||
log.T.Ln("searching for reverse crypt keys")
|
||||
hdr, pld, _, _ := eng.FindCloaked(on1.Cloak)
|
||||
if hdr == nil || pld == nil {
|
||||
log.E.F("failed to find key for %s",
|
||||
eng.GetLocalNodeAddress().String())
|
||||
return
|
||||
}
|
||||
// We need to find the PayloadPub to match.
|
||||
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, on1.FromPub)
|
||||
ciph.Encipher(blk, on1.Nonce, b[*c:])
|
||||
// shift the header segment upwards and pad the
|
||||
// remainder.
|
||||
copy(b[start:first], b[first:second])
|
||||
copy(b[first:second], b[second:last])
|
||||
copy(b[second:last], slice.NoisePad(crypt.ReverseLayerLen))
|
||||
if b[start:start+2].String() != reverse.MagicString {
|
||||
// It's for us!
|
||||
log.T.Ln("handling response")
|
||||
eng.handleMessage(BudgeUp(b, last), on1)
|
||||
break
|
||||
}
|
||||
sess := eng.FindSessionByHeader(hdr)
|
||||
if sess != nil {
|
||||
eng.DecSession(sess.ID,
|
||||
eng.GetLocalNodeRelayRate()*lnwire.MilliSatoshi(len(b))/1024/1024, false, "reverse")
|
||||
eng.handleMessage(BudgeUp(b, start), on1)
|
||||
}
|
||||
default:
|
||||
// If a reverse is not followed by an onion crypt the
|
||||
// message is incorrectly formed, just drop it.
|
||||
return
|
||||
}
|
||||
} else if prev != nil {
|
||||
// we need to forward this message onion.
|
||||
log.T.Ln("forwarding reverse")
|
||||
eng.Send(on.AddrPort, b)
|
||||
} else {
|
||||
log.E.Ln("we do not forward nonsense! scoff! snort!")
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
||||
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/session"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/cryptorand"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
// BuyNewSessions performs the initial purchase of 5 sessions as well as adding
|
||||
// different hop numbers to relays with existing sessions. Note that all 5 of
|
||||
// the sessions will be paid the amount specified, not divided up.
|
||||
func (eng *Engine) BuyNewSessions(amount lnwire.MilliSatoshi,
|
||||
hook func()) (e error) {
|
||||
|
||||
var nodes [5]*Node
|
||||
nodes = eng.SessionManager.SelectUnusedCircuit()
|
||||
for i := range nodes {
|
||||
if nodes[i] == nil {
|
||||
e = fmt.Errorf("failed to find nodes %d", i)
|
||||
return
|
||||
}
|
||||
}
|
||||
// Get a random return hop session (index 5).
|
||||
var returnSession *Session
|
||||
returnHops := eng.SessionManager.GetSessionsAtHop(5)
|
||||
if len(returnHops) > 1 {
|
||||
cryptorand.Shuffle(len(returnHops), func(i, j int) {
|
||||
returnHops[i], returnHops[j] = returnHops[j], returnHops[i]
|
||||
})
|
||||
}
|
||||
// There must be at least one, and if there was more than one the first
|
||||
// index of returnHops will be a randomly selected one.
|
||||
returnSession = returnHops[0]
|
||||
conf := nonce.NewID()
|
||||
var s [5]*session.Layer
|
||||
for i := range s {
|
||||
s[i] = session.New(byte(i))
|
||||
}
|
||||
var confirmChans [5]chan bool
|
||||
var pendingConfirms int
|
||||
for i := range nodes {
|
||||
confirmChans[i] = nodes[i].
|
||||
PaymentChan.Send(amount, s[i])
|
||||
pendingConfirms++
|
||||
}
|
||||
var success bool
|
||||
for pendingConfirms > 0 {
|
||||
// The confirmation channels will signal upon success or failure
|
||||
// according to the LN payment send protocol once either the HTLCs
|
||||
// confirm on the way back or the path fails.
|
||||
select {
|
||||
case success = <-confirmChans[0]:
|
||||
if success {
|
||||
pendingConfirms--
|
||||
}
|
||||
case success = <-confirmChans[1]:
|
||||
if success {
|
||||
pendingConfirms--
|
||||
}
|
||||
case success = <-confirmChans[2]:
|
||||
if success {
|
||||
pendingConfirms--
|
||||
}
|
||||
case success = <-confirmChans[3]:
|
||||
if success {
|
||||
pendingConfirms--
|
||||
}
|
||||
case success = <-confirmChans[4]:
|
||||
if success {
|
||||
pendingConfirms--
|
||||
}
|
||||
}
|
||||
}
|
||||
// todo: handle payment failures!
|
||||
o := SendKeys(conf, s, returnSession, nodes[:], eng.KeySet)
|
||||
res := eng.PostAcctOnion(o)
|
||||
eng.SendWithOneHook(nodes[0].AddrPort, res, func(id nonce.ID, b slice.Bytes) {
|
||||
eng.SessionManager.Lock()
|
||||
defer eng.SessionManager.Unlock()
|
||||
var sessions [5]*Session
|
||||
for i := range nodes {
|
||||
log.D.F("confirming and storing session at hop %d %s for %s with"+
|
||||
" %v initial"+
|
||||
" balance", i, s[i].ID, nodes[i].AddrPort.String(), amount)
|
||||
sessions[i] = NewSession(s[i].ID, nodes[i], amount,
|
||||
s[i].Header, s[i].Payload, byte(i))
|
||||
eng.SessionManager.Add(sessions[i])
|
||||
eng.Sessions = append(eng.Sessions, sessions[i])
|
||||
eng.SessionManager.PendingPayments.Delete(s[i].PreimageHash())
|
||||
}
|
||||
hook()
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"runtime"
|
||||
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
// Send a message to a peer via their AddrPort.
|
||||
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()
|
||||
eng.ForEachNode(func(n *Node) bool {
|
||||
if as == n.AddrPort.String() {
|
||||
_, f, l, _ := runtime.Caller(1)
|
||||
log.T.F("%s sending message to %v %s:%d",
|
||||
eng.GetLocalNode().AddrPort.String(), addr, f, l)
|
||||
n.Transport.Send(b)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
func (eng *Engine) SendExit(port uint16, msg slice.Bytes, id nonce.ID,
|
||||
target *Session, hook Callback) {
|
||||
|
||||
hops := []byte{0, 1, 2, 3, 4, 5}
|
||||
s := make(Sessions, len(hops))
|
||||
s[2] = target
|
||||
se := eng.SelectHops(hops, s)
|
||||
var c Circuit
|
||||
copy(c[:], se)
|
||||
o := SendExit(port, msg, id, se[len(se)-1], c, eng.KeySet)
|
||||
log.D.Ln("sending out exit onion")
|
||||
res := eng.PostAcctOnion(o)
|
||||
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
||||
}
|
||||
|
||||
func (eng *Engine) MakeExit(port uint16, msg slice.Bytes, id nonce.ID,
|
||||
exit *Session) (c Circuit, o Skins) {
|
||||
|
||||
hops := []byte{0, 1, 2, 3, 4, 5}
|
||||
s := make(Sessions, len(hops))
|
||||
s[2] = exit
|
||||
se := eng.SelectHops(hops, s)
|
||||
copy(c[:], se)
|
||||
o = SendExit(port, msg, id, se[len(se)-1], c, eng.KeySet)
|
||||
return
|
||||
}
|
||||
|
||||
func (eng *Engine) SendExitNew(c Circuit, o Skins, hook Callback) {
|
||||
log.D.Ln("sending out exit onion")
|
||||
res := eng.PostAcctOnion(o)
|
||||
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
||||
)
|
||||
|
||||
func (eng *Engine) SendGetBalance(target *Session, hook Callback) {
|
||||
hops := []byte{0, 1, 2, 3, 4, 5}
|
||||
s := make(Sessions, len(hops))
|
||||
s[2] = target
|
||||
se := eng.SelectHops(hops, s)
|
||||
var c Circuit
|
||||
copy(c[:], se)
|
||||
confID := nonce.NewID()
|
||||
o := GetBalance(target.ID, confID, se[5], c, eng.KeySet)
|
||||
log.D.Ln("sending out getbalance onion")
|
||||
res := eng.PostAcctOnion(o)
|
||||
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"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/util/slice"
|
||||
)
|
||||
|
||||
func (eng *Engine) SendIntro(id nonce.ID, target *Session, ident *pub.Key,
|
||||
hook func(id nonce.ID, b slice.Bytes)) {
|
||||
|
||||
log.I.Ln(target.Hop)
|
||||
hops := []byte{0, 1, 2, 3, 4, 5}
|
||||
s := make(Sessions, len(hops))
|
||||
s[2] = target
|
||||
se := eng.SelectHops(hops, s)
|
||||
var c Circuit
|
||||
copy(c[:], se)
|
||||
o := HiddenService(id, ident, se[len(se)-1], c, eng.KeySet)
|
||||
log.D.Ln("sending out intro onion")
|
||||
res := eng.PostAcctOnion(o)
|
||||
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
||||
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/balance"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/confirm"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/crypt"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/exit"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/forward"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/getbalance"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/reverse"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
func (eng *Engine) SendOnion(ap *netip.AddrPort, o Skins,
|
||||
responseHook func(id nonce.ID, b slice.Bytes), timeout time.Duration) {
|
||||
|
||||
if timeout == 0 {
|
||||
timeout = DefaultTimeout
|
||||
}
|
||||
b := Encode(o.Assemble())
|
||||
var billable []nonce.ID
|
||||
var ret nonce.ID
|
||||
var last nonce.ID
|
||||
var port uint16
|
||||
var postAcct []func()
|
||||
var sessions Sessions
|
||||
// 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)
|
||||
if s == nil {
|
||||
continue
|
||||
}
|
||||
sessions = append(sessions, s)
|
||||
// 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
|
||||
billable = append(billable, s.ID)
|
||||
break
|
||||
}
|
||||
switch on2 := o[i+1].(type) {
|
||||
case *forward.Layer:
|
||||
billable = append(billable, s.ID)
|
||||
postAcct = append(postAcct,
|
||||
func() {
|
||||
eng.DecSession(s.ID,
|
||||
s.RelayRate*
|
||||
lnwire.MilliSatoshi(len(b))/1024/1024, true,
|
||||
"forward")
|
||||
})
|
||||
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
|
||||
postAcct = append(postAcct,
|
||||
func() {
|
||||
eng.DecSession(s.ID,
|
||||
s.Services[j].RelayRate*
|
||||
lnwire.MilliSatoshi(len(b)/2)/1024/1024,
|
||||
true, "exit")
|
||||
})
|
||||
break
|
||||
}
|
||||
billable = append(billable, s.ID)
|
||||
last = on2.ID
|
||||
skip = true
|
||||
case *getbalance.Layer:
|
||||
last = s.ID
|
||||
billable = append(billable, s.ID)
|
||||
skip = true
|
||||
}
|
||||
case *confirm.Layer:
|
||||
last = on.ID
|
||||
case *balance.Layer:
|
||||
last = on.ID
|
||||
}
|
||||
}
|
||||
if responseHook == nil {
|
||||
responseHook = func(_ nonce.ID, _ slice.Bytes) {
|
||||
log.D.Ln("nil response hook")
|
||||
}
|
||||
}
|
||||
eng.PendingResponses.Add(last, len(b), sessions, billable, ret, port,
|
||||
responseHook, postAcct)
|
||||
log.T.Ln("sending out onion")
|
||||
eng.Send(ap, b)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
||||
)
|
||||
|
||||
func (eng *Engine) SendPing(c Circuit, hook Callback) {
|
||||
hops := []byte{0, 1, 2, 3, 4, 5}
|
||||
s := make(Sessions, len(hops))
|
||||
copy(s, c[:])
|
||||
se := eng.SelectHops(hops, s)
|
||||
copy(c[:], se)
|
||||
confID := nonce.NewID()
|
||||
o := Ping(confID, se[len(se)-1], c, eng.KeySet)
|
||||
res := eng.PostAcctOnion(o)
|
||||
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/cybriq/qu"
|
||||
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/key/pub"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/cryptorand"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
type Referrers map[pub.Bytes][]pub.Bytes
|
||||
|
||||
func (eng *Engine) hiddenserviceBroadcaster(hsk *pub.Key) {
|
||||
log.D.F("propagating hidden service introduction for %x", hsk.ToBytes())
|
||||
done := qu.T()
|
||||
me := eng.GetLocalNodeAddress()
|
||||
intr := &intro.Layer{
|
||||
Key: hsk, AddrPort: me,
|
||||
}
|
||||
msg := make(slice.Bytes, intro.Len)
|
||||
c := slice.NewCursor()
|
||||
intr.Encode(msg, c)
|
||||
nPeers := eng.NodesLen()
|
||||
peerIndices := make([]int, nPeers)
|
||||
for i := 0; i < nPeers; i++ {
|
||||
peerIndices[i] = i
|
||||
}
|
||||
cryptorand.Shuffle(nPeers, func(i, j int) {
|
||||
peerIndices[i], peerIndices[j] = peerIndices[j], peerIndices[i]
|
||||
})
|
||||
// Since relays will also gossip this information, we will start a ticker
|
||||
// that sends out the hidden service introduction once a second until it
|
||||
// runs out of known relays to gossip to.
|
||||
ticker := time.NewTicker(time.Second)
|
||||
var cursor int
|
||||
for {
|
||||
select {
|
||||
case <-eng.C.Wait():
|
||||
return
|
||||
case <-done:
|
||||
return
|
||||
case <-ticker.C:
|
||||
n := eng.FindNodeByIndex(peerIndices[cursor])
|
||||
n.Transport.Send(msg)
|
||||
cursor++
|
||||
}
|
||||
}
|
||||
}
|
||||
89
pkg/relay/hiddenservice.go
Normal file
89
pkg/relay/hiddenservice.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/cybriq/qu"
|
||||
|
||||
"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/messages/hiddenservice"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
|
||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/cryptorand"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
type Referrers map[pub.Bytes][]pub.Bytes
|
||||
|
||||
func (eng *Engine) hiddenserviceBroadcaster(hs *intro.Layer) {
|
||||
log.D.F("propagating hidden service introduction for %x", hs.Key.ToBytes())
|
||||
done := qu.T()
|
||||
intr := &intro.Layer{
|
||||
Key: hs.Key, AddrPort: hs.AddrPort, Bytes: hs.Bytes,
|
||||
}
|
||||
msg := make(slice.Bytes, intro.Len)
|
||||
c := slice.NewCursor()
|
||||
intr.Encode(msg, c)
|
||||
nPeers := eng.NodesLen()
|
||||
peerIndices := make([]int, nPeers)
|
||||
for i := 0; i < nPeers; i++ {
|
||||
peerIndices[i] = i
|
||||
}
|
||||
cryptorand.Shuffle(nPeers, func(i, j int) {
|
||||
peerIndices[i], peerIndices[j] = peerIndices[j], peerIndices[i]
|
||||
})
|
||||
// Since relays will also gossip this information, we will start a ticker
|
||||
// that sends out the hidden service introduction once a second until it
|
||||
// runs out of known relays to gossip to.
|
||||
ticker := time.NewTicker(time.Second)
|
||||
var cursor int
|
||||
for {
|
||||
select {
|
||||
case <-eng.C.Wait():
|
||||
return
|
||||
case <-done:
|
||||
return
|
||||
case <-ticker.C:
|
||||
n := eng.FindNodeByIndex(peerIndices[cursor])
|
||||
n.Transport.Send(msg)
|
||||
cursor++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func HiddenService(id nonce.ID, il *intro.Layer, client *Session, s Circuit,
|
||||
ks *signer.KeySet) Skins {
|
||||
|
||||
var prvs [3]*prv.Key
|
||||
for i := range prvs {
|
||||
prvs[i] = ks.Next()
|
||||
}
|
||||
n := GenNonces(6)
|
||||
var returnNonces [3]nonce.IV
|
||||
copy(returnNonces[:], n[3:])
|
||||
var pubs [3]*pub.Key
|
||||
pubs[0] = s[3].PayloadPub
|
||||
pubs[1] = s[4].PayloadPub
|
||||
pubs[2] = client.PayloadPub
|
||||
return Skins{}.
|
||||
ReverseCrypt(s[0], ks.Next(), n[0], 3).
|
||||
ReverseCrypt(s[1], ks.Next(), n[1], 2).
|
||||
ReverseCrypt(s[2], ks.Next(), n[2], 1).
|
||||
HiddenService(id, il, prvs, pubs, returnNonces).
|
||||
ReverseCrypt(s[3], prvs[0], n[3], 3).
|
||||
ReverseCrypt(s[4], prvs[1], n[4], 2).
|
||||
ReverseCrypt(client, prvs[2], n[5], 1)
|
||||
}
|
||||
|
||||
func (eng *Engine) hiddenservice(hs *hiddenservice.Layer, b slice.Bytes,
|
||||
c *slice.Cursor, prev types.Onion) {
|
||||
|
||||
log.D.F("%s adding introduction for key %s", eng.GetLocalNodeAddress(),
|
||||
hs.Layer.Key.ToBase32())
|
||||
eng.Introductions.AddIntro(hs.Layer.Key, b[*c:])
|
||||
log.I.Ln("stored new introduction, starting broadcast")
|
||||
go eng.hiddenserviceBroadcaster(&hs.Layer)
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
|
||||
"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/messages/intro"
|
||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
@@ -65,3 +67,27 @@ func (in *Introductions) AddNotified(nodeID nonce.ID, ident pub.Bytes) {
|
||||
}
|
||||
in.Unlock()
|
||||
}
|
||||
|
||||
func (eng *Engine) SendIntro(id nonce.ID, target *Session, intr *intro.Layer,
|
||||
hook func(id nonce.ID, b slice.Bytes)) {
|
||||
|
||||
hops := []byte{0, 1, 2, 3, 4, 5}
|
||||
s := make(Sessions, len(hops))
|
||||
s[2] = target
|
||||
se := eng.SelectHops(hops, s)
|
||||
var c Circuit
|
||||
copy(c[:], se)
|
||||
o := HiddenService(id, intr, se[len(se)-1], c, eng.KeySet)
|
||||
log.D.Ln("sending out intro onion")
|
||||
res := eng.PostAcctOnion(o)
|
||||
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
||||
}
|
||||
|
||||
func (eng *Engine) intro(intr *intro.Layer, b slice.Bytes,
|
||||
c *slice.Cursor, prev types.Onion) {
|
||||
|
||||
if intr.Validate() {
|
||||
log.D.F("sending out intro to %s at %s to all known peers",
|
||||
intr.Key.ToBase32(), intr.AddrPort.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
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/key/signer"
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
||||
)
|
||||
|
||||
// GetBalance sends out a request in a similar way to SendExit except the node
|
||||
// being queried can be any of the 5.
|
||||
func GetBalance(id, confID nonce.ID, client *Session,
|
||||
s Circuit, ks *signer.KeySet) Skins {
|
||||
|
||||
var prvs [3]*prv.Key
|
||||
for i := range prvs {
|
||||
prvs[i] = ks.Next()
|
||||
}
|
||||
n := GenNonces(6)
|
||||
var retNonces [3]nonce.IV
|
||||
copy(retNonces[:], n[3:])
|
||||
var pubs [3]*pub.Key
|
||||
pubs[0] = s[3].PayloadPub
|
||||
pubs[1] = s[4].PayloadPub
|
||||
pubs[2] = client.PayloadPub
|
||||
return Skins{}.
|
||||
ReverseCrypt(s[0], ks.Next(), n[0], 3).
|
||||
ReverseCrypt(s[1], ks.Next(), n[1], 2).
|
||||
ReverseCrypt(s[2], ks.Next(), n[2], 1).
|
||||
GetBalance(id, confID, prvs, pubs, retNonces).
|
||||
ReverseCrypt(s[3], prvs[0], n[3], 0).
|
||||
ReverseCrypt(s[4], prvs[1], n[4], 0).
|
||||
ReverseCrypt(client, prvs[2], n[5], 0)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
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/key/signer"
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
||||
)
|
||||
|
||||
func HiddenService(id nonce.ID, ident *pub.Key, client *Session, s Circuit,
|
||||
ks *signer.KeySet) Skins {
|
||||
|
||||
var prvs [3]*prv.Key
|
||||
for i := range prvs {
|
||||
prvs[i] = ks.Next()
|
||||
}
|
||||
n := GenNonces(6)
|
||||
var returnNonces [3]nonce.IV
|
||||
copy(returnNonces[:], n[3:])
|
||||
var pubs [3]*pub.Key
|
||||
pubs[0] = s[3].PayloadPub
|
||||
pubs[1] = s[4].PayloadPub
|
||||
pubs[2] = client.PayloadPub
|
||||
return Skins{}.
|
||||
ReverseCrypt(s[0], ks.Next(), n[0], 3).
|
||||
ReverseCrypt(s[1], ks.Next(), n[1], 2).
|
||||
ReverseCrypt(s[2], ks.Next(), n[2], 1).
|
||||
HiddenService(id, ident, prvs, pubs, returnNonces).
|
||||
ReverseCrypt(s[3], prvs[0], n[3], 3).
|
||||
ReverseCrypt(s[4], prvs[1], n[4], 2).
|
||||
ReverseCrypt(client, prvs[2], n[5], 1)
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
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/key/signer"
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
// SendExit constructs a message containing an arbitrary payload to a node (3rd
|
||||
// hop) with a set of 3 ciphers derived from the hidden PayloadPub of the return
|
||||
// hops that are layered progressively after the Exit message.
|
||||
//
|
||||
// The Exit node forwards the packet it receives to the local port specified in
|
||||
// the Exit message, and then uses the ciphers to encrypt the reply with the
|
||||
// three ciphers provided, which don't enable it to decrypt the header, only to
|
||||
// encrypt the payload.
|
||||
//
|
||||
// The response is encrypted with the given layers, the ciphers are already
|
||||
// given in reverse order, so they are decoded in given order to create the
|
||||
// correct payload encryption to match the PayloadPub combined with the header's
|
||||
// given public From key.
|
||||
//
|
||||
// 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, id nonce.ID,
|
||||
client *Session, s Circuit, ks *signer.KeySet) Skins {
|
||||
|
||||
var prvs [3]*prv.Key
|
||||
for i := range prvs {
|
||||
prvs[i] = ks.Next()
|
||||
}
|
||||
n := GenNonces(6)
|
||||
var returnNonces [3]nonce.IV
|
||||
copy(returnNonces[:], n[3:])
|
||||
var pubs [3]*pub.Key
|
||||
pubs[0] = s[3].PayloadPub
|
||||
pubs[1] = s[4].PayloadPub
|
||||
pubs[2] = client.PayloadPub
|
||||
return Skins{}.
|
||||
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, id, payload).
|
||||
ReverseCrypt(s[3], prvs[0], n[3], 3).
|
||||
ReverseCrypt(s[4], prvs[1], n[4], 2).
|
||||
ReverseCrypt(client, prvs[2], n[5], 1)
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"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/messages/session"
|
||||
)
|
||||
|
||||
// SendKeys provides a pair of private keys that will be used to generate the
|
||||
// Purchase header bytes and to generate the ciphers provided in the Purchase
|
||||
// message to encrypt the Session that is returned.
|
||||
//
|
||||
// The OnionSkin key, its cloaked public key counterpart used in the ToHeaderPub
|
||||
// field of the Purchase message preformed header bytes, but the Ciphers
|
||||
// provided in the Purchase message, for encrypting the Session to be returned,
|
||||
// uses the Payload key, along with the public key found in the encrypted crypt
|
||||
// of the header for the Reverse relay.
|
||||
//
|
||||
// This message's last crypt is a Confirmation, which allows the client to know
|
||||
// that the keys were successfully delivered.
|
||||
//
|
||||
// This is the only onion that uses the node identity keys. The payment preimage
|
||||
// hash must be available or the relay should not forward the remainder of the
|
||||
// packet.
|
||||
//
|
||||
// If hdr/pld cipher keys are nil there must be a HeaderPub available on the
|
||||
// session for the hop. This allows this function to send keys to any number of
|
||||
// hops, but the very first SendKeys must have all in order to create the first
|
||||
// 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 *Session, hop []*Node, ks *signer.KeySet) Skins {
|
||||
|
||||
n := GenNonces(6)
|
||||
sk := Skins{}
|
||||
for i := range s {
|
||||
if i == 0 {
|
||||
sk = sk.Crypt(hop[i].IdentityPub, nil, ks.Next(),
|
||||
n[i], 0).Session(s[i])
|
||||
} else {
|
||||
sk = sk.ForwardSession(hop[i], ks.Next(), n[i], s[i])
|
||||
}
|
||||
}
|
||||
return sk.
|
||||
ForwardCrypt(client, ks.Next(), n[5]).
|
||||
Confirmation(id, 0)
|
||||
}
|
||||
@@ -26,3 +26,15 @@ func Ping(id nonce.ID, client *Session, s Circuit,
|
||||
ForwardCrypt(client, ks.Next(), n[5]).
|
||||
Confirmation(id, 0)
|
||||
}
|
||||
|
||||
func (eng *Engine) SendPing(c Circuit, hook Callback) {
|
||||
hops := []byte{0, 1, 2, 3, 4, 5}
|
||||
s := make(Sessions, len(hops))
|
||||
copy(s, c[:])
|
||||
se := eng.SelectHops(hops, s)
|
||||
copy(c[:], se)
|
||||
confID := nonce.NewID()
|
||||
o := Ping(confID, se[len(se)-1], c, eng.KeySet)
|
||||
res := eng.PostAcctOnion(o)
|
||||
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
||||
}
|
||||
@@ -1,12 +1,46 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/cryptorand"
|
||||
)
|
||||
import "git-indra.lan/indra-labs/indra/pkg/util/cryptorand"
|
||||
|
||||
// SelectUnusedCircuit accepts an array of 5 Node entries where all or some are
|
||||
// empty and picks nodes for the remainder that do not have a hop at that
|
||||
// position.
|
||||
func (sm *SessionManager) SelectUnusedCircuit() (c [5]*Node) {
|
||||
sm.Lock()
|
||||
defer sm.Unlock()
|
||||
// Create a shuffled slice of Nodes to randomise the selection process.
|
||||
nodeList := make([]*Node, len(sm.nodes)-1)
|
||||
copy(nodeList, sm.nodes[1:])
|
||||
for i := range nodeList {
|
||||
if _, ok := sm.SessionCache[nodeList[i].ID]; !ok {
|
||||
log.T.F("adding session cache entry for node %s", nodeList[i].ID)
|
||||
sm.SessionCache[nodeList[i].ID] = &Circuit{}
|
||||
}
|
||||
}
|
||||
var counter int
|
||||
out:
|
||||
for counter < 5 {
|
||||
for i := range sm.SessionCache {
|
||||
if counter == 5 {
|
||||
break out
|
||||
}
|
||||
if sm.SessionCache[i][counter] == nil {
|
||||
for j := range nodeList {
|
||||
if nodeList[j].ID == i {
|
||||
c[counter] = nodeList[j]
|
||||
counter++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (sm *SessionManager) SelectHops(hops []byte,
|
||||
alreadyHave Sessions) (so Sessions) {
|
||||
|
||||
|
||||
sm.Lock()
|
||||
defer sm.Unlock()
|
||||
ws := make(Sessions, 0)
|
||||
@@ -1,37 +0,0 @@
|
||||
package relay
|
||||
|
||||
// SelectUnusedCircuit accepts an array of 5 Node entries where all or some are
|
||||
// empty and picks nodes for the remainder that do not have a hop at that
|
||||
// position.
|
||||
func (sm *SessionManager) SelectUnusedCircuit() (c [5]*Node) {
|
||||
sm.Lock()
|
||||
defer sm.Unlock()
|
||||
// Create a shuffled slice of Nodes to randomise the selection process.
|
||||
nodeList := make([]*Node, len(sm.nodes)-1)
|
||||
copy(nodeList, sm.nodes[1:])
|
||||
for i := range nodeList {
|
||||
if _, ok := sm.SessionCache[nodeList[i].ID]; !ok {
|
||||
log.T.F("adding session cache entry for node %s", nodeList[i].ID)
|
||||
sm.SessionCache[nodeList[i].ID] = &Circuit{}
|
||||
}
|
||||
}
|
||||
var counter int
|
||||
out:
|
||||
for counter < 5 {
|
||||
for i := range sm.SessionCache {
|
||||
if counter == 5 {
|
||||
break out
|
||||
}
|
||||
if sm.SessionCache[i][counter] == nil {
|
||||
for j := range nodeList {
|
||||
if nodeList[j].ID == i {
|
||||
c[counter] = nodeList[j]
|
||||
counter++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -2,11 +2,29 @@ package relay
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"runtime"
|
||||
|
||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
// Send a message to a peer via their AddrPort.
|
||||
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()
|
||||
eng.ForEachNode(func(n *Node) bool {
|
||||
if as == n.AddrPort.String() {
|
||||
_, f, l, _ := runtime.Caller(1)
|
||||
log.T.F("%s sending message to %v %s:%d",
|
||||
eng.GetLocalNode().AddrPort.String(), addr, f, l)
|
||||
n.Transport.Send(b)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
// SendWithOneHook is used for onions with only one confirmation hook. Usually
|
||||
// as returned from PostAcctOnion this is the last, confirmation or response
|
||||
// layer in an onion.Skins.
|
||||
@@ -2,13 +2,19 @@ package relay
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
|
||||
"fmt"
|
||||
|
||||
"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/key/signer"
|
||||
"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/messages/session"
|
||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/cryptorand"
|
||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||
)
|
||||
|
||||
// A Session keeps track of a connection session. It specifically maintains the
|
||||
@@ -35,7 +41,7 @@ func NewSession(
|
||||
pldPrv *prv.Key,
|
||||
hop byte,
|
||||
) (s *Session) {
|
||||
|
||||
|
||||
var e error
|
||||
if hdrPrv == nil || pldPrv == nil {
|
||||
if hdrPrv, e = prv.GenerateKey(); check(e) {
|
||||
@@ -111,3 +117,149 @@ func (c Circuit) String() (o string) {
|
||||
|
||||
// Sessions are arbitrary length lists of sessions.
|
||||
type Sessions []*Session
|
||||
|
||||
func (eng *Engine) session(on *session.Layer, b slice.Bytes,
|
||||
c *slice.Cursor, prev types.Onion) {
|
||||
|
||||
log.D.Ln(prev == nil)
|
||||
log.T.F("incoming session %x", 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.D.F("Adding session %s to %s", pi.ID, eng.GetLocalNodeAddress())
|
||||
eng.AddSession(NewSession(pi.ID,
|
||||
eng.GetLocalNode(), pi.Amount, on.Header, on.Payload, on.Hop))
|
||||
eng.handleMessage(BudgeUp(b, *c), on)
|
||||
} else {
|
||||
log.E.Ln("dropping session message without payment")
|
||||
}
|
||||
}
|
||||
|
||||
// BuyNewSessions performs the initial purchase of 5 sessions as well as adding
|
||||
// different hop numbers to relays with existing sessions. Note that all 5 of
|
||||
// the sessions will be paid the amount specified, not divided up.
|
||||
func (eng *Engine) BuyNewSessions(amount lnwire.MilliSatoshi,
|
||||
hook func()) (e error) {
|
||||
|
||||
var nodes [5]*Node
|
||||
nodes = eng.SessionManager.SelectUnusedCircuit()
|
||||
for i := range nodes {
|
||||
if nodes[i] == nil {
|
||||
e = fmt.Errorf("failed to find nodes %d", i)
|
||||
return
|
||||
}
|
||||
}
|
||||
// Get a random return hop session (index 5).
|
||||
var returnSession *Session
|
||||
returnHops := eng.SessionManager.GetSessionsAtHop(5)
|
||||
if len(returnHops) > 1 {
|
||||
cryptorand.Shuffle(len(returnHops), func(i, j int) {
|
||||
returnHops[i], returnHops[j] = returnHops[j], returnHops[i]
|
||||
})
|
||||
}
|
||||
// There must be at least one, and if there was more than one the first
|
||||
// index of returnHops will be a randomly selected one.
|
||||
returnSession = returnHops[0]
|
||||
conf := nonce.NewID()
|
||||
var s [5]*session.Layer
|
||||
for i := range s {
|
||||
s[i] = session.New(byte(i))
|
||||
}
|
||||
var confirmChans [5]chan bool
|
||||
var pendingConfirms int
|
||||
for i := range nodes {
|
||||
confirmChans[i] = nodes[i].
|
||||
PaymentChan.Send(amount, s[i])
|
||||
pendingConfirms++
|
||||
}
|
||||
var success bool
|
||||
for pendingConfirms > 0 {
|
||||
// The confirmation channels will signal upon success or failure
|
||||
// according to the LN payment send protocol once either the HTLCs
|
||||
// confirm on the way back or the path fails.
|
||||
select {
|
||||
case success = <-confirmChans[0]:
|
||||
if success {
|
||||
pendingConfirms--
|
||||
}
|
||||
case success = <-confirmChans[1]:
|
||||
if success {
|
||||
pendingConfirms--
|
||||
}
|
||||
case success = <-confirmChans[2]:
|
||||
if success {
|
||||
pendingConfirms--
|
||||
}
|
||||
case success = <-confirmChans[3]:
|
||||
if success {
|
||||
pendingConfirms--
|
||||
}
|
||||
case success = <-confirmChans[4]:
|
||||
if success {
|
||||
pendingConfirms--
|
||||
}
|
||||
}
|
||||
}
|
||||
// todo: handle payment failures!
|
||||
o := SendSessions(conf, s, returnSession, nodes[:], eng.KeySet)
|
||||
res := eng.PostAcctOnion(o)
|
||||
eng.SendWithOneHook(nodes[0].AddrPort, res, func(id nonce.ID, b slice.Bytes) {
|
||||
eng.SessionManager.Lock()
|
||||
defer eng.SessionManager.Unlock()
|
||||
var sessions [5]*Session
|
||||
for i := range nodes {
|
||||
log.D.F("confirming and storing session at hop %d %s for %s with"+
|
||||
" %v initial"+
|
||||
" balance", i, s[i].ID, nodes[i].AddrPort.String(), amount)
|
||||
sessions[i] = NewSession(s[i].ID, nodes[i], amount,
|
||||
s[i].Header, s[i].Payload, byte(i))
|
||||
eng.SessionManager.Add(sessions[i])
|
||||
eng.Sessions = append(eng.Sessions, sessions[i])
|
||||
eng.SessionManager.PendingPayments.Delete(s[i].PreimageHash())
|
||||
}
|
||||
hook()
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// SendSessions provides a pair of private keys that will be used to generate the
|
||||
// Purchase header bytes and to generate the ciphers provided in the Purchase
|
||||
// message to encrypt the Session that is returned.
|
||||
//
|
||||
// The OnionSkin key, its cloaked public key counterpart used in the ToHeaderPub
|
||||
// field of the Purchase message preformed header bytes, but the Ciphers
|
||||
// provided in the Purchase message, for encrypting the Session to be returned,
|
||||
// uses the Payload key, along with the public key found in the encrypted crypt
|
||||
// of the header for the Reverse relay.
|
||||
//
|
||||
// This message's last crypt is a Confirmation, which allows the client to know
|
||||
// that the keys were successfully delivered.
|
||||
//
|
||||
// This is the only onion that uses the node identity keys. The payment preimage
|
||||
// hash must be available or the relay should not forward the remainder of the
|
||||
// packet.
|
||||
//
|
||||
// If hdr/pld cipher keys are nil there must be a HeaderPub available on the
|
||||
// session for the hop. This allows this function to send keys to any number of
|
||||
// hops, but the very first SendSessions must have all in order to create the first
|
||||
// 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 SendSessions(id nonce.ID, s [5]*session.Layer,
|
||||
client *Session, hop []*Node, ks *signer.KeySet) Skins {
|
||||
|
||||
n := GenNonces(6)
|
||||
sk := Skins{}
|
||||
for i := range s {
|
||||
if i == 0 {
|
||||
sk = sk.Crypt(hop[i].IdentityPub, nil, ks.Next(),
|
||||
n[i], 0).Session(s[i])
|
||||
} else {
|
||||
sk = sk.ForwardSession(hop[i], ks.Next(), n[i], s[i])
|
||||
}
|
||||
}
|
||||
return sk.
|
||||
ForwardCrypt(client, ks.Next(), n[5]).
|
||||
Confirmation(id, 0)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/forward"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/getbalance"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/hiddenservice"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/noop"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/response"
|
||||
"git-indra.lan/indra-labs/indra/pkg/messages/reverse"
|
||||
@@ -113,13 +114,13 @@ func (o Skins) GetBalance(id, confID nonce.ID, prvs [3]*prv.Key,
|
||||
})
|
||||
}
|
||||
|
||||
func (o Skins) HiddenService(id nonce.ID, addr *pub.Key, prvs [3]*prv.Key,
|
||||
func (o Skins) HiddenService(id nonce.ID, intr *intro.Layer, prvs [3]*prv.Key,
|
||||
pubs [3]*pub.Key, nonces [3]nonce.IV) Skins {
|
||||
return append(o, &hiddenservice.Layer{
|
||||
ID: id,
|
||||
Identity: addr,
|
||||
Ciphers: GenCiphers(prvs, pubs),
|
||||
Nonces: nonces,
|
||||
ID: id,
|
||||
Layer: *intr,
|
||||
Ciphers: GenCiphers(prvs, pubs),
|
||||
Nonces: nonces,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -133,7 +134,7 @@ func (o Skins) Response(id nonce.ID, res slice.Bytes, port uint16) Skins {
|
||||
}
|
||||
|
||||
func (o Skins) Session(sess *session.Layer) Skins {
|
||||
// SendKeys can apply to from 1 to 5 nodes, if either key is nil then
|
||||
// SendSessions can apply to from 1 to 5 nodes, if either key is nil then
|
||||
// this crypt just doesn't get added in the serialization process.
|
||||
if sess.Header == nil || sess.Payload == nil {
|
||||
return o
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"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/key/sig"
|
||||
"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/messages/magicbytes"
|
||||
@@ -177,4 +178,14 @@ func (s *Splicer) Bytes(b []byte) *Splicer {
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Splicer) Signature(sb sig.Bytes) *Splicer {
|
||||
copy(s.b[*s.c:s.c.Inc(sig.Len)], sb[:])
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Splicer) ReadSignature(sb *sig.Bytes) *Splicer {
|
||||
copy(sb[:], s.b[*s.c:s.c.Inc(sig.Len)])
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Splicer) Done() {}
|
||||
|
||||
@@ -15,7 +15,6 @@ type Sim chan slice.Bytes
|
||||
|
||||
func NewSim(bufs int) Sim { return make(Sim, bufs) }
|
||||
func (d Sim) Send(b slice.Bytes) {
|
||||
log.D.Ln("sim transport sending")
|
||||
d <- b
|
||||
}
|
||||
func (d Sim) Receive() <-chan slice.Bytes {
|
||||
|
||||
Reference in New Issue
Block a user