要么改变世界,要么适应世界

链博——DAPP练手项目(一)

2023-01-02 10:56:13
29
目录

背景

我们都知道目前主流的网站都是属于web2.0的应用,例如新浪微博,明明我们才是内容的输出者,可是最终收益的大头却是这些公司,而且我们的文章放在他们的服务器,他们想怎么着就怎么着;而区块链技术的出现,让去中心化思想渐入人心,我们的数据,将由我们自己掌控!

链上博客,让我们的文章写入区块链,人人都是自己数据的掌控者!

主要功能:

  1. 查看所有人写的文章
  2. 编写和修改自己的文章

这里我们使用智能合约来实现,借助trufflevue,实现我们的DAPP的开发.

实现效果

本项目重点在于怎么使用JavaScript,具体而言是使用web3.js和智能合约交互,对于系统的性能优化尚未考虑

image-20230102094246205.png


image-20230102094343131


image-20230102094601906

合约实现

与现有流行的B/C模式相类似,我们需要一个类似的数据提供方,只不过web2.0时代下,该数据提供方是由一个中心服务器提供,在web3.0下,该数据由区块链提供,智能合约就有点类似数据提供方。

我们这里使用Solidity实现我们的合约.

Node.js 安装

【链接】

Ganache 安装

可以下载命令行版本的,也可以下载桌面版本的,我这里使用的是桌面版本的

【链接】

truffle 安装

使用包管理工具安装truffle,它是针对基于以太坊的Solidity语言的一套开发框架:

$ npm install -g truffle
$ truffle --version
Truffle v5.7.1 (core: 5.7.1)
Ganache v7.6.0
Solidity v0.5.16 (solc-js)
Node v18.12.1
Web3.js v1.8.1

如果安装过程遇到网络问题,可以尝试换源或者使用cnpm

然后使用truffle来初始化项目:

$ mkdir chainBlogContracts
$ cd chainBlogContracts
$ truffle init

我们看一下项目结构:

$ tree
D:.
│  truffle-config.js
│
├─contracts
├─migrations
└─test

contracts存放我们的合约文件,migrations文件夹下存放部署脚本,test文件夹下存放测试脚本。

truffle-config.js是配置文件,目前我们暂时修改的是networks对象:

networks: {
    development: {
     host: "127.0.0.1",     // Localhost (default: none)
     port: 7545,            // Standard Ethereum port (default: none)
     network_id: "*",       // Any network (default: none)
    }
}

其中,端口和Ganache中设置的一致.

编写合约

详细实现请参考【链接】

在contracts文件夹下,新建一个ChainBlog.sol文件,在这里完成我们的合约逻辑。

我们可以将所有的文章存放到一个数组中,此外,我们使用一个结构体来表示一篇文章:

    struct Blog {
        uint256 id;
        address author;
        string title;
        string summary;
        string content;
        uint256 createTimestamp;
        uint256 lastUpdateTimestamp;
    }
    // 记录每个人编写的文章
    Blog[] public blogList;
    
    event AddBlogEvent(uint256 id, address author, string title, string summary, string content, uint256 createTimestamp);
    event ModifyBlogEvent(uint256 id, address author, string old_title, string old_summary, string old_content, string new_title, string new_summary, string new_content, uint256 modifyTimestamp);
    event DeleteBlogEvent(uint256 id, address author, string title, string summary, string content, uint256 deleteTimestamp);

blogList设置成public可以方便我们从外部直接访问该数组。我们还定义了几个事件,这几个事件后文会用到;触发事件,可以理解成我们往日志系统中写入一个日志,能合约的前端UI,例如,Appsweb.js,或者任何与Ethereum JSON-RPC API连接的东西,都可以侦听这些事件。

编写文章

我这里设置博客的ID对应其在数组中的下标,因此如果想要获取某一篇文章,可以直接根据ID转换成索引。

	function addBlog( string memory _title, string memory _summary, string memory _content ) public {
        uint256 currentId = blogList.length;
        blogList.push(
            Blog(currentId, msg.sender, _title, _summary, _content, block.timestamp, block.timestamp)
        );
        emit AddBlogEvent(currentId, msg.sender, _title, _summary, _content, block.timestamp);
    }

修改文章

修改文章时候,只有文章的所有者才能有权修改,且要判断传进来的id是否有效:

	function modifyBlogByUserAndId(
        uint256 id,
        string memory _title,
        string memory _summary,
        string memory _content
    ) public {
        require(id >= 0 && id < blogList.length, "Id invalid!");
        require(msg.sender == blogList[id].author, "You can only modify your blog!");
        uint256 idx = id;
        string memory old_title = blogList[idx].title;
        string memory old_summary = blogList[idx].summary;
        string memory old_content = blogList[idx].content;
        blogList[id] = Blog(id, msg.sender, _title, _summary, _content, blogList[id].createTimestamp, block.timestamp);
        emit ModifyBlogEvent(id, msg.sender, old_title, old_summary, old_content, _title, _summary, _content, block.timestamp);
    }

删除文章

值得注意的是,在Solidity中,删除数组中的元素后,会将该元素所在的位置都置为初始值,并且长度不变,在这里你可以选择将最后的元素复制到该地址,然后使用pop()函数将最后一个元素删除,这里我并没有选择这样子,因为我想把文章ID和其在数组中的下边对应起来同时一篇文章应该有一个固定的id

    function deleteBlogByUserAndId(uint256 id) public {
        require(id >= 0 && id < blogList.length, "Id invalid!");
        require(
            msg.sender == blogList[id].author,
            "You can only delete your blog!"
        );
        uint256 idx = id;
        string memory old_title = blogList[idx].title;
        string memory old_summary = blogList[idx].summary;
        string memory old_content = blogList[idx].content;
        delete blogList[idx];
        emit DeleteBlogEvent( id, msg.sender, old_title, old_summary, old_content, block.timestamp);
    }

获取文章列表

我们虽然在前面将blogList设置成了public,但是实际上Solidity编译时候会生成一个对应的get方法,该方法的接收一个参数作为下标,因此我们无法通过访问该变量直接获取所有的文章,我们需要暴露一个函数,使其返回所有的文章:

	function getAll() public view returns(Blog[] memory){
        return blogList;
    }

view关键字说明该方法会访问状态变量(blogList),但是不会修改它,明确这一点很重要,它可以为调用者节省gas

部署和连接咱们的智能合约将在之后进行。

参考链接

【1】ChainBlog(本项目完整源码).https://github.com/YaleXin/ChainBlog

【2】web3.js中文文档.https://learnblockchain.cn/docs/web3.js/

【3】Solidity中文文档.https://learnblockchain.cn/docs/solidity/

【4】truffle文档.https://learnblockchain.cn/docs/truffle/getting-started/running-migrations.html

【5】开发、部署第一个DApp.https://learnblockchain.cn/2018/01/12/first-dapp/

【6】DApp教程:用Truffle 开发一个链上记事本.https://learnblockchain.cn/2019/03/30/dapp_noteOnChain/

【7】Vue.js https://cn.vuejs.org/

历史评论
开始评论