Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Postman #255

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 32 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,37 @@

Complete documentation for Envoy can be found at [https://trisa.dev/envoy](https://trisa.dev/envoy).

## Deploying Envoy

The simplest way to deploy Envoy is onto a Kubernetes cluster using Helm. The helm chart and values description for deploying Envoy can be found here:

[artifacthub.io/packages/helm/trisacrypto/envoy](https://artifacthub.io/packages/helm/trisacrypto/envoy)

If you're developing Envoy or looking to run it locally, see the instructions below.

## Envoy Implementation Options

| Open Source | One-Time Setup | Managed Service |
| ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Envoy is open source (MIT License). Download, install, integrate, host and support your own Envoy node and service. | The Envoy team will install and configure your Envoy node in your environment while you host, maintain, and support the node on an ongoing basis. | The Envoy team will install, configure, host, maintain, and support an Envoy node for you. Includes dedicated, provenance-aware node with regional deployments. |
| | | |

If you’d like more information on the one-time integration service or managed services, [schedule a demo](https://rtnl.link/p2WzzmXDuSu) with the Envoy team!


## Envoy Support

| | Open Source | One-Time Setup | Managed Service |
| ------------------------------- | ---------------------- | ---------------------- | ---------------------- |
| [Envoy Documentation](https://trisa.dev/envoy/index.html) | ✓ | ✓ | ✓ |
| Access to [TRISA Slack Community](https://trisa-workspace.slack.com/) | ✓ | ✓ | ✓ |
| Training from Envoy Team | | ✓ | ✓ |
| Dedicated Support | | | ✓ |
| Response Time* | Within 5 business days | Within 5 business days | Within 3 business days |


*The Envoy team's business hours are 9AM - 6PM Eastern.

## Running Envoy Locally

**NOTE**: Development is happening rapidly on the node right now; if these instructions don't work correctly, please open an issue on GitHub so we can review the docs.
Expand Down Expand Up @@ -77,29 +108,4 @@ $ docker compose exec counterparty.local envoy createuser -e [email] -r admin

You can access the counterparty at [http://localhost:9000](http://localhost:9000) or at [http://counterparty.local:9000](http://counterparty.local:9000) if you have edited your hosts file.

> **NOTE**: Due to the way cookie domains work with the credentials, you can only be logged into either the envoy development node or the counterparty development node at the same time. It's annoying, but you'll have to login again when switching between nodes unfortunately.


## Envoy Implementation Options

| Open Source | One-Time Setup | Managed Service |
| ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Envoy is open source (MIT License). Download, install, integrate, host and support your own Envoy node and service. | The Envoy team will install and configure your Envoy node in your environment while you host, maintain, and support the node on an ongoing basis. | The Envoy team will install, configure, host, maintain, and support an Envoy node for you. Includes dedicated, provenance-aware node with regional deployments. |
| | | |

If you’d like more information on the one-time integration service or managed services, [schedule a demo](https://rtnl.link/p2WzzmXDuSu) with the Envoy team!


## Envoy Support

| | Open Source | One-Time Setup | Managed Service |
| ------------------------------- | ---------------------- | ---------------------- | ---------------------- |
| [Envoy Documentation](https://trisa.dev/envoy/index.html) | ✓ | ✓ | ✓ |
| Access to [TRISA Slack Community](https://trisa-workspace.slack.com/) | ✓ | ✓ | ✓ |
| Training from Envoy Team | | ✓ | ✓ |
| Dedicated Support | | | ✓ |
| Response Time* | Within 5 business days | Within 5 business days | Within 3 business days |



*The Envoy team's business hours are 9AM - 6PM Eastern.
> **NOTE**: Due to the way cookie domains work with the credentials, you can only be logged into either the envoy development node or the counterparty development node at the same time. It's annoying, but you'll have to login again when switching between nodes unfortunately.
24 changes: 24 additions & 0 deletions pkg/postman/direction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package postman

type Direction uint8

const (
Unknown Direction = iota
DirectionIncoming
DirectionOutgoing
)

func (d Direction) String() string {
switch d {
case DirectionIncoming:
return "incoming"
case DirectionOutgoing:
return "outgoing"
default:
return "unknown"
}
}

func (d Direction) Valid() bool {
return d > Unknown && d <= DirectionOutgoing
}
39 changes: 39 additions & 0 deletions pkg/postman/direction_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package postman_test

import (
"testing"

"github.com/stretchr/testify/require"
"github.com/trisacrypto/envoy/pkg/postman"
)

func TestDirectionString(t *testing.T) {
tests := []struct {
direction postman.Direction
expected string
}{
{postman.Unknown, "unknown"},
{postman.DirectionIncoming, "incoming"},
{postman.DirectionOutgoing, "outgoing"},
}

for _, test := range tests {
require.Equal(t, test.expected, test.direction.String())
}
}

func TestDirectionValid(t *testing.T) {
tests := []struct {
direction postman.Direction
expected bool
}{
{postman.Unknown, false},
{postman.DirectionIncoming, true},
{postman.DirectionOutgoing, true},
{postman.Direction(3), false},
}

for _, test := range tests {
require.Equal(t, test.expected, test.direction.Valid())
}
}
10 changes: 7 additions & 3 deletions pkg/postman/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import (
)

var (
ErrNoCounterpartyInfo = errors.New("no counterparty info available on packet")
ErrNoUnsealingKey = errors.New("cannot open incoming envelope without unsealing key")
ErrNoSealingKey = errors.New("cannot seal outgoing envelope without sealing key")
ErrNoCounterpartyInfo = errors.New("no counterparty info available on packet")
ErrNoUnsealingKey = errors.New("cannot open incoming envelope without unsealing key")
ErrNoSealingKey = errors.New("cannot seal outgoing envelope without sealing key")
ErrDatabaseNotReady = errors.New("no database has been set on the packet")
ErrCounterpartyNotReady = errors.New("no counterparty has been set on the packet")
ErrTransactionNotReady = errors.New("cannot resolve transaction from packet")
ErrPacketNotReady = errors.New("packet was not instantiated correctly")
)
167 changes: 69 additions & 98 deletions pkg/postman/postman.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,73 +22,75 @@ transaction or an update to an old transaction the following things must happen:
package postman

import (
"errors"
"fmt"

"github.com/google/uuid"
"github.com/rs/zerolog"

"github.com/rs/zerolog/log"
"github.com/trisacrypto/envoy/pkg/store/models"
"github.com/trisacrypto/envoy/pkg/trisa/peers"

api "github.com/trisacrypto/trisa/pkg/trisa/api/v1beta1"
"github.com/trisacrypto/trisa/pkg/trisa/envelope"
"github.com/trisacrypto/trisa/pkg/trisa/keys"
)

type Direction uint8

const (
Unknown Direction = iota
DirectionIncoming
DirectionOutgoing
)

// Packets contain both an incoming and an outgoing message and are used to ensure that
// an entire tranfer packet can be correctly constructed for both envelopes.
// Packet is the base struct for all messaging interactions including TRISA, TRP and
// Sunrise messages. It handles all of the common functionality such as validation,
// envelope storage, transaction updates, and audit logging. Generally a user will not
// use this struct directly but will use the protocol structs which embed this struct.
// TODO: add audit log information
type Packet struct {
DB models.PreparedTransaction // Database interaction methods
In *Incoming // The incoming message that needs to be decrypted
Out *Outgoing // The outgoing message that needs to be encrypted
Log zerolog.Logger // The log context for more effective logging
Counterparty *models.Counterparty // The remote identified counterparty
Transaction *models.Transaction // The associated transaction with the packet
Peer peers.Peer // The remote peer the transfer is being conducted with
PeerInfo *peers.Info // The peer info for finding the counterparty
User *models.User // The user that created the request, if any
APIKey *models.APIKey // The api key that created the request, if any
Request Direction // Determines if the initial message was incoming or outgoing
Reply Direction // Determines if the reply was incoming or outgoing
Log zerolog.Logger // the log context so that postman actions can be debugged
DB models.PreparedTransaction // database context for storing and updating transactions
In *Incoming // the incoming message received from the remote counteparty
Out *Outgoing // the outgoing message sent to the remote counterparty
Transaction *models.Transaction // the transaction db record that this packet is associated with
Counterparty *models.Counterparty // the counterparty db record associated with the transaction
StorageKey keys.PublicKey // the public key used to encrypt the stored envelope
Request Direction // the direction of the request (e.g. the initial message)
Reply Direction // direction of the reply (e.g. the response to the request)
}

func Send(payload *api.Payload, envelopeID uuid.UUID, transferState api.TransferState, log zerolog.Logger) (packet *Packet, err error) {
//===========================================================================
// Packet Constructors
//===========================================================================

// Send is the basic mechanism to create a new packet whose request is outgoing.
func Send(payload *api.Payload, envelopeID uuid.UUID, transferState api.TransferState) (packet *Packet, err error) {
packet = &Packet{
In: &Incoming{},
Out: &Outgoing{},
Log: log,
Request: DirectionOutgoing,
Reply: DirectionIncoming,
}

// Create the logger to log messages with (can be overriden by external caller)
packet.Log = log.With().
Str("envelope_id", envelopeID.String()).
Str("direction", packet.Request.String()).
Logger()

// Add parent to submessages
packet.In.packet = packet
packet.Out.packet = packet

opts := []envelope.Option{
envelopeOpts := []envelope.Option{
envelope.WithEnvelopeID(envelopeID.String()),
envelope.WithTransferState(transferState),
}

if packet.Out.Envelope, err = envelope.New(payload, opts...); err != nil {
if packet.Out.Envelope, err = envelope.New(payload, envelopeOpts...); err != nil {
return nil, fmt.Errorf("could not create envelope for payload: %w", err)
}

return packet, nil
}

func SendReject(reject *api.Error, envelopeID uuid.UUID, log zerolog.Logger) (packet *Packet, err error) {
// SendReject creates a new packet with an error payload to send as an outgoing request.
func SendReject(reject *api.Error, envelopeID uuid.UUID) (packet *Packet, err error) {
packet = &Packet{
In: &Incoming{},
Out: &Outgoing{},
Log: log,
Request: DirectionOutgoing,
Reply: DirectionIncoming,
}
Expand All @@ -105,32 +107,33 @@ func SendReject(reject *api.Error, envelopeID uuid.UUID, log zerolog.Logger) (pa
return packet, nil
}

func Receive(in *api.SecureEnvelope, log zerolog.Logger, peer peers.Peer) (packet *Packet, err error) {
// Receive is the basic mechanism to create a new packet whose request is incoming.
// TODO: modify this to a more simple form.
func Receive(envelope *envelope.Envelope) (packet *Packet) {
packet = &Packet{
In: &Incoming{original: in},
In: &Incoming{Envelope: envelope},
Out: &Outgoing{},
Log: log,
Peer: peer,
Request: DirectionIncoming,
Reply: DirectionOutgoing,
}

// Create the logger to log messages with (can be overridden by external caller)
packet.Log = log.With().
Str("envelope_id", envelope.ID()).
Str("direction", packet.Request.String()).
Logger()

// Add parent to submessages
packet.In.packet = packet
packet.Out.packet = packet

if packet.In.Envelope, err = envelope.Wrap(packet.In.original); err != nil {
return nil, fmt.Errorf("could not wrap incoming secure envelope: %w", err)
}

if packet.PeerInfo, err = peer.Info(); err != nil {
return nil, fmt.Errorf("could not identify counterparty in transaction: %w", err)
}

packet.Counterparty = packet.PeerInfo.Model()
return packet, nil
return packet
}

//===========================================================================
// Packet Helper Methods
//===========================================================================

// Returns the envelopeID from the request envelope (e.g. the first envelope in the packet)
func (p *Packet) EnvelopeID() string {
switch p.Request {
Expand All @@ -143,71 +146,39 @@ func (p *Packet) EnvelopeID() string {
}
}

// Receive updates the incoming message with the specified secure envelope, e.g. in the
// case where the outgoing message has been sent and this is the reply that was received
// from the remote server.
func (p *Packet) Receive(in *api.SecureEnvelope) (err error) {
p.In = &Incoming{
original: in,
packet: p,
}

if p.In.Envelope, err = envelope.Wrap(in); err != nil {
return fmt.Errorf("could not wrap incoming secure envelope: %w", err)
// Attempts to identify the proper protocol to send the packet based on the counterparty.
func (p *Packet) Protocol() string {
if p.Counterparty == nil {
return ""
}

return nil
return p.Counterparty.Protocol
}

// Reject creates an outgoing message with a TRISA error that contains the specified
// TRISA rejection error, e.g. in the case where an incoming message is being handled.
func (p *Packet) Reject(code api.Error_Code, message string, retry bool) error {
reject := &api.Error{
Code: code, Message: message, Retry: retry,
// Updates the transaction from the current state in the database.
func (p *Packet) RefreshTransaction() (err error) {
if p.Transaction, err = p.DB.Fetch(); err != nil {
return err
}
return p.Error(reject)
return nil
}

// Creates a TRISA error envelope from the TRISA error message and sets it as the
// outgoing message (e.g. in the case where an incoming message is being handled).
func (p *Packet) Error(reject *api.Error, opts ...envelope.Option) (err error) {
if p.Out.Envelope, err = p.In.Envelope.Reject(reject, opts...); err != nil {
p.Log.Debug().Err(err).Msg("could not prepare rejection envelope")
return fmt.Errorf("could not create rejection envelope: %w", err)
// Ready checks if the packet has everything it needs to perform its work.
func (p *Packet) Ready() (err error) {
if p.DB == nil {
err = errors.Join(err, ErrDatabaseNotReady)
}
return nil
}

// Creates a TRISA payload envelope to as the outgoing message in order to send a
// response back to the remote that initiated the transfer.
func (p *Packet) Send(payload *api.Payload, state api.TransferState) (err error) {
if p.Out.Envelope, err = p.In.Envelope.Update(payload, envelope.WithTransferState(state)); err != nil {
p.Log.Debug().Err(err).Msg("could not prepare outgoing payload")
return fmt.Errorf("could not create outgoing payload: %w", err)
if p.Transaction == nil {
err = errors.Join(err, ErrTransactionNotReady)
}
return nil
}

func (p *Packet) ResolveCounterparty() (err error) {
if p.Counterparty == nil {
if p.PeerInfo == nil {
if p.Peer == nil {
return ErrNoCounterpartyInfo
}

if p.PeerInfo, err = p.Peer.Info(); err != nil {
return err
}
}

p.Counterparty = p.PeerInfo.Model()
err = errors.Join(err, ErrCounterpartyNotReady)
}
return nil
}

func (p *Packet) RefreshTransaction() (err error) {
if p.Transaction, err = p.DB.Fetch(); err != nil {
return err
if !p.Request.Valid() || !p.Reply.Valid() || p.In == nil || p.Out == nil {
err = errors.Join(err, ErrPacketNotReady)
}
return nil

return err
}
Loading
Loading