Merge branch 'main' into ind-bootstrap

This commit is contained in:
Colin Lyons
2022-12-29 14:41:53 +00:00
13 changed files with 271 additions and 156 deletions

View File

@@ -22,7 +22,7 @@ jobs:
go-version: 1.19 go-version: 1.19
- name: Build - name: Build
run: go release -v ./... run: go build -v ./...
- name: Test - name: Test
run: go test -v ./... run: go test -v ./...

View File

@@ -13,23 +13,25 @@ import (
"github.com/Indra-Labs/indra/pkg/key/pub" "github.com/Indra-Labs/indra/pkg/key/pub"
"github.com/Indra-Labs/indra/pkg/key/signer" "github.com/Indra-Labs/indra/pkg/key/signer"
"github.com/Indra-Labs/indra/pkg/node" "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/slice"
"github.com/Indra-Labs/indra/pkg/types" "github.com/Indra-Labs/indra/pkg/types"
"github.com/Indra-Labs/indra/pkg/wire" "github.com/Indra-Labs/indra/pkg/wire"
"github.com/Indra-Labs/indra/pkg/wire/cipher" "github.com/Indra-Labs/indra/pkg/wire/cipher"
"github.com/Indra-Labs/indra/pkg/wire/confirmation" "github.com/Indra-Labs/indra/pkg/wire/confirm"
"github.com/Indra-Labs/indra/pkg/wire/delay" "github.com/Indra-Labs/indra/pkg/wire/delay"
"github.com/Indra-Labs/indra/pkg/wire/exit" "github.com/Indra-Labs/indra/pkg/wire/exit"
"github.com/Indra-Labs/indra/pkg/wire/forward" "github.com/Indra-Labs/indra/pkg/wire/forward"
"github.com/Indra-Labs/indra/pkg/wire/layer" "github.com/Indra-Labs/indra/pkg/wire/layer"
"github.com/Indra-Labs/indra/pkg/wire/noop" "github.com/Indra-Labs/indra/pkg/wire/noop"
"github.com/Indra-Labs/indra/pkg/wire/purchase" "github.com/Indra-Labs/indra/pkg/wire/purchase"
"github.com/Indra-Labs/indra/pkg/wire/reply"
"github.com/Indra-Labs/indra/pkg/wire/response" "github.com/Indra-Labs/indra/pkg/wire/response"
"github.com/Indra-Labs/indra/pkg/wire/reverse"
"github.com/Indra-Labs/indra/pkg/wire/session" "github.com/Indra-Labs/indra/pkg/wire/session"
"github.com/Indra-Labs/indra/pkg/wire/token" "github.com/Indra-Labs/indra/pkg/wire/token"
log2 "github.com/cybriq/proc/pkg/log" log2 "github.com/cybriq/proc/pkg/log"
"github.com/cybriq/qu" "github.com/cybriq/qu"
"go.uber.org/atomic"
) )
const DefaultDeadline = 10 * time.Minute const DefaultDeadline = 10 * time.Minute
@@ -47,8 +49,10 @@ type Client struct {
*address.ReceiveCache *address.ReceiveCache
Circuits Circuits
Sessions Sessions
*confirm.Confirms
sync.Mutex sync.Mutex
*signer.KeySet *signer.KeySet
atomic.Bool
qu.C qu.C
} }
@@ -56,9 +60,10 @@ func New(tpt ifc.Transport, hdrPrv *prv.Key, no *node.Node,
nodes node.Nodes) (c *Client, e error) { nodes node.Nodes) (c *Client, e error) {
no.Transport = tpt no.Transport = tpt
no.HeaderPriv = hdrPrv no.HeaderPrv = hdrPrv
no.HeaderPub = pub.Derive(hdrPrv) no.HeaderPub = pub.Derive(hdrPrv)
c = &Client{ c = &Client{
Confirms: confirm.NewConfirms(),
Node: no, Node: no,
Nodes: nodes, Nodes: nodes,
ReceiveCache: address.NewReceiveCache(), ReceiveCache: address.NewReceiveCache(),
@@ -69,158 +74,175 @@ func New(tpt ifc.Transport, hdrPrv *prv.Key, no *node.Node,
} }
// Start a single thread of the Client. // Start a single thread of the Client.
func (c *Client) Start() { func (cl *Client) Start() {
out: out:
for { for {
if c.runner() { if cl.runner() {
break out break out
} }
} }
} }
func (c *Client) runner() (out bool) { func (cl *Client) runner() (out bool) {
select { select {
case <-c.C.Wait(): case <-cl.C.Wait():
c.Cleanup() cl.Cleanup()
out = true out = true
break break
case b := <-c.Node.Receive(): case b := <-cl.Node.Receive():
// process received message // process received message
var onion types.Onion var onion types.Onion
var e error var e error
cur := slice.NewCursor() c := slice.NewCursor()
if onion, e = wire.PeelOnion(b, cur); check(e) { if onion, e = wire.PeelOnion(b, c); check(e) {
break break
} }
switch on := onion.(type) { switch on := onion.(type) {
case *cipher.OnionSkin: case *cipher.OnionSkin:
log.I.Ln(reflect.TypeOf(on)) log.T.Ln(reflect.TypeOf(on))
c.cipher(on, b, cur) cl.cipher(on, b, c)
case *confirmation.OnionSkin: case *confirm.OnionSkin:
log.I.Ln(reflect.TypeOf(on)) log.T.Ln(reflect.TypeOf(on))
c.confirmation(on, b, cur) cl.confirm(on, b, c)
case *delay.OnionSkin: case *delay.OnionSkin:
log.I.Ln(reflect.TypeOf(on)) log.T.Ln(reflect.TypeOf(on))
c.delay(on, b, cur) cl.delay(on, b, c)
case *exit.OnionSkin: case *exit.OnionSkin:
log.I.Ln(reflect.TypeOf(on)) log.T.Ln(reflect.TypeOf(on))
c.exit(on, b, cur) cl.exit(on, b, c)
case *forward.OnionSkin: case *forward.OnionSkin:
log.I.Ln(reflect.TypeOf(on)) log.T.Ln(reflect.TypeOf(on))
c.forward(on, b, cur) cl.forward(on, b, c)
case *layer.OnionSkin: case *layer.OnionSkin:
log.I.Ln(reflect.TypeOf(on)) log.T.Ln(reflect.TypeOf(on))
c.layer(on, b, cur) cl.layer(on, b, c)
case *noop.OnionSkin: case *noop.OnionSkin:
log.I.Ln(reflect.TypeOf(on)) log.T.Ln(reflect.TypeOf(on))
c.noop(on, b, cur) cl.noop(on, b, c)
case *purchase.OnionSkin: case *purchase.OnionSkin:
log.I.Ln(reflect.TypeOf(on)) log.T.Ln(reflect.TypeOf(on))
c.purchase(on, b, cur) cl.purchase(on, b, c)
case *reply.OnionSkin: case *reverse.OnionSkin:
log.I.Ln(reflect.TypeOf(on)) log.T.Ln(reflect.TypeOf(on))
c.reply(on, b, cur) cl.reply(on, b, c)
case *response.OnionSkin: case *response.OnionSkin:
log.I.Ln(reflect.TypeOf(on)) log.T.Ln(reflect.TypeOf(on))
c.response(on, b, cur) cl.response(on, b, c)
case *session.OnionSkin: case *session.OnionSkin:
log.I.Ln(reflect.TypeOf(on)) log.T.Ln(reflect.TypeOf(on))
c.session(on, b, cur) cl.session(on, b, c)
case *token.OnionSkin: case *token.OnionSkin:
log.I.Ln(reflect.TypeOf(on)) log.T.Ln(reflect.TypeOf(on))
c.token(on, b, cur) cl.token(on, b, c)
default: default:
log.I.S("unrecognised packet", b) log.I.S("unrecognised packet", b)
} }
} }
return false return
} }
func (c *Client) cipher(on *cipher.OnionSkin, b slice.Bytes, cur *slice.Cursor) { func (cl *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 // This either is in a forward only SendKeys message or we are the buyer
// and these are our session keys. // and these are our session keys.
} }
func (c *Client) confirmation(on *confirmation.OnionSkin, b slice.Bytes, func (cl *Client) confirm(on *confirm.OnionSkin, b slice.Bytes,
cur *slice.Cursor) { c *slice.Cursor) {
// This will be an 8 byte nonce that confirms a message passed, ping and // When a confirm arrives check if it is registered for and run
// cipher onions return these, as they are pure forward messages that // the hook that was registered with it.
// send a message one way and the confirmation is the acknowledgement. cl.Confirms.Lock()
log.I.S(on) for i := range (*cl).Cnf {
if on.ID == (*cl).Cnf[i].ID {
(*cl).Cnf[i].Hook(on)
}
}
cl.Confirms.Unlock()
} }
func (c *Client) delay(on *delay.OnionSkin, b slice.Bytes, cur *slice.Cursor) { func (cl *Client) RegisterConfirmation(id nonce.ID, hook confirm.Hook) {
cl.Confirms.Lock()
cl.Cnf = append((*cl).Cnf, confirm.Callback{
ID: id,
Time: time.Now(),
Hook: hook,
})
cl.Confirms.Unlock()
}
func (cl *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 // 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 // elapses. The accounting for the remainder of the message adds a
// factor to the effective byte consumption in accordance with the time // factor to the effective byte consumption in accordance with the time
// to be stored. // to be stored.
} }
func (c *Client) exit(on *exit.OnionSkin, b slice.Bytes, cur *slice.Cursor) { func (cl *Client) exit(on *exit.OnionSkin, b slice.Bytes, cur *slice.Cursor) {
// payload is forwarded to a local port and the response is forwarded // payload is forwarded to a local port and the response is forwarded
// back with a reply header. // back with a reply header.
} }
func (c *Client) forward(on *forward.OnionSkin, b slice.Bytes, cur *slice.Cursor) { func (cl *Client) forward(on *forward.OnionSkin, b slice.Bytes, c *slice.Cursor) {
// forward the whole buffer received onwards. Usually there will be a // forward the whole buffer received onwards. Usually there will be a
// layer.OnionSkin under this which will be unwrapped by the receiver. // layer.OnionSkin under this which will be unwrapped by the receiver.
if on.AddrPort.String() == c.Node.AddrPort.String() { if on.AddrPort.String() == cl.Node.AddrPort.String() {
// it is for us, we want to unwrap the next // it is for us, we want to unwrap the next
// part. // part.
log.I.Ln("processing new message") log.T.Ln("processing new message", *c)
c.Node.Send(b[*cur:]) b = append(b[*c:], slice.NoisePad(int(*c))...)
cl.Node.Send(b)
} else { } else {
// we need to forward this message onion. // we need to forward this message onion.
log.I.Ln("forwarding") log.T.Ln("forwarding")
c.Send(on.AddrPort, b) cl.Send(on.AddrPort, b)
} }
} }
func (c *Client) layer(on *layer.OnionSkin, b slice.Bytes, cur *slice.Cursor) { func (cl *Client) layer(on *layer.OnionSkin, b slice.Bytes, c *slice.Cursor) {
// this is probably an encrypted layer for us. // this is probably an encrypted layer for us.
log.I.Ln("decrypting onion skin") log.T.Ln("decrypting onion skin")
// log.I.S(on, b[*cur:].ToBytes()) // log.I.S(on, b[*c:].ToBytes())
rcv := c.ReceiveCache.FindCloaked(on.Cloak) rcv := cl.ReceiveCache.FindCloaked(on.Cloak)
if rcv == nil { if rcv == nil {
log.I.Ln("no matching key found from cloaked key") log.T.Ln("no matching key found from cloaked key")
return return
} }
on.Decrypt(rcv.Key, b, cur) on.Decrypt(rcv.Key, b, c)
log.I.S(b[*cur:].ToBytes()) b = append(b[*c:], slice.NoisePad(int(*c))...)
c.Node.Send(b[*cur:]) log.T.S(b.ToBytes())
cl.Node.Send(b)
} }
func (c *Client) noop(on *noop.OnionSkin, b slice.Bytes, cur *slice.Cursor) { func (cl *Client) noop(on *noop.OnionSkin, b slice.Bytes, c *slice.Cursor) {
// this won't happen normally // this won't happen normally
} }
func (c *Client) purchase(on *purchase.OnionSkin, b slice.Bytes, cur *slice.Cursor) { func (cl *Client) purchase(on *purchase.OnionSkin, b slice.Bytes, c *slice.Cursor) {
// Create a new Session. // Create a new Session.
s := &Session{} s := &Session{}
c.Mutex.Lock() cl.Mutex.Lock()
s.Deadline = time.Now().Add(DefaultDeadline) s.Deadline = time.Now().Add(DefaultDeadline)
c.Sessions = append(c.Sessions, s) cl.Sessions = append(cl.Sessions, s)
c.Mutex.Unlock() cl.Mutex.Unlock()
} }
func (c *Client) reply(on *reply.OnionSkin, b slice.Bytes, cur *slice.Cursor) { func (cl *Client) reply(on *reverse.OnionSkin, b slice.Bytes, c *slice.Cursor) {
// Reply means another OnionSkin is coming and the payload encryption // Reply means another OnionSkin is coming and the payload encryption
// uses the Payload key. // uses the Payload key.
if on.AddrPort.String() == c.Node.AddrPort.String() { if on.AddrPort.String() == cl.Node.AddrPort.String() {
// it is for us, we want to unwrap the next part. // it is for us, we want to unwrap the next part.
c.Node.Send(b) cl.Node.Send(b)
} else { } else {
// we need to forward this message onion. // we need to forward this message onion.
c.Send(on.AddrPort, b) cl.Send(on.AddrPort, b)
} }
} }
func (c *Client) response(on *response.OnionSkin, b slice.Bytes, cur *slice.Cursor) { func (cl *Client) response(on *response.OnionSkin, b slice.Bytes, cur *slice.Cursor) {
// Response is a payload from an exit message. // Response is a payload from an exit message.
} }
func (c *Client) session(s *session.OnionSkin, b slice.Bytes, cur *slice.Cursor) { func (cl *Client) session(s *session.OnionSkin, b slice.Bytes, cur *slice.Cursor) {
// Session is returned from a Purchase message in the reply layers. // 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 // Session has a nonce.ID that is given in the last layer of a LN sphinx
@@ -229,26 +251,31 @@ func (c *Client) session(s *session.OnionSkin, b slice.Bytes, cur *slice.Cursor)
// nonce, so long as it has not yet expired. // nonce, so long as it has not yet expired.
} }
func (c *Client) token(t *token.OnionSkin, b slice.Bytes, cur *slice.Cursor) { func (cl *Client) token(t *token.OnionSkin, b slice.Bytes, cur *slice.Cursor) {
// not really sure if we are using these. // not really sure if we are using these.
return return
} }
func (c *Client) Cleanup() { func (cl *Client) Cleanup() {
// Do cleanup stuff before shutdown. // Do cleanup stuff before shutdown.
} }
func (c *Client) Shutdown() { func (cl *Client) Shutdown() {
c.C.Q() if cl.Bool.Load() {
return
}
log.I.Ln("shutting down client", cl.Node.AddrPort.String())
cl.Bool.Store(true)
cl.C.Q()
} }
func (c *Client) Send(addr *netip.AddrPort, b slice.Bytes) { func (cl *Client) Send(addr *netip.AddrPort, b slice.Bytes) {
// first search if we already have the node available with connection // first search if we already have the node available with connection
// open. // open.
as := addr.String() as := addr.String()
for i := range c.Nodes { for i := range cl.Nodes {
if as == c.Nodes[i].Addr { if as == cl.Nodes[i].Addr {
c.Nodes[i].Transport.Send(b) cl.Nodes[i].Transport.Send(b)
return return
} }
} }

View File

@@ -13,11 +13,14 @@ import (
"github.com/Indra-Labs/indra/pkg/slice" "github.com/Indra-Labs/indra/pkg/slice"
"github.com/Indra-Labs/indra/pkg/transport" "github.com/Indra-Labs/indra/pkg/transport"
"github.com/Indra-Labs/indra/pkg/wire" "github.com/Indra-Labs/indra/pkg/wire"
"github.com/Indra-Labs/indra/pkg/wire/confirm"
log2 "github.com/cybriq/proc/pkg/log" log2 "github.com/cybriq/proc/pkg/log"
"github.com/cybriq/qu"
) )
func TestPing(t *testing.T) { func TestPing(t *testing.T) {
log2.CodeLoc = true log2.CodeLoc = true
// log2.SetLogLevel(log2.Trace)
var clients [4]*Client var clients [4]*Client
var nodes [4]*node.Node var nodes [4]*node.Node
var transports [4]ifc.Transport var transports [4]ifc.Transport
@@ -38,7 +41,6 @@ func TestPing(t *testing.T) {
} }
pldPub := pub.Derive(pldPrv) pldPub := pub.Derive(pldPrv)
addr := slice.GenerateRandomAddrPortIPv4() addr := slice.GenerateRandomAddrPortIPv4()
log.I.S(addr)
nodes[i], _ = node.New(addr, hdrPub, pldPub, hdrPrv, pldPrv, transports[i]) nodes[i], _ = node.New(addr, hdrPub, pldPub, hdrPrv, pldPrv, transports[i])
if clients[i], e = New(transports[i], hdrPrv, nodes[i], nil); check(e) { if clients[i], e = New(transports[i], hdrPrv, nodes[i], nil); check(e) {
t.Error(e) t.Error(e)
@@ -70,12 +72,25 @@ func TestPing(t *testing.T) {
hop[i] = clients[0].Nodes[i] hop[i] = clients[0].Nodes[i]
} }
os := wire.Ping(pn, clients[0].Node, hop, ks) os := wire.Ping(pn, clients[0].Node, hop, ks)
log.I.S(os) // log.I.S(os)
quit := qu.T()
log.I.S("sending ping with ID", os[len(os)-1].(*confirm.OnionSkin))
clients[0].RegisterConfirmation(
os[len(os)-1].(*confirm.OnionSkin).ID,
func(cf *confirm.OnionSkin) {
log.I.S("received ping confirmation ID", cf)
quit.Q()
},
)
o := os.Assemble() o := os.Assemble()
b := wire.EncodeOnion(o) b := wire.EncodeOnion(o)
// log.I.S(b.ToBytes())
hop[0].Send(b) hop[0].Send(b)
time.Sleep(time.Second * 5) go func() {
time.Sleep(time.Second * 2)
quit.Q()
t.Error("ping got stuck")
}()
<-quit.Wait()
for _, v := range clients { for _, v := range clients {
v.Shutdown() v.Shutdown()
} }

View File

@@ -25,28 +25,28 @@ var (
// except when the netip.AddrPort is known via the packet sender address. // except when the netip.AddrPort is known via the packet sender address.
type Node struct { type Node struct {
nonce.ID nonce.ID
Addr string Addr string
AddrPort *netip.AddrPort AddrPort *netip.AddrPort
HeaderPub, PayloadPub *pub.Key HeaderPub, PayloadPub *pub.Key
HeaderPriv, PayloadPriv *prv.Key HeaderPrv, PayloadPrv *prv.Key
ifc.Transport ifc.Transport
} }
// New creates a new Node. netip.AddrPort is optional if the counterparty is not // New creates a new Node. netip.AddrPort is optional if the counterparty is not
// in direct connection. // in direct connection.
func New(addr *netip.AddrPort, hdr, pld *pub.Key, hdrPriv, pldPriv *prv.Key, func New(addr *netip.AddrPort, hdr, pld *pub.Key, hdrPrv, pldPrv *prv.Key,
tpt ifc.Transport) (n *Node, id nonce.ID) { tpt ifc.Transport) (n *Node, id nonce.ID) {
id = nonce.NewID() id = nonce.NewID()
n = &Node{ n = &Node{
ID: id, ID: id,
Addr: addr.String(), Addr: addr.String(),
AddrPort: addr, AddrPort: addr,
Transport: tpt, Transport: tpt,
HeaderPub: hdr, HeaderPub: hdr,
PayloadPub: pld, PayloadPub: pld,
HeaderPriv: hdrPriv, HeaderPrv: hdrPrv,
PayloadPriv: pldPriv, PayloadPrv: pldPrv,
} }
return return
} }

View File

@@ -23,10 +23,6 @@ var (
// //
// After ~10 seconds these can be purged from the cache as they are otherwise a // After ~10 seconds these can be purged from the cache as they are otherwise a
// DoS vector buffer flooding. // DoS vector buffer flooding.
//
// The Decode function wipes the original message data for security as the
// private keys inside it are no longer needed and any secret should only have
// one storage, so it doesn't appear in any GC later.
type OnionSkin struct { type OnionSkin struct {
Header, Payload *pub.Key Header, Payload *pub.Key
types.Onion types.Onion

View File

@@ -7,15 +7,15 @@ import (
"github.com/Indra-Labs/indra/pkg/slice" "github.com/Indra-Labs/indra/pkg/slice"
"github.com/Indra-Labs/indra/pkg/types" "github.com/Indra-Labs/indra/pkg/types"
"github.com/Indra-Labs/indra/pkg/wire/cipher" "github.com/Indra-Labs/indra/pkg/wire/cipher"
"github.com/Indra-Labs/indra/pkg/wire/confirmation" "github.com/Indra-Labs/indra/pkg/wire/confirm"
"github.com/Indra-Labs/indra/pkg/wire/delay" "github.com/Indra-Labs/indra/pkg/wire/delay"
"github.com/Indra-Labs/indra/pkg/wire/exit" "github.com/Indra-Labs/indra/pkg/wire/exit"
"github.com/Indra-Labs/indra/pkg/wire/forward" "github.com/Indra-Labs/indra/pkg/wire/forward"
"github.com/Indra-Labs/indra/pkg/wire/layer" "github.com/Indra-Labs/indra/pkg/wire/layer"
"github.com/Indra-Labs/indra/pkg/wire/magicbytes" "github.com/Indra-Labs/indra/pkg/wire/magicbytes"
"github.com/Indra-Labs/indra/pkg/wire/purchase" "github.com/Indra-Labs/indra/pkg/wire/purchase"
"github.com/Indra-Labs/indra/pkg/wire/reply"
"github.com/Indra-Labs/indra/pkg/wire/response" "github.com/Indra-Labs/indra/pkg/wire/response"
"github.com/Indra-Labs/indra/pkg/wire/reverse"
"github.com/Indra-Labs/indra/pkg/wire/session" "github.com/Indra-Labs/indra/pkg/wire/session"
"github.com/Indra-Labs/indra/pkg/wire/token" "github.com/Indra-Labs/indra/pkg/wire/token"
log2 "github.com/cybriq/proc/pkg/log" log2 "github.com/cybriq/proc/pkg/log"
@@ -42,8 +42,8 @@ func PeelOnion(b slice.Bytes, c *slice.Cursor) (on types.Onion, e error) {
return return
} }
on = o on = o
case confirmation.MagicString: case confirm.MagicString:
o := &confirmation.OnionSkin{} o := &confirm.OnionSkin{}
if e = o.Decode(b, c); check(e) { if e = o.Decode(b, c); check(e) {
return return
} }
@@ -78,8 +78,8 @@ func PeelOnion(b slice.Bytes, c *slice.Cursor) (on types.Onion, e error) {
return return
} }
on = o on = o
case reply.MagicString: case reverse.MagicString:
o := &reply.OnionSkin{} o := &reverse.OnionSkin{}
if e = o.Decode(b, c); check(e) { if e = o.Decode(b, c); check(e) {
return return
} }

View File

@@ -17,14 +17,14 @@ import (
"github.com/Indra-Labs/indra/pkg/testutils" "github.com/Indra-Labs/indra/pkg/testutils"
"github.com/Indra-Labs/indra/pkg/types" "github.com/Indra-Labs/indra/pkg/types"
"github.com/Indra-Labs/indra/pkg/wire/cipher" "github.com/Indra-Labs/indra/pkg/wire/cipher"
"github.com/Indra-Labs/indra/pkg/wire/confirmation" "github.com/Indra-Labs/indra/pkg/wire/confirm"
"github.com/Indra-Labs/indra/pkg/wire/delay" "github.com/Indra-Labs/indra/pkg/wire/delay"
"github.com/Indra-Labs/indra/pkg/wire/exit" "github.com/Indra-Labs/indra/pkg/wire/exit"
"github.com/Indra-Labs/indra/pkg/wire/forward" "github.com/Indra-Labs/indra/pkg/wire/forward"
"github.com/Indra-Labs/indra/pkg/wire/layer" "github.com/Indra-Labs/indra/pkg/wire/layer"
"github.com/Indra-Labs/indra/pkg/wire/purchase" "github.com/Indra-Labs/indra/pkg/wire/purchase"
"github.com/Indra-Labs/indra/pkg/wire/reply"
"github.com/Indra-Labs/indra/pkg/wire/response" "github.com/Indra-Labs/indra/pkg/wire/response"
"github.com/Indra-Labs/indra/pkg/wire/reverse"
"github.com/Indra-Labs/indra/pkg/wire/session" "github.com/Indra-Labs/indra/pkg/wire/session"
"github.com/Indra-Labs/indra/pkg/wire/token" "github.com/Indra-Labs/indra/pkg/wire/token"
log2 "github.com/cybriq/proc/pkg/log" log2 "github.com/cybriq/proc/pkg/log"
@@ -73,9 +73,9 @@ func TestOnionSkins_Confirmation(t *testing.T) {
if oncn, e = PeelOnion(onb, c); check(e) { if oncn, e = PeelOnion(onb, c); check(e) {
t.FailNow() t.FailNow()
} }
var cf *confirmation.OnionSkin var cf *confirm.OnionSkin
var ok bool var ok bool
if cf, ok = oncn.(*confirmation.OnionSkin); !ok { if cf, ok = oncn.(*confirm.OnionSkin); !ok {
t.Error("did not unwrap expected type") t.Error("did not unwrap expected type")
t.FailNow() t.FailNow()
} }
@@ -226,8 +226,8 @@ func TestOnionSkins_Layer(t *testing.T) {
t.Error(e) t.Error(e)
t.FailNow() t.FailNow()
} }
oc := &confirmation.OnionSkin{} oc := &confirm.OnionSkin{}
if oc, ok = onc.(*confirmation.OnionSkin); !ok { if oc, ok = onc.(*confirm.OnionSkin); !ok {
t.Error("did not unwrap expected type") t.Error("did not unwrap expected type")
t.FailNow() t.FailNow()
} }
@@ -300,8 +300,8 @@ func TestOnionSkins_Reply(t *testing.T) {
if onf, e = PeelOnion(onb, c); check(e) { if onf, e = PeelOnion(onb, c); check(e) {
t.FailNow() t.FailNow()
} }
var cf *reply.OnionSkin var cf *reverse.OnionSkin
if cf, ok = onf.(*reply.OnionSkin); !ok { if cf, ok = onf.(*reverse.OnionSkin); !ok {
t.Error("did not unwrap expected type", reflect.TypeOf(onf)) t.Error("did not unwrap expected type", reflect.TypeOf(onf))
t.FailNow() t.FailNow()
} }

View File

@@ -1,4 +1,4 @@
package confirmation package confirm
import ( import (
"fmt" "fmt"

View File

@@ -0,0 +1,77 @@
package confirm
import (
"sync"
"time"
"github.com/Indra-Labs/indra/pkg/nonce"
)
type Hook func(cf *OnionSkin)
type Callback struct {
nonce.ID
time.Time
Onion *OnionSkin
Hook Hook
}
type Confirms struct {
sync.Mutex
Cnf []Callback
}
func NewConfirms() *Confirms {
cn := Confirms{
Cnf: make([]Callback, 0),
}
return &cn
}
func (cn *Confirms) Add(cb *Callback) {
cn.Lock()
(*cn).Cnf = append((*cn).Cnf, *cb)
cn.Unlock()
}
func (cn *Confirms) Confirm(id nonce.ID) bool {
cn.Lock()
defer cn.Unlock()
for i := range (*cn).Cnf {
if id == (*cn).Cnf[i].ID {
(*cn).Cnf[i].Hook((*cn).Cnf[i].Onion)
// delete Callback.
end := i + 1
// if this is the last one, the end is the last one
// also.
if end > len((*cn).Cnf)-1 {
end = len((*cn).Cnf) - 1
}
(*cn).Cnf = append((*cn).Cnf[:i], (*cn).Cnf[end:]...)
return true
}
}
return false
}
// Flush clears out entries older than timePast.
func (cn *Confirms) Flush(timePast time.Time) {
cn.Lock()
defer cn.Unlock()
var foundCount int
for i := range (*cn).Cnf {
if (*cn).Cnf[i].Time.Before(timePast) {
foundCount++
}
}
if foundCount > 0 {
cnNew := NewConfirms()
for i := range (*cn).Cnf {
if !(*cn).Cnf[i].Time.Before(timePast) {
(*cnNew).Cnf = append((*cnNew).Cnf,
(*cn).Cnf[i])
}
}
(*cn).Cnf = (*cnNew).Cnf
}
}

View File

@@ -13,15 +13,15 @@ import (
"github.com/Indra-Labs/indra/pkg/slice" "github.com/Indra-Labs/indra/pkg/slice"
"github.com/Indra-Labs/indra/pkg/types" "github.com/Indra-Labs/indra/pkg/types"
"github.com/Indra-Labs/indra/pkg/wire/cipher" "github.com/Indra-Labs/indra/pkg/wire/cipher"
"github.com/Indra-Labs/indra/pkg/wire/confirmation" "github.com/Indra-Labs/indra/pkg/wire/confirm"
"github.com/Indra-Labs/indra/pkg/wire/delay" "github.com/Indra-Labs/indra/pkg/wire/delay"
"github.com/Indra-Labs/indra/pkg/wire/exit" "github.com/Indra-Labs/indra/pkg/wire/exit"
"github.com/Indra-Labs/indra/pkg/wire/forward" "github.com/Indra-Labs/indra/pkg/wire/forward"
"github.com/Indra-Labs/indra/pkg/wire/layer" "github.com/Indra-Labs/indra/pkg/wire/layer"
"github.com/Indra-Labs/indra/pkg/wire/noop" "github.com/Indra-Labs/indra/pkg/wire/noop"
"github.com/Indra-Labs/indra/pkg/wire/purchase" "github.com/Indra-Labs/indra/pkg/wire/purchase"
"github.com/Indra-Labs/indra/pkg/wire/reply"
"github.com/Indra-Labs/indra/pkg/wire/response" "github.com/Indra-Labs/indra/pkg/wire/response"
"github.com/Indra-Labs/indra/pkg/wire/reverse"
"github.com/Indra-Labs/indra/pkg/wire/session" "github.com/Indra-Labs/indra/pkg/wire/session"
"github.com/Indra-Labs/indra/pkg/wire/token" "github.com/Indra-Labs/indra/pkg/wire/token"
) )
@@ -46,7 +46,7 @@ func (o OnionSkins) Cipher(hdr, pld *pub.Key) OnionSkins {
} }
func (o OnionSkins) Confirmation(id nonce.ID) OnionSkins { func (o OnionSkins) Confirmation(id nonce.ID) OnionSkins {
return append(o, &confirmation.OnionSkin{ID: id}) return append(o, &confirm.OnionSkin{ID: id})
} }
func (o OnionSkins) Delay(d time.Duration) OnionSkins { func (o OnionSkins) Delay(d time.Duration) OnionSkins {
@@ -86,7 +86,7 @@ func (o OnionSkins) Purchase(nBytes uint64, prvs [3]*prv.Key,
}) })
} }
func (o OnionSkins) Reply(ip *netip.AddrPort) OnionSkins { func (o OnionSkins) Reply(ip *netip.AddrPort) OnionSkins {
return append(o, &reply.OnionSkin{AddrPort: ip, Onion: os}) return append(o, &reverse.OnionSkin{AddrPort: ip, Onion: os})
} }
func (o OnionSkins) Response(res slice.Bytes) OnionSkins { func (o OnionSkins) Response(res slice.Bytes) OnionSkins {
rs := response.OnionSkin(res) rs := response.OnionSkin(res)

View File

@@ -14,12 +14,12 @@ import (
"github.com/Indra-Labs/indra/pkg/testutils" "github.com/Indra-Labs/indra/pkg/testutils"
"github.com/Indra-Labs/indra/pkg/types" "github.com/Indra-Labs/indra/pkg/types"
"github.com/Indra-Labs/indra/pkg/wire/cipher" "github.com/Indra-Labs/indra/pkg/wire/cipher"
"github.com/Indra-Labs/indra/pkg/wire/confirmation" "github.com/Indra-Labs/indra/pkg/wire/confirm"
"github.com/Indra-Labs/indra/pkg/wire/exit" "github.com/Indra-Labs/indra/pkg/wire/exit"
"github.com/Indra-Labs/indra/pkg/wire/forward" "github.com/Indra-Labs/indra/pkg/wire/forward"
"github.com/Indra-Labs/indra/pkg/wire/layer" "github.com/Indra-Labs/indra/pkg/wire/layer"
"github.com/Indra-Labs/indra/pkg/wire/purchase" "github.com/Indra-Labs/indra/pkg/wire/purchase"
"github.com/Indra-Labs/indra/pkg/wire/reply" "github.com/Indra-Labs/indra/pkg/wire/reverse"
log2 "github.com/cybriq/proc/pkg/log" log2 "github.com/cybriq/proc/pkg/log"
) )
@@ -54,7 +54,7 @@ func PeelOnionSkin(t *testing.T, b slice.Bytes,
} }
func PeelConfirmation(t *testing.T, b slice.Bytes, func PeelConfirmation(t *testing.T, b slice.Bytes,
c *slice.Cursor) (cn *confirmation.OnionSkin) { c *slice.Cursor) (cn *confirm.OnionSkin) {
var ok bool var ok bool
var e error var e error
@@ -62,7 +62,7 @@ func PeelConfirmation(t *testing.T, b slice.Bytes,
if on, e = PeelOnion(b, c); check(e) { if on, e = PeelOnion(b, c); check(e) {
t.Error(e) t.Error(e)
} }
if cn, ok = on.(*confirmation.OnionSkin); !ok { if cn, ok = on.(*confirm.OnionSkin); !ok {
t.Error("did not unwrap expected type", reflect.TypeOf(on)) t.Error("did not unwrap expected type", reflect.TypeOf(on))
} }
return return
@@ -84,7 +84,7 @@ func PeelPurchase(t *testing.T, b slice.Bytes,
} }
func PeelReply(t *testing.T, b slice.Bytes, func PeelReply(t *testing.T, b slice.Bytes,
c *slice.Cursor) (rp *reply.OnionSkin) { c *slice.Cursor) (rp *reverse.OnionSkin) {
var ok bool var ok bool
var e error var e error
@@ -92,7 +92,7 @@ func PeelReply(t *testing.T, b slice.Bytes,
if on, e = PeelOnion(b, c); check(e) { if on, e = PeelOnion(b, c); check(e) {
t.Error(e) t.Error(e)
} }
if rp, ok = on.(*reply.OnionSkin); !ok { if rp, ok = on.(*reverse.OnionSkin); !ok {
t.Error("did not unwrap expected type", reflect.TypeOf(on)) t.Error("did not unwrap expected type", reflect.TypeOf(on))
} }
return return
@@ -151,7 +151,7 @@ func TestPing(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[0].HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(hop[0].HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(hop[0].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[0].HeaderPrv, b, c)
// Forward(hop[1].AddrPort). // Forward(hop[1].AddrPort).
f1 := PeelForward(t, b, c) f1 := PeelForward(t, b, c)
@@ -162,7 +162,7 @@ func TestPing(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[1].HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(hop[1].HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(hop[1].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[1].HeaderPrv, b, c)
// Forward(hop[2].AddrPort). // Forward(hop[2].AddrPort).
f2 := PeelForward(t, b, c) f2 := PeelForward(t, b, c)
@@ -173,7 +173,7 @@ func TestPing(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[2].HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(hop[2].HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(hop[2].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[2].HeaderPrv, b, c)
// Forward(client.AddrPort). // Forward(client.AddrPort).
f3 := PeelForward(t, b, c) f3 := PeelForward(t, b, c)
@@ -184,7 +184,7 @@ func TestPing(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(client.HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(client.HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(client.HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(client.HeaderPrv, b, c)
// Confirmation(id). // Confirmation(id).
co := PeelConfirmation(t, b, c) co := PeelConfirmation(t, b, c)
@@ -232,7 +232,7 @@ func TestSendKeys(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[0].HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(hop[0].HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(hop[0].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[0].HeaderPrv, b, c)
// Forward(hop[1].AddrPort). // Forward(hop[1].AddrPort).
f1 := PeelForward(t, b, c) f1 := PeelForward(t, b, c)
@@ -243,7 +243,7 @@ func TestSendKeys(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[1].HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(hop[1].HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(hop[1].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[1].HeaderPrv, b, c)
// Forward(hop[2].AddrPort). // Forward(hop[2].AddrPort).
f2 := PeelForward(t, b, c) f2 := PeelForward(t, b, c)
@@ -254,7 +254,7 @@ func TestSendKeys(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[2].HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(hop[2].HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(hop[2].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[2].HeaderPrv, b, c)
// Cipher(hdr, pld). // Cipher(hdr, pld).
var onc types.Onion var onc types.Onion
@@ -285,7 +285,7 @@ func TestSendKeys(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[3].HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(hop[3].HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(hop[3].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[3].HeaderPrv, b, c)
// Forward(hop[4].AddrPort). // Forward(hop[4].AddrPort).
f4 := PeelForward(t, b, c) f4 := PeelForward(t, b, c)
@@ -296,7 +296,7 @@ func TestSendKeys(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[4].HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(hop[4].HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(hop[4].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[4].HeaderPrv, b, c)
// Forward(client.AddrPort). // Forward(client.AddrPort).
f5 := PeelForward(t, b, c) f5 := PeelForward(t, b, c)
@@ -307,7 +307,7 @@ func TestSendKeys(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(client.HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(client.HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(client.HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(client.HeaderPrv, b, c)
// Confirmation(id). // Confirmation(id).
co := PeelConfirmation(t, b, c) co := PeelConfirmation(t, b, c)
@@ -355,7 +355,7 @@ func TestSendPurchase(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[0].HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(hop[0].HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(hop[0].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[0].HeaderPrv, b, c)
// Forward(hop[1].AddrPort). // Forward(hop[1].AddrPort).
f1 := PeelForward(t, b, c) f1 := PeelForward(t, b, c)
@@ -366,7 +366,7 @@ func TestSendPurchase(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[1].HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(hop[1].HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(hop[1].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[1].HeaderPrv, b, c)
// Forward(hop[2].AddrPort). // Forward(hop[2].AddrPort).
f2 := PeelForward(t, b, c) f2 := PeelForward(t, b, c)
@@ -377,7 +377,7 @@ func TestSendPurchase(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[2].HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(hop[2].HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(hop[2].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[2].HeaderPrv, b, c)
// Purchase(nBytes, prvs, pubs). // Purchase(nBytes, prvs, pubs).
pr := PeelPurchase(t, b, c) pr := PeelPurchase(t, b, c)
@@ -394,7 +394,7 @@ func TestSendPurchase(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[3].HeaderPub), replies[0]). // OnionSkin(address.FromPubKey(hop[3].HeaderPub), replies[0]).
PeelOnionSkin(t, b, c).Decrypt(hop[3].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[3].HeaderPrv, b, c)
// Reply(hop[4].AddrPort). // Reply(hop[4].AddrPort).
rp2 := PeelReply(t, b, c) rp2 := PeelReply(t, b, c)
@@ -404,7 +404,7 @@ func TestSendPurchase(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[4].HeaderPub), replies[1]). // OnionSkin(address.FromPubKey(hop[4].HeaderPub), replies[1]).
PeelOnionSkin(t, b, c).Decrypt(hop[4].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[4].HeaderPrv, b, c)
// Reply(client.AddrPort). // Reply(client.AddrPort).
rp3 := PeelReply(t, b, c) rp3 := PeelReply(t, b, c)
@@ -414,7 +414,7 @@ func TestSendPurchase(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(client.HeaderPub), replies[2]). // OnionSkin(address.FromPubKey(client.HeaderPub), replies[2]).
PeelOnionSkin(t, b, c).Decrypt(client.HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(client.HeaderPrv, b, c)
} }
@@ -454,7 +454,7 @@ func TestSendExit(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[0].HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(hop[0].HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(hop[0].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[0].HeaderPrv, b, c)
// Forward(hop[1].AddrPort). // Forward(hop[1].AddrPort).
f1 := PeelForward(t, b, c) f1 := PeelForward(t, b, c)
@@ -465,7 +465,7 @@ func TestSendExit(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[1].HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(hop[1].HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(hop[1].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[1].HeaderPrv, b, c)
// Forward(hop[2].AddrPort). // Forward(hop[2].AddrPort).
f2 := PeelForward(t, b, c) f2 := PeelForward(t, b, c)
@@ -476,7 +476,7 @@ func TestSendExit(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[2].HeaderPub), set.Next()). // OnionSkin(address.FromPubKey(hop[2].HeaderPub), set.Next()).
PeelOnionSkin(t, b, c).Decrypt(hop[2].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[2].HeaderPrv, b, c)
// Exit(port, prvs, pubs, payload). // Exit(port, prvs, pubs, payload).
pr := PeelExit(t, b, c) pr := PeelExit(t, b, c)
@@ -498,7 +498,7 @@ func TestSendExit(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[3].HeaderPub), replies[0]). // OnionSkin(address.FromPubKey(hop[3].HeaderPub), replies[0]).
PeelOnionSkin(t, b, c).Decrypt(hop[3].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[3].HeaderPrv, b, c)
// Reply(hop[4].AddrPort). // Reply(hop[4].AddrPort).
rp2 := PeelReply(t, b, c) rp2 := PeelReply(t, b, c)
@@ -508,7 +508,7 @@ func TestSendExit(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(hop[4].HeaderPub), replies[1]). // OnionSkin(address.FromPubKey(hop[4].HeaderPub), replies[1]).
PeelOnionSkin(t, b, c).Decrypt(hop[4].HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(hop[4].HeaderPrv, b, c)
// Reply(client.AddrPort). // Reply(client.AddrPort).
rp3 := PeelReply(t, b, c) rp3 := PeelReply(t, b, c)
@@ -518,6 +518,6 @@ func TestSendExit(t *testing.T) {
} }
// OnionSkin(address.FromPubKey(client.HeaderPub), replies[2]). // OnionSkin(address.FromPubKey(client.HeaderPub), replies[2]).
PeelOnionSkin(t, b, c).Decrypt(client.HeaderPriv, b, c) PeelOnionSkin(t, b, c).Decrypt(client.HeaderPrv, b, c)
} }

View File

@@ -1,4 +1,4 @@
package reply package reverse
import ( import (
"net" "net"

View File

@@ -8,21 +8,21 @@ var (
// URL is the git URL for the repository. // URL is the git URL for the repository.
URL = "github.com/Indra-Labs/indra" URL = "github.com/Indra-Labs/indra"
// GitRef is the gitref, as in refs/heads/branchname. // GitRef is the gitref, as in refs/heads/branchname.
GitRef = "refs/heads/ind-bootstrap" GitRef = "refs/heads/protocol"
// ParentGitCommit is the commit hash of the parent HEAD. // ParentGitCommit is the commit hash of the parent HEAD.
ParentGitCommit = "2dc3027cf28b04b52b8bca3839a96267122f8b92" ParentGitCommit = "83a692da2d96ef804e799e802d6815b07aa66c77"
// BuildTime stores the time when the current binary was built. // BuildTime stores the time when the current binary was built.
BuildTime = "2022-12-29T02:49:15Z" BuildTime = "2022-12-29T12:06:37Z"
// SemVer lists the (latest) git tag on the release. // SemVer lists the (latest) git tag on the release.
SemVer = "v0.0.255" SemVer = "v0.0.259"
// PathBase is the path base returned from runtime caller. // PathBase is the path base returned from runtime caller.
PathBase = "/home/lyo/Seafile/Git/indra-labs/indra/" PathBase = "/home/loki/src/github.com/Indra-Labs/indra/"
// Major is the major number from the tag. // Major is the major number from the tag.
Major = 0 Major = 0
// Minor is the minor number from the tag. // Minor is the minor number from the tag.
Minor = 0 Minor = 0
// Patch is the patch version number from the tag. // Patch is the patch version number from the tag.
Patch = 255 Patch = 259
) )
// Version returns a pretty printed version information string. // Version returns a pretty printed version information string.