Open andresberrios opened 6 years ago
I have done some work on this and currently:
Is possible to aggregate the ops in actions, trxs and blocks. But since only trxs are sent to ZMQ there is no need to aggregate them into blocks.
Only applied trx's are broadcast so there is no undo's per trx.
There is a limitation with the forks sending trxs with the undo ops:
Example: 4 trxs inside 2 blocks
- B1
- T1
- MODIFY (V1 to 3)
- REMOVE (V1)
- T2
- ADD (V1 1)
- B2
- T3
- MODIFY (V1 to 2)
- T4
- MODIFY (v1 to 4)
- REMOVE (v1)
pop_block B2, B1
B2 ADD (V1 1)
B1 REMOVE (V1) ADD (V1 2)
As you can see when sending T3 we don't know that T4 will optimize the undo stack, also the ops reflect the actual state change. But on B2 undo operation the undo stack session only emits the op that reverts to the last state of B1. I can't see any way to group this op in any trxs on B2.
I believe that the best we can do is to broadcast trxs and when a fork happens broadcast a block with all the undo ops not grouped by trx or action.
Ok, I see what you mean, and I also think it would be fine for the majority of use cases to send the optimized changeset, but when we discussed the tracking of operations using a before_emplace
event for example, I thought then you would use that to reverse the operations as soon as they come and store the reversed ones before they get applied to the state DB, by checking the current data before it is modified. That way you can just store them right next to where you store the forward operations, since each operation should have its reversed one, and they would all be grouped by transaction and action.
But I think it's fine to maybe for the moment just send the undo ops per block, it would work well for most purposes.
Sorry I didn't get that idea.
For this type of implementation:
I believe that we need only a before_modify to be able to generate a modify for the old_value. Or at least a way to send both old_value and new_value in the applied_modify.
Should we send all the reverse trx's with undo ops or in this case if we send in the modify op (old_value, new_value) inst it also possible that the receivers to apply the reverse of ops and we just send a pop_block event? This way there inst a need in plugin end to store undo ops and create that logic. But will need for the receivers to store the block for eventual reversing it.
This would remove the need for emitting the undo ops.
The reversed ops would be unoptimized but then It would be possible to send reversed trxs.
I guess this is a tradeoff we need to address with the needs of dapp developers.
- I believe that we need only a before_modify to be able to generate a modify for the old_value. Or at least a way to send both old_value and new_value in the applied_modify.
Yes, I think both options are fine, but maybe it's better to not send both values from chainbase since maybe other plugins will need only one, so the chainbase signal code would be doing extra work that wouldn't apply to cases other than ours.
- Should we send all the reverse trx's with undo ops or in this case if we send in the modify op (old_value, new_value) inst it also possible that the receivers to apply the reverse of ops and we just send a pop_block event? This way there inst a need in plugin end to store undo ops and create that logic. But will need for the receivers to store the block for eventual reversing it.
I think we should avoid sending the reverse operations in the normal transaction messages, since we would be using a lot more bandwidth than necessary since the undos will not happen very often. It's better if the plugin keeps track of that and sends it only when it's required.
- This would remove the need for emitting the undo ops.
Yes it would :) but maybe we should leave the signals in chainbase but just not use them, because they might be useful for other people making other plugins.
- The reversed ops would be unoptimized but then It would be possible to send reversed trxs.
Yes, they would be unoptimized but when the receiver decides to apply them, they can very easily optimize them themselves. We can provide the optimizing code in the receiver library for them to use. It would simply need to go through the operations and remove the pairs that cancel each other (in the case of emplace and erase) or overwrite each other (in the case of modify). I can handle that part in the receiver code.
Functionality:
transaction
messages.block_id
, and the list of actions executed in that transaction (including inline ones).Implementation ideas:
pre_apply_action
andapplied_transaction
events to track the amount of operations each action performed, along with the transaction ID.pop_block
for handling block undos and sending the reversed transaction messages.