Runtime::generate_uuid() 在 Scrypto 中安全吗?

Is Runtime::generate_uuid() safe in Scrypto?

我想在 Scrypto 蓝图中制作一款游戏,用户可以在其中玩他们的 Gumball NFT。

我的蓝图有一个 pub fn attack(&self, my_gumball: Proof, other_gumball_key: NonFungibleId) 方法,通过给它分配 1 到 10 之间的随机伤害来攻击另一个 NFT。我应该为此使用 Runtime::generate_uuid() 吗?

好问题!我将在这里给你一个来自 Radix 的小例子,因为随机数的生成是所有 blockchains/DLTs/public 网络都在努力解决的问题,这是一个真正难以解决的问题。

首先,我假设你使用 UUIDs 作为你的 dApp 的随机数,所以整个回复都是基于此。在引擎盖下,当您调用 Uuid::generate 函数时,在发生的一长串调用的末尾,以下函数是处理 UUID 生成的函数:https://github.com/radixdlt/radixdlt-scrypto/blob/24168ae772215af5169549a7a2cc1adeb666baa6/radix-engine/src/engine/id_allocator.rs#L78

如果您查看此函数,您会发现此函数使用交易哈希 + 下一个可用 ID 为您生成 UUID。下一个可用的 ID 没有什么特别的,它只是一个计数器,每次我们需要生成一个新的 ID 时它都会递增。此方法所做的只是在将 tx_hash + next_id 加载到 u128 之前对 tx_hash + next_id 进行两次哈希处理,这几乎就是 UUID 的生成方式。这意味着 UUID 是一个伪随机数,如果有人知道交易哈希是什么,那么他们将能够确定您将使用的“随机”数。

让我们暂且抛开所有的理论,用代码尝试一些事情。这是一些简单的 Scrypto 代码,向您展示 not-random UUID 的真实情况:

use radix_engine::engine::{IdAllocator, IdSpace};
use scrypto::prelude::*;

#[test]
fn test_randomness() {
     // Creating the ID allocator
    let mut id_allocator: IdAllocator = IdAllocator::new(IdSpace::Application);

    // A fictional transaction hash
    let tx_hash: H256 = H256::from_str("0e4c5812f00b3c821335c54b3bbc835a157df1149480f6469a4dc6b51489e989").unwrap();

    // Generating four UUIDs
    println!("Generated UUID: {:?}", id_allocator.new_uuid(tx_hash).unwrap());
    println!("Generated UUID: {:?}", id_allocator.new_uuid(tx_hash).unwrap());
    println!("Generated UUID: {:?}", id_allocator.new_uuid(tx_hash).unwrap());
    println!("Generated UUID: {:?}", id_allocator.new_uuid(tx_hash).unwrap());
}

正如我们所说,IdAllocator.new_uuid 方法需要一个到 运行 的交易哈希,所以我为它提供了一个样本交易哈希。如果我们 运行 这段代码并查看输出,你和我都会在命令行终端中看到以下内容:

Generated UUID: 333873524275763974188597434119212610710
Generated UUID: 315396769568132504258157739854036837613
Generated UUID: 31497316649309892037047888539219683042
Generated UUID: 300332381675622117598720587595812830316

您可能会问,为什么我们都得到相同的输出,这不是随机的吗?

我们双方将获得的输出与 UUID 的随机性完全相同,依赖于交易哈希的变化和 next_id。所以很容易看出这不是随机函数而是伪随机函数。

所以回答你的问题:

Someone would be able to guess the number 5-10 before the scrypto code can even generate it?

是的!内存池中的交易具有可见的哈希值,因此存在哈希部分。再加上了解你的蓝图和交易期间分配的 ID 数量的人将 100% 的时间能够在 scrypto 代码之前猜出随机数,上面的代码是如何做到这一点的一个例子。

结论:Uuid::generate 可以很好地生成 UUID,但不能很好地生成随机数,因为它不是真正的随机数函数。