尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
基于智能合约的去中心化抽奖:随机数与加密算法实践
简介这是一份面向计算机相关专业毕业生的区块链方向完整毕业设计项目主题为基于区块链的去中心化抽奖平台。项目源码已经过本地编译并正常运行评审得分在95分以上难度适中兼顾智能合约编写、去中心化应用交互与区块链底层原理学习。压缩包共2000个文件、约4.64MB以1700个JavaScript文件和120个Markdown文档为主另有94个C头文件、19个C源文件及JSON、Java、Shell、CSS等配置与辅助资源覆盖从底层密码学算法到上层应用展示的完整链路。已有96人学习适合作为毕业设计参考、区块链技术课程设计或入门实践的完整素材。源码、文档、测试与配置齐全目录结构清晰便于按模块阅读、修改和二次开发。1. 去中心化抽奖的第一性问题随机数不能被信任中心化抽奖平台的运营者手里握着两把作弊钥匙随机数生成器是黑盒用户余额存在自建数据库。想让自己人中奖只是一个 if 语句的事。基于区块链的去中心化抽奖平台要拆掉这两把钥匙思路是把规则写进智能合约随机数在链上生成奖池由合约托管任何人都能重放整个流程验证公平性。解包这套毕业设计源码时根目录下同时出现 secp256k1.c、KeccakP-1600-inplace32BI.c、org_bitcoin_NativeSecp256k1.c说明它不是套个 Web 壳子而是把椭圆曲线签名和 Keccak 哈希真正落到了 Java 后端与合约层。对准备答辩或想跑通一个完整 DApp 工程的人来说这套代码展示了抽奖、加密、链上交互三部分是怎么拼接的。2. 随机数生成方案选型与 Commit-Reveal 合约实现2.1 链上随机数的难点不在随机而在可验证很多区块链抽奖 demo 直接用block.timestamp、block.difficulty或blockhash当随机种子这类写法在演示环境跑得通但拿上台面就是作弊口子。block.timestamp由矿工或验证者写入出块时间允许小幅偏移blockhash虽然看起来不可预测但它对矿工来说是已知的矿工可以尝试构造多个候选块挑一个对自己最有利的结果广播。抽奖是单次开奖、单次定输赢的场景这种可预测性已经足够威胁公平性。要解决的不是“能不能随机”而是“随机结果出来后参与者能不能验证这个结果没有被任何一方操纵”。去中心化抽奖对随机数的要求有三个层次不可预测、不可操纵、可验证。前两者靠密码学第三个要靠合约把整个生成过程公开在链上。2.2 常用方案对比与选型理由随机数方案信任假设链上成本抗偏置能力适用场景block.timestamp / blockhash信任验证者不操纵最低弱演示项目RANDAO信任最后一个参与者诚实中中早期 PoS 项目Commit-Reveal 链上混合信任多数参与者及时揭示中中中小规模抽奖、教学项目Chainlink VRF信任预言机节点高强生产级抽奖这套源码包里既然带了完整的 ECDSA 和 Keccak 实现设计者选择 Commit-Reveal 是合理的不需要引入预言机服务不需要为随机性单独付 gas随机种子由所有参与者的秘密共同贡献。代价是每个参与者必须在揭示窗口内行动有人不揭示整局就会卡住所以合约里必须有时间锁和惩罚设计。2.3 合约中的提交与揭示实现Commit-Reveal 的核心流程分两段。参与者在提交阶段发送承诺值commitment keccak256(secret, nonce)合约只存哈希等所有人提交完毕参与者再公开secret和nonce合约重新计算哈希并与承诺比对。这样任何人在提交阶段都看不到其他人的秘密也就没法针对性地改变自己的选择。// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; contract Lottery { enum Phase { Open, Commit, Reveal, Closed } Phase public phase; mapping(address bytes32) public commitments; address[] public players; uint256 public seedAccumulator; modifier onlyPhase(Phase p) { require(phase p, wrong phase); _; } function commit(bytes32 h) external onlyPhase(Phase.Commit) { require(h ! bytes32(0), empty commitment); if (commitments[msg.sender] bytes32(0)) { players.push(msg.sender); } commitments[msg.sender] h; } function reveal(string calldata secret, uint256 nonce) external onlyPhase(Phase.Reveal) { bytes32 h keccak256(abi.encodePacked(secret, nonce)); require(h commitments[msg.sender], reveal mismatch); seedAccumulator ^ uint256(h); } function finalize() external onlyPhase(Phase.Closed) { require(players.length 0, no players); uint256 winnerIndex seedAccumulator % players.length; address winner players[winnerIndex]; // 教学演示把奖池余额转给 winner (bool sent, ) winner.call{value: address(this).balance}(); require(sent, transfer failed); } }代码里commit用bytes32存承诺reveal时把secret和nonce一起编码后取keccak256。重点在seedAccumulator ^ uint256(h)用异或混合所有揭示值只要有一个参与者的秘密是不可预测的最终种子对其他人就不可预测。nonce的作用是防止字典攻击secret建议用openssl rand -hex 32这类方式生成长度至少 32 字节否则短字符串很容易被暴力枚举出来。2.4 时间锁与防卡死设计Commit-Reveal 的一个现实问题是“有人提交了但就是不揭示”整局游戏会卡在 Reveal 阶段。常见做法是给每个阶段设置 deadline超过时间后容许调用finalize按已揭示的参与者开奖或者没收未揭示者的押金分给诚实参与者。源码包里的实现也遵循了这个模式合约记录commitDeadline和revealDeadline每个函数入口用block.timestamp与 deadline 比较超时即拒绝操作。提示Commit-Reveal 只能让偏置成本变大不能绝对杜绝偏置。生产环境建议直接接 Chainlink VRF教学项目里保留 Commit-Reveal 是为了把密码学过程透明地展示在链上。3. secp256k1 签名与 Keccak 哈希底层加密库的编译与调用3.1 这两个库在抽奖平台里承担什么源码包里的secp256k1.c、org_bitcoin_NativeSecp256k1.c是 libsecp256k1 的 C 实现和 JNI 桥接层KeccakP-1600-inplace32BI.c、KeccakP-1600-opt64.c是 Keccak 置换内核的两种平台优化实现。它们不是无关紧要的第三方代码而是平台身份验证和哈希承诺的基石。在 EVM 生态里交易签名本身就走 secp256k1 曲线合约里ecrecover能从签名中恢复出签名者地址。链下如果要生成钱包、离线签名、或者校验某个地址是否真的提交过某个承诺就必须使用同一套加密原语否则签名格式对不上。而 Keccak-256 承担的是承诺计算和地址派生以太坊地址就是keccak256(公钥)[12..32]这 20 个字节。只要项目还在用 Solidity 合约链下计算就必须用与链上一致的 Keccak而不是标准 SHA-256。3.2 编译 native 库与模块参数这套源码的 Java 后端通过 JNI 调用 C 层本地编译时推荐按下面的参数配置cd src/main/native/libsecp256k1 ./autogen.sh ./configure --enable-jni --enable-module-recovery --enable-experimental make -j4--enable-jni会生成org_bitcoin_NativeSecp256k1对应的 JNI 接口没有这个参数Java 侧只能拿到 C API无法直接调用。--enable-module-recovery打开公钥恢复模块这是交易签名和ecrecover的基础关闭后签名恢复函数不可用。--enable-experimental用于放宽编译告警遇到个别宏未定义时可以打开。编译完成后把生成的.so或.dylib放到java.library.path指向的目录或者直接用System.load(/绝对路径/libsecp256k1.so)加载。3.3 Java 侧的签名与验签import org.bitcoin.NativeSecp256k1; import org.bouncycastle.util.encoders.Hex; NativeSecp256k1.init(); byte[] privateKey Hex.decode(你的私钥hex字符串); byte[] messageHash keccak256(sealedData); // 32 字节 byte[] signature NativeSecp256k1.secp256k1_ecdsa_sign(messageHash, privateKey); boolean ok NativeSecp256k1.secp256k1_ecdsa_verify(messageHash, signature, publicKey);secp256k1_ecdsa_sign接收 32 字节的消息哈希和 32 字节的私钥返回的签名是 64 字节的r || s。这里有个容易踩的坑链上ecrecover需要 65 字节签名前 64 字节是r和s最后 1 字节是 recovery id。如果 Java 侧只拿到 64 字节必须额外做一次签名恢复来补出 recovery id或者调用secp256k1_ecdsa_sign_recoverable这一个支持可恢复签名的接口。验签时公钥可以是压缩格式33 字节或未压缩格式65 字节地址派生必须用未压缩格式再取哈希否则算出的地址和前端钱包地址对不上。3.4 Keccak-256 与 SHA3-256 的差异很多人在这个点上吃过亏Java 的MessageDigest.getInstance(SHA3-256)算出来和 Solidity 的keccak256对不上。原因是 Keccak 原始算法和最后被 NIST 标准化的 SHA3 使用了不同的填充字节前者是0x01后者是0x06输出完全不同。算法填充规则链上对应函数Keccak-2560x01Soliditykeccak256、以太坊地址SHA3-2560x06NIST 标准 SHA3SHA-256另起填充Bitcoin 相关计算Java 侧要和 Solidity 结果对齐得用 BouncyCastle 的Keccak.Digest256import org.bouncycastle.jcajce.provider.digest.Keccak; byte[] data abc.getBytes(StandardCharsets.UTF_8); byte[] hash new Keccak.Digest256().digest(data);后端在做承诺校验、地址派生、事件摘要时都要检查本地用的是不是 Keccak-256。源码包里同时出现KeccakP-1600-inplace32BI.c和KeccakP-1600-opt64.c分别针对 32 位和 64 位平台优化编译时按目标平台选择其一即可不要在 Java 侧混用两种实现。4. 抽奖平台从部署到联调状态机与完整流程4.1 本地链环境与 Hardhat 配置联调阶段我建议用 Ganache 做本地链、Hardhat 做编译部署工具。Ganache 即时出块且支持evm_increaseTime手动推进时间正好用来测 deadline 相关的分支。Hardhat 配置文件里只需要一个网络指向本地节点module.exports { solidity: 0.8.17, networks: { ganache: { url: http://127.0.0.1:7545, accounts: [process.env.PRIVATE_KEY], }, }, };PRIVATE_KEY用环境变量注入别写死在配置文件里。Ganache 启动时默认会创建 10 个带测试 ETH 的账户参与抽奖时可以用不同账户模拟多个用户。这样做的好处是每一笔交易都能在区块浏览器里对应到独立地址评审时更容易解释“谁提交了承诺、谁揭示了结果”。4.2 抽奖合约状态机与部署脚本整套抽奖逻辑是一个有限状态机Open 等待启动Commit 收集承诺Reveal 校验并混合种子Closed 开奖转账。每个状态之间的转换必须由明确的函数调用触发并且受 deadline 限制。阶段入口函数前置条件结束后进入Openstart(config)合约刚部署CommitCommitcommit(h)启动后、TTL 未到RevealRevealreveal(secret, nonce)已存在有效承诺ClosedClosedfinalize()奖池有余量奖池转移流程结束部署脚本用 Hardhat 自带的 ethers 接口// scripts/deploy.js const hre require(hardhat); async function main() { const Lottery await hre.ethers.getContractFactory(Lottery); const lottery await Lottery.deploy(); await lottery.deployed(); console.log(Lottery address:, lottery.address); } main().then(() process.exit(0));部署后把合约地址记录下来前端 Web3 连接和事件重放脚本都要用。不要直接用 Remix 部署线上节点再手动复制地址自动化脚本能保证每次联调用的都是最新编译产物避免“改了一行代码但链上跑的还是旧合约”这种低级问题。4.3 端到端联调与常见坑完整流程可以拆成四条命令# 1. 启动本地链 npx ganache -p 7545 --chain.chainId 1337 # 2. 部署合约 npx hardhat run scripts/deploy.js --network ganache # 3. 参与抽奖提交承诺、揭示、开奖 node scripts/play.js --action commit --contract 0x... node scripts/play.js --action reveal --contract 0x... # 4. 验证 winner 地址 node scripts/play.js --action winner --contract 0x...联调中常见的问题有这么几个。第一是 nonce 冲突用同一个账户连续发起多笔交易时客户端如果没有自动管理 nonce会出现交易被替换或卡在 pending解决办法是在交易参数里显式设置nonce: provider.getTransactionCount(address)。第二是 deadline 判断合约里如果用了绝对时间戳而本地链的时间没有同步会导致合约一启动就进入 Closed 状态排查时先看commitDeadline - block.timestamp的值。第三是reveal时传入的secret如果是中文编码后字节流和提交时不一致建议统一用十六进制字符串。这几个问题在源码包的排错文档里都有对应说明评审时也是高频提问点。5. 验证抽奖公平性链上事件重放与结果自校验脚本5.1 事件日志是链上唯一可信的真相finalize()执行之后链上只会留下一个 winner 地址但这不足以证明开奖过程诚实。要证明“winner 不是合约 owner 暗箱指定”必须回到事件日志里把每个参与者的Committed和Revealed重新拉出来校验一遍。事件日志由交易收据持久化任何节点都无法篡改这是合约层给出的最直接的验证凭证。5.2 事件重放与哈希一致性校验脚本下面这个脚本会拉取合约的全部Committed和Revealed事件对每个揭示数据重新计算 Keccak-256与提交事件里的承诺值比对// verify.js const { ethers } require(ethers); async function verify(contractAddress, abi, rpcUrl) { const provider new ethers.providers.JsonRpcProvider(rpcUrl); const lottery new ethers.Contract(contractAddress, abi, provider); const [commits, reveals] await Promise.all([ lottery.queryFilter(Committed), lottery.queryFilter(Revealed), ]); for (const e of reveals) { const { participant, secret, nonce } e.args; const expected ethers.utils.keccak256( ethers.utils.defaultAbiCoder.encode( [string, uint256], [secret, nonce] ) ); const chainCommit commits.find( (c) c.args.who participant )?.args.commitment; if (expected ! chainCommit) { throw new Error(address ${participant} 的揭示与承诺不一致); } } console.log(校验通过共 ${reveals.length} 个有效揭示); } verify(process.argv[2], ABI, process.argv[3]).catch((e) console.error(e));abi从artifacts/contracts/Lottery.sol/Lottery.json里读取rpcUrl指向本地节点或已部署链的 RPC 地址。脚本返回值非零说明有参与者揭示造假此时整个开奖结果不可信需要回滚或重新开奖。生产环境里可以把这个脚本接进 CI每次合约地址更新后自动跑一遍校验归档验证报告。5.3 把校验脚本做成链下监控任务更进一步的用法是把校验从“手动执行”变成“定时巡检”。开奖结束后的一个小时内是最容易出现质疑的时间窗口挂一个 cron 任务每小时检查一次承诺与揭示的一致性异常时写日志告警0 * * * * node /opt/lottery-ops/verify.js 0x合约地址 http://127.0.0.1:8545 /var/log/lottery-verify.log 21脚本返回非零码时告警系统会收到通知运营人员再结合区块浏览器确认数据是否被构造或覆盖。这套机制把“去中心化”从口号变成可操作的技术流程合约负责执行链下脚本负责监督互相制衡。本文还有配套的精品资源点击获取
RELATED

相关推荐

浏览器扫码实战:getUserMedia + jsQR从零实现自定义扫一扫

浏览器扫码实战:getUserMedia + jsQR从零实现自定义扫一扫

简介:基于HTML5与JavaScript的浏览器端二维码扫描实现方案,面向前端开发人员、移动端H5项目团队以及需要快速集成扫码能力的内部系统,解决网页直接调用摄像头、自定义扫码界面并识别二维码的常见需求。压缩包仅有45KB大小,共3个文…

📅 2026/9/15 2:19:02
基于MLP的虚假新闻检测:从TF-IDF特征到Keras模型实现

基于MLP的虚假新闻检测:从TF-IDF特征到Keras模型实现

简介:基于Python多层感知器(MLP)打造的虚假新闻检测器,是一份完整的课程设计资源。项目面向机器学习与自然语言处理入门者,系统演示了从新闻文本清洗、分词、TF-IDF向量化,到MLP模型构建、训练、评估与调优…

📅 2026/9/15 2:19:02
OpenMetadata AutoPilot 应用配置指南:`AutoPilotAppConfig` 字段说明与底层实现

OpenMetadata AutoPilot 应用配置指南:`AutoPilotAppConfig` 字段说明与底层实现

OpenMetadata AutoPilot 应用配置指南:AutoPilotAppConfig 字段说明与底层实现 【免费下载链接】OpenMetadata The Open Context Layer for Data and AI , OpenMetadata is the open platform for building trusted data context and business semantics for humans…

📅 2026/9/15 2:19:02
MORE NEWS

更多资讯

📰

CANoe与CAPL:汽车电子HiL测试的核心能力双引擎

1. 为什么汽车测试岗JD里总写着“熟悉CANoe/CAPL”——这不是凑数,而是岗位能力的硬分水岭你刷过多少次汽车电子测试工程师的招聘启事?几乎每一条都带着这么一句:“熟练使用CANoe及CAPL脚本开发”。不是“了解”,不是“接触过”&a…

📰

全端小程序商城解决方案:跨平台开发与多商户系统实践

1. 项目概述:全端小程序商城解决方案这套源码最吸引人的地方在于"全端覆盖"和"多商户入驻"两大核心能力。作为从业十年的老码农,我见过太多团队为了适配不同小程序平台而疲于奔命——微信一套代码、抖音另起炉灶、支付宝再搞特殊处理…

📰

外泌体体内追踪怎么做?放射性同位素标记全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

基于Hadoop+Spark+Hive的Steam游戏推荐系统实战

1. 为什么我坚持用 HadoopSparkHive 这套组合做 Steam 游戏推荐先说结论:如果你只是想交一份"看起来不错"的课设,Python 读取 CSV 再用 sklearn 算个相似度,一天就能交差。但如果你想搞懂一个真实的推荐系统在离线链路里到底是怎么…

📰

基于 TASKS.md 的轻量任务管理:knowledge-work-plugins 任务管理技能实战指南

基于 TASKS.md 的轻量任务管理:knowledge-work-plugins 任务管理技能实战指南 【免费下载链接】knowledge-work-plugins Open source repository of plugins primarily intended for knowledge workers to use in Claude Cowork 项目地址: https://gitcode.com/Gi…

📰

小智桌面整理工具使用指南:从下载安装到高效配置全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬