Payment Proposal: Pay-By-Representative (PBR)

What's up, naners? I've been thinking of a way to optimize exchanges and online services payments, removing the need of deposit accounts. I'm here today to explain a possible way to solve this problem without needing an on-chain update, because it would require a huge updating process. I call it Pay-By-Representative. Basically, it uses the representative field as a memo or payment ID. And it can reduce the number of blocks by using only three instead of four. Here's how it works:

Exchanges or online services would use something called a PBR address. A PBR address is formed by the original account, the representative and a 5-byte checksum. It uses the prefix of "nanopbr_": nanopbr_[base32(account)][base32(representative)][base32(checksum)]. The checksum is the Blake2b-40 of the account concatenated with the representative, just like normal addresses, with the difference that it also takes the representative.

Wallets would need to detect when someone used a PBR address. Here's what the wallets would need to do:
1 - Do the base32 parsing and verify the checksum (obviously).
2 - Publish a send-change block that sends the determined amount to the account and also changes the representative (possible with state blocks).
3 - Publish a change block that changes back the fake representative to the old representative, only for the health of the network.

After all, exchanges would publish a receive block and credit the amount in the user's account in their database.
Total: 3 blocks (send-change, change, receive) instead of 4 (send, receive, send, receive) than they currently do.

But now you can ask me: OK, but an user already can use the representative as a memo, we don't need this new address type. And yes, he/she can, but it's a little worse, so here's a comparion between a manual PBR and an automatic PBR:

Automatic PBR:
1 - User inputs PBR address.
2 - Wallet publishes a send-change block.
3 - Wallet publishes a change block.
4 - Exchange/online service receives.
Total: 3 blocks (send-change, change, receive).

Manual PBR:
1 - User changes representative manually.
2 - User sends amount.
3 - User changes representative again.
4 - Exchange/online service receives.
Total: 4 blocks (change, send, change, receive).
Problems: user may forget to change the representative before sending and end up losing his/her NANOs, user may forget to change the representative after sending, thus affecting the health of the network, user may forget, uses 4 blocks rather than 3 and takes more work to do.

How to implement: First, if people approve it, wallets would implement this new address type. Then, exchanges would use this new address type for deposits.

Exchanges have incentives to do this, because it reduces the work for them, and they only have to manage one nano account.

So what do you think about it? Please give me feedback. The only person I asked right now was Anarkrypto, and as he found it interesting, so I decided to post here.

It sounds like you're describing the discussion from this thread. Unique payment ID via payment protocol

What's the purpose of doing this alternative checksumming mechanism? Wouldn't the payment requestor just provide a unique representative number to set when making payment which they would look for when matching up the payment ID?

@clemahieu Hi! Anyway, thanks for reading. I explain the difference between providing representative separately and using this new address type here:

Automatic PBR:
1 - User inputs PBR address.
2 - Wallet publishes a send-change block.
3 - Wallet publishes a change block.
4 - Exchange/online service receives.
Total: 3 blocks (send-change, change, receive).

Manual PBR:
1 - User changes representative manually.
2 - User sends amount.
3 - User changes representative again.
4 - Exchange/online service receives.
Total: 4 blocks (change, send, change, receive).
Problems: user may forget to change the representative before sending and end up losing his/her NANOs, user may forget to change the representative after sending, thus affecting the health of the network, user may forget, uses 4 blocks rather than 3 and takes more work to do.

It's an interesting concept and way of attaching additional data to a block, but I feel this is even more expensive than just including a memo or payment ID field in blocks (in terms of network utilization and ledger size anyway). Instead of just having a few bytes for a memo field, you'd be consuming an additional 32 bytes for the representative change, and then another ~500 bytes or so for the subsequent change block which reverts the representative back.

As Colin mentioned, there's a few other proposals for payment identification currently being discussed and evaluated which offer the same functionality while taking up less space on the ledger (but potentially add additional complexity).

@koczadly I don't think adding representative would add 32 bytes because state blocks always include the representative, changing it or not. And yes, it would use ~500 bytes for the other block, but currently it uses 4 blocks, so using 3 is better.

I'm not sure on the actual internal workings of the node, but (in principle) if a block doesn't change the representative of the account, the node doesn't need to store the representative value again as it could be inferred from the preceding block which last changed the representative. The block JSON itself might always contain a representative field, but the internal database might not store that alongside the block.

@koczadly I think it stores the representative, isn't the purpose of having state blocks to only need to store the last block? (Discarding pending sends and that some nodes would need to store the whole blockchain for new nodes.)

Yeah you're right, but it would still be storing the representative - just not in that specific block. If the node were to implement pruning and discarded of previous blocks, they could then store the representative value in the oldest block. For instance, imagine the following account block chain:

(tail) [#1 open] <- [#2] <- [#3] <- [#4 change] <- [#5] (head)

In this example, only block #1 and #4 would contain the representative value in the database. If we wanted to find the representative of block #3, we could scan the previous blocks until we find the representative change (block #1 changes the rep, so we infer that it's rep field is the same). If we were to prune this chain to store only the latest block (#5), we would first find the representative (by scanning, in this case it's the same as in block #4), then we'd store that representative value in block #5 (so we still have it in the database), and can now safely discard blocks 1-4.

I could be wrong, and this may not be how the node stores blocks, but it's a potential optimization which could be implemented in the future and should be considered.

@koczadly This happened in the past, but it's the opposite with state blocks. Currently, every block stores current representantive. Storing previous blocks only to know the representative wouldn't be optimized, and prunning would be more difficult.

I know - the node would still store and know which representative account is tied to each block, it just wouldn't necessarily directly store it inside the block itself. And you're right, it wouldn't be CPU optimized - but it'd be much more memory efficient. But pruning should be no harder really, it's just an extra step to retrieve the representative from historical blocks and update the value in the database.

@koczadly But that's exactly why nano uses state blocks instead of legacy blocks, it wouldn't be memory efficient because it would need to store entirely the previous blocks only to know the representative.

State blocks are only really useful over legacy blocks when they're the head block (as far as I'm aware), as they allow you to retrieve and verify the latest account state. With the optimization I outlined, you wouldn't need to keep previous blocks. Going with the original account chain example, let's assume we want to prune away blocks #1 and #2 (not a full prune for this example):

Full chain: (tail) [#1 open ] <- [#2] <- [#3] <- [#4 change ] <- [#5] (head)
Pruned chain: (tail) ? <- [#3] <- [#4 change ] <- [#5] (head)

Block #3 would be updated to include the representative in the database, which was previously stored in block #1. Now we no longer need blocks #1 or #2, and can discard them.

@koczadly It would require more processing for looking at the chain, only to save 32-bytes, which are a small part of the block total size.

You're right, but it depends on how often a node needs to look at the historical chain. I suppose it could add some complexities or performance penalties to full bootstrapping. But it's not only 32 bytes - most blocks don't change the representative of an account, so you're actually saving a large percentage of the block count * 32 bytes. Let's assume only 5% of blocks change the representative, that's 119195241 * 0.95 * 32 which is 3.6GB of redundant disk usage at the current ledger size. Nothing huge, but when you consider that NF seem to be in opposition to including an ID field in a block to save space, this definitely needs to be considered.

@koczadly I'm not against including an ID field, I think it would make things easier and only need 2 blocks. This proposal was planned to use 3 instead of 4, as they don't want to include an ID field. And yes, you're right, it would need to store the representative, but it would also need only 3 blocks instead of 4.

What you've proposed is definitely an interesting idea that would work, but I'm just not sure if it's any better or has any advantages over the ID field proposal. Sure, the ID field requires a new block type and node changes, but this proposal would still require a whole standard to be created and wallets/services would need to get on board and integrate it. In my mind it'd just be simpler to go with the ID field, though I'm personally not sold on that idea so much either.

@koczadly Do you mean an ID field added to the block?

Yeah, a new property in the actual block and Nano protocol itself. There were some discussions about including it in the v2 state blocks, but I think they're looking into other alternatives which don't take up space in the ledger.

@koczadly Yes, as I said, this would be better. I created this suggestion because I thought NF was against adding an ID field. Would they create a "merchant address" that also includes the ID, or use it separately?

I'm honestly not too sure, there were different concepts proposed. But the general idea was a block would contain an ID string or integer which refers to a specific payment. I assume an integrated address-ID format could be created, encoding both the destination and ID (similar to what Monero has) for a user to enter into their wallet.

From what I can tell, the primary reason they were against the ID field was it requires additional storage space in the ledger. The issue I see with your proposed solution is that it's gonna take even more space, requiring an additional block or two on top of the payment.