Daily aggregator

This is just to start some discussion and to describe my current thoughts on this.

An idea was mentioned on the forum a month ago that I’m finding more and more interesting. Beam will have the “unlink” option in the wallet to use the Lelantus MW (tx gets put into the shielded pool and gains anonymity over time). I think we could use a similar UX to send a tx to the daily.grin.mw (which can be thought of as a shielded pool) and show the user Estimated time left which would be the time remaining until the daily.grin.mw broadcasts the tx. Similarly, the daily.grin.mw could report the current ‘unlinking potential’ to differentiate between a day that had 5 transaction and one that had 500. The ‘Stop unlinking’ UX button would cost some fees and would mean that we spend the input that we sent to the daily.grin.mw server (the server should note that and remove the transaction containing this input from its daily transaction pool - in case of broadcast race-condition, the daily.grin.mw should rebroadcast again the daily aggregated tx without this tx. This seems to provide an interesting and in a way very similar ‘unlinking’ process as the Lelantus MW - both take a long time and have a linearly growing anonymity set depending on the amount of opt-in transactions. There are some differences between the two which I tried to capture below:


  1. the drawback of making a daily tx is that it takes 24h + confirmations time to confirm the tx while lelantus is just the regular confirmations time
  2. daily can prune outputs while lelantus can’t
  3. daily doesn’t add complexity and doesn’t come with a greater fee cost
  4. both are opt-in and require users to wait to gain anonymity
  5. lelantus has the advantage of having a dynamic ‘time interval’ as opposed to static 24h that daily requires
  6. daily aggregator service is centralized and needs to be trusted

Daily Aggregator


  • doesn’t require consensus changes or change to the node behaviour
  • prunning is preserved


  • requires hours before a tx is broadcasted so the ‘effective confirmation time’ is much longer
  • the time to wait is static and can’t be decided by the user (max 24h of being in a Lelantus pool)
  • centralized and needs to be trusted

Lelantus MW


  • not centralized, no trust required
  • tx can get broadcasted right away
  • the unlinking potential is unlimited in time


  • Adds a lot of complexity to the protocol that can’t ever be thrown away
  • Adds privacy at the cost of scalability (outputs can’t be prunned) which takes away one of the main MW advantages


If trusted, the Daily aggregator can be thought of as an offchain Lelantus. People can opt-in to send a transaction to the daily aggregator server instead of the dandelion network and after a day, the tx gets broadcasted with an anonymity set as big as the number of opt-in txs that happened during that day. I know that this is “just a central coinjoin server”, but I’ve not seen anyone talk about the outcome resemblance to Lelantus.

The outputs that were ‘unlinked’ could even have the ‘potential unlinkability’ score written down next to them if we wanted to distinguish them - I’m not sure we do though.

Note: The above differences are copied from https://gist.github.com/phyro/45f754d583c1296a4f91ac05959369cf which is my understanding after some keybase discussion on the topic

Edit: As was pointed below by @david, I skipped a very important part of comparison which is transacting inside the shielded pool. This can’t be done with daily.grin.mw afaict.


That is awesome. Not sure how this would work, I dont know how many could fit into a block, but it could be cool to have an aggregator service that allows users to pick the anonymity set number rather than the time. Say I want max privacy (a new feature beam just enabled) then I would want to wait however long it took to get a full block of aggregation, right? I dont know if that is possible, but that would be cool.

It would be cool to know estimates on what the max anonymity set could be. I am pretty sure that beam lelantus-mw doesnt have unlinking potential that is unlimited in time. I believe they have sliding windows of 64k anonymity sets. I dont think 64k anonymity set is possible with aggregation, but monero works pretty well with ~10.

A few potential concerns, but the first one is not a big deal because this idea is still an improvement. But if different users opt for different anonymity sets, it makes it easier to predict who is doing what compared to when all users use the same anonymity set parameters. The other concern is harder to swallow, but centralized aggregation servers are easy targets for governments and come with liabilities, especially if the server takes any fees, but maybe others.


I was thinking about the time vs #txs a bit. The issue I currently see with waiting for anonymity set to reach a specific size N is that an adversary could submit N-1 txs to the shielded pool and the tx that is not from them could be linked. The user would be left with an impression that their tx is unlinked when the adversary actually knows the tx that was sent. This is more of an issue when you don’t have full blocks (which we won’t for a long time) and the fees are low. The only solution I see is to use a time-based alternative which is what was proposed with daily.grin.mw.

Yeah, I agree with the concerns. I think that the time intervals we use in real life should be good enough: minute, hour, day (minute is just a direct broadcast) so you’d have only 2 “shielded pools” hourly and daily. Hour would only make sense to be enabled later though. Regarding the centralization, I completely agree, would be great to improve this somehow.

Thanks for getting this discussion started!

I think you’re missing some very important nuance when comparing to Lelantus. You’re comparing the 2 as if the only way you’ll be using Lelantus is to “mix” coins as part of a self-send. But ideally, you would transact via Lelantus as well, which is where real privacy comes from…

Simply sending to the Lelantus pool and then removing from the pool later won’t give near the same privacy benefits. daily.grin.mw will never be able to compete with that, so it would be wise to set our expectations accordingly.


Right! That’s a very necessary correction. The daily idea is only for mixing on an interval and going out. The solution can’t provide privacy on the transacting front inside the pool like Lelantus does. I do wonder how that looks like on Beam though. If we transact on the shielded Lelantus, we don’t have to wait for these long ‘unlinkings’, correct?

I still think that the daily unlinking is still quite powerful for those that want privacy. I expect that at some point we will get enough volume that hourly.grin.mw will become possible (and much more wanted) that we will shorten the time needed to get clean outputs.

Using those coins before waiting the unlinking time still risks leaking some metadata, especially to the receiving party (the receiver can “guess” which peg-in transaction was yours). Ideally, you would wait the long unlinking time after moving coins into the shielded pool, and then I think you should be able to use them relatively freely (assuming sufficient volume). There’s nuance everywhere when talking about privacy though, so I’m just speaking very generally.


Yeah, beams new max privacy function forces users to wait until 64k anonymity is hit. That way the recipient can’t hurt the sender’s anonymity

Posting images I found on my laptop that are regarding the daily aggregator when I was thinking about it.
Here’s an example of the simplest daily aggregator.

It’s a straightforward centralized implementation. It works well and is simple to implement. The main issue would be if the service is silently forced to keep logs of the data. This means you either end up shutting it down (if you’re allowed to?) or keep leaking data to some party. Months ago, I was thinking of ways to resolve this and I think the pre-block cut-through is interesting in that it is able to lose information through transactions. Here’s one example that I have already mentioned, but I don’t believe I’ve shared it as a picture.

With this scheme, even if the daily aggregator is forced to log transactions, it can’t reveal the input/output links because in the end, it only knows which inputs and kernels belonged together because the transaction that is broadcasted at the end has completely different outputs than the daily aggregator saw. Similarly the service to which the daily aggregator sends data to can’t tell the links. None of the two services involved now knows any input/output links if everything goes through so they can’t give useful information. Only daily aggregator could do that if it broadcasted early, but this could get caught because we know which outputs we expect on the chain. They would both need to be forced to log by and provide the data to the same entity. What’s good here is that this can horizontally scale, meaning you can have N services in this aggregator chain and each would replace all the outputs, which means that if the whole chain goes through, if they don’t collude, the “attacker” would need to get the logs from all of the services on the chain. Likewise, if any service was dishonest and would broadcast early, it would be possible to see this.

Note: In case of more than 1 such service, you’d do onion encryption to encrypt data for all the services in the chain.

There are of course downsides, like what happens if they are offline? Perhaps distributing these across the globe so that we are not vulnerable to regulations in a single country would be good. There’s also a problem of how to replace a service, where do we get the public keys to encrypt for? In theory, this could be a public endpoint that a wallet would query to get the public keys to encrypt the data. This makes it easy to update (though has other downsides).

TLDR: it’s possible to make a scheme where if the aggregated tx goes through a chain of N static nodes, it is guaranteed that no single node that saw the aggregated tx knows the input/output links if at least one node was honest. The nodes can log the tx if needed so it’s safe in this way for the nodes. Similarly, users get their privacy so they are protected.

The takeaway here is that it might be interesting to think of other schemes that use the pre-block cut-through to achieve some other interesting properties.


Grin need rule №1 when grin wallet want to send transaction it have before to work on 3 ore 5 transactions from another nods.

Nice idea, could be an option down the road if aggregators begin to see real use. Thanks for including a great graph as well.

Yeah, what’s neat is that you can start with a simple design and then upgrade the aggregator in a backwards compatible way by making the aggregator first send empty set of encrypted transition to the obfuscator who would do nothing in this case. Then later people as they upgrade start sending the encrypted messages.

Might be hard to illustrate, but all the kernels would still exist, right? The graphics make it look like one big tx, and we even word it that way (like blocks are one big tx) but in reality the kernels are still there. Is that true for aggregation services too? I think if the kernels are still there then more information exists that could help deaggregate

Yes, the kernels are still there, but there is no public information that can help de-aggregate (any de-aggregation into single kernel transactions is possible with appropriate choice of kernel offsets).

1 Like

Indeed, the likelihood of someone deaggregating the daily transaction is no different than deaggregating the block transaction.

1 Like

Thanks @oryhp and @tromp makes sense. No different than a full block other than skipping mempool observation it seems. That also avoids the centralized server from needing any control of the funds, which is better for being trustless and legal reasons.

1 Like

I think I found a way to improve the scheme in 2nd image by guaranteeing that either all aggregators happen or just the first one. This can be achieved by connecting the services in a circle so having

Aggr1 -> Aggr2 -> Aggr3 -> Aggr4 ... -> AggrN -> Aggr1

Round 1:
Aggr1 aggregates the txs and before sending the aggregated tx to Aggr2 picks a random offset x, subtracts it from the offset and sends (x*G, aggr1_sig) to Aggr2 (aggr1_sig is x*G signed with aggr1.public_key) who would then assume a valid kernel with commitment x*G exists. Then Aggr2 does the same, aggregates it’s own transactions (which perform a cut-through on all the outputs), pick a random y and subtract y from the offset and send the new tx with (y*G, aggr2_sig) to the next aggregator in line. This would continue and at the end, when Aggr1 gets the aggregated tx back.
They have their aggregated tx, but it has some fake kernels. These will now get removed.

Round 2:
First Aggr1 removes its fake x*G kernel and adds back x to the offset and sends it to the Aggr2 who does the same and so on. At the end, they must all play a role, the scheme makes 2 cycle passes and nobody knows the inputs/outputs at the end (unless everyone colluded) while being able to validate the transaction and preventing a DOS.

I think there are only 2 scenarios that can happen:

  1. They don’t manage to make a full circle
    This means that it comes with a guarantee that you trust Aggr1 not to leak the information
  2. They manage to make a full circle
    This means that nobody knows any input/output links unless they all colluded

P.S. this is much simpler than it looks. I’ll take some time later to present the idea better

Would it be possible to cancel a transaction which has been submitted to the aggregator? I can see how transactions get aggregated, but can they also be “unaggregated”?

Easiest way to cancel is to double spend it.