-
Couldn't load subscription status.
- Fork 17
Introduce update transaction API (PATCH /transactions/{transactionId}) #145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 4 commits
8772cb0
d6acc5f
b7667a2
1ca8c8a
53d85c2
1cd7fd4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| // Copyright © 2024 Kaleido, Inc. | ||
| // Copyright © 2024 - 2025 Kaleido, Inc. | ||
| // | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| // | ||
|
|
@@ -79,6 +79,8 @@ type TxAction string | |
| const ( | ||
| // TxActionStateTransition is a special value used for state transition entries, which are created using SetSubStatus | ||
| TxActionStateTransition TxAction = "StateTransition" | ||
| // TxActionExternalUpdate is used to record updates to the transaction through API calls | ||
| TxActionExternalUpdate TxAction = "ExternalUpdate" | ||
| // TxActionAssignNonce indicates that a nonce has been assigned to the transaction | ||
| TxActionAssignNonce TxAction = "AssignNonce" | ||
| // TxActionRetrieveGasPrice indicates the operation is getting a gas price | ||
|
|
@@ -179,6 +181,85 @@ func (mtx *ManagedTX) SetSequence(i int64) { | |
| mtx.SequenceID = fmt.Sprintf("%.12d", i) | ||
| } | ||
|
|
||
| // ApplyExternalTxUpdates applies the external updates to the managed transaction | ||
| // and returns the updates that were applied | ||
| // ApplyExternalTxUpdates applies the external updates to the managed transaction | ||
| // and returns the updates that were applied, whether any update occurred, and a map | ||
| // of changed fields with their old and new values for easy JSON marshalling. | ||
| func (mtx *ManagedTX) ApplyExternalTxUpdates(txUpdate TXUpdatesExternal) (txUpdates TXUpdates, updated bool, valueChangeMap map[string]map[string]string) { | ||
| txUpdates = TXUpdates{} | ||
| valueChangeMap = make(map[string]map[string]string) | ||
|
|
||
| if txUpdate.To != nil && mtx.To != *txUpdate.To { | ||
| valueChangeMap["to"] = map[string]string{ | ||
|
||
| "old": mtx.To, | ||
| "new": *txUpdate.To, | ||
| } | ||
| mtx.To = *txUpdate.To | ||
| txUpdates.To = txUpdate.To | ||
| updated = true | ||
| } | ||
|
|
||
| if txUpdate.Nonce != nil { | ||
| if mtx.Nonce == nil || mtx.Nonce.Int().Cmp(txUpdate.Nonce.Int()) != 0 { | ||
| valueChangeMap["nonce"] = map[string]string{ | ||
| "old": mtx.Nonce.String(), | ||
| "new": txUpdate.Nonce.String(), | ||
| } | ||
| mtx.Nonce = txUpdate.Nonce | ||
| txUpdates.Nonce = txUpdate.Nonce | ||
| updated = true | ||
| } | ||
| } | ||
| if txUpdate.Gas != nil { | ||
| if mtx.Gas == nil || mtx.Gas.Int().Cmp(txUpdate.Gas.Int()) != 0 { | ||
| valueChangeMap["gas"] = map[string]string{ | ||
| "old": mtx.Gas.String(), | ||
| "new": txUpdate.Gas.String(), | ||
| } | ||
| mtx.Gas = txUpdate.Gas | ||
| txUpdates.Gas = txUpdate.Gas | ||
| updated = true | ||
| } | ||
| } | ||
|
|
||
| if txUpdate.Value != nil { | ||
| if mtx.Value == nil || mtx.Value.Int().Cmp(txUpdate.Value.Int()) != 0 { | ||
| valueChangeMap["value"] = map[string]string{ | ||
| "old": mtx.Value.String(), | ||
| "new": txUpdate.Value.String(), | ||
| } | ||
| mtx.Value = txUpdate.Value | ||
| txUpdates.Value = txUpdate.Value | ||
| updated = true | ||
| } | ||
| } | ||
|
|
||
| if txUpdate.GasPrice != nil { | ||
| if mtx.GasPrice == nil || mtx.GasPrice.String() != txUpdate.GasPrice.String() { | ||
| valueChangeMap["gasPrice"] = map[string]string{ | ||
| "old": mtx.GasPrice.String(), | ||
| "new": txUpdate.GasPrice.String(), | ||
| } | ||
| mtx.GasPrice = txUpdate.GasPrice | ||
| txUpdates.GasPrice = txUpdate.GasPrice | ||
| updated = true | ||
| } | ||
| } | ||
|
|
||
| if txUpdate.TransactionData != nil && mtx.TransactionData != *txUpdate.TransactionData { | ||
| valueChangeMap["transactionData"] = map[string]string{ | ||
| "old": mtx.TransactionData, | ||
| "new": *txUpdate.TransactionData, | ||
| } | ||
| mtx.TransactionData = *txUpdate.TransactionData | ||
| txUpdates.TransactionData = txUpdate.TransactionData | ||
| updated = true | ||
| } | ||
|
|
||
| return txUpdates, updated, valueChangeMap | ||
| } | ||
|
|
||
| // TXUpdates specifies a set of updates that are possible on the base structure. | ||
| // | ||
| // Any non-nil fields will be set. | ||
|
|
@@ -208,6 +289,15 @@ type TXUpdates struct { | |
| ErrorMessage *string `json:"errorMessage,omitempty"` | ||
| } | ||
|
|
||
| type TXUpdatesExternal struct { | ||
| To *string `json:"to,omitempty"` | ||
| Nonce *fftypes.FFBigInt `json:"nonce,omitempty"` | ||
| Gas *fftypes.FFBigInt `json:"gas,omitempty"` | ||
| Value *fftypes.FFBigInt `json:"value,omitempty"` | ||
| GasPrice *fftypes.JSONAny `json:"gasPrice,omitempty"` | ||
| TransactionData *string `json:"transactionData,omitempty"` | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please can we ensure we have a very clear knowledge (documented would be great in field-level docs) for how you clear this field. Noting that means distinguishing between:
We must ensure this works - even with the reference OSS handler. The most common edit for experienced users fixing transaction that is broken, will be to turn it into a null transaction:
This type of inert transaction that can be mined at very minimal execution transaction, is the safest way to clear bubbles where a transaction is breaking the pipe. Noting a really sticky derivation of this made possible by this PR is:
|
||
| } | ||
|
|
||
| type TXCompletion struct { | ||
| Sequence *int64 `json:"sequence,omitempty"` | ||
| ID string `json:"id"` | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the design I believe is that this is a helper function that policy implementations (which hold the complex threading model) can call back into, at the point the "task" of editing a transaction has made it to the right locked in-memory code that is safe to make the change.
Will validate this understanding through the rest of the review.