diff --git a/pkg/client/client.go b/pkg/client/client.go index 1ab30004..d69140ac 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -2,6 +2,7 @@ package client import ( "net/netip" + "sync" "github.com/Indra-Labs/indra" "github.com/Indra-Labs/indra/pkg/ifc" @@ -41,6 +42,7 @@ type Client struct { *address.ReceiveCache Circuits Sessions + sync.Mutex qu.C } @@ -58,101 +60,169 @@ func New(tpt ifc.Transport, hdrPrv *prv.Key, no *node.Node, return } +// Start a single thread of the Client. func (c *Client) Start() { out: for { - select { - case <-c.C.Wait(): - c.Cleanup() + if c.runner() { break out - case b := <-c.Node.Receive(): - // process received message - var onion types.Onion - var e error - cursor := slice.NewCursor() - if onion, e = wire.PeelOnion(b, cursor); check(e) { - break - } - switch on := onion.(type) { - case *cipher.OnionSkin: - // This either is in a forward only SendKeys - // message or we are the buyer and these are our - // session keys. - break - - case *confirmation.OnionSkin: - // 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. - break - - case *delay.OnionSkin: - - break - - case *exit.OnionSkin: - // payload is forwarded to a local port and the - // response is forwarded back with a reply - // header. - break - - case *forward.OnionSkin: - // forward the whole buffer received onwards. - // Usually there will be an 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. - } else { - // we need to forward this message onion. - } - break - - case *layer.OnionSkin: - // this is probably an encrypted layer for us. - break - - case *noop.OnionSkin: - // this won't happen normally - break - - case *purchase.OnionSkin: - // Purchase requires a return of arbitrary data. - break - - case *reply.OnionSkin: - // Reply means another OnionSkin is coming and - // the payload encryption uses the Payload key. - break - - case *response.OnionSkin: - // Response is a payload from an exit message. - break - - case *session.OnionSkin: - // 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 Bolt 4 onion routed - // payment path that will cause the seller to - // activate the accounting onion the two keys it - // sent back with the nonce. - break - - case *token.OnionSkin: - // not really sure if we are using these. - break - - default: - log.I.S("unrecognised packet", b) - } } } } +func (c *Client) runner() (out bool) { + select { + case <-c.C.Wait(): + c.Cleanup() + out = true + break + case b := <-c.Node.Receive(): + // process received message + var onion types.Onion + var e error + cursor := slice.NewCursor() + if onion, e = wire.PeelOnion(b, cursor); check(e) { + break + } + switch on := onion.(type) { + case *cipher.OnionSkin: + c.cipher(on, b) + break + + case *confirmation.OnionSkin: + c.confirmation(on, b) + break + + case *delay.OnionSkin: + c.delay(on, b) + break + + case *exit.OnionSkin: + c.exit(on, b) + break + + case *forward.OnionSkin: + c.forward(on, b) + break + + case *layer.OnionSkin: + c.layer(on, b) + break + + case *noop.OnionSkin: + c.noop(on, b) + break + + case *purchase.OnionSkin: + c.purchase(on, b) + break + + case *reply.OnionSkin: + c.reply(on, b) + break + + case *response.OnionSkin: + c.response(on, b) + break + + case *session.OnionSkin: + c.session(on, b) + break + + case *token.OnionSkin: + c.token(on, b) + break + + default: + log.I.S("unrecognised packet", b) + } + } + return false +} + +func (c *Client) cipher(on *cipher.OnionSkin, b slice.Bytes) { + // 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) { + // 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. +} + +func (c *Client) delay(on *delay.OnionSkin, b slice.Bytes) { + // this is a message to hold the message in the buffer until a time + // 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) { + // 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. + if on.AddrPort.String() == c.Node.AddrPort.String() { + // it is for us, we want to unwrap the next + // part. + c.Node.Send(b) + } else { + // we need to forward this message onion. + c.Send(on.AddrPort, b) + } +} + +func (c *Client) layer(on *layer.OnionSkin, b slice.Bytes) { + // this is probably an encrypted layer for us. +} + +func (c *Client) noop(on *noop.OnionSkin, b slice.Bytes) { + // this won't happen normally +} + +func (c *Client) purchase(on *purchase.OnionSkin, b slice.Bytes) { + // Create a new Session. + s := &Session{} + c.Mutex.Lock() + c.Sessions = append(c.Sessions, s) + c.Mutex.Unlock() +} + +func (c *Client) reply(on *reply.OnionSkin, b slice.Bytes) { + // Reply means another OnionSkin is coming and the payload encryption + // uses the Payload key. + if on.AddrPort.String() == c.Node.AddrPort.String() { + // it is for us, we want to unwrap the next part. + c.Node.Send(b) + } else { + // we need to forward this message onion. + c.Send(on.AddrPort, b) + } + +} + +func (c *Client) response(on *response.OnionSkin, b slice.Bytes) { + // Response is a payload from an exit message. +} + +func (c *Client) session(s *session.OnionSkin, b slice.Bytes) { + // 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 + // Bolt 4 onion routed payment path that will cause the seller to + // activate the accounting onion the two keys it sent back with the + // nonce, so long as it has not yet expired. +} + +func (c *Client) token(t *token.OnionSkin, b slice.Bytes) { + // not really sure if we are using these. + return +} + func (c *Client) Cleanup() { // Do cleanup stuff before shutdown. } @@ -160,3 +230,18 @@ func (c *Client) Cleanup() { func (c *Client) Shutdown() { c.C.Q() } + +func (c *Client) Send(addr *netip.AddrPort, b slice.Bytes) { + // first search if we already have the node available with connection + // open. + as := addr.String() + for i := range c.Nodes { + if as == c.Nodes[i].Addr { + c.Nodes[i].Transport.Send(b) + return + } + } + // If we got to here none of the addresses matched, and we need to + // establish a new peer connection to them. + +} diff --git a/pkg/client/session.go b/pkg/client/session.go index e415aa29..329c2ca1 100644 --- a/pkg/client/session.go +++ b/pkg/client/session.go @@ -1,6 +1,8 @@ package client import ( + "time" + "github.com/Indra-Labs/indra/pkg/key/address" "github.com/Indra-Labs/indra/pkg/key/signer" "github.com/Indra-Labs/indra/pkg/node" @@ -15,6 +17,7 @@ type Session struct { *node.Node Remaining uint64 HeaderKey, PayloadKey *address.SendEntry + Deadline time.Time *signer.KeySet } diff --git a/pkg/node/node.go b/pkg/node/node.go index 0c6bba1f..324f3574 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -25,6 +25,7 @@ var ( // except when the netip.AddrPort is known via the packet sender address. type Node struct { nonce.ID + Addr string *netip.AddrPort HeaderPub, PayloadPub *pub.Key HeaderPriv, PayloadPriv *prv.Key @@ -33,13 +34,14 @@ type Node struct { // New creates a new Node. netip.AddrPort is optional if the counterparty is not // in direct connection. -func New(ip *netip.AddrPort, hdr, pld *pub.Key, hdrPriv, pldPriv *prv.Key, +func New(addr *netip.AddrPort, hdr, pld *pub.Key, hdrPriv, pldPriv *prv.Key, tpt ifc.Transport) (n *Node, id nonce.ID) { id = nonce.NewID() n = &Node{ ID: id, - AddrPort: ip, + Addr: addr.String(), + AddrPort: addr, Transport: tpt, HeaderPub: hdr, PayloadPub: pld, diff --git a/version.go b/version.go index 67b30921..59ccb367 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 = "493e661bd056e6c6d21eab841dddea39788d62aa" + ParentGitCommit = "1f73709fbafd7e43150a298f32d9f00520e7e47b" // BuildTime stores the time when the current binary was built. - BuildTime = "2022-12-28T10:03:58Z" + BuildTime = "2022-12-28T12:22:54Z" // SemVer lists the (latest) git tag on the build. - SemVer = "v0.0.252" + SemVer = "v0.0.253" // 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 = 252 + Patch = 253 ) // Version returns a pretty printed version information string.