l2iterative / bonsai-sdk-php
Bonsai SDK for PHP
Requires
- php-64bit: >=8.3
- guzzlehttp/guzzle: ^7.8
Requires (Dev)
- donatj/mock-webserver: ^2.7
- l2iterative/mock-webserver-ext: ^1.1
- phpunit/phpunit: ^8.5
- ramsey/uuid: ^4.7
- squizlabs/php_codesniffer: *
This package is not auto-updated.
Last update: 2024-09-29 20:40:37 UTC
README
当前的 Bonsai API 实现是用 Rust 编写的。由于 Bonsai 需要API密钥并且会消耗配额来生成零知识证明,因此使用 Bonsai 构建的web3应用程序需要后端。
与大多数web3应用程序一样,后端可以是极简的,因为在前端,大部分链上交互可以通过例如 Metamask的以太坊提供者API 完成。
后端将是什么样子?
出于上述原因,后端通常分为以下六类
- 纯
- 纯 Typescript 实现,可能使用 WebAssembly,通常通过 Rust 实现(见 wasm-bindgen)
- 纯 PHP 实现
- 纯 Python 实现,如 Django 和 Flask
- 使用 FFI(外部函数接口)
许多因素会影响选择哪一种
- 网络托管提供商的可用性。
- 有许多价格合理的 TypeScript 和 PHP 网络托管平台。
- 运行 Python,传统上使用完整的云虚拟机(如 AWS EC2 实例)。出于成本和自动扩展能力的原因,倾向于避免使用完整的虚拟机。
- 性能。
- 开发友好性。
- 可能最重要的是:理想情况下,学习周期少,不需要进行任何修改。
- Rust 在这里很有用,因为 RISC Zero 程序现在是用 Rust 编写的,并且 Rust 中的 WebAssembly 已被用于 web 开发,尤其是密码学。
- 另一种编程语言与 Rust 更相似。在这三者中,PHP 与 Rust 最相似。PHP 以非常友好地对初学者为特点。
- 所有这些都提供了 Web3 SDK 实现,但 TypeScript 现在的支持最好。
了解这一点很重要,即这个后端将采用极简主义风格。我们之所以有后端,是因为API密钥不能暴露给客户端。后端可能仅包括
- 对数据库的访问,例如 MySQL,用于链下数据存储
- 与垃圾邮件防止系统的集成,例如 Cloudflare Turnstile 或 Google reCAPTCHA
- 连接到RISC Zero Bonsai API,这也是这个仓库的目的
如何使用
文档可以在 docs 文件夹中找到。它设计得与这里的Bonsai Rust API相似
https://github.com/risc0/risc0/blob/main/bonsai/sdk/src/alpha.rs
集成测试可以作为教程。它可以像以下这样简单
<?php use L2Iterative\BonsaiSDK\Client; use L2Iterative\BonsaiSDK\SessionId; use L2Iterative\BonsaiSDK\SnarkId; $client = new Client("https://api.bonsai.xyz", $test_key, $test_version); $input_id = $client->upload_input($input_data); $session_id = new SessionId($client->create_session($test_img_id, $input_id, [])); $status = NULL; while(true) { usleep(300); $status = $session_id->status($client); if($status->status != 'RUNNING') { break; } } // error handling if the status is not successful $snark_id = new SnarkId($client->create_snark($session_id->uuid)); $status = NULL; while(true) { usleep(300); $status = $snark_id->status($client); if($status->status != 'RUNNING') { break; } } // error handling if the status is not successful // the proof can be forwarded to the frontend to make RPC calls
序列化器
使用不同编程语言与RISC Zero交互时遇到的另一个挑战是关于类型。这个问题对PHP尤其重要,因为PHP没有为不同的字长提供单独的类型。
例如,在PHP中,我们只有 int
表示整数,没有其他。但这可以对应于Rust中的i8、i16、i32、i64、u8、u16、u32、u64。还有PHP难以表示的数字,如i128和u128。
由于Rust类型是这里缺失的信息,开发者别无选择,只能显式提供Rust类型。为此,开发者需要包裹所有元素。
以下是一个非常全面的示例,可以作为教程。
public function test_serialization() { $example = new Struct([ 'u8v' => new SameTypeArray([ new U8(1), new U8(231), new U8(123) ]), 'u16v' => new SameTypeArray([ new U16(124), new U16(41374) ]), 'u32v' => new SameTypeArray([ new U32(14710471), new U32(3590275702), new U32(1), new U32(2) ]), 'u64v' => new SameTypeArray([ new U64(352905235952532), new U64(2147102974910410) ]), 'i8v' => new SameTypeArray([ new I8(-1), new I8(120), new I8(-22) ]), 'i16v' => new SameTypeArray([ new I16(-7932) ]), 'i32v' => new SameTypeArray([ new I32(-4327), new I32(35207277) ]), 'i64v' => new SameTypeArray([ new I64(-1), new I64(1) ]), 'u8s' => new U8(3), 'bs' => new Boolean(true), 'some_s' => new Some(new U16(5)), 'none_s' => new None(), 'strings' => new BinaryString("Here is a string."), 'stringv' => new SameTypeArray([ new BinaryString("string a"), new BinaryString("34720471290497230") ]) ]); $serializer = new Serializer(); $serializer->serialize($example); $php_output = $serializer->output(); $rust_output = [3, 1, 231, 123, 2, 124, 41374, 4, 14710471, 3590275702, 1, 2, 2, 658142100, 82167, 1578999754, 499911, 3, 4294967295, 120, 4294967274, 1, 4294959364, 2, 4294962969, 35207277, 2, 4294967295, 4294967295, 1, 0, 3, 1, 1, 5, 0, 17, 1701995848, 544434464, 1953701985, 1735289202, 46, 2, 8, 1769108595, 1629513582, 17, 842478643, 825701424, 875575602, 858928953, 48]; $this->assertEquals($php_output, $rust_output); }
相应的Rust结构定义如下。
#[derive(Default, Debug, Serialize)] pub struct SA { pub u8v: Vec<u8>, pub u16v: Vec<u16>, pub u32v: Vec<u32>, pub u64v: Vec<u64>, pub i8v: Vec<i8>, pub i16v: Vec<i16>, pub i32v: Vec<i32>, pub i64v: Vec<i64>, pub u8s: u8, pub bs: bool, pub some_s: Option<u16>, pub none_s: Option<u32>, pub strings: String, pub stringv: Vec<String> }
请注意,我们在Rust中关注的是String而不是二进制字符串,因为当前的序列化器在处理 Vec<u8>
时会有不同的麻烦。如果某个Rust代码需要使用 Vec<u8>
并且不能忍受包装器,那么迄今为止最好的解决方案是让PHP通过 str_split($str)
将字符串分解成一个字节数组。