Skip to content
This repository has been archived by the owner on Aug 1, 2022. It is now read-only.

General transaction handling #225

Closed
2 of 7 tasks
xla opened this issue Mar 16, 2020 · 17 comments
Closed
2 of 7 tasks

General transaction handling #225

xla opened this issue Mar 16, 2020 · 17 comments
Labels
feature Something that doesn't exist yet
Milestone

Comments

@xla
Copy link
Contributor

xla commented Mar 16, 2020

Transaction handling is core to the interactions and feedbacks when interacting with the Registry. To enable functionality vital for the deliveyor Identities I and Orgs I, we ought to solve transaction handling, presentation and the handling of non-trivial unlocking of dependent actions in the UI. As it is vital for further development we should make it part of the Identities I deliverable.

This issue superseeds #89, #212 and #214

Summarising a meeting of the Application team to come to a common understanding of how to address the need for general transaction infrastructure, we assume that complex flows in the app depend on states of certain dependent transactions to advance. These state changes allow for new/added capabilities or permissions, which control what actions a user can perform.

Requirementes

  • get transactions by id
  • get list of known transactions
  • global view on transactions
  • cache relevant transactions
  • configurable confirmation unit (amount of blocks for a bar to go full in the transaction progress indicator)
  • derive capabilities and permissions from relevant transaction state

Constraints

  • get realtime updates for transaction state changes and their respective capabilities/permissions
  • exisiting entity responses should convey their capabilities/permissions

API

A single endpoint is proposed to full-fill all requriements, including realtime update, assuming we go with the most naive version of polling in the beginning, so that we don't require extra infrastructure/complexity (e.g.: GraphQL subscriptions, websockets, SSE).

query($ids: [ID!]) {
  listTransactions(ids: $ids): [Transaction!]!
}

type Transaction {
  costs: Cost!
  id: ID!
  messages: [Message!]!
  receiver: Receiver!
  sender: Sender!
  state: State!
  submission_time: Timestamp!
}

type Cost {
  deposit: Int!
  fee: Int!
}

union Receiver = Org | User
union Sender = Org | User

union State = Unconfirmed | Confirmed | Failed

type Unconfirmed {}

type Confirmed {
  block: Block
}

type Failed {
  block: Block
  error: # Come up with error shape
}

type Block {
  hash: ID!
  height: Int
}

Open Questions

  • Which model (push/poll) is more feasible for our realtime needs/
  • How does the error state of a transaction look?
  • Where does te confirmation config for units live: Upstream or Registry?
  • What is the structure of the capabilities/permission responses?

There are dependent conversations/issues that need to be fleshed out, i.e.: state/storage/data-access and routing/navigation.

UI

  • once in place in the proxy, handle status/progress/error & transaction costs in the ui
  • continuously poll for transactions and update the transaction center and detail view accordingly
  • animate the transaction center: slide up/down of cards, slide in of the whole center on first transaction
@xla xla added proxy feature Something that doesn't exist yet labels Mar 16, 2020
@xla xla added this to the Identities I milestone Mar 16, 2020
@cloudhead
Copy link
Contributor

Couple notes:

  • With the proposed API, how do I get the list of all pending transactions?
  • It would make sense to move the Block to the TransactionState:
enum TransactionState {
  Unconfirmed,
  Confirmed(Block),
  Failed(Block, Error),
}
  • What do you mean by "known transactions"? Known to who?
  • How do I cross-reference a transaction with an entity (eg. an org)? Do I peek into the Message?

@cloudhead
Copy link
Contributor

cloudhead commented Mar 17, 2020

Which model (push/poll) is more feasible for our realtime needs

Given the low latency and implementation cost, I would agree with you that poll is the way to go for now.

How does the error state of a transaction look?

Perhaps we can look at this from the perspective of: "What does the user want to know, when a tx failed?"

Where does te confirmation config for units live: Upstream or Registry?

Upstream. This is a user parameter, not a protocol parameter.

What is the structure of the capabilities/permission responses?

I wonder what's the best way to think about this. Should this be embedded when you fetch an entity? Eg. getOrg(id) responds with Org { state: Pending }.

@xla
Copy link
Contributor Author

xla commented Mar 17, 2020

With the proposed API, how do I get the list of all pending transactions?

The returned list can be filtered by state.

It would make sense to move the Block to the TransactionState:

This makes me happy.

What do you mean by "known transactions"? Known to who?

Known to the local instance/session (implies surviving through app shutdowns) of Upstream.

How do I cross-reference a transaction with an entity (eg. an org)? Do I peek into the Message?

We talked about embedding the related transaction in the entity reponse.

I wonder what's the best way to think about this. Should this be embedded when you fetch an entity? Eg. getOrg(id) responds with Org { state: Pending }.

It's a bit more nuanced in many places, so what we actually want is to have a clearer ways of communicating what is permitted, to iilustrated: User A has two orgs: X and Y. While X is already registered, the transaction for Y is still pending, therefore it should only be allowed to add members for org X but not yet for Y.

@cloudhead
Copy link
Contributor

User A has two orgs: X and Y. While X is already registered, the transaction for Y is still pending, therefore it should only be allowed to add members for org X but not yet for Y.

In that case, getOrg(Y) would return state: Confirmed, and getOrg(X) would return state: Pending, no?

The returned list can be filtered by state.

The pending part is unimportant, I was wondering more, if you don't have the tx ids, how can you query all pending or unconfirmed txs? This would have to happen on app startup for example.

@MeBrei
Copy link
Contributor

MeBrei commented Mar 17, 2020

Regarding the API:
We might need to add more information to Transaction. For the summary view we need info like:

  • time of submission
  • author/payer of the tx
  • target of the tx
  • costs

Screenshot 2020-03-17 at 15 54 27

@xla
Copy link
Contributor Author

xla commented Mar 18, 2020

In that case, getOrg(Y) would return state: Confirmed, and getOrg(X) would return state: Pending, no?

This implies that the consumer of these calls can then decide based on that what actions are allowed. Which again might lead to incoherent UX where the action is enabled for the user to perform but is rejected at the proxy or even worse at the Registry level. An alternative thinking is to embed is something more specific like: { "allowed": [ "unregister" ], "pending": [ "member/add", "member/remove" ] }. The added benefit is that this helpful to distinct between owned/controllable entities and entities that you have no access to, where the user would never have permissions to perform any of those actions.

The pending part is unimportant, I was wondering more, if you don't have the tx ids, how can you query all pending or unconfirmed txs? This would have to happen on app startup for example.

I see, I think this is a confusion with the subtetlies of the GraphQL schema syntax. In our example above: query($ids: [ID!]) { we don't have a ! after the list syntax. Which means it's optional. So the UI can always resync the entire transaction list when needed, e.g.: on startup. In turn that keeps the transaction caching/storage in the proxy.

@xla
Copy link
Contributor Author

xla commented Mar 18, 2020

@MeBrei Updated the schema according to the latest developments:

  • add cost
  • add receiver and sender
  • flesh out state
  • add submission time

@cloudhead What is the correct way of tracking confirmation units (blocks) for a transaction? Is it by following the height of the change and delta it against the height of the block it was included in?

@cloudhead
Copy link
Contributor

@cloudhead What is the correct way of tracking confirmation units (blocks) for a transaction? Is it by following the height of the change and delta it against the height of the block it was included in?

If by "change" you mean chain then yes: It's basically height(active_chain) - height(tx) + 1

I see, I think this is a confusion with the subtetlies of the GraphQL schema syntax.

Aha! Optional by default 🤦‍♂️

This implies that the consumer of these calls can then decide based on that what actions are allowed.

Gotcha, yeah I guess I was thinking for now to disallow all actions on a pending entity, for simplicity. I thought that was what was decided 🤷‍♂️

@xla
Copy link
Contributor Author

xla commented Mar 18, 2020

Gotcha, yeah I guess I was thinking for now to disallow all actions on a pending entity, for simplicity. I thought that was what was decided man_shrugging

We did and it's still the case, but there are more complex scenarios. Another example: you can register a project when there is an org or a user registered, so it's not necessarily tied to one single entity. Another one, you might get a fully registered org back, but you can only transfer money to it if you have a user account. For these examples I think a simple state flag on a single entity response is breaking apart.

@cloudhead
Copy link
Contributor

I see -- that to me is a different concern than the pending transaction one: this is the general problem of what actions are available/allowed in the UI based on user state. One is related to transaction settlement, the other isn't.

@xla
Copy link
Contributor Author

xla commented Mar 26, 2020

There are subtleties we can't address at the moment. For example is there no way to get height information form the Registry client (i.e. height of a block and current height of the longest chain). For sender and receiver we need to wait for orgs and users need to land proper. We also need settle on how we handle the majority of our current transactions, which don't have a receiver per se, like registrations of entities. /cc @radicle-dev/product

@xla
Copy link
Contributor Author

xla commented Mar 26, 2020

Height related functionality tracked in radicle-dev/radicle-registry#285.

@MeBrei
Copy link
Contributor

MeBrei commented Mar 26, 2020

The receiver in the ui sense is the one benefiting from the transaction. So in the case of Org registration, it is the org itself, for member registration it is the user handle being added as a member, in project registration it is the project etc. This could also be information that the ui picks from the message depending on the kind

@xla
Copy link
Contributor Author

xla commented Mar 26, 2020

I don't think that metaphor holds as the receiver of registration transactions won't benefit from the funds transferred in the respective transaction - as in their account balance won't be positive because of the settlement of the tx. It might be I miss-understand, but @cloudhead should be able to clarify on this. IMHO it's a wrong metaphor which might lead to confusion as to the direction and beneficiary of system/network txs.

@cloudhead
Copy link
Contributor

It's a bit of a slippery slope -- who's the receiver when unregistering a project? The org or the project?

@MeBrei MeBrei mentioned this issue Mar 26, 2020
14 tasks
@rudolfs rudolfs mentioned this issue Apr 1, 2020
26 tasks
@MeBrei MeBrei mentioned this issue Apr 3, 2020
18 tasks
@xla
Copy link
Contributor Author

xla commented Apr 7, 2020

Summing up an offline discussion between @MeBrei and @xla:

  • encode errors in the status variants
  • encode settlement progress in the status variants
  • add timestamps to the status variants
  • delete the receiver
  • users in relation to txs should always carry along the coco id to be fetched with the identity endpoint

Anything missing?

@xla xla removed this from the Identities I milestone Apr 8, 2020
@xla xla added this to the Orgs I milestone Apr 8, 2020
@MeBrei MeBrei mentioned this issue Apr 16, 2020
18 tasks
xla added a commit that referenced this issue Apr 23, 2020
Follow-up to #293
Related to #225

* Use more verbose prop name for component
* Fix yarn lock
@xla
Copy link
Contributor Author

xla commented May 6, 2020

Superseeded #347

@xla xla closed this as completed May 6, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature Something that doesn't exist yet
Projects
None yet
Development

No branches or pull requests

3 participants