fix: async approval add polling - #2381
Conversation
| return storage.transactions.get(transactionId) | ||
| } | ||
|
|
||
| async listAllPendingTransactions(): Promise<Array<Transaction>> { |
There was a problem hiding this comment.
This is used by signing worker and I think it could break something there in condition that compares refreshedTx.status !== 'pending', or at least result in unnecessary processing txes that are not pending-signature. That module will probably need to adjust to new status.
There was a problem hiding this comment.
I've modified the signingWorker to now look at refreshedTx.status !== 'pending' && refreshedTx.status !== 'awaiting-signature' and modified the signAndExecute (updated the doc at the top to explain the lifecycle/what happens on each tick)
| } | ||
| if (opts?.expectedStatus && existing.status !== opts.expectedStatus) { | ||
| return false | ||
| } |
There was a problem hiding this comment.
whats the idea behind expectedStatus / how will it be used?
There was a problem hiding this comment.
it's currently used in the applySigningResult
const applied = await this.store.setTransactionSigned(
tx.id,
now,
signingResult.txId,
{ expectedStatus: tx.status }
)
if (!applied) {
const current = await this.store.getTransaction(tx.id)
return { status: current!.status }
}
and I think this was when I was trying tackle the scenario where someone hits approve twice on the execute where the version approve click has already gone through the sign/execute flow but then another one is in flight so this would prevent the setTransactionSigned row from matching (since the status would be executed) and then the UI shows the transaction as completed
c8ad1b6 to
f8da20f
Compare
a736ab7 to
baf5639
Compare
70842ca to
93ce357
Compare
048d1e3 to
ef34ac9
Compare
Signed-off-by: rukmini-basu-da <rukmini.basu@digitalasset.com>
Signed-off-by: rukmini-basu-da <rukmini.basu@digitalasset.com>
Signed-off-by: rukmini-basu-da <rukmini.basu@digitalasset.com>
Signed-off-by: rukmini-basu-da <rukmini.basu@digitalasset.com>
Signed-off-by: rukmini-basu-da <rukmini.basu@digitalasset.com>
Signed-off-by: rukmini-basu-da <rukmini.basu@digitalasset.com>
Signed-off-by: rukmini-basu-da <rukmini.basu@digitalasset.com>
258e6ec to
8e45c47
Compare
Signed-off-by: rukmini-basu-da <rukmini.basu@digitalasset.com>
| // await this.expectActivityWithStatus( | ||
| // commandId, | ||
| // 'awaiting-signature' | ||
| // ) |
There was a problem hiding this comment.
maybe you can have it wait until its no longer pending?
| ) | ||
| if (refreshed.status === 'awaiting-signature') { | ||
| return { | ||
| status: 'pending', |
There was a problem hiding this comment.
why status pending and not awaiting-signature?
There was a problem hiding this comment.
yeah, this is a bit weird because this value goes on as SignResult and the api spec has SignResultPending (where the status is a const: pending), so this would fail the schema validation. Kinda related to #2381 (comment)
I eventually wanted to add the SigningResultAwaitingSignature but figured that could happen in a follow up pr, but maybe it makes sense to add now. wdyt?
| // dApp reports pending for anything not yet signed | ||
| // awaiting-signature can be a gateway UI internal distinction for polling | ||
| this.notifier.emit('txChanged', { | ||
| ...tx, | ||
| status: status === 'awaiting-signature' ? 'pending' : status, | ||
| externalTxId: signingResult.txId, | ||
| }) |
There was a problem hiding this comment.
I dont quite see a reason to not send awaiting-signature to the dapp as well, but maybe add a TODO + tech debt issue for now
| if (tx.externalTxId) { | ||
| signingResult = await driver | ||
| .getTransaction({ | ||
| userId, | ||
| txId: tx.externalTxId, | ||
| }) | ||
| .then(handleSigningError) | ||
| } else { | ||
| signingResult = await driver | ||
| .signTransaction(signTransactionParams) | ||
| .then(handleSigningError) | ||
| } |
There was a problem hiding this comment.
why was this removed?
There was a problem hiding this comment.
Moved the path to polling the provider for the signature to refreshTransaction. And also, signWithDriver is only called from sign now and sign calls loadPreparedTransactionForSigning first so any tx with an externalTxId throws before signWithDriver runs so the if(tx.externalTxId) would never be reachable
| debug: { signingResult, tx }, | ||
| }) | ||
|
|
||
| await this.applySigningResult(tx, signingResult) |
There was a problem hiding this comment.
do we need to handle the return value? I can't tell if the if statement should check signingResult.status or appliedSigningResult.status (or if it matters)?
There was a problem hiding this comment.
good call, this guard should help if another request tries to sign or execute the same transaction concurrently (which ideally should be prevented by loadPreparedTransactionForSigning), but it's worth having to make sure the applied result and driver result agree on the status
Signed-off-by: rukmini-basu-da <rukmini.basu@digitalasset.com>
Adds a polling mechanism and a new status

awaiting-signature:1.pending: prepared command not sent anywhere
2.awaiting-signature: sent to signing provider but haven't received signature yet (this happens after we hit approve the first time)
3. signed: found signature on signing provider (so as soon as we go to this activity on the ui, it starts querying the signing driver for this tx if it's in awaiting-signature and then as soon as it finds it we update the sqllite status)
4. executed: happens after we hit approve the second timeand happy path if the execute against the ledger is successful
5. failed: as is, can happen if something goes wrong at any step but would also include if something fails on the execute step