2

I am working on the development of a software wallet. I am using Horizon Post Transaction API to post signed transaction. Following an issue I ran into, I am trying to understand which responses are final and immutable and which are provisional.

Timeout - Provisional

According to this, timeout is not a final answer:

A stellar-core instance may drop your transaction after failing to include it in several ledgers (currently 3). However other distributed instances may still rebroadcast the tx. See e.g. stellar/stellar-core#1811.

When Horizon provides you with a timeout, it means that Horizon has not had timely confirmation that your tx has made it into a ledger. It does not guarantee that the tx will never make it.

Success - Final & Immutable

According to this, success is considered final & immutable:

Success is a consensus-level return so you don't need to look it up again. You can see this is so because the response includes the result XDR, and such XDR could only come from stellar-core after ledger application.

Transaction malformed?

Are transaction_malformed results final & immutable?

Transaction failed with transaction result_code == tx_failed?

Are transaction_failed results with transaction result_code == tx_failed like this one final & immutable?

I assume they are, as they include the result XDR like the aforementioned success result.

Transaction failed with transaction result_code != tx_failed?

What about transaction_failed results with transaction result_code != tx_failed?

Sample transaction results codes are:

  • tx_bad_auth
  • tx_no_source_account
  • tx_bad_seq

These are trickier since the HTTP response body includes a result XDR as you mentioned but the transaction failure will not appear on the chain like the previous case when transaction result_code == tx_failed and fees are deducted. In general, such transactions may soon be valid, if account sequence changes for example.

Can I treat such results as final and immutable?

2

In short, there are two cases when the transaction fails: during validation and during execution. The rule of thumb: any transaction that has been recorded on the ledger is final and immutable. So if you can fetch it from Horizon, it means that the transaction is final.

Transactions that were rejected during the validation phase, and thus weren't included in the ledger, can be resubmitted (even without modifications). There is a key difference in behavior between those two cases.

If a transaction passes preliminary checks (it's not malformed, the fee is correct, all fields are set, the transaction is signed, timebounds restrictions are met, etc.), it is included in the candidate transaction set and then applied on the ledger. In this case, even if the transaction is failed, the validator increments the sequence number of the source account and charges tx fees.

Therefore,

  • success - final, transaction passed
  • tx_failed - final, transaction failed, no point to resubmit since the source sequence has changed and it will be rejected during validation upon next submission
  • timeout - provisional, you need to either resubmit transaction as-is or increase fees before resubmission if surge pricing is in effect (check the /fee_stats endpoint)
  • transaction_malformed, tx_no_source_account - rejected during validation, no point to resubmit as it will never succeed
  • tx_bad_auth, tx_bad_seq - rejected during validation, might become valid in the future, say, once the sequence or signers of the source account are changed, but from a wallet perspective it makes sense to treat it the same way transaction_malformed is handled since it's hard to automatically determine when this tx can be resubmitted
| improve this answer | |
  • I am currently implementing the transaction posting mechanism. I know that in case of timeout, the transaction may still be included later in the blockchain without the wallet posting it again explicitly. Therefore I must treat this response as provisional. I am trying to understand if there is any chance that other failures (transaction_malformed, tx_no_source_account, tx_bad_auth, tx_bad_seq...) will later end up with the transaction in the blockchain without the wallet posting it again explicitly. – Alon Apr 26 at 6:27
  • 1
    As I mentioned earlier, for timeout case it's better to resubmit the transaction. You are losing nothing if it was already processed (it won't be applied second time), but if it was discarded earlier by the mempol (like in case of surge pricing) it helps to ensure that transaction is processed. All rejected statuses (tx_bad_auth,tx_bad_seq,transaction_malformed, etc.) actually mean that the transaction has been discarded before processing and won't ever be applied unless you'll explicitly try to submit it later. – Orbit Lens Apr 26 at 11:45
  • Are there forks in Stellar? Could it be that I get a success and then there is a fork which does not contain the transaction? – Alon May 26 at 14:07
  • 1
    In theory it's possible, but on practice it will take the entire network to conspire against you, deliberately rewriting transactions history. And I doubt such a thing may happen, as validators will carry tremendous losses in case if the network trust is compromised. – Orbit Lens May 27 at 1:58
  • We have monitors which post simple transactions with a single XLM payment ~10 times per hour in order to monitor our Stellar integration. I found a case a couple of weeks ago in which we got a Horizon timeout response after 30 seconds. We attempted to post again and failed on tx_bad_seq. We treated it as a failure. We were surprised to find the transaction on the chain: stellar.expert/explorer/public/tx/…. I filed this as a new Horizon issue: github.com/stellar/go/issues/2628 – Alon May 27 at 6:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.