Updating receive balances without a receive block (or signature) when the send is cemented?

This is probably a dumb question, but what are the implications of automatically updating receiving account balances (without a separate receive block or signature) when the corresponding send transaction is cemented? Would this be possible?

In my mind, if your node has already achieved quorum to cement a send, why would it not be able to also increase the receiving account's balance at the same time (without a separate block or signature)? This would eliminate the issue of pending transactions (aside from opens, but maybe you could auto-open too) and it would eliminate the need for separate receive blocks. Especially since receives and opens are always balance increases, never decreases

I'm sure I'm missing some obvious issues, but I couldn't get the thought out of my mind after thinking about it for a few hours. I'll probably wake up tomorrow and realize how bad this idea is :joy:

5 Likes

We have discussed this internally several times recently, but are still stuck on some technical issues so have not written a public proposal yet. The main one being bootstrapping a certain account, currently you can trace an account all the way to the genesis block through the receive blocks and bootstrap all those blocks in a top-down approach. Without it you need to bootstrap the whole ledger before you know what your true receive balance is because you would only have the send blocks for the account and no way to know who has sent anything to you. Also nodes which want to display these "local" receive blocks would show them in a different order depending on when they "received" the send block, not a big problem but a little inconsistent. There's also pruning considerations, currently an account can be easily pruned as the total balance is at the head of each account, we would need to request (with a new message) total receive balances from the network for each account.

It would however make integrations easier, would eliminate stale pending blocks, cps==tps, ledger size reduction, halve bootstrapping bandwidth (in theory ~50% would be receive blocks), halve work generation, but doesn't come without trade-offs. I personally would love to see it though :slight_smile:

3 Likes

Yea, this is one of the ideas I really like and think could solve a lot of things simultaneously. The big issue doing it the naive way is that if someone repeatedly sends to you, and they're added to your chain, it would make you unable to transact sends because the "previous" would continuously be changing.

However, if we were to separate sends/receives into different chains we might be able to make this scheme work. Receives wouldn't need to have signatures really, and as Wesley pointed out, this would roll all pending entries for a single account into one entry through pruning. Accounts would only need to issue one signed receive to pull everything pending for them into their account, provided all the receives have been created.

4 Likes

Can't you circumnavigate the "naive" way simply by having those "automatically generated" receives as unconfirmed (not cemented) and change their order based on something universal among all nodes (like alphabetically based on link) and then only confirm then as the chain owner adds a new block with his signature? Therefore confirming all automatically generated between his last signature and this one?

I don't know the details of your discussions over there, but I imagine something like this:

The node process and confirms a send. It locally generate a receive on the receiver chain without signature. A new send is made. A new receive is generated, but since receives can be done in any order, you order all the non signature receives by link. Then the chain owner sends a signed block (send or change). That confirms all the blocks between his last signed block and the new one.

For example, an account has frontier N (signed block). Now someone else starts sending them Nano. The node locally starts creating receives and adding them. To every new receive it re creates all "pending" receives according to the link order. So for the first send with link 500 it creates a receive with height N+1. Now the second link is 858. Because 858 > 500, it creates a new receive with height N+2. The third send has link 150. The node locally re orders the receives. Now link 150 is N+1, link 500 is N+2 and link 858 is N+3 and so on. For every new send to that account the node has to recreate the chain of receives. This should not be very resources demanding since it's only about ordering a list and rehashing every block. Anyway, this goes on until the chain owner send a signed block. He will send a block with height N+X, x being in theory the amount of receives that is pending, but could be anything. If it does not generate a fork, fine you just confirmed all the receives you had pending. If it does, you confirm it anyway as a fork (the receives are not cemented, they are only cemented by a posterior signed block, and the network never votes for a non cemented, non signed block over a signed one). The receives that were orphaned (between frontier and N+X) are recreated and added on top of the signed block.

The ordering process guarantees that all nodes have the same order of receives, even if they were all created locally and have never been on the network. If somehow you see a send that you don't understand the previous, it means you either generated wrong receives or you missed a send and therefore didn't create the appropriate receive locally, and the fact that the receives are not confirmed prevents the issue you described, he could send with the "wrong" previous, that would only affect which of the receives were confirmed and which will continue to be "unconfirmed".

EDIT: More considerations:
Instead of re organizing the chain at the arrival of every new send, we could do it only once when a block is to be cemented. Having the unconfirmed receives in a different orders in different nodes would not be an issue since they would all add to the same info (balance and rep). Once a block needs to be cemented you re org to make sure all chain on all nodes are equal.

Downside of this option is that you can't prune send blocks whose receive is still unconfirmed, just like now. This could be solved triggering confirmation process for locally generated receives under some specific condition, like amount of pending receives. Then nodes would order the receive list, and request confirmation for the one that hit the the criteria for confirmation, while other nodes would validate if the same conditions apply.

I don't think this would cause the same issue as described by Colin above because the account owner would still not have to use the "correct" (latest) receive, just one posterior to the one that was cemented. So a condition like "if unconfirmed receives list > 100 blocks, confirm first 50 unconfirmed", would still leave room for the account owner to send with any height from 51-100

1 Like

With info from discord, the node already keeps track of all pending receives in a separate space. I don't know the DB details, but it must obviously contain link and amount to be added to the receiver account. Which is basically the same thing as an unsigned receive block. So the only question is how to add those blocks into the account chain. Instead of simply ordering by link (new block positions are random) we order first by amount (largest to smaller) then by link. This way we automatically make the list of pending receives more stable in the beginning than in the end, which makes it less likely to yield synchronization problems. Also makes it easier to receive "relevant" receives and ignore dust.

To copy what I wrote in discord:

I'l call regular blocks, created by the account owner with prefix s (signed) and receives created locally by nodes based on confirmed sends from other accounts u (unsigned).

So account A: has frontier sA. Then he sends more nano, now his chain is sA -> sB with the hashes linked. Now imagine someone sends nano to him. In my system his chain would be sA -> sB -> uC, since block C was created locally and does not have his signature. But it is still linked by hashes. For bootstrap purposes, the frontier is still sB though. since unsigned blocks are generated locally (the network has never seen uC, each node created his own version locally). Now uC will only be confirmed after the account owner sends a new block on top of it. So account owner adds sD to his chain and it becomes: sA -> sB -> uC -> sD. when that block reaches other nodes, they might:

  1. Not be aware of uC (they never saw the send that created it). They would refuse sD as invalid.

  2. Be aware of uC but have other locally created receives as well that the account owner did not see or took into account. If those other receives come after uC in the ordering process, that's not a problem His chain was sA -> sB -> uC -> uD. He would simply fork his local chain, rolling back uD and then adding it on top of sD. So his new chain would become sA -> sB -> uC -> sD -> uE. uD became uE because it's hash changed (the previous changed)

  3. Be aware of uC but have other receives as well that comes BEFORE uC in the ordering process. So sA -> sB -> uD -> uC. Now he would refuse sD because for the desired height of sD, the previous should be uD, not uC.

Item 1 is not really a problem most of the time. When the node receives a block referencing an unknown previous, it already waits a little to see if it shows up. Here the same would occur and most of the time a block would show up and the node would locally create uC and validate sD.

Only 3 would be a proper problem (requiring the account owner to re create his send), but by ordering by amount first, we make this a lot rarer. During a spam on your account, you can just receive the valuable receives anyway, the spam will never take their place in the ordering system.

1 Like