-
Notifications
You must be signed in to change notification settings - Fork 3.8k
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
Multiple messages #1266
Multiple messages #1266
Conversation
Also ref #903 I believe. |
Current way multiple messages are handled: Consider a transaction with 4 messages where the second message fails. Currently the first message will successfully update the state, and once the second msg fails, msgs 3 and 4 are ignored. Thus, only the first message will successfully change the state and Deliver(tx) will return the error result from the second msg's handler. I'm not sure if this is the behavior we want. We could also just refuse to update the state unless all 4 msgs are successful. Thoughts? |
I do not think this is the behavior we want; I think we want all transactions, including multi-Msg transactions, to be atomic: either all Msgs are successful and are applied to the state, or any fail and the state is unchanged. As I understand it, the primary use case for multi-Msg transactions (besides just convenience) is atomicity: perhaps I want to finalize a governance proposal and spend funds (which the proposal will release) only if it passes, or trade a DEX order (in the future) and send tokens I just purchased to pay a friend, but not if the DEX order failed to match. Alternatively, the transaction itself could include a flag to determine what happens when one Msg errors - either "stop execution but leave existing state changes", "continue anyways", or "revert previous state changes". I don't have particular use cases for the first two in mind but that would be more flexible. |
Got it. Transaction handling is now atomic on latest commit. |
That was a stunning turnaround time. I think that's fine for now unless anyone has a compelling use-case for a different execution strategy. |
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.
Thanks! Quickly reviewed, needs a rebase against develop, some changes to multi-Msg result construction, and tests for all the multi-signer functionality.
baseapp/baseapp.go
Outdated
var msg = tx.GetMsg() | ||
if msg == nil { | ||
var msgs = tx.GetMsgs() | ||
if msgs == nil { |
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.
or len(msgs) == 0
, I think, no reason to allow transactions with zero messages
baseapp/baseapp.go
Outdated
|
||
// Set gas utilized | ||
result.GasUsed = ctx.GasMeter().GasConsumed() | ||
result = handler(ctx, msg) |
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.
We need to combine the results in some reasonably intelligent manner:
- Return all codes / data / logs / info / tags
- Add up gas used, return total used at the end
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.
Good point.
Seems like we can just append data and logs together, possibly with some seperator in between individual msg data/logs.
Tags and ValidatorUpdates should also be appended but dropping redundant elements.
Where is GasWanted set? Do we have to change its behavior with multiple msgs?
The code should only be nonzero if the msg fails, right?
Maybe we should do this:
Since we abort running the tx on the first message that fails, the result on a failed transaction should just be the result of the first failed message.
In the case where the tx successfully executes, we accumulate data/logs/info/tags in the way described above and return the result with code 0.
I think this makes it much easier to parse a failed result of a transaction since the user only cares about which message caused the failure.
Thoughts?
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.
Seems like we can just append data and logs together, possibly with some seperator in between individual msg data/logs.
I think the invariant always needs to hold that running a multi-Msg transaction has the same effect on the state as running the Msgs individually in separate transactions:
- Tags should be appended, we do not want to drop elements, this may block on Switch to list of lists of tags for transaction results, begin block, and end block tendermint/abci#273
- ValidatorUpdates need to be combined, not dropping redundant elements but rather adding/removing power, the updates should be cumulative
- Not sure about
GasWanted
, I don't understand why this field is inResult
anyways. For now this can probably be set to the sum ofGasWanted
for all the individual results.
Since we abort running the tx on the first message that fails, the result on a failed transaction should just be the result of the first failed message. In the case where the tx successfully executes, we accumulate data/logs/info/tags in the way described above and return the result with code 0.
I think this is OK (to follow ABCI, we have to do this anyways); it would be nice to log that the previous messages succeeded somehow for ease of debugging.
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.
Quick question:
In ValidatorUpdates, do we expect address and pubkey to be set correctly. Or do we expect just one to be set correctly like we currently do in x/auth/BaseAccount?
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.
I think the invariant always needs to hold that running a multi-Msg transaction has the same effect on the consensus state as running the Msgs individually in separate transactions
This currently is not respected with the way I handle transaction handling. If msgs are run in individual transactions, then the sequence number will increment each time.
If 5 msgs in the same transaction have the same signer, when that transaction is processed, the sequence number for the signer address is only incremented once.
This is because signer addresses are accumulated. (See stdTx.GetSigners() in x/auth/std_tx.go)
I think this is the behavior we want, but it will break the invariant.
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.
In ValidatorUpdates, do we expect address and pubkey to be set correctly. Or do we expect just one to be set correctly like we currently do in x/auth/BaseAccount?
Send the address, I think we're getting rid of pubkeys in expectation of large future pubkeys (e.g. BLS).
If 5 msgs in the same transaction have the same signer, when that transaction is processed, the sequence number for the signer address is only incremented once.
This is because signer addresses are accumulated. (See stdTx.GetSigners() in x/auth/std_tx.go)
I stand corrected, this is an exception (should be the only one I think).
@@ -638,6 +639,318 @@ func TestValidatorChange(t *testing.T) { | |||
|
|||
//---------------------------------------- | |||
|
|||
// Use burn and send msg types to test multiple msgs in one tx | |||
type testBurnMsg struct { |
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.
Will now need a GetMemo()
function (which can return an empty string); rebase against develop (#1280 has since been merged).
docs/guides/guide.md
Outdated
@@ -15,7 +15,7 @@ The design of the Cosmos SDK is based on the principles of "capabilities systems | |||
## Tx & Msg | |||
|
|||
The SDK distinguishes between transactions (Tx) and messages | |||
(Msg). A Tx is a Msg wrapped with authentication and fee data. | |||
(Msg). A Tx is a list of Msg's wrapped with authentication and fee data. |
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.
Msgs
, no apostrophe
x/auth/ante_test.go
Outdated
func newTestTx(ctx sdk.Context, msg sdk.Msg, privs []crypto.PrivKey, accNums []int64, seqs []int64, fee StdFee) sdk.Tx { | ||
signBytes := StdSignBytes(ctx.ChainID(), accNums, seqs, fee, msg) | ||
return newTestTxWithSignBytes(msg, privs, accNums, seqs, fee, signBytes) | ||
func newTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []int64, seqs []int64, fee StdFee) sdk.Tx { |
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.
Can we add a new test transaction type with multiple required signers (with different account numbers / sequences) and test all the signature validity checking for that case?
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.
Don't believe we need a new test transaction type for that? The signers, account numbers, and sequences are accumulated from the msgs passed in.
Tests for this were strewn across the test file. In my latest commit, I have a seperate function that tests multimsg signer functionality with different account numbers/sequences.
I think all requested changes have been addressed. @cwgoes |
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.
See comment re: validator set updates
baseapp/baseapp.go
Outdated
logs = append(logs, fmt.Sprintf("Msg %d: %s", i + 1, finalResult.Log)) | ||
|
||
// Cumulate Validator updates | ||
for _, val := range result.ValidatorUpdates { |
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.
- I think this logic isn't quite right. Need to deal with the case of a power decrease too (which will have a lower power, not a negative power). Look at what Tendermint does to compute the change.
- Can we separate this out into a function and add a few unit tests? Clearer & might be useful elsewhere.
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.
Haha, nevermind - ValidatorSetUpdates
should never have been in the result type to begin with.
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 deliverTx should never change the validator set, right? It all happens in EndBlocker?
AccountNumber: accnum, | ||
Sequence: sequence, | ||
FeeBytes: fee.Bytes(), | ||
MsgsBytes: msgBytes, |
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.
Why did we remove the memo?
Also needs a changelog update |
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.
utACK
We need to clean up baseapp a bit, although this PR doesn't need to block on it - #1329.
Note on the The As part of this PR, we discovered that there isn't a well-defined way to accumulate validator set updates for multi-Msg transactions - depending on whether modules track updates in their own store or track a diff from the validator set sent by Tendermint in Gaia didn't set We thought restricting |
* Started work on multiple msgs, types and x/auth tests pass * Fix issues in x, examples, and baseapp * Added baseapp tests for multiple msgs * Documentation fixes * Fix baseapp tests with sdk.Int * Modify test * Transaction handling is now atomic * Fix test comment * Minor doc fixes and code cleanup * Added baseapp result changes * Use address in validator update accumulation * Started work on multiple msgs, types and x/auth tests pass * Fix issues in x, examples, and baseapp * Added baseapp tests for multiple msgs * Documentation fixes * Fix baseapp tests with sdk.Int * Modify test * Transaction handling is now atomic * Fix test comment * Minor doc fixes and code cleanup * Added baseapp result changes * Use address in validator update accumulation * Added ante tests for multisigner * Remove validatorUpdates from tx result * Better error logs * Put Memo in StdSignBytes and formatting * Updated changelog
Addresses issues in #669 .
Currently all tests in types and x/auth pass.