I've tried both the account_history_api.get_ops_in_block and condenser_api.get_ops_in_block endpoints in v0.19.4 and the normal get_ops_in_block in v0.19.2. The op_in_trx always returns 0.
Example:
{'trx_id': '24b4ea88506a8a1354fafdc095df5a1a3ca3d9cd', 'block': 20019921, 'trx_in_block': 24, 'op_in_trx': 0, 'op': ['comment', {}]}
{'trx_id': '24b4ea88506a8a1354fafdc095df5a1a3ca3d9cd', 'block': 20019921, 'trx_in_block': 24, 'op_in_trx': 0, 'op': ['comment_options', {}]}
{'trx_id': '24b4ea88506a8a1354fafdc095df5a1a3ca3d9cd', 'block': 20019921, 'trx_in_block': 24, 'op_in_trx': 0, 'op': ['custom_json', {}]}
These number I'm assuming should be the index of the operation within the transaction like trx_in_block is for transactions in blocks.
So just a small explanation of why this is important for external indexing: when building a database of all operations (not transactions), there's no combination of fields that you can use as a unique identifier for multiple operations within the same transaction.
Common example: https://steemd.com/tx/46a5206f99db334fe99e391585d8dffb2dafcda2
This also applies to virtual operations:
{'trx_id': '0000000000000000000000000000000000000000', 'block': 20019922, 'trx_in_block': 65, 'op_in_trx': 0}
{'trx_id': '0000000000000000000000000000000000000000', 'block': 20019922, 'trx_in_block': 65, 'op_in_trx': 0}
{'trx_id': '0000000000000000000000000000000000000000', 'block': 20019922, 'trx_in_block': 65, 'op_in_trx': 0}
They all return with the same trx_in_block and op_in_trx leaving no specific identifier that's unique to the transaction.
Ideally all ops/vops can be uniquely indexed on (block_num, trx_in_block, op_in_trx).
All vops will be trx -1 and increment from there.
This should be done in relation to #2301
Disregard the last comment, this is the spec.
All ops will be unique on (block, trx_in_block, op_in_trx, virtual_op)
All regular ops will have virtual_op == 0.
All virtual ops will have the (block, trx_in_block, op_in_trx) set to the last op that occurred before the virtual was emitted. For example, if a limit order was filled as a result of an op, then (block, trx_in_block, op_in_trx, 0) is the op and (block, trx_in_block, op_in_trx, 1) would be the associated virtual op.
If a virtual op is emitted and it is not a part of a transaction, then trx_in_block == -1 and op_in_trx == 0. This way, if you treat trx_in_block as an unsigned integer, and sort on this tuple, you will get the order in which every v/op was applied.
I doubled checked and op_in_trx returns to correct result pre-appbase. (Most transactions only have 1 op, this the index of 0). But some ops do have a non-zero value.
It does not appear to be returning correctly in appbase.
Confirmed - just tested against a 0.19.2 node (https://rpc.buildteam.io) and op_in_trx does return properly, where it's not correctly returning in appbase. I was operating under the assumption that api.steemit.com was still running 0.19.2 when I ran these tests. My apologies!
Also thank you very much for looking into this. I really appreciate it.
Most helpful comment
Disregard the last comment, this is the spec.
All ops will be unique on
(block, trx_in_block, op_in_trx, virtual_op)All regular ops will have
virtual_op == 0.All virtual ops will have the
(block, trx_in_block, op_in_trx)set to the last op that occurred before the virtual was emitted. For example, if a limit order was filled as a result of an op, then(block, trx_in_block, op_in_trx, 0)is the op and(block, trx_in_block, op_in_trx, 1)would be the associated virtual op.If a virtual op is emitted and it is not a part of a transaction, then
trx_in_block == -1andop_in_trx == 0. This way, if you treattrx_in_blockas an unsigned integer, and sort on this tuple, you will get the order in which every v/op was applied.