Tooling: Drand for Cartesi

New Proposal

Cartesi PRNG

This project is a component of a larger set of tools for generating random numbers on Cartesi’s convenience layer.

Project Description

The goal of this project is to create a framework for Cartesi that will make it easy for web3 developers to create DApps using Cartesi. The framework will:

  • use React and TypeScript to create a modular and reusable component library that will provide a common PRNG (Pseudo-Random Number Generator) algorithm to Cartesi technology;

  • use Web3.js and Cartesi’s SDK to connect to the blockchain and the Cartesi Machine;

  • be documented with Docusaurus;

  • be published on npm and GitHub and maintained with semantic versioning.

Our project will offer a easy and affordable way to generate random numbers.

Value proposition

We believe that creating these tools for developers we remove a important entry barrier for them. Random numbers are an essential part of many apps and DApps, specially games. So, as we add this feature to the convenience layer, we expect to see an increase in Cartesi’s adoption and in our community growth.

Here are some types of games that could benefit from this framework:

  • A decentralized soccer game that uses Cartesi’s random algorithm to generate fair and unpredictable moves for both players;

  • A decentralized card game that uses Cartesi’s off-chain computation to handle encryption algorithms and game logic to randomize the deck;

  • RPG games that use PRNGs to determine loot drops, enemy spawns, critical hits, etc.

How you will use Cartesi, specifically?

This project aims to add tooling to build DApps on Cartesi, and this is why it makes sense to have community’s support. The following sections describe how this algorithm will interact with the Cartesi environment to generate a trustless random number.

Drand PRNG

Bob initiates a new random number process by sending an input to the Cartesi Rollups. The frontend is unaware of whether the DApp backend inside the Cartesi Machine will require a random number. As needed, the DApp backend will request a random number from the Random Server. The Random Server will then signal the Convenience Middleware to hold all subsequent inputs until the beacon arrives.

The Convenience API will periodically inspect the Cartesi Machine to check if there are any inputs awaiting a beacon. When the Convenience API detects an input waiting for a random number, it will request the latest beacon from the Drand network and send it to the Cartesi Rollups.

The Random Server will calculate the creation time of the beacon by subtracting a safe number of seconds to prevent any prior knowledge of the beacon by the user. Within this safe time, it will load the pending random requests sent before that timestamp and respond with a generated seed. Finally, the DApp will receive that seed to generate a random number.

When an input backend execution requesting a random number arrives, it will force any subsequent inputs (whether they require a random number or not) to be stored until the next Drand beacon arrives. This rule ensures the correct sequence of input execution.

We know that the user’s DApp calls the rollup server, we change the arrow direction to make the problem easier to think about. In reality, DApps will call our middleware and our middleware will call the rollup server.

The DApp’s owner can run an instance of the Convenience API to provide this random number functionality.

Milestones

Milestone 1: Convenience POC

POC to demonstrate the use case of generating a random number with the Convenience tools.

  • Duration: 8 weeks

  • Deliverables:

  1. An automated build and test process of the npm library artifact using Github Actions;

  2. An automated packaging and release process for the npm library;

  3. The entire codebase under a permissive MIT open-source license;

  4. A readme file that explains the project;

  5. A Convenience Middleware with a new attribute in input_metadata to receive the generated seed;

  6. A library for the Cartesi Machine that obtains all the seeds and produces a random number sequence;

  7. A Convenience API (web2 application) that reads the drand’s beacon and sends it to Cartesi Rollups as needed;

  8. An example of a simple game project that can help new developers learn how to use the solution;

  • Funds request (USD) for milestone 1: $10,500 USD

Milestone 2: Convenience Edge Cases

  • Duration: 5 weeks

  • Deliverables:

  1. A list of edge cases (Drand signature error, drand timeout);

  2. An method to update the Drand’s public key;

  3. An automated tests to cover edge cases;

  • Funds request (USD) for milestone 2: $6,563 USD

Milestone 3: Convenience Web3 Client

  • Duration: 3 weeks
  • Deliverables:
  1. An automated build and test process of the library artifact using Github Actions;

  2. An automated packaging and release process for the npm library;

  3. The entire codebase under a permissive MIT open-source license;

  4. A readme file that provides an overview of the project;

  • Funds request (USD) for milestone 3: $3,937 USD

Total funds requested

$21,000 USD

About your Team

Calindra

Calindra helps our partners to execute their digital strategy through a experienced and talented team.

Calindra Team

Contributions to the Cartesi community:

Links and resources

Website: calindra.tech

LinkedIn: /company/calindra

Github: /Calindra

ERC-20 Payee address

0x7cE0AA3DFbB8abdCD0Ea426769ffD21302DAA8B8

4 Likes

Hey thanks for the submission, we’ll take a look and promote to the community to give feedback as well.

Great work on the submission!

I have one suggestion, though. Perhaps the MiddleWare could notify the DApp when the input arrives, but before the randomness is ready.

This may be useful for example for the DApp to lock some funds or prepare for when the randomness is finished.

Another alternative would be for the DApp to call the MiddleWare instead of the other way around. Maybe there are situations in which only the DApp knows that some randomness is necessary (for example, if only the DApp knows that the deadline for joining a roulette round is over).

Congrats!

2 Likes

Nice! This looks super useful and the deliverables list looks quite comprehensive. DApps having access to drand can be a game changer.

I also prefer that the DApp calls the MiddleWare, instead of the other way around…sometimes an input is not even aware that it would need a random number. Another example, adding to the one Augusto mentioned, would be an input to attack a goblin. If the goblin is out of reach, there is no need to roll the attack dice.

I tried searching for the periodicity of the drand inputs, but it seems like they depend on the network you choose? The fact that subsequent inputs are stored until the next Drand makes a DApp’s response time possibly quite slow…and it makes it freeze if drand goes down. So I think its worth it to deal with the timeout edge case you mentioned.
And maybe, for a future grant, we should think of being able to continue processing some inputs regardless of a previous one waiting for a random number…if they don’t affect eachother. But that sounds like a hard problem in itself.

Anyway, +1 for putting this to vote…it would be a nice tool to have :slight_smile:

3 Likes

Hey there! Thanks for answering our questions. I see you’ve given a rundown of the Drand PRNG process, but I’m curious about its approach to tackling security concerns. Specifically, how does it deal with potential manipulations of the random number generation process? Maybe I’m not well-versed enough, but I know this is a major concern when generating random numbers algorithmically. If you’ve addressed this implicitly and I missed it, can you make it more explicit or point to the portion that explains it?

Also, I’m wondering what your plans are to get developers in the Cartesi community on board with this PRNG tool. Do you have any strategies in mind to promote its adoption? We’re of course here to help multiply your efforts in doing this.

1 Like

Thanks for the submission! I bleive drand can be quite handy.
After the changes suggested above and the clarification of Joe’s doubts, I have no objections and would be glad to see it going through community voting.

There are many ways of generating random numbers when making a Cartesi DApp, but the developer has to understand them and properly implement the strategy for making use of those. This is the kind of proposal that enables more developers to create games and other applications putting their effort on the specific problem they want to solve instead of taking a pretty large detour to make something that is mostly straight forward to use in web2 and is pretty cumbersome in web3.

About the middleware notifying about the random number being ready or the DApp code polling, I see that different Dapps might prefer one approach or the other so my suggestion is making that configurable (or exposing two different interfaces).

+1 for putting this proposal to vote

Hi @Augusto! The DApp will call the middleware in the same way it calls the Rollups today. This way it will be easier to adopt the solution.

This is not perfect, but good enough. There is a good explanation of the attacks scenarios here: Security model | Drand - Distributed Randomness Beacon.

Yes, the periodicity depends on the network. For this application, the Fastnet with 3s frequency will be the best option to not buffer too much input.

Thank you for your support! I would be very happy to contribute to the growth of Cartesi’s gaming community.

Hi @Augusto! The DApp will call the middleware in the same way it calls the Rollups today. This way it will be easier to adopt the solution.

I see, but for example if the randomness is not ready, then a call to next would not return anything, right?

I ask because there are situations in which only the DApp can tell if randomness is necessary.

Yes, the middleware will hold the input until the beacon arrives.

Hi everybody, sorry it took me so long to get here. I really like the proposal, the ideas discussed here are very interesting, and I believe that if this is successful it will be a great hit!

+1 to putting it to vote!

Some additional questions/comments:

  • I’m not sure I understand who is expected to run the off-chain Convenience API. The front-end application?

  • I believe the proposed idea is that the Convenience Middleware inside the CM will wrap or decorate the existing back-end HTTP API. Is that the case?

  • I agree with @Augusto’s comments that it may be tricky to know when randomness is necessary. I believe both scenarios are plausible:

    1. The client (front-end application) has enough logic to know when randomness is necessary, in which case it can signal the middleware to hold subsequent inputs until the beacon arrives (which is what this proposal supports)
    2. Only the back-end knows when randomness is necessary, in which case it would need to signal to the middleware that further input processing can only happen after a new beacon arrives. I don’t think this is covered by the current proposal, but I think that’s ok - we could stick to use-case (1) for a first implementation, but keep (2) in mind
  • A futuristic idea/comment: I was wondering if we could avoid sending an input to each DApp that needs randomness. Ideally we would like to send this input to some “broadcast” address and have all interested DApps receive it. Maybe we could have a “Drand DApp” and have other DApps run it inside using a “Cascades” approach…? Or have explicit support in Cartesi Rollups for “broadcast inputs” (e.g., inputs sent to address(0))

Cheers!

What I was thinking is along these lines, but I have not thought about all the consequences:

Untitled Diagram.drawio

Concerning @milton-cartesi’s futuristic idea, it would be much easier once we have the Unified Input box.

Hi guys, may we send the beacon through the Alice input?
That way, if Bob’s input is older than 3s + safe margin, we save some transactions.

Yeah, for use case (2) something like this would be necessary - although I would probably suggest something like adding request_seed: true to the /finish JSON body, since at this point the machine needs to yield to wait for the beacon. I think the middleware would need to issue a notice or report to signal that a beacon input is needed to move on.

But I’m wondering… maybe you’re right, and even for use case (1) maybe it’s still safe to flip it and consider that the DApp back-end is always the sole authority to decide when randomness is necessary. What do you think @fabio.oshiro ?

I think in theory you could, but then you may need to enforce some structure to the DApp-specific (Alice’s) input, so that you can compose them together. Maybe a beacon input could be something like:

{ beacon: "da4e..54a0", input: ... }

where input is the DApp-specific input, and can be any bytes array. The middleware would then have to recognize the beacon input, use it to unblock the inputs, and forward the DApp-specific input to the DApp.

It’s becoming clearer to me that we should define some general standards to allow “routing” (having components like the middleware recognize inputs they should process), so that DApps can simultaneously use several frameworks like this one.

When the backend receives an input to roll the dice, it could make a request to the middleware, sending the input time. The middleware would hold the response until the safe beacon arrives. Of course, this request would be wrapped in a library like:

// js code
const randomNumber = await Cartesi.random()

I will update the sequence diagram.

Hi guys!
I revised the proposal to make the frontend “dumb”

@Augusto @milton-cartesi

Hey there! I like the update! I am just a bit confused about whether the “Cartesi Random Server” should be kept separated from the middleware.

I understand that maybe there are indeed two separate concerns:

  1. An API to request and return random numbers
  2. Support for holding/restraining inputs until appropriate conditions are met

In practice, however, I guess the middleware will need to recognize the beacon input to be able to unblock the inputs. So I’m not 100% sure if it’s worth it to split the two functionalities.

Cheers!

Sure, the RandomServer could be renamed as RandomService and be accessed by an endpoint in the middleware.