diff --git a/pkg/client/client.go b/pkg/client/client.go index d69140ac..9f028a32 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -2,13 +2,16 @@ package client import ( "net/netip" + "reflect" "sync" + "time" "github.com/Indra-Labs/indra" "github.com/Indra-Labs/indra/pkg/ifc" "github.com/Indra-Labs/indra/pkg/key/address" "github.com/Indra-Labs/indra/pkg/key/prv" "github.com/Indra-Labs/indra/pkg/key/pub" + "github.com/Indra-Labs/indra/pkg/key/signer" "github.com/Indra-Labs/indra/pkg/node" "github.com/Indra-Labs/indra/pkg/slice" "github.com/Indra-Labs/indra/pkg/types" @@ -29,6 +32,8 @@ import ( "github.com/cybriq/qu" ) +const DefaultDeadline = 10 * time.Minute + var ( log = log2.GetLogger(indra.PathBase) check = log.E.Chk @@ -43,20 +48,23 @@ type Client struct { Circuits Sessions sync.Mutex + *signer.KeySet qu.C } func New(tpt ifc.Transport, hdrPrv *prv.Key, no *node.Node, nodes node.Nodes) (c *Client, e error) { - hdrPub := pub.Derive(hdrPrv) - var n *node.Node - n, _ = node.New(no.AddrPort, hdrPub, nil, hdrPrv, nil, tpt) + no.Transport = tpt + no.HeaderPriv = hdrPrv + no.HeaderPub = pub.Derive(hdrPrv) c = &Client{ - Node: n, - Nodes: nodes, - C: qu.T(), + Node: no, + Nodes: nodes, + ReceiveCache: address.NewReceiveCache(), + C: qu.T(), } + c.ReceiveCache.Add(address.NewReceiver(hdrPrv)) return } @@ -80,59 +88,47 @@ func (c *Client) runner() (out bool) { // process received message var onion types.Onion var e error - cursor := slice.NewCursor() - if onion, e = wire.PeelOnion(b, cursor); check(e) { + cur := slice.NewCursor() + if onion, e = wire.PeelOnion(b, cur); check(e) { break } switch on := onion.(type) { case *cipher.OnionSkin: - c.cipher(on, b) - break - + log.I.Ln(reflect.TypeOf(on)) + c.cipher(on, b, cur) case *confirmation.OnionSkin: - c.confirmation(on, b) - break - + log.I.Ln(reflect.TypeOf(on)) + c.confirmation(on, b, cur) case *delay.OnionSkin: - c.delay(on, b) - break - + log.I.Ln(reflect.TypeOf(on)) + c.delay(on, b, cur) case *exit.OnionSkin: - c.exit(on, b) - break - + log.I.Ln(reflect.TypeOf(on)) + c.exit(on, b, cur) case *forward.OnionSkin: - c.forward(on, b) - break - + log.I.Ln(reflect.TypeOf(on)) + c.forward(on, b, cur) case *layer.OnionSkin: - c.layer(on, b) - break - + log.I.Ln(reflect.TypeOf(on)) + c.layer(on, b, cur) case *noop.OnionSkin: - c.noop(on, b) - break - + log.I.Ln(reflect.TypeOf(on)) + c.noop(on, b, cur) case *purchase.OnionSkin: - c.purchase(on, b) - break - + log.I.Ln(reflect.TypeOf(on)) + c.purchase(on, b, cur) case *reply.OnionSkin: - c.reply(on, b) - break - + log.I.Ln(reflect.TypeOf(on)) + c.reply(on, b, cur) case *response.OnionSkin: - c.response(on, b) - break - + log.I.Ln(reflect.TypeOf(on)) + c.response(on, b, cur) case *session.OnionSkin: - c.session(on, b) - break - + log.I.Ln(reflect.TypeOf(on)) + c.session(on, b, cur) case *token.OnionSkin: - c.token(on, b) - break - + log.I.Ln(reflect.TypeOf(on)) + c.token(on, b, cur) default: log.I.S("unrecognised packet", b) } @@ -140,59 +136,74 @@ func (c *Client) runner() (out bool) { return false } -func (c *Client) cipher(on *cipher.OnionSkin, b slice.Bytes) { +func (c *Client) cipher(on *cipher.OnionSkin, b slice.Bytes, cur *slice.Cursor) { // This either is in a forward only SendKeys message or we are the buyer // and these are our session keys. } -func (c *Client) confirmation(on *confirmation.OnionSkin, b slice.Bytes) { +func (c *Client) confirmation(on *confirmation.OnionSkin, b slice.Bytes, + cur *slice.Cursor) { // This will be an 8 byte nonce that confirms a message passed, ping and // cipher onions return these, as they are pure forward messages that // send a message one way and the confirmation is the acknowledgement. + log.I.S(on) } -func (c *Client) delay(on *delay.OnionSkin, b slice.Bytes) { - // this is a message to hold the message in the buffer until a time +func (c *Client) delay(on *delay.OnionSkin, b slice.Bytes, cur *slice.Cursor) { + // 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. } -func (c *Client) exit(on *exit.OnionSkin, b slice.Bytes) { +func (c *Client) exit(on *exit.OnionSkin, b slice.Bytes, cur *slice.Cursor) { // payload is forwarded to a local port and the response is forwarded // back with a reply header. } -func (c *Client) forward(on *forward.OnionSkin, b slice.Bytes) { - // forward the whole buffer received onwards. Usually there will be an - // OnionSkin under this which will be unwrapped by the receiver. +func (c *Client) forward(on *forward.OnionSkin, b slice.Bytes, cur *slice.Cursor) { + // forward the whole buffer received onwards. Usually there will be a + // layer.OnionSkin under this which will be unwrapped by the receiver. if on.AddrPort.String() == c.Node.AddrPort.String() { // it is for us, we want to unwrap the next // part. - c.Node.Send(b) + log.I.Ln("processing new message") + c.Node.Send(b[*cur:]) } else { // we need to forward this message onion. + log.I.Ln("forwarding") c.Send(on.AddrPort, b) } } -func (c *Client) layer(on *layer.OnionSkin, b slice.Bytes) { +func (c *Client) layer(on *layer.OnionSkin, b slice.Bytes, cur *slice.Cursor) { // this is probably an encrypted layer for us. + log.I.Ln("decrypting onion skin") + // log.I.S(on, b[*cur:].ToBytes()) + rcv := c.ReceiveCache.FindCloaked(on.Cloak) + if rcv == nil { + log.I.Ln("no matching key found from cloaked key") + return + } + on.Decrypt(rcv.Key, b, cur) + log.I.S(b[*cur:].ToBytes()) + c.Node.Send(b[*cur:]) } -func (c *Client) noop(on *noop.OnionSkin, b slice.Bytes) { +func (c *Client) noop(on *noop.OnionSkin, b slice.Bytes, cur *slice.Cursor) { // this won't happen normally } -func (c *Client) purchase(on *purchase.OnionSkin, b slice.Bytes) { +func (c *Client) purchase(on *purchase.OnionSkin, b slice.Bytes, cur *slice.Cursor) { // Create a new Session. s := &Session{} c.Mutex.Lock() + s.Deadline = time.Now().Add(DefaultDeadline) c.Sessions = append(c.Sessions, s) c.Mutex.Unlock() } -func (c *Client) reply(on *reply.OnionSkin, b slice.Bytes) { +func (c *Client) reply(on *reply.OnionSkin, b slice.Bytes, cur *slice.Cursor) { // Reply means another OnionSkin is coming and the payload encryption // uses the Payload key. if on.AddrPort.String() == c.Node.AddrPort.String() { @@ -205,11 +216,11 @@ func (c *Client) reply(on *reply.OnionSkin, b slice.Bytes) { } -func (c *Client) response(on *response.OnionSkin, b slice.Bytes) { +func (c *Client) response(on *response.OnionSkin, b slice.Bytes, cur *slice.Cursor) { // Response is a payload from an exit message. } -func (c *Client) session(s *session.OnionSkin, b slice.Bytes) { +func (c *Client) session(s *session.OnionSkin, b slice.Bytes, cur *slice.Cursor) { // Session is returned from a Purchase message in the reply layers. // // Session has a nonce.ID that is given in the last layer of a LN sphinx @@ -218,7 +229,7 @@ func (c *Client) session(s *session.OnionSkin, b slice.Bytes) { // nonce, so long as it has not yet expired. } -func (c *Client) token(t *token.OnionSkin, b slice.Bytes) { +func (c *Client) token(t *token.OnionSkin, b slice.Bytes, cur *slice.Cursor) { // not really sure if we are using these. return } diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 3d9419e6..65f901f5 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -2,10 +2,83 @@ package client import ( "testing" + "time" + + "github.com/Indra-Labs/indra/pkg/ifc" + "github.com/Indra-Labs/indra/pkg/key/prv" + "github.com/Indra-Labs/indra/pkg/key/pub" + "github.com/Indra-Labs/indra/pkg/key/signer" + "github.com/Indra-Labs/indra/pkg/node" + "github.com/Indra-Labs/indra/pkg/nonce" + "github.com/Indra-Labs/indra/pkg/slice" + "github.com/Indra-Labs/indra/pkg/transport" + "github.com/Indra-Labs/indra/pkg/wire" + log2 "github.com/cybriq/proc/pkg/log" ) -func TestNew(t *testing.T) { - +func TestPing(t *testing.T) { + log2.CodeLoc = true + var clients [4]*Client + var nodes [4]*node.Node + var transports [4]ifc.Transport + var e error + for i := range transports { + transports[i] = transport.NewSim(4) + } + for i := range nodes { + var hdrPrv, pldPrv *prv.Key + if hdrPrv, e = prv.GenerateKey(); check(e) { + t.Error(e) + t.FailNow() + } + hdrPub := pub.Derive(hdrPrv) + if pldPrv, e = prv.GenerateKey(); check(e) { + t.Error(e) + t.FailNow() + } + pldPub := pub.Derive(pldPrv) + addr := slice.GenerateRandomAddrPortIPv4() + log.I.S(addr) + nodes[i], _ = node.New(addr, hdrPub, pldPub, hdrPrv, pldPrv, transports[i]) + if clients[i], e = New(transports[i], hdrPrv, nodes[i], nil); check(e) { + t.Error(e) + t.FailNow() + } + clients[i].AddrPort = nodes[i].AddrPort + } + // add each node to each other's Nodes except itself. + for i := range nodes { + for j := range nodes { + if i == j { + continue + } + clients[i].Nodes = append(clients[i].Nodes, nodes[j]) + } + } + // Start up the clients. + for _, v := range clients { + go v.Start() + } + pn := nonce.NewID() + var ks *signer.KeySet + if _, ks, e = signer.New(); check(e) { + t.Error(e) + t.FailNow() + } + var hop [3]*node.Node + for i := range clients[0].Nodes { + hop[i] = clients[0].Nodes[i] + } + os := wire.Ping(pn, clients[0].Node, hop, ks) + log.I.S(os) + o := os.Assemble() + b := wire.EncodeOnion(o) + // log.I.S(b.ToBytes()) + hop[0].Send(b) + time.Sleep(time.Second * 5) + for _, v := range clients { + v.Shutdown() + } } // diff --git a/pkg/client/session.go b/pkg/client/session.go index 329c2ca1..d5483d87 100644 --- a/pkg/client/session.go +++ b/pkg/client/session.go @@ -52,7 +52,7 @@ func (s Sessions) Find(t nonce.ID) (se *Session) { // NewSession creates a new Session. // // Purchasing a session the seller returns a token, based on a requested data -// allocation, +// allocation. func NewSession(id nonce.ID, rem uint64, hdr, pld *address.SendEntry, kr *signer.KeySet) (s *Session) { diff --git a/pkg/node/node.go b/pkg/node/node.go index 324f3574..6bc63c5d 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -25,8 +25,8 @@ var ( // except when the netip.AddrPort is known via the packet sender address. type Node struct { nonce.ID - Addr string - *netip.AddrPort + Addr string + AddrPort *netip.AddrPort HeaderPub, PayloadPub *pub.Key HeaderPriv, PayloadPriv *prv.Key ifc.Transport diff --git a/pkg/node/node_test.go b/pkg/node/node_test.go index 3f35ce23..1fb368e6 100644 --- a/pkg/node/node_test.go +++ b/pkg/node/node_test.go @@ -2,17 +2,20 @@ package node import ( "fmt" + "net/netip" "testing" "github.com/Indra-Labs/indra/pkg/transport" ) +var testAddrPort, _ = netip.ParseAddrPort("1.1.1.1:20000") + func TestNodes_Add(t *testing.T) { n := NewNodes() const nNodes = 10000 for i := 0; i < nNodes; i++ { var nn *Node - nn, _ = New(nil, nil, nil, nil, nil, transport.NewSim(0)) + nn, _ = New(&testAddrPort, nil, nil, nil, nil, transport.NewSim(0)) n = n.Add(nn) } if n.Len() != nNodes { @@ -26,7 +29,7 @@ func TestNodes_DeleteByID(t *testing.T) { var e error for i := 0; i < nNodes; i++ { var nn *Node - nn, _ = New(nil, nil, nil, nil, nil, transport.NewSim(0)) + nn, _ = New(&testAddrPort, nil, nil, nil, nil, transport.NewSim(0)) n.Add(nn) } for i := range n { @@ -42,7 +45,7 @@ func TestNodes_DeleteByAddrPort(t *testing.T) { var e error for i := 0; i < nNodes; i++ { var nn *Node - nn, _ = New(nil, nil, nil, nil, nil, transport.NewSim(0)) + nn, _ = New(&testAddrPort, nil, nil, nil, nil, transport.NewSim(0)) n.Add(nn) } for i := range n { @@ -57,7 +60,7 @@ func TestNodes_FindByID(t *testing.T) { const nNodes = 10000 for i := 0; i < nNodes; i++ { var nn *Node - nn, _ = New(nil, nil, nil, nil, nil, transport.NewSim(0)) + nn, _ = New(&testAddrPort, nil, nil, nil, nil, transport.NewSim(0)) n.Add(nn) } for i := range n { @@ -73,7 +76,7 @@ func TestNodes_FindByAddrPort(t *testing.T) { const nNodes = 10000 for i := 0; i < nNodes; i++ { var nn *Node - nn, _ = New(nil, nil, nil, nil, nil, transport.NewSim(0)) + nn, _ = New(&testAddrPort, nil, nil, nil, nil, transport.NewSim(0)) n.Add(nn) } for i := range n { diff --git a/pkg/wire/confirmation/confirmation.go b/pkg/wire/confirmation/confirmation.go index caf868f0..bc0edb57 100644 --- a/pkg/wire/confirmation/confirmation.go +++ b/pkg/wire/confirmation/confirmation.go @@ -1,6 +1,8 @@ package confirmation import ( + "fmt" + "github.com/Indra-Labs/indra" "github.com/Indra-Labs/indra/pkg/nonce" "github.com/Indra-Labs/indra/pkg/slice" @@ -34,6 +36,11 @@ type OnionSkin struct { nonce.ID } +func (x *OnionSkin) String() string { + return fmt.Sprintf("\n\tnonce: %x\n", + x.ID) +} + func (x *OnionSkin) Inner() types.Onion { return nil } func (x *OnionSkin) Insert(o types.Onion) {} func (x *OnionSkin) Len() int { return MinLen } diff --git a/pkg/wire/forward/forward.go b/pkg/wire/forward/forward.go index 393bb367..7182d424 100644 --- a/pkg/wire/forward/forward.go +++ b/pkg/wire/forward/forward.go @@ -1,6 +1,7 @@ package forward import ( + "fmt" "net" "net/netip" @@ -26,6 +27,11 @@ type OnionSkin struct { types.Onion } +func (x *OnionSkin) String() string { + s, _ := x.AddrPort.MarshalBinary() + return fmt.Sprintf("\n\taddrport: %x %v\n", s, x.AddrPort.String()) +} + func (x *OnionSkin) Inner() types.Onion { return x.Onion } func (x *OnionSkin) Insert(o types.Onion) { x.Onion = o } func (x *OnionSkin) Len() int { diff --git a/pkg/wire/layer/layer.go b/pkg/wire/layer/layer.go index 0dc7a312..07bdd721 100644 --- a/pkg/wire/layer/layer.go +++ b/pkg/wire/layer/layer.go @@ -2,6 +2,7 @@ package layer import ( "crypto/cipher" + "fmt" "github.com/Indra-Labs/indra" "github.com/Indra-Labs/indra/pkg/ciph" @@ -42,8 +43,13 @@ type OnionSkin struct { types.Onion } +func (x *OnionSkin) String() string { + return fmt.Sprintf("\n\tnonce: %x\n\tto: %x,\n\tfrom: %x,\n", + x.To.GetCloak(), pub.Derive(x.From).ToBytes(), x.Nonce) +} + const MinLen = magicbytes.Len + nonce.IVLen + - address.Len + pub.KeyLen + slice.Uint32Len + address.Len + pub.KeyLen func (x *OnionSkin) Inner() types.Onion { return x.Onion } func (x *OnionSkin) Insert(o types.Onion) { x.Onion = o } diff --git a/pkg/wire/layers.go b/pkg/wire/layers.go index 34864819..b8b34d63 100644 --- a/pkg/wire/layers.go +++ b/pkg/wire/layers.go @@ -72,6 +72,7 @@ func (o OnionSkins) OnionSkin(to *address.Sender, from *prv.Key) OnionSkins { return append(o, &layer.OnionSkin{ To: to, From: from, + Nonce: nonce.New(), Onion: os, }) } diff --git a/version.go b/version.go index 59ccb367..89638530 100644 --- a/version.go +++ b/version.go @@ -10,11 +10,11 @@ var ( // GitRef is the gitref, as in refs/heads/branchname. GitRef = "refs/heads/main" // ParentGitCommit is the commit hash of the parent HEAD. - ParentGitCommit = "1f73709fbafd7e43150a298f32d9f00520e7e47b" + ParentGitCommit = "d77ece43a28634a1c8502bac025840d70f3b23e2" // BuildTime stores the time when the current binary was built. - BuildTime = "2022-12-28T12:22:54Z" + BuildTime = "2022-12-28T16:34:23Z" // SemVer lists the (latest) git tag on the build. - SemVer = "v0.0.253" + SemVer = "v0.0.255" // PathBase is the path base returned from runtime caller. PathBase = "/home/loki/src/github.com/Indra-Labs/indra/" // Major is the major number from the tag. @@ -22,7 +22,7 @@ var ( // Minor is the minor number from the tag. Minor = 0 // Patch is the patch version number from the tag. - Patch = 253 + Patch = 255 ) // Version returns a pretty printed version information string.