Repository navigation
feat(server): chain submission store, sendTransaction and tracker for create_job and fund - #100
Conversation
Deploying puls3 with
|
| Latest commit: |
c1c81de
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://77cb4116.puls3-4lw.pages.dev |
| Branch Preview URL: | https://feat-97-chain-submission-tra.puls3-4lw.pages.dev |
moises-cisneros
left a comment
There was a problem hiding this comment.
Checked out the branch and ran it locally: dart analyze is clean and dart test passes (195 tests), including the integration suite against Postgres/Redis.
The migration is additive, duplicate-insert conflicts are mapped by index name, and the conditional UPDATE ... WHERE state='submitted' keeps a final state from being overwritten. No blockers from my side. Suggestions:
recordSendhas nostate='submitted'guard (serverpod_chain_submission_store.dart). A tracker holding a stalesubmittedrow can bumpsendAttempts/lastSentAton a row that just becameconfirmed. It could be oneUPDATE ... SET sendAttempts = sendAttempts + 1 WHERE id = $1 AND state = 'submitted'instead of read-then-write.sendTransaction(soroban_rpc_client.dart) returns the node'shashwithout comparing it to the persistedtransactionhash. Worth documenting that callers must check it, or taking the expected hash as a parameter.markFailed(id, code)accepts any string, and it is returned to the client aserrorCode. I see #102 adds theisKnowncheck, so this only matters if #100 lands alone.- Question on scope:
SubmissionOutcomeCode.allhas noAuthorizationExpiredorHireAlreadyRated, whichapi.mdlists forsetAgentWallet/giveFeedback. I assume those arrive with their purposes, just confirming. - Starvation of
listSubmitted(ordered byupdatedAt, whichrecordSenddoes not bump) is already named in the PR as deferred. #102 addresses it withrecordCheck.
…tted recordSend locked the row but only checked that it exists, so a send recorded after a confirm or fail still bumped sendAttempts and lastSentAt of a final record. It now returns false unless the record is still submitted, in the Serverpod store and the in-memory fake alike.
|
Thanks for running the suites locally, that's really helpful.
Suite after the fix: 197 passed (integration included). |
moises-cisneros
left a comment
There was a problem hiding this comment.
Re-checked the updated branch locally: dart analyze is clean and dart test passes (197 tests, integration included). The recordSend guard in 526f3ad does what I asked, and the shared contract test covers both the in-memory fake and the Serverpod store. Thanks for the quick fix.
Approving. The sendTransaction hash check moving to #101 is fine with me.
* fix(server): keep JSON-RPC error code and message and type request rejections * fix(server): rotate, isolate and validate chain submission store records listSubmitted lists never-sent records first, then the least recently sent, so stuck records cannot starve the batch. An unreadable row is logged and failed with EscrowCallFailed instead of failing the whole batch. recordSend only counts sends of submitted records, insertSubmitted checks the purpose/preparation pairing and markFailed only accepts known outcome codes. * test(server): record the testnet create_job and fund transactions of escrow job 3 getTransaction with xdrFormat json from soroban-testnet.stellar.org for create_job 3baba183... and fund 43cd3e84... (contracts/deployments/testnet.json). * feat(server): parse escrow job_created and job_funded events from transactions * feat(server): add the submission ledger port and its Soroban RPC adapter * feat(server): add the escrow effects port with a no-op implementation * feat(server): add the chain submission tracker pass Each pass polls getTransaction for a batch of submitted records, resends the persisted envelope when due, expires records past their time bounds by chain time, and confirms createJob and fund through EscrowEffects. A record that cannot be read or updated stays submitted for the next pass without stopping the batch. * test(server): cover tracker recovery of persisted submissions after a restart * feat(server): add a periodic tracker loop without overlapping passes * feat(server): start the chain submission tracker when PULS3_TRACKER_ENABLED is true * fix(server): list chain submissions by last check so stuck records cannot starve the tracker listSubmitted put never-sent rows first and skipped the sent-rows query when they filled the batch, so limit or more records the tracker never sends (untracked purposes, SUCCESS without an effect) starved createJob and fund records. Its two queries could also list one row twice. Add a server-only lastCheckedAt column (set on insert, backfilled from createdAt) and index (state, lastCheckedAt, id). listSubmitted is now one query ordered by lastCheckedAt then id, and the tracker calls the new conditional recordCheck for every record a pass leaves submitted. recordSend keeps pacing resends only. * fix(server): time out a hung tracker pass so later passes run A pass that never completed, such as one stuck on a database call, kept the loop's in-flight marker set, so every later tick was skipped. TrackerLoop now takes a pass timeout, logs a timed-out pass as an error and lets the next tick start a new pass. The wiring uses a five minute timeout. * fix(server): stop the tracker and close its http client on shutdown server.dart dropped the loop startChainTracker returned, so it was never stopped and its HTTP client never closed. TrackerLoop now takes an onStop callback that runs after the pass in flight, the wiring uses it to close the client, and server.dart registers the loop's stop as a Serverpod shutdown task.
Recorded getTransaction fixtures include key_hash, the public SHA-256 of a ledger key in a TTL entry, which the generic-api-key rule flags. The allowlist matches only that JSON field with a 64-character hex value.
|
Merged into Before merging I added a narrow gitleaks allowlist for What this means for #96: the store, |
Refs #97 (foundation; the tracker itself follows in a second PR)
Summary
Foundation for the escrow relay tracker, split out so #96 can build on it now:
lib/src/chain/submission_values.dart):SubmissionPurpose,SubmissionStateand outcome codes, with wire names exactly as indocs/architecture/api.md.SorobanRpcClient.sendTransaction: returns a typedSendTransactionResult(PENDING,DUPLICATE,TRY_AGAIN_LATER,ERROR); transport errors becomeLedgerUnavailable.chain_submissionmodel and migration (additive only). Server-only columns (signedEnvelopeXdr,validUntil,hireId, send counters) never reach the client protocol.ChainSubmissionStore: interface, Postgres implementation and an in-memory fake, with one contract test suite run against both. It's named Store because Serverpod already generates aChainSubmissionRepository.API for #96
submitEscrowCallshould callinsertSubmitted(statesubmitted) beforesendTransaction, per relay step 4.Acceptance criteria (from #97, partial)
confirmed/failed) is never overwritten (single conditional update)create_job/fundconfirmation effects: next PRVerification evidence
dart analyze --fatal-infos: clean (server and client).dart test: 195 passed (181 unit, 14 integration against Postgres viadocker compose).serverpod generateleaves no diff.listSubmittedstarvation, per-row isolation,recordSendguard) are addressed in the tracker PR, where they apply.Notes for reviewers
migration_registry.txtconflict; the later branch recreates its migration on top.