Endless
  • πŸš€README
  • Discovery
    • πŸš€Endless Web3 Genesis Cloud
    • πŸ’ŽBusiness Model
    • 🎯Vision
    • ✈️Roadmap
    • πŸͺ™Economics
    • πŸ‘€Team
      • Yu Xiong
      • Amit Kumar Jaiswal
      • Ned
      • 0xfun
      • Scott Trowbridge
      • Neeraj Sharma LLB
      • Amjad Suleman
      • Binu Paul
      • Eduard Romulus GOEAN
    • ❀️Developer Community
  • Endless Chain
    • Tech Docs
      • Account Address Format
      • Endless Account
      • Endless Coin(EDS)
      • Sponsored Transaction
      • On-Chain Multisig
      • Randomness
      • Safety Transaction
      • Token Locking & Distribution
    • Start
      • Learn about Endless
        • Accounts
        • Resources
        • Events
        • Transactions and States
        • Gas and Storage Fees
        • Computing Transaction Gas
        • Blocks
        • Staking
          • Delegated Staking
        • Governance
        • Endless Blockchain Deep Dive
          • Validator Nodes Overview
          • Fullnodes Overview
          • Node Networks and Synchronization
        • Move - A Web3 Language and Runtime
      • Explore Endless
      • Latest Endless Releases
      • Networks
    • Build
      • Tutorials
        • Your First Transaction
        • Your First Fungible Asset
        • Your First NFT
        • Your First Move Module
        • Your First Multisig
      • Learn the Move Language
        • The Move Book
          • Getting Started
            • Introduction
            • Modules and Scripts
          • Primitive Types
            • Move Tutorial
            • Integers
            • Bool
            • Address
            • Vector
            • Signer
            • References
            • Tuples and Unit
          • Basic Concepts
            • Local Variables and Scope
            • Equality
            • Abort and Assert
            • Conditionals
            • While, For, and Loop
            • Functions
            • Structs and Resources
            • Constants
            • Generics
            • Abilities
            • Uses and Aliases
            • Friends
            • Packages
            • Package Upgrades
            • Unit Tests
          • Global Storage
            • Global Storage - Structure
            • Global Storage - Operators
          • Reference
            • Libraries
            • Move Coding Conventions
        • Advanced Move Guides
          • Objects
            • Creating Objects
            • Configuring objects
            • Using objects
          • Move Scripts
            • Writing Move Scripts
            • Compiling Move Scripts
            • Running Move Scripts
            • Move Scripts Tutorial
          • Resource Accounts
          • Modules on Endless
          • Cryptography
          • Gas Profiling
          • Security
      • Endless Standards
        • Object
        • Endless Fungible Asset Standard
        • Endless Digital Asset Standard
        • Endless Wallet Standard
      • Endless APIs
        • Fullnode Rest API
        • Indexer Restful API
          • Indexer Installation
        • GRPC Transaction Stream
          • Running Locally
          • Custom Processors
            • End-to-End Tutorial
            • Parsing Transactions
          • Self-Hosted Transaction Stream Service
      • Endless SDKs
        • TypeScript SDK
          • Account
          • SDK Configuration
          • Fetch data from chain
          • Transaction Builder
          • HTTP Client
          • Move Types
          • Testing
          • Typescript
        • Rust SDK
        • Go SDK
      • Endless CLI
        • Install the Endless CLI
          • Install On Mac
          • Install On Alibaba Cloud
          • Install On Linux
          • Install On Windows
        • CLI Configuration
        • Use Endless CLI
          • Working With Move Contracts
            • Arguments in JSON Tutorial
          • Trying Things On-Chain
            • Look Up On-Chain Account Info
            • Create Test Accounts
          • Running A Local Network
            • Running a Public Network
          • Managing a Network Node
      • Integrate with Endless
        • Endless Token Overview
        • Application Integration Guide
      • Endless VSCode extension
      • Advanced Builder Guides
        • Develop Locally
          • Running a Local Network
          • Run a Localnet with Validator
    • Nodes
      • Learn about Nodes
      • Run a Validator and VFN
        • Node Requirements
        • Deploy Nodes
          • Using Docker
          • Using AWS
          • Using Azure
          • Using GCP
        • Connect Nodes
          • Connect to a Network
        • Verify Nodes
          • Node Health
          • Validator Leaderboard
      • Run a Public Fullnode
        • PFN Requirements
        • Deploy a PFN
          • Using Pre-compiled Binary
          • Using Docker
          • Using GCP 🚧 (under_construction)
        • Verify a PFN
        • Modify a PFN
          • Upgrade your PFN
          • Generate a PFN Identity
          • Customize PFN Networks
      • Bootstrap a Node
        • Bootstrap from a Snapshot
        • Bootstrap from a Backup
      • Configure a Node
        • State Synchronization
        • Data Pruning
        • Telemetry
        • Locating Node Files
          • Files For Mainnet
          • Files For Testnet
          • Files For Devnet
      • Monitor a Node
        • Node Inspection Service
        • Important Node Metrics
        • Node Health Checker
    • Reference
      • Endless Error Codes
      • Move Reference Documentation
      • Endless Glossary
    • FAQs
  • Endless Bridge
    • Intro to Endless Bridge
    • How to use bridge
    • Liquidity Management
    • Faucet
    • Developer Integration
      • Contract Integration
        • Message Contract
        • Execute Contract
      • Server-Side Integration
        • Message Sender
        • Example of Message Listener Service (Rust)
        • Example of Token Cross-Chain (JS)
  • Endless Wallet
    • User Guide
    • Basic Tutorial
    • FAQs
    • MultiAccount
    • SDK
      • Functions
      • Events
  • GameFi
    • Intro
    • GameFi & Endless
  • Endless Modules
    • Stacks
    • Storage
    • Module List
  • Endless Ecosystem
    • Intro
    • Show Cases
    • App Demo
  • Whitepaper
  • Endless SCAN
    • User Guide
  • MULTI-SIGNATURE
    • Multi-Signature User Guide
  • Regulations
    • Privacy Policy
    • Terms of Service
    • Funding Terms - Disclaimer
Powered by GitBook
On this page
  • Reliable Randomness
  • Weighted Threshold VUF Essentials
  • PVSS Process
  • API Usage
  • Demonstration
  • Security Considerations
  • Test and Abort Attack
  • Under Gas Attack
  • Random, but not a secret
Export as PDF
  1. Endless Chain
  2. Tech Docs

Randomness

Randomization is an important feature in blockchain. The application of randomization results, such as generated random numbers, can be achieved similarly to, e.g., the election of block nodes, while trusted random numbers can provide support for numerous DApps, such as lotto, games, etc.

In many other chains, the implementation of trusted random number generation requires the use of off-chain random number generation services, such as Ethereum, which requires off-chain beacons such as Chainlink or drand to provide external randomness.

The disadvantages of relying on external beacons are that the randomness is too expensive or too slow to be generated for DApps that require higher speed random numbers; in addition, the external randomness must be sent to the contract through transactions, which complicates DApp development and user interaction.

Reliable Randomness

If a sequence is to be identified as random, it has to possess the following qualities:

  • Unpredictable β€” The result must be unknowable ahead of time.

  • Unbiased β€” Each outcome must be equally possible.

  • Provable β€” The result must be independently verifiable.

  • Tamper-proof β€” The process of generating randomness must be resistant to manipulation by any entity.

  • Non-reproducible β€” The process of generating randomness cannot be reproduced unless the original sequence is preserved.

Weighted Threshold VUF Essentials

Here we discuss threshold VUF with an idealized key generation algorithm. The advantages of this approach, rather than immediately considering VUF keying using a DKG, is that it lets us focus on the VUF security and avoid any nuances due to the key generation phase.

An (n,t)(n, t)(n,t) -threshold VUF scheme for a finite message space M\mathbb {M}M is a tuple of polynomial time computable algorithms βˆ‘=(KeyGen,ShareSign,ShareVerify,Verify,Aggregate)\sum=(KeyGen, ShareSign, ShareVerify, Verify, Aggregate)βˆ‘=(KeyGen,ShareSign,ShareVerify,Verify,Aggregate) with the following properties:

  1. Setup(1k,n,w,t)β†’pp(1^{k}, n, w, t) \to pp(1k,n,w,t)β†’pp.

    pppppp (public parameter) consists of a generator h∈Gh \in \mathbb{G}h∈G and a random generator H:{0,1}βˆ—β†’GH:\{0, 1\}^{*} \to \mathbb{G}H:{0,1}βˆ—β†’G. Let w=[w1,⋯ ,wn]w = [w_{1},\cdots,w_{n}]w=[w1​,β‹―,wn​] be the weights of the participants, W:=βˆ‘i∈[n]wi\mathbb{W} := \sum_{i \in [n]}{w_{i}}W:=βˆ‘i∈[n]​wi​ be the total weights, and ttt be the threshold.

  2. KeyGen(pp)β†’sk(pp) \to sk(pp)β†’sk, {vki}i∈[n]\{vk_{i}\}_{i \in [n]}{vki​}i∈[n]​, {ski}i∈[n]\{sk_{i}\}_{i \in [n]}{ski​}i∈[n]​.

    The key generation algorithm takes the public parameters then outputs:

    • a secret key sk:=hask := h^{a}sk:=ha, where aaa is a random number in group G\mathbb{G}G

    • a vector of secret signing keys {sk1,…,skn}\{sk_1, \ldots, sk_n\}{sk1​,…,skn​}, using H:{0,1}βˆ—β†’GH:\{0, 1\}^{*} \to \mathbb{G}H:{0,1}βˆ—β†’G.

    • (aj)j∈[W](a_{j})_{j \in [W]}(aj​)j∈[W]​ is derived from ShamirShare(a,t,Wa, t, Wa,t,W)

    • a vector of verification keys {vk1,⋯ ,vkn}\{vk_1, \cdots, vk_n\}{vk1​,β‹―,vkn​}, where vki:=(hri,⋯ ,hriasi+wi)vk_{i} := (h^{r_{i}}, \cdots ,h^{r_{i}a_{s_{i}+w_{i}}})vki​:=(hri​,β‹―,hri​asi​+wi​​)

    • a vector of public keys equal to verification keys, i.e., pki:=vki,i∈[n]pk_{i} := vk_{i}, i \in [n]pki​:=vki​,i∈[n].

    The public key size of a party in our weighted VUF is proportional to its weight, i.e., the public key vkivk_{i}vki​ of party iii contains wi+1w_{i} + 1wi​+1 group elements.

  3. ShareSign(m,ski)β†’Οƒi(m,sk_{i}) β†’ \sigma_{i}(m,ski​)β†’Οƒi​.

    The partial signing algorithm is a possibly randomized algorithm that takes as input a message mmm and a secret key share skisk_iski​. It generates a signature share Οƒi\sigma_{i}Οƒi​.

  4. ShareVerify(m,Οƒi,vki)β†’0/1(m, Οƒ_i, vk_i) \to 0/1(m,Οƒi​,vki​)β†’0/1.

    The signature share verification function is a deterministic algorithm that takes as input a message mmm, a public key share vkivk_ivki​, and a signature share Οƒi\sigma_iΟƒi​. It outputs 1 (accept) or 0 (reject).

    The function asserts e(hri,Οƒi)=?e(h,H(m))e(h^{r_{i}}, \sigma_{i}) \stackrel{?}{=} e(h, H(m))e(hri​,Οƒi​)=?e(h,H(m))

  5. Aggregate(S,(Οƒi,i)i∈S)β†’Οƒ/βŠ₯Aggregate(S, {(Οƒ_i, i)}_{i \in S}) β†’ Οƒ/βŠ₯Aggregate(S,(Οƒi​,i)i∈S​)β†’Οƒ/βŠ₯.

    The signature share combining algorithm takes as input the public key pkpkpk, a vector of public key shares (pk1,⋯ ,pkn)(pk_1, \cdots, pk_n)(pk1​,β‹―,pkn​), a message mmm, and a set SSS of t+1t + 1t+1 signature shares (Οƒi,i)(\sigma_i, i)(Οƒi​,i) (with corresponding indices). It outputs either a signature Οƒ\sigmaΟƒ or βŠ₯.

  6. Derive(m,vk,Οƒ)→ρ(m, vk, \sigma) β†’ \rho(m,vk,Οƒ)→ρ.

    The output derivation algorithm is a deterministic algorithm that takes as input a public key pkpkpk, a message mmm, and a signature σ\sigmaσ, and outputs the VUF output hohoho and the proof π\piπ.

  7. Verify(m,pk,ρ,Ο€),pk:=vki,i∈[n](m, pk, \rho, \pi), pk :={vk}_{i}, i \in [n](m,pk,ρ,Ο€),pk:=vki​,i∈[n].

    The signature verification algorithm is a deterministic algorithm that takes as input a public key pkpkpk, a message mmm, and a VUF output hohoho and proof Ο€\piΟ€.

    • verify Ο€=?(S,Οƒii∈S)\pi \stackrel{?}{=} (S, {\sigma_{i}}_{i \in S})Ο€=?(S,Οƒi​i∈S​)

    • run Derive of Step 6: Derive(S,{Οƒi,vki}i∈S,m)→ρ′Derive(S, \{\sigma_{i}, vk_{i}\}_{i \in S}, m) \to \rho'Derive(S,{Οƒi​,vki​}i∈S​,m)→ρ′

    • check if ho==ρ′ho == \rho'ho==ρ′, and outputs 1 (accept) or 0 (reject).


  • Unpredictable depends on below:

    • Aggregate(S,(Οƒi,i)i∈S)β†’ΟƒAggregate(S, {(Οƒ_i, i)}_{i \in S}) β†’ ΟƒAggregate(S,(Οƒi​,i)i∈S​)β†’Οƒ outcomes aggregated signature, which is the income of VUF output. The procedure takes place in every block's 1st transaction (Block Metadata transaction), which means not any User or Validator could predict randomness in User Transaction of the current block.

  • Provable depends on below:

    • Pr[Pr[Pr[ShareVerify(m,ShareSign(m,ski),pki)(m, ShareSign(m,sk_i), pk_i)(m,ShareSign(m,ski​),pki​) = 1] = 1$$

    • Pr[Pr[Pr[Verify(m, Aggregate({(Οƒi,i)}i∈S),pk)=1:Οƒi=ShareSign(m,ski)βˆ€i∈S]=1βˆ’negl(Ξ»)(\{(\sigma_{i}, i)\}_{i \in S}), pk) = 1 : \sigma_i = ShareSign(m,sk_i) βˆ€i \in S] = 1 βˆ’ negl(Ξ»)({(Οƒi​,i)}i∈S​),pk)=1:Οƒi​=ShareSign(m,ski​)βˆ€i∈S]=1βˆ’negl(Ξ»)

  • Tamper-proof depends on below:

    • Pr[ShareVerify(m,Οƒi,pki)=1]=1Pr[ShareVerify(m, Οƒ_i, pk_i) = 1] = 1Pr[ShareVerify(m,Οƒi​,pki​)=1]=1

    • Pr[Verify(m,pk,Οƒ)=1]=1Pr[Verify(m, pk, \sigma) = 1] = 1Pr[Verify(m,pk,Οƒ)=1]=1

  • Unbiased depends on below:

    • VUF output hohoho is unbiased if

      • derivation algorithm is unbiased

      • input(m,pk,Οƒ)(m, pk, \sigma)(m,pk,Οƒ) is unbiased, e.g., use hash56(m) as input in the derivation function.

PVSS Process

Endless PVSS procedure, described as below:

  1. Public Parameter Setup

    • define groups GGG and G^\hat{G}G^, generator ggg, hhh in group GGG and g^\hat gg^​ in group G^\hat{G}G^; nnn as numbers of validators, threshold ttt

    • denote pp (public parameter) = (g,h,g^,n,t)(g, h, \hat{g}, n, t)(g,h,g^​,n,t), the following functions will take pp as default input parameter.

  2. PVSS KeyGen(i)β†’(dki,eki)KeyGen(i) \to (dk_{i}, ek_{i})KeyGen(i)β†’(dki​,eki​)

    • the key generation protocol generates decryption key dkidk_{i}dki​, and the corresponding encryption key ekiek_{i}eki​

    • validator_{i}, indexed by i,i∈ni, i \in ni,i∈n privately keeps dkidk_{i}dki​, and publishes ekiek_{i}eki​

    • denote ek=[eki]i∈nek = [ek_i]_{i \in n}ek=[eki​]i∈n​

  3. PVSS Share(ek,s)β†’trxShare(ek, s) \to trxShare(ek,s)β†’trx

    • leader node takes sss (secret) as input and generates secret shares s1,s2,⋯ ,sns_1, s_2, \cdots, s_ns1​,s2​,β‹―,sn​

    • leader node encrypts sis_isi​ share with encryption key ekiek_{i}eki​, outputs encrypted cic_{i}ci​ and proof Ο€i\pi_{i}Ο€i​

    • leader node generates commitment comcomcom for secret shares {si},i∈n\{s_{i}\}, i \in n{si​},i∈n

    • leader node commits transaction trx, taking (com,{ci,Ο€i}com, \{c_{i}, \pi_{i}\}com,{ci​,Ο€i​}) as PVSS transcript

    • commit transaction actually is {trx1,trx2,⋯ ,trxi}\{trx_{1}, trx_{2}, \cdots, trx_{i}\}{trx1​,trx2​,β‹―,trxi​}, Step. 5 will aggregate these separate trxitrx_{i}trxi​ into one trx

  4. PVSS Verify(ek,trx)β†’true/false(ek, trx) \to true/false(ek,trx)β†’true/false

    • assert (com,ci)β†’true/false(com, c_{i}) \to true/false(com,ci​)β†’true/false

    • assert (ci,eki,Ο€i)β†’true/false(c_{i}, ek_{i}, \pi_{i}) \to true/false(ci​,eki​,Ο€i​)β†’true/false

    check if comcomcom is a commitment to valid shares of some secret sss and the cic_{i}ci​ values consist of encryptions of valid shares of the same secret

  5. PVSS Agg(trx1,trx2)β†’trx(trx_{1}, trx_{2}) \to trx(trx1​,trx2​)β†’trx

    • public function Aggregate that anyone can use to aggregate two PVSS transcripts into a single transcript.

    • repeat to call Agg() to aggregate all trxitrx_{i}trxi​ into one trx, to save nodes broadcast effort, also save space.

  6. PVSS DecShare(trx,i,dki)β†’si(trx, i, dk_{i}) \to s_{i}(trx,i,dki​)β†’si​

    • take as input aggregated PVSS transcript trx, index iii, i-th decryption key dkidk_{i}dki​, outputs cic_{i}ci​

  7. PVSS Recon(ek,trx,ci,{dki}i∈S)β†’s(ek, trx, c_{i}, \{dk_{i}\}_{i \in S}) \to s(ek,trx,ci​,{dki​}i∈S​)β†’s

    • each validator node decrypts cic_{i}ci​ with owned dkidk_{i}dki​ to get its share sis_{i}si​, and publishes sis_{i}si​ with corresponding NIZK proof ziz_{i}zi​

    • anyone could verify sis_{i}si​ with proof ziz_{i}zi​, and if shares collected combined weight greater than threshold ttt, the original secret sss could be recovered.


The Endless chain has built-in provision for trusted random number generation, which can better enhance the security of services based on this type of service. At the implementation level, Endless utilizes the Weighted Publicly Verifiable Secret Sharing (wPVSS) algorithm, which can be run efficiently by each verifying node and which is aggregatable to help reduce communication overhead. Meanwhile, there is a communication-efficient aggregatable weighted distributed key generation (wDKG) generation protocol on Endless. Finally, Endless has a novel Weighted Verifiable Random Function (wVRF), which the verifying node evaluates each round, with a constant amount of communication per verifying node, rather than a linear relationship with the amount pledged by the verifier.

API Usage

  • randomness::u8_integer() returns a u8 random number, evenly samples an 8-bit from Endless Randomness

  • randomness::u16_integer()

  • ...

  • randomness::u8_range(min_incl: u8, max_excl: u8) returns a u8 random number with specified minimal and maximum bound

  • ...

  • randomness::u256_range(min_incl: u256, max_excl: u256)

By repeatedly calling these functions, multiple objects can be safely sampled to generate multiple "unbiased" random numbers.

Demonstration

Endless Roll

Here we build a lottery system and pick a random winner from n participants. In the centralized world with a trusted server, the roll algorithm is the backend simply calls a random integer sampling function (random.randint(0, n-1) in Python, or Math.floor(Math.random() * n) in JS).

In Endless chain, we build a lottery move module:

module module_owner::lottery {
    // ...
 
    struct LotteryState {
        players: vector<address>,
        winner_idx: std::option::Option<u64>,
    }
 
    fun load_lottery_state_mut(): &mut Lottery {
        // authentication check 
        // return global borrow_mut LotteryState
        // ...
    }
 
    #[randomness]
    entry fun decide_winner() {
        let lottery_state = load_lottery_state_mut();
        let n = vector::length(&lottery_state.players);
        let winner_idx = endless_framework::randomness::u64_range(0, n);
        lottery_state.winner_idx = std::option::some(winner_idx);
    }
}
  • let winner_idx = endless_framework::randomness::u64_range(0, n); is the randomness API call that returns a u64 integer in range [0, n) uniformly at random.

  • #[randomness] enables the move compiler to check the outermost of the call stack of decide_winner is private.

Functions using randomness should be defined with private, to avoid Test and Abort attacks. decide_winner is an entry function, instead of public entry.

Security Considerations

Test and Abort Attack

If decide_winner() were accidentally marked public, malicious players can deploy their own contract to:

  1. call decide_winner();

  2. read the lottery result (assuming the lottery module has some getter functions for the result);

  3. abort if the result is not in their favor. By repeatedly calling their own contract until a txn succeeds, malicious users can bias the uniform distribution of the winner (dApp developer’s initial design).

Under Gas Attack

Users should also put prevention of Undergassed Attack into security considerations when writing business logic.

Imagine such a dApp. It defines a private entry function for a user to:

  1. toss a coin (gas cost: 9), then

  2. get a reward (gas cost: 10) if coin=1, or do some cleanup (gas cost: 100) otherwise.

A malicious user can control its account balance, so it covers at most 108 gas units (or set transaction parameter max_gas=108), and the cleanup branch (total gas cost: 110) will always abort with an out-of-gas error. The user then repeatedly calls the entry function until it gets the reward.

Here are some ideas of how to prevent undergassing attacks generally:

  • Make your entry function gas independent from the randomness outcome. The simplest example is to not β€œact” on the randomness outcome, i.e., read it and store it for later. Note that calling any other functions can have variable gas costs. For example, when calling randomness to decide which player should win, and then depositing the winnings to the winner might seem like a fixed gas cost. But, 0x1::coin::transfer / 0x1::fungible_asset::transfer can have a variable cost based on the user’s on-chain state.

  • If your dApp involves a trusted admin/admin group, only allow the trusted to execute randomness transactions (i.e., require an admin signer).

  • Make the path that is most beneficial have the highest gas (as the attacker can only abort paths with gas above a threshold he chooses). NOTE: that this can be tricky to get right, and the gas schedule can change, and is even harder to get right when there are more than 2 possible outcomes.

Random, but not a secret

Please keep in mind that random returned from calling the randomness API is randomness, but not a secret. The original seed in each block lies in 0x1::randomness::PerBlockRandomness which is public on the Endless chain.

Users should not try to do the following:

  • Use the randomness API to generate an asymmetric key pair, discard the private key, then think the public key is safe.

  • Use the randomness API to shuffle some opened cards, veil them, and think no one knows the permutation.

PreviousOn-Chain MultisigNextSafety Transaction

Last updated 5 months ago

Endless framework module provide a series of function randomness APIs:

randomness.move