Chain Message:利用区块链打造一个的无法被篡改的留言页面
前言
我对学习一项新技术的最低标准是:“入门”。在我看来,达到所谓的入门,需要两方面的能力
- 对该领域的的核心概念有一定的理解,至少说听业内人士提到某个概念,能大概明白是什么;
- 进行实践,利用自己掌握到的知识,能够自己动手完成一个像样的作品。
最近我对区块链技术比较感兴趣,学习了核心概念后,想着用它来做点什么。我在寻思用什么项目来检验我的入门水平的时候,突然想到了留言板这么一个idea。
传统的留言板大多都是基于中心化的服务来实现的,这意味着,控制这个中心化的人可以将其他用户的留言进行删除或者是篡改。但是如果说我们能够利用区块链几乎不可能被篡改的能力,将用户的留言发送到链上,并且将智能合约的代码公开,那么这将是一个完美的闭环,不管是留言的留言者,还是说这个合约的创建者,都无法篡改这些内容。然后再加上一个前端页面,当用户访问的时候获取链上的留言,这样大家就都能看得到了。哪怕将前端网页下线,在链上根据开源的合约代码和公开合约的ABI信息,也能够查看到留言信息。
说干就干!
项目技术栈
- Solidity - 以太坊智能合约的编程语言
- React - 前端框架
- Tailwind CSS - 样式框架
- Wagmi - Web3开发库
- Cloudflare Workers - 后端代理服务
项目目录树(使用我写的开源CLI:Treex生成):
1 | 📁 ./ |
正文
Github 开源地址:https://github.com/shiquda/chain-message
在线预览:https://msg.shiquda.link/
部署教程: https://github.com/shiquda/chain-message/blob/main/DEPLOY_GUIDE.md
如果你想要充分理解这篇文章内容,最好需要有一定的区块链知识。没有也没关系,相信以读者聪明的脑袋瓜,可以随时利用大模型轻松理解。如果你对此感兴趣,想要动手实践的话,你还需要一个钱包插件。(我使用的是OKX的Web3钱包)
智能合约
首先我们需要编写智能合约代码。智能合约可以简单理解为在区块链上运行的一串代码,而整个区块链可以看作是一个状态机,智能合约的交互使得状态机从一个状态变化到另一个状态。智能合约支持两类调用,一种是Read类型的,调用此类合约不需要花费Gas手续费;另一种是Write类型,由于涉及到写操作,这类开销是比较大的,需要花费Gas。
我编写的这个智能合约代码只允许两个接口,一个是 postMessage
,这是一个Write Contract,让用户传入他想要提交的留言。其中名字是可选的,如果不填就会视作是匿名留言。此外应该要支持markdown格式,这一点在前端处理即可。
1 | function postMessage(string memory _name, string memory _content) public { |
在留言发送后,智能合约会自动保存前面的这几个参数之外,还会保存发送时的时间戳等信息。
1 | event MessagePosted( |
还有一个接口,用于查询消息的数量。这个接口是只读的,进行合约调用的话,相当于只是在区块链上进行状态的查询,并不会消耗gas。这个接口其实项目暂时没有用到。
1 | function getMessageCount() public view returns (uint256) { |
除此之外,就没有预留其他任何接口了,这意味着只要留言上链,哪怕是我也无法删除。
接下来讲讲我是如何部署智能合约。我使用的是以太坊基金会推荐的,对于新手比较友好的一个在线IDE:Remix。
我们将本地写好合约的贴到这个remix上,然后然后选择编译。编译之后,在左侧选择部署选项卡。
选择Walletconnect,并且根据提示连接你的钱包插件。连接之后,点击部署,在钱包插件确认之后就能部署上链了。
部署合约是需要消耗比较高的Gas费的,所以说你可能需要从中心化的交易所中提取一些一些到你的钱包。我上次部署的时候大概是花了0.37刀。部署完成之后你还可以到Etherscan上添加你的合约源码,这样大家就可以公开审查这个合约代码,确保这个系统的不可篡改性。
这里有一点要注意的是,我使用的是智能合约的event log来进行留言的记录。之所以这么做而不选择使用storage直接存储到区块链上的原因,是因为那样会消耗更多的Gas费。使用Event log记录的方式就可以减少这个消耗,让用户提交留言的时候少花一些Gas费。在我测试的时候,提交一次留言需要使用0.05美元,大概折合人民币3毛多。
当然,其实测试的时候先上测试网进行测试是一个比较稳妥的方式,但是我这边太心急了,想马上能够把第一个属于自己的智能合约部署到主网上,大家最好不要学我这么做🤣
前端实现
前端实现,我使用的是React框架,Tailwind CSS对样式进行简单的美化。这边使用的对接web3的库是wagmi,感谢前人Web3开发者,已经帮我们普通开发者做了很多事情了。这个库可以自动处理和用户的钱包插件交互。
具体实现就不多赘述了,感兴趣的读者可以查看源码。
在线预览:https://msg.shiquda.link/
Cloudflare Workers
前端的内容比较简单,但这时出现了一个新的问题:怎么样让用户能够获取区块链上的留言信息呢?
我首先尝试一种方案,利用公开的以太坊节点来进行日志的获取。但是我尝试几个后发现大多数都不支持无鉴权的调用。对于获取Event log信息,我找到一个免费的, https://ethereum.publicnode.com ,但也仅支持查询最多5万个区块的信息,这与我的预期显然是不符合的。因此我干脆使用需要鉴权的Etherscan的API。
但这里又有一个问题,如果说密钥直接存储到前端的话,泄露之后,不就会被他人滥用了?
于是我想到了Cloudflare Workers做一个简单的封装。当用户发起请求到Cloudflare Workers的时候,worker通过在CF配置的环境变量的APIkey,然后再将请求发往EtherScan,做一层转发。再根据传回的内容返回给用户,这样前端页面就可以进行实时的消息获取了。
但这样,还是有可能被人利用接口进行event log的查询。我们再设置一个环境变量,让接口只能用于我们部署的合约的查询,这样就大大降低了滥用者的收益。
这边还有一个坑点,就是说CF的Workers域名是被GFW封锁的。想要解决这个问题,可以使用自己的域名或者子域名做一个转发,这样就能绕过限制了,让用户直连也能够请求到所需的留言信息。
要进行Workers的配置,你需要在Cloudflare的左侧找到Workers 和Pages,然后创建 > Workers > Hello world > 填写名称并部署 > 进入修改源码
,粘贴下面的代码。
1 | // worker.js |
然后找到Worker的设置 > 变量和机密 > 变量和机密 > 添加
,添加CONTRACT_ADDRESS
和ETHERSCAN_API_KEY
这两个环境变量。
如果你需要绑定自己的域名,你需要在CF进入域名的管理页面,然后找到左侧的“Workers 路由”,选择添加路由并绑定前面创建的Worker。
静态网页生成
一切就绪后,我们完全可以使用生成一个静态页面,然后把这个静态页面上传到我们的博客或者是云托管服务上面,这样就能够实现独立的运行了。
推荐使用Cloudflare Pages的方式进行部署。使用这种方式的教程请见Github 上的部署教程。
克隆项目,安装依赖并配置
1 | git clone https://github.com/shiquda/chain-message.git |
然后参考.env.example
填写需要的环境变量。一切就绪后,使用下面的命令进行构建:
1 | pnpm build |
dist/
中的文件就是静态网页了,你可以把它放到托管服务上。
结语
说完这么多,真的是感慨万千。假如说一百年后,当我离开这个世界之后,这些留言可能还能存在区块链上,甚至别人还能够访问到、看到我自己的,或者是别人网友给我的留言,真的是一件非常酷的事情。
留言地址:https://msg.shiquda.link/
不管怎么样,先给10年后的自己留个言。(多打了一个字,已经无法挽回了😄)
实现这个idea的所有代码,包括智能合约代码,前端代码,还有cloudflare workers代码,我都放到github仓库上,要是你也对这个感兴趣,可以自己部署一个玩玩看。
我前几天申请了一个以太坊的地址域名:shiquda.eth。使用这个ENS域名,就可以找到我的钱包地址,也就是部署这个合约的地址。如果文章对您有所启发,可以给我这个地址打钱😄(真是太不要脸了)