diff --git a/src/config/sidebar.ts b/src/config/sidebar.ts index d2fb9304b86..12f9cadce7e 100644 --- a/src/config/sidebar.ts +++ b/src/config/sidebar.ts @@ -323,6 +323,10 @@ export const SIDEBAR: Partial> = { url: "cre/guides/workflow/time-in-workflows", highlightAsCurrent: ["cre/guides/workflow/time-in-workflows-ts", "cre/guides/workflow/time-in-workflows-go"], }, + { + title: "Using Randomness in Workflows", + url: "cre/guides/workflow/using-randomness", + }, ], }, { @@ -420,10 +424,6 @@ export const SIDEBAR: Partial> = { url: "cre/concepts/non-determinism", highlightAsCurrent: ["cre/concepts/non-determinism-go", "cre/concepts/non-determinism-ts"], }, - { - title: "Random in CRE", - url: "cre/concepts/random-in-cre", - }, { title: "TypeScript Runtime Environment", url: "cre/concepts/typescript-wasm-runtime", diff --git a/src/content/cre/concepts/non-determinism-go.mdx b/src/content/cre/concepts/non-determinism-go.mdx index 100b4c3f68d..76834e2dabb 100644 --- a/src/content/cre/concepts/non-determinism-go.mdx +++ b/src/content/cre/concepts/non-determinism-go.mdx @@ -79,7 +79,7 @@ Go's built-in `rand` package generates different random sequences on each node, **The problem:** Each node generates different random values, breaking consensus. -**The solution:** Use `runtime.Rand()` from the CRE SDK, which provides consensus-safe random number generation. All nodes generate the same sequence of random values, enabling consensus. See [Random in CRE](/cre/concepts/random-in-cre) for details. +**The solution:** Use `runtime.Rand()` from the CRE SDK, which provides consensus-safe random number generation. All nodes generate the same sequence of random values, enabling consensus. See [Using Randomness in Workflows](/cre/guides/workflow/using-randomness) for details. ## 6. Working with LLMs @@ -114,5 +114,5 @@ Large Language Models (LLMs) generate different responses for the same prompt, e ## Related concepts - **[Time in CRE](/cre/guides/workflow/time-in-workflows-go)**: Learn about DON Time and why `runtime.Now()` is required -- **[Random in CRE](/cre/concepts/random-in-cre)**: Understand consensus-safe random number generation +- **[Using Randomness in Workflows](/cre/guides/workflow/using-randomness)**: Understand consensus-safe random number generation - **[Consensus Computing](/cre/concepts/consensus-computing)**: Deep dive into how nodes reach agreement diff --git a/src/content/cre/getting-started/before-you-build-go.mdx b/src/content/cre/getting-started/before-you-build-go.mdx index d9547b74bc8..861d35457d9 100644 --- a/src/content/cre/getting-started/before-you-build-go.mdx +++ b/src/content/cre/getting-started/before-you-build-go.mdx @@ -31,7 +31,7 @@ If your workflow needs random values (e.g., selecting a winner or generating non {/* prettier-ignore */} {/* prettier-ignore */} diff --git a/src/content/cre/guides/workflow/using-evm-client/onchain-write/overview-go.mdx b/src/content/cre/guides/workflow/using-evm-client/onchain-write/overview-go.mdx index 0ee855ddada..125cc10a3db 100644 --- a/src/content/cre/guides/workflow/using-evm-client/onchain-write/overview-go.mdx +++ b/src/content/cre/guides/workflow/using-evm-client/onchain-write/overview-go.mdx @@ -180,7 +180,7 @@ max := big.NewInt(1000) randomValue := new(big.Int).Rand(rnd, max) ``` -**Note**: For a complete understanding of how randomness works in CRE, including the difference between DON mode and Node mode randomness, see [Random in CRE](/cre/concepts/random-in-cre). +**Note**: For a complete understanding of how randomness works in CRE, including the difference between DON mode and Node mode randomness, see [Using Randomness in Workflows](/cre/guides/workflow/using-randomness). ### Constructing input structs diff --git a/src/content/cre/concepts/random-in-cre.mdx b/src/content/cre/guides/workflow/using-randomness.mdx similarity index 99% rename from src/content/cre/concepts/random-in-cre.mdx rename to src/content/cre/guides/workflow/using-randomness.mdx index 5c29596e986..6e378ea4064 100644 --- a/src/content/cre/concepts/random-in-cre.mdx +++ b/src/content/cre/guides/workflow/using-randomness.mdx @@ -1,12 +1,12 @@ --- section: cre -title: "Random in CRE" +title: "Using Randomness in Workflows" sdkLang: "go" date: Last Modified metadata: description: "Generate random numbers safely in CRE: use runtime.Rand() to ensure all nodes get the same values and maintain consensus." datePublished: "2025-11-04" - lastModified: "2025-11-04" + lastModified: "2026-02-05" --- import { Aside } from "@components" diff --git a/src/content/cre/llms-full-go.txt b/src/content/cre/llms-full-go.txt index 4cffc4df16f..043e7261ef0 100644 --- a/src/content/cre/llms-full-go.txt +++ b/src/content/cre/llms-full-go.txt @@ -4878,6 +4878,226 @@ By following this pattern, you can manage your secrets securely without ever exp --- +# Using Randomness in Workflows +Source: https://docs.chain.link/cre/guides/workflow/using-randomness +Last Updated: 2026-02-05 + + + +## The problem: Why randomness needs special handling + +Workflows often need randomness for various purposes: generating nonces, selecting winners from a list, or creating unpredictable values. However, in a decentralized network, naive use of random number generators creates a critical problem: + +**If each node generates different random values, they cannot reach consensus on the workflow's output.** + +For example, if your workflow selects a lottery winner using each node's local random generator, different nodes would select different winners, making it impossible to agree on a single result to write onchain. + +## The solution: Consensus-safe randomness + +CRE provides randomness through the `runtime.Rand()` method, which returns a standard Go `*rand.Rand` object. This random generator is managed by the CRE platform to ensure all nodes generate the same sequence of random values, enabling consensus while still providing unpredictability across different workflow executions. + +### Usage + +```go +// Get the random generator from the runtime +rnd, err := runtime.Rand() +if err != nil { + return err +} + +// Use it with standard Go rand methods +randomInt := rnd.Intn(100) // Random int in [0, 100) +randomBigInt := new(big.Int).Rand(rnd, big.NewInt(1000)) // Random big.Int +``` + +## Common use cases + +- Selecting a winner from a lottery or pool +- Generating nonces for transactions +- Creating random identifiers or values +- Any random selection that needs to be agreed upon by all nodes + +## Working with big.Int random values + +For Solidity `uint256` types, you often need random `*big.Int` values: + +```go +rnd, err := runtime.Rand() +if err != nil { + return err +} + +// Generate a random number in the range [0, max) +max := new(big.Int) +max.SetString("1000000000000000000", 10) // 1 ETH in wei + +randomAmount := new(big.Int).Rand(rnd, max) +// randomAmount is a random value between 0 and 1 ETH +``` + +## Complete example: Random lottery + +Here's a complete example that demonstrates using DON mode randomness to select a lottery winner and generate a prize amount: + +```go +//go:build wasip1 + +package main + +import ( + "fmt" + "log/slog" + "math/big" + + "github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron" + "github.com/smartcontractkit/cre-sdk-go/cre" + "github.com/smartcontractkit/cre-sdk-go/cre/wasm" +) + +type Config struct { + Schedule string `json:"schedule"` +} + +type MyResult struct { + WinnerIndex int + Winner string + RandomBigInt string +} + +func InitWorkflow(config *Config, logger *slog.Logger, secretsProvider cre.SecretsProvider) (cre.Workflow[*Config], error) { + return cre.Workflow[*Config]{ + cre.Handler(cron.Trigger(&cron.Config{Schedule: config.Schedule}), onCronTrigger), + }, nil +} + +func onCronTrigger(config *Config, runtime cre.Runtime, trigger *cron.Payload) (*MyResult, error) { + logger := runtime.Logger() + logger.Info("Running random lottery") + + // Define participants + participants := []string{"Alice", "Bob", "Charlie", "Diana", "Eve"} + logger.Info("Participants in lottery", "count", len(participants), "names", participants) + + // Get the DON mode random generator + rnd, err := runtime.Rand() + if err != nil { + return nil, fmt.Errorf("failed to get random generator: %w", err) + } + + // Select a random winner (index in range [0, 5)) + winnerIndex := rnd.Intn(len(participants)) + winner := participants[winnerIndex] + logger.Info("Selected winner", "index", winnerIndex, "winner", winner) + + // Generate a random prize amount up to 1,000,000 wei + maxPrize := big.NewInt(1000000) + randomPrize := new(big.Int).Rand(rnd, maxPrize) + logger.Info("Generated random prize", "amount", randomPrize.String()) + + // Return the results + result := &MyResult{ + WinnerIndex: winnerIndex, + Winner: winner, + RandomBigInt: randomPrize.String(), + } + + logger.Info("Random lottery complete!", "result", result) + return result, nil +} + +func main() { + wasm.NewRunner(cre.ParseJSON[Config]).Run(InitWorkflow) +} +``` + +**What this example demonstrates:** + +1. **DON mode context**: The randomness is called directly in the trigger callback (DON mode), ensuring all nodes in the network would select the same winner and prize amount. + +2. **Random selection**: Uses `rnd.Intn(len(participants))` to select a random index from the participant list. The `Intn(n)` method returns a value in the range `[0, n)`. + +3. **Random big.Int for Solidity**: Generates a `*big.Int` value suitable for use with Solidity `uint256` types. + +4. **Error handling**: Properly checks for errors when calling `runtime.Rand()`. + +When you run this workflow multiple times, each execution will select different winners and prize amounts (because each execution gets a different seed), but within a single execution, all nodes in the DON would arrive at the same winner. + +## Best practices + +### Do: + +- **Always use `runtime.Rand()`** for randomness in your workflows +- **Check for errors** when calling `runtime.Rand()` + ```go + rnd, err := runtime.Rand() + if err != nil { + return fmt.Errorf("failed to get random generator: %w", err) + } + ``` + +### Don't: + +- **Don't use Go's global `rand` package** directly. Always get your random generator from `runtime.Rand()` first. + +## Mode-aware behavior + +The randomness provided by `runtime.Rand()` is **mode-aware**. The examples above demonstrate DON mode (the default execution mode for workflows). There is also a Node mode with different random behavior, used in advanced scenarios. Each mode provides a different type of randomness. + +### DON mode (default) + +The examples above all use DON mode. In this mode: + +- All nodes generate the **same** random sequence +- Enables consensus on random values +- This is the mode your main workflow callback runs in + +### Node mode + +When using `cre.RunInNodeMode`, you can access Node mode randomness: + +- Each node generates **different** random values +- Useful for scenarios where per-node variability is accepted +- Access via `nodeRuntime.Rand()` inside the Node mode function + +**Example:** + +```go +resultPromise := cre.RunInNodeMode(config, runtime, + func(config *Config, nodeRuntime cre.NodeRuntime) (int, error) { + rnd, err := nodeRuntime.Rand() + if err != nil { + return 0, err + } + // Each node generates a different value + return rnd.Intn(100), nil + }, + cre.ConsensusMedianAggregation[int](), +) +``` + +### Important: Mode isolation + +Random generators are tied to the mode they were created in. **Do not** attempt to use a random generator from one mode in another mode—it will cause a panic and crash your workflow. + +## FAQ + +**Is the randomness cryptographically secure?** + +The randomness is sourced from the host environment's secure random generator, but the standard Go `*rand.Rand` object is **not** intended for cryptographic purposes. For cryptographic operations, use dedicated crypto libraries. + +**What happens if I try to use randomness in the wrong mode?** + +The SDK will panic with the error: `"random cannot be used outside the mode it was created in"`. This is intentional—it prevents subtle consensus bugs. + +**Can I use the same random generator across multiple calls?** + +Yes. Once you call `runtime.Rand()` and get a `*rand.Rand` object, you can reuse it within the same execution mode. Each call to methods like `Intn()` will produce the next value in the deterministic sequence. + +--- + # Simulating Workflows Source: https://docs.chain.link/cre/guides/operations/simulating-workflows Last Updated: 2025-11-04 @@ -7204,226 +7424,6 @@ Learn more about how to use CRE capabilities with built-in consensus: --- -# Random in CRE -Source: https://docs.chain.link/cre/concepts/random-in-cre -Last Updated: 2025-11-04 - - - -## The problem: Why randomness needs special handling - -Workflows often need randomness for various purposes: generating nonces, selecting winners from a list, or creating unpredictable values. However, in a decentralized network, naive use of random number generators creates a critical problem: - -**If each node generates different random values, they cannot reach consensus on the workflow's output.** - -For example, if your workflow selects a lottery winner using each node's local random generator, different nodes would select different winners, making it impossible to agree on a single result to write onchain. - -## The solution: Consensus-safe randomness - -CRE provides randomness through the `runtime.Rand()` method, which returns a standard Go `*rand.Rand` object. This random generator is managed by the CRE platform to ensure all nodes generate the same sequence of random values, enabling consensus while still providing unpredictability across different workflow executions. - -### Usage - -```go -// Get the random generator from the runtime -rnd, err := runtime.Rand() -if err != nil { - return err -} - -// Use it with standard Go rand methods -randomInt := rnd.Intn(100) // Random int in [0, 100) -randomBigInt := new(big.Int).Rand(rnd, big.NewInt(1000)) // Random big.Int -``` - -## Common use cases - -- Selecting a winner from a lottery or pool -- Generating nonces for transactions -- Creating random identifiers or values -- Any random selection that needs to be agreed upon by all nodes - -## Working with big.Int random values - -For Solidity `uint256` types, you often need random `*big.Int` values: - -```go -rnd, err := runtime.Rand() -if err != nil { - return err -} - -// Generate a random number in the range [0, max) -max := new(big.Int) -max.SetString("1000000000000000000", 10) // 1 ETH in wei - -randomAmount := new(big.Int).Rand(rnd, max) -// randomAmount is a random value between 0 and 1 ETH -``` - -## Complete example: Random lottery - -Here's a complete example that demonstrates using DON mode randomness to select a lottery winner and generate a prize amount: - -```go -//go:build wasip1 - -package main - -import ( - "fmt" - "log/slog" - "math/big" - - "github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron" - "github.com/smartcontractkit/cre-sdk-go/cre" - "github.com/smartcontractkit/cre-sdk-go/cre/wasm" -) - -type Config struct { - Schedule string `json:"schedule"` -} - -type MyResult struct { - WinnerIndex int - Winner string - RandomBigInt string -} - -func InitWorkflow(config *Config, logger *slog.Logger, secretsProvider cre.SecretsProvider) (cre.Workflow[*Config], error) { - return cre.Workflow[*Config]{ - cre.Handler(cron.Trigger(&cron.Config{Schedule: config.Schedule}), onCronTrigger), - }, nil -} - -func onCronTrigger(config *Config, runtime cre.Runtime, trigger *cron.Payload) (*MyResult, error) { - logger := runtime.Logger() - logger.Info("Running random lottery") - - // Define participants - participants := []string{"Alice", "Bob", "Charlie", "Diana", "Eve"} - logger.Info("Participants in lottery", "count", len(participants), "names", participants) - - // Get the DON mode random generator - rnd, err := runtime.Rand() - if err != nil { - return nil, fmt.Errorf("failed to get random generator: %w", err) - } - - // Select a random winner (index in range [0, 5)) - winnerIndex := rnd.Intn(len(participants)) - winner := participants[winnerIndex] - logger.Info("Selected winner", "index", winnerIndex, "winner", winner) - - // Generate a random prize amount up to 1,000,000 wei - maxPrize := big.NewInt(1000000) - randomPrize := new(big.Int).Rand(rnd, maxPrize) - logger.Info("Generated random prize", "amount", randomPrize.String()) - - // Return the results - result := &MyResult{ - WinnerIndex: winnerIndex, - Winner: winner, - RandomBigInt: randomPrize.String(), - } - - logger.Info("Random lottery complete!", "result", result) - return result, nil -} - -func main() { - wasm.NewRunner(cre.ParseJSON[Config]).Run(InitWorkflow) -} -``` - -**What this example demonstrates:** - -1. **DON mode context**: The randomness is called directly in the trigger callback (DON mode), ensuring all nodes in the network would select the same winner and prize amount. - -2. **Random selection**: Uses `rnd.Intn(len(participants))` to select a random index from the participant list. The `Intn(n)` method returns a value in the range `[0, n)`. - -3. **Random big.Int for Solidity**: Generates a `*big.Int` value suitable for use with Solidity `uint256` types. - -4. **Error handling**: Properly checks for errors when calling `runtime.Rand()`. - -When you run this workflow multiple times, each execution will select different winners and prize amounts (because each execution gets a different seed), but within a single execution, all nodes in the DON would arrive at the same winner. - -## Best practices - -### Do: - -- **Always use `runtime.Rand()`** for randomness in your workflows -- **Check for errors** when calling `runtime.Rand()` - ```go - rnd, err := runtime.Rand() - if err != nil { - return fmt.Errorf("failed to get random generator: %w", err) - } - ``` - -### Don't: - -- **Don't use Go's global `rand` package** directly. Always get your random generator from `runtime.Rand()` first. - -## Mode-aware behavior - -The randomness provided by `runtime.Rand()` is **mode-aware**. The examples above demonstrate DON mode (the default execution mode for workflows). There is also a Node mode with different random behavior, used in advanced scenarios. Each mode provides a different type of randomness. - -### DON mode (default) - -The examples above all use DON mode. In this mode: - -- All nodes generate the **same** random sequence -- Enables consensus on random values -- This is the mode your main workflow callback runs in - -### Node mode - -When using `cre.RunInNodeMode`, you can access Node mode randomness: - -- Each node generates **different** random values -- Useful for scenarios where per-node variability is accepted -- Access via `nodeRuntime.Rand()` inside the Node mode function - -**Example:** - -```go -resultPromise := cre.RunInNodeMode(config, runtime, - func(config *Config, nodeRuntime cre.NodeRuntime) (int, error) { - rnd, err := nodeRuntime.Rand() - if err != nil { - return 0, err - } - // Each node generates a different value - return rnd.Intn(100), nil - }, - cre.ConsensusMedianAggregation[int](), -) -``` - -### Important: Mode isolation - -Random generators are tied to the mode they were created in. **Do not** attempt to use a random generator from one mode in another mode—it will cause a panic and crash your workflow. - -## FAQ - -**Is the randomness cryptographically secure?** - -The randomness is sourced from the host environment's secure random generator, but the standard Go `*rand.Rand` object is **not** intended for cryptographic purposes. For cryptographic operations, use dedicated crypto libraries. - -**What happens if I try to use randomness in the wrong mode?** - -The SDK will panic with the error: `"random cannot be used outside the mode it was created in"`. This is intentional—it prevents subtle consensus bugs. - -**Can I use the same random generator across multiple calls?** - -Yes. Once you call `runtime.Rand()` and get a `*rand.Rand` object, you can reuse it within the same execution mode. Each call to methods like `Intn()` will produce the next value in the deterministic sequence. - ---- - # CRE Templates Source: https://docs.chain.link/cre/templates Last Updated: 2025-11-21 @@ -8745,7 +8745,7 @@ Go's built-in `rand` package generates different random sequences on each node, **The problem:** Each node generates different random values, breaking consensus. -**The solution:** Use `runtime.Rand()` from the CRE SDK, which provides consensus-safe random number generation. All nodes generate the same sequence of random values, enabling consensus. See [Random in CRE](/cre/concepts/random-in-cre) for details. +**The solution:** Use `runtime.Rand()` from the CRE SDK, which provides consensus-safe random number generation. All nodes generate the same sequence of random values, enabling consensus. See [Using Randomness in Workflows](/cre/guides/workflow/using-randomness) for details. ## 6. Working with LLMs @@ -8780,7 +8780,7 @@ Large Language Models (LLMs) generate different responses for the same prompt, e ## Related concepts - **[Time in CRE](/cre/guides/workflow/time-in-workflows-go)**: Learn about DON Time and why `runtime.Now()` is required -- **[Random in CRE](/cre/concepts/random-in-cre)**: Understand consensus-safe random number generation +- **[Using Randomness in Workflows](/cre/guides/workflow/using-randomness)**: Understand consensus-safe random number generation - **[Consensus Computing](/cre/concepts/consensus-computing)**: Deep dive into how nodes reach agreement --- @@ -8808,7 +8808,7 @@ If your workflow needs random values (e.g., selecting a winner or generating non @@ -11156,7 +11156,7 @@ max := big.NewInt(1000) randomValue := new(big.Int).Rand(rnd, max) ``` -**Note**: For a complete understanding of how randomness works in CRE, including the difference between DON mode and Node mode randomness, see [Random in CRE](/cre/concepts/random-in-cre). +**Note**: For a complete understanding of how randomness works in CRE, including the difference between DON mode and Node mode randomness, see [Using Randomness in Workflows](/cre/guides/workflow/using-randomness). ### Constructing input structs diff --git a/src/features/redirects/redirects.json b/src/features/redirects/redirects.json index b576db48edb..d36c068fe2d 100644 --- a/src/features/redirects/redirects.json +++ b/src/features/redirects/redirects.json @@ -2609,6 +2609,11 @@ "source": "cre/getting-started/conclusion", "destination": "cre/getting-started/before-you-build", "statusCode": 301 + }, + { + "source": "cre/concepts/random-in-cre", + "destination": "cre/guides/workflow/using-randomness", + "statusCode": 301 } ] }