Architecture Description¶
The goal of this document is to outline the interactions between the various components of the system. It aims to give a fairly concrete overview of all the different pieces of software involved and how they interact.
Prerequisites¶
We expect a certain baseline understanding of what we are doing in general: building software systems for Shops that sell physical goods. If you are new to this, you might first want to read our introduction blog post. This overview was written with the following assumptions:
Readers have conceptual familiarity with Public-Key signature schemes.
Note
As regards mapping Shops and Relays, this is taken care of by Smart Contracts, which maintain Registries that manage configuration and discovery of Shops and Relays.
For the complete terminology reference, please see Terminology.
Architecture Overview¶
On a high level, the system is composed of a frontend client, a relay, and smart contracts hosted on an ethereum blockchain.
The frontend client can be used to create and manage Shops, and interacts with the relay and smart contracts under the hood.
The relay is responsible for processing requests from the frontend client and for maintaining the state of the Shop.
The smart contracts are supplying discoverability and access control for Shops.
The frontend client and relay communicate with each other via a WebSocket connection. See Transport Overview for more details.
The frontend client and relay communicate with the smart contracts via the Ethereum JSON-RPC protocol. See Smart Contracts for more details.
“Architecture Diagram”¶
Creating a Shop¶
The first thing a merchant needs to do is create the shop itself.
This is done by minting an NFT since the Shop Registry is an extension of EIP721.
Thus it also supplies the standard conformant mint
function.
Note
The merchant will also configure other shop parameters via the same mechanism as outlined in Shop and Relay Synchronization by mutating the Manifest
object to e.g. establish accepted currencies and the like. We will not dive deeper into those details and instead remain focused on giving an overview of the system’s different parts and how they interact.
Adding Users and Logging in¶
Next, we introduce the concept of Users, how they are registered with a Shop, and how they get access to the Listings and Inventory. In terms of interactions, we differentiate Users into Clerks, Admins and Guests:
Admins can add or remove Clerks.
Registered are all Users that have a Wallet Address (WA) assigned to the Shop Registry.
Registered Users can authorise KeyCards (KC) which give them write access to all the objects in the Shop
Guests can also obtain KCs, which are needed to file orders, but they don’t need to be added to the shop registry. See Object Visibility for more on this.
Note
There is only one Owner, who also counts as an Admin. Only the Owner can promote Clerks into Admins and down-grade them again.
Onboarding a Clerk¶
To add a new Clerk, an Admin creates an Invite Secret IS
and a corresponding Invite Verifier IV
via their Client software and sends a link containing the IS
to the Clerk via some confidential side-channel (for example E-Mail or Signal). Its public part, the IV
, is submitted to the Shop Registry.
IS
andIV
are the secret and public parts, respectively, of an ECDSA key-pair.The
IS
is single use.All Admins can see all
IV
s for their Shop.
Warning
While this process could be modelled with a hash-function, we need to account for what is known as front running in Ethereum and similar systems, where the inputs to function call on a contract are revealed to the public. Using signatures instead ensures only the Clerk had possession of the IS
.
When the Clerk opens the IS
-containing link they are presented with a registration page. The main goal here is to connect with a Wallet software, such as MetaMask, to get the WA of the new Clerk. We could also prompt for other metadata about the Clerk at this point such as their name, avatar or e-mail address.
The IS
is used by calling the contract function redeemInvite
, changing the invite status for the Clerk. The contract uses ecrecover to extract the IV and checks whether it is still valid. At the end of the process the new WA is added to the Shop’s configuration, completing the Clerk’s registration.
The “Adding a Clerk” flowchart shows the interaction between the different actors.
“Adding a Clerk” flowchart¶
Logging in¶
The Login mechanism is necessary for elevated RPC commands that are not public, like changing the Inventory of the Shop.
It follows a simple challenge/response scheme to protect against replay attacks <https://en.wikipedia.org/wiki/Replay_attack>_.
It is started by sending AuthenticateRequest to the Relay.
The Clerk proves possession of their KC by signing the
challenge
the Relay responded with.The Relay checks the
signature
in ChallengeSolvedRequest.Afterwards, the Clerk can use the KC to sign PatchSets and perform actions on the Shop.
The “Logging in” flowchart, below, shows the interaction between the different systems.
“Logging in” flowchart¶
Shop and Relay Synchronization¶
Note
Our working assumption is that Relays and their Clients will build the Listing and Inventory state by means of Eventual Consistency. Meaning: all changes to a Listing consist of individual Patches. To gain the current state of a Listing, all patches are replayed and applied locally.
Items in the Shop: Retrieving the Listing¶
Now that the Clerk can do things in the Shop, let’s discuss the first common action: putting up items for display in the Shop, also known as: the listings.
The Client connects to the Relay and opens a subscription for some or all listings, using the SubscriptionRequest.
The Relay will send all applicable Patches via SubscriptionPushRequest to the Client.
The Client code should verify Listing state against on-chain data, by computing and comparing the state root hash.
The “Retrieving the Listing” flowchart, below, shows the interaction between the different systems.
“Retrieving the Listing” flowchart¶
Updating the Listing¶
To change the Listing we need to create Patches. Patches to a Listing will result in a new reduced state of the Shop, from which we create a root hash. To keep Relays honest, this root hash is saved on-chain, protecting against omissions or other corruptions. See Shop Configuration for more details.
Patches must have a known type. Currently we foresee the following types of patches:
Creating and updating
Listing
objectsChanging inventory count of an Item (See
Shop
Inventory
)Creating
Order
objects and assigning Items to themCreating
Tag
objects and assigning Items to them
See CBOR References for a full list. For more details on how to create patches, see Patching Objects.
“Replaying the mutations/patches results in a specific state, which computes into a specific hash deterministically.”¶
Note
We might have a second class of Events in the future: Events that are non-durable. Specifically, these might be order-related events or other session data that will have to be kept in sync between relays but which will not mutate the Listing and thus not change the root hash saved on-chain.
Selling Items¶
Now that we can add and remove Listings from a Shop, let’s discuss how they are actually sold with our system.
Warning
To protect from selling items twice, the Relays will help with bookkeeping. The Relays will track inventory and mark items as reserved once an order is finalized.
Creating and Completing an Order¶
In our system, customers can browse a shop and place orders independently. When a customer wants to make a purchase, they first create a Guest KeyCard which allows them to create orders in the shop without requiring a Clerk’s assistance.
When a Guest adds an Item to their Order, the Client creates a patch operation to modify the order object. This is done using our Patching Objects system, which allows for precise modifications to shop objects. For example, a guest might append a new item to their order or increase the quantity of an existing item using patch operations.
The Relay processes these patches, checks inventory availability, and broadcasts the updated state to the Guest and to the Clerks.
The flowchart below shows the interactions that eventually result in an order being filled.
“Filling an Order” flowchart¶
The Relay keeps track of inventory across all concurrent orders, ensuring that the same item isn’t sold twice. However, items are not reserved until checkout begins.
Warning
Item counts are checked against the total stock. Orders don’t lock up inventory until checkout has begun. This means that the stock count can change between adding an item to the order and checking out. Guests will receive inventory status updates when changes affect their order.
Completing the Purchase¶
Once the Order is filled with the desired Items, the Guest initiates check-out by submitting patches that add shipping information and chosen currencies, finally changing the order OrderState
to Committed
. This is also accomplished using the Patching Objects system, which allows for transactional updates to shop objects.
When the order is committed, the Relay processes the patches, locks up the inventory, and provides the Guest with payment information including the payment id and the amount to pay by setting the PaymentDetails
object on the order.
The Guest reconstructs the PaymentRequest
data structure, which is needed for the pay
contract call. If their wallet does not support calling contracts directly, they can use getPaymentAddress
to complete payment by manually sending the amount to the returned address, which is a counter-factual CREATE2 deployment that will call pay() once the merchant sweeps it.
The Relay supports both payment modes transparently, for vanilla Ether and for all configured ERC20s. Once the Guest has paid for their purchase, the inventory is updated through additional patches applied by the Relay, and the on-chain data is updated.
The “Completing the Purchase” flowchart, below, shows the interaction between the different systems.
“Completing the Purchase” flowchart¶
Privacy Considerations¶
Warning
Now and next: There is a bunch of things in this current iteration we want to test, knowing that they won’t remain that way. There might be breaking changes in future versions of the protocol.
- Listing data will be public - we want to make it possible to be indexed and aggregated in the future.
Inventory counts, too? This will give a clear transparent picture of how good or bad a shop is doing and is not in the interest of merchants.
- Users that are running the shop. This should be private/internal company data.
We need to think about how to protect this data from being leaked. Probably using separate logs with stricter access control or encryption.
- The merchantAddr will be where all purchases are collected.
Can someone backtrace all sales from that account address?
Assuming yes, will we see the paymentAddr of individual sales and thus their account address, too?
- What exactly hashes into the Order?
Can Listing changes be correlated to purchases?