TRUFFLE03:智能合约开发与测试

2020-04-29  本文已影响0人  杨强AT南京

  智能合约开启所谓的DApp的概念,相对于传统的以Web Server位中心的服务供应;以太坊提供的是基于Javascript的调用,可以在浏览器环境中调用 ,这也许就是去中心化?可能更加吸引人的是基于EVM的数据存储与访问的概念。基于浏览器的Cookie存储与内置运行环境安全性是与可信性是需要用EVM替代。
  本主题最后给出了一个在Node.js环境中访问以太坊智能合约的编程应用模式,还是把合约访问放在中心化的服务器后端。官方的DApp例子是基于浏览器环境的。


创建并编译合约

创建合约

  1. 在contracts目录下创建
    • 这里我们使用一个老的可用的合约。
    pragma solidity ^0.5.0;

    contract Contrac_Base{
        uint  age;
        constructor() public {
            age = 200;
        }

        function  getAge() public view returns (uint){
            // return 99;
            return age;
        }

        function setAge(uint age_) public {
            age = age_;
        }
    }

  1. 创建好的文件结构截图
Truffle项目测试结构

编译合约

智能合约编译

编译合约与部署合约

编写部署脚本

const Contrac_Base = artifacts.require("Contrac_Base");

module.exports = function(deployer) {
  deployer.deploy(Contrac_Base);
};

智能合约测试部署

部署

C:\01works\08eth\Tru\tru_3rd>truffle deploy --reset

Compiling your contracts...
===========================
> Compiling .\contracts\Contrac_Base.sol
> Compiling .\contracts\Migrations.sol
> Artifacts written to C:\01works\08eth\Tru\tru_3rd\build\contracts
> Compiled successfully using:
   - solc: 0.5.16+commit.9c3226ce.Emscripten.clang



Starting migrations...
======================
> Network name:    'development'
> Network id:      1585295887794
> Block gas limit: 0x6691b7     


1_initial_migration.js
======================

   Replacing 'Migrations'
   ----------------------
   > transaction hash:    0xedf830f94ba41b3082462f56f8251a38a70904244e655be91d1b45637c463721
   > Blocks: 0            Seconds: 0
   > contract address:    0x76b01681c7263378c63A48F2055AB4DD1A8dFf9D
   > block number:        9
   > block timestamp:     1585300375
   > account:             0x9977bAea7Bc77E315EF36E6d1B9371CEAbF7949E
   > balance:             99.98145234
   > gas used:            164175
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.0032835 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:           0.0032835 ETH


2_init_deploy.js
================

   Deploying 'Contrac_Base'
   ------------------------
   > transaction hash:    0xa78f77cef0e3045d0d7888b544b5f044e79ac42d1982994d8abcc44577d51c6b
   > Blocks: 0            Seconds: 0
   > contract address:    0x90C267bf7CeE9C62d5aa1255Efa7213C3118Cbde
   > block number:        11
   > block timestamp:     1585300375
   > account:             0x9977bAea7Bc77E315EF36E6d1B9371CEAbF7949E
   > balance:             99.97827914
   > gas used:            116319
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00232638 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.00232638 ETH


Summary
=======
> Total deployments:   2
> Final cost:          0.00560988 ETH

测试

理解测试的工作过程

测试命令

测试脚本文件

    const Contrac_Base = artifacts.require("Contrac_Base");

    // 可以直接使用web3实例对象
    // console.log(web3);

    console.log("开始测试!");

    C:\01works\08eth\Tru\tru_3rd>truffle test .\test\2_test_base.js
    Using network 'development'.


    Compiling your contracts...
    ===========================
    > Compiling .\contracts\Contrac_Base.sol
    > Compiling .\contracts\Migrations.sol
    > Artifacts written to C:\Users\gaoke\AppData\Local\Temp\test-2020227-6316-p465kl.ndst
    > Compiled successfully using:
       - solc: 0.5.16+commit.9c3226ce.Emscripten.clang

    开始测试!


      0 passing (0ms)

编写测试脚本

    const Contrac_Base = artifacts.require("Contrac_Base");

    contract(
        'Contrac_Base', 
        async function (accounts){   // 使用同步操作
            // console.log(accounts);
            it("简单合同测试:", async function(){
                var contract_instance = await Contrac_Base.deployed();

                // 调用交易
                await contract_instance.setAge(88);

                var result = await contract_instance.getAge.call();  // 可以传递参数
                var age = result.toNumber();
                console.log(age)
                assert.equal(result, 88, "不是200");
                assert.equal(age, 88, "不是200");
            });

        }
    );

    const Contrac_Base = artifacts.require("Contrac_Base");

    contract(
        'Contrac_Base', 
        function (accounts){   // 使用同步操作
            console.log(accounts);
            it("简单合同测试:", function(){
                var contract_instance;
                return Contrac_Base.deployed().then(function (instance){
                    contract_instance = instance;
                    // return contract_instance.getAge();
                    return contract_instance.getAge.call();   // 上面方式也可以
                }).then(function(result){
                    console.log("---------------------");
                    console.log(result);
                    console.log("---------------------");
                    console.log(result.toNumber());
                    console.log("---------------------");
                    assert.equal(result, 200, "数据不正确");
                });
            });

        }
    );

使用Truffle的结果开发

    var Web3 = require("web3");
    var contrtact = require("../build/contracts/Contrac_Base.json");

    async function run(){
        console.log("start");
        var web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:8545"));
        // console.log(web3);

        const networkId = await web3.eth.net.getId();
        // 获取部署的地址
        const deployedNetwork = contrtact.networks[networkId];
        console.log(networkId);
        // 创建合约实例
        var instance_contract = new web3.eth.Contract(
            contrtact.abi,
            deployedNetwork.address,
        );
        // 获取账号
        const accounts = await web3.eth.getAccounts();
        console.log(accounts)
        // 调用
        var result = await instance_contract.methods.getAge().call()
        console.log(result);
        // 发起一个交易
        await instance_contract.methods.setAge(128).send({ from: accounts[0]});;
        // 查询交易的结果
        result = await instance_contract.methods.getAge().call()
        console.log(result);
    }

    // 这个函数可以在web服务,浏览器等地方都可以编写。
    run();


附录

pragma solidity >=0.4.21 <0.7.0;

contract Health{
    // 上链的数据结构
    struct H_Data{   // 需要可以增加时间点
        uint32      heartbeat;    // 心跳
        uint32      stepnumber;   // 步数
        uint32      temperature;  // 体温
    }

    struct User_Info{
        address    owner;         // 数据拥有者
        string     name;          // 身份证编号
        string     birth;         // 出生日期
        uint32     visitCount;    // 总访问次数
    }

    struct Visitor{
        string    name;          // 数据访问者
        uint32     visitCount;    // 数据访问者访问次数
    }

    // 拥有者信息
    User_Info          user;    
    // 上链数据
    H_Data[]           h_data;
    // 授权用户
    mapping(address => Visitor) public visitors;

    // 构造器:初始化时需要拥有者信息
    constructor(string memory name, string memory birth) public{
        // 初始化拥有者信息
        user.name = name;
        user.birth = birth;
        user.owner = msg.sender; 
    }

    // 查询用户信息
    function getUserInfo() public view returns (string memory name, string memory birth){
        // 可以考虑授权限制
        name = user.name;
        birth = user.birth;
    }

    // 更新链上数据
    function addData(uint32 heartbeat, uint32 stepnumber, uint32 temperature) public {
        // 可以考虑授权限制
        // 创建一个数据记录
        H_Data memory item = H_Data(
            {
                heartbeat: heartbeat, 
                stepnumber: stepnumber, 
                temperature: temperature});
        h_data.push(item);
    }
    // 用户访问健康数据
    function getHData(uint idx) public view returns (uint32 heartbeat, uint32 stepnumber, uint32 temperature) { 
        // 权限控制
        require(visitors[msg.sender].visitCount > 0  || msg.sender == user.owner, "没有访问权限");
        // 增加计数
        // 自己实现了 ----------------
        // 返回数据
        if(idx < h_data.length){
            heartbeat = h_data[idx].heartbeat;
            stepnumber = h_data[idx].stepnumber;
            temperature = h_data[idx].temperature;
        }
        else{
            heartbeat = 0;
            stepnumber = 0;
            temperature = 0;
        }
    }
    function  getRecordCount() public view  returns(uint256 len){
        len = h_data.length;
    }

    // 授权用户访问
    function authToUser(address to, string memory name) public{
        // 只有拥有者可以授权
        require(
            msg.sender == user.owner,
            "只有拥有者可以授权!"
        );
        visitors[to].name = name;
        visitors[to].visitCount = 1;
    }

}
var Web3 = require("web3");
var contrtact = require("../build/contracts/Health.json");
// python(Django) + go + javascript(Express) 
async function run(){
    console.log("start");
    var web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:8545"));
    // console.log(web3);

    const networkId = await web3.eth.net.getId();
    // 获取部署的地址
    const deployedNetwork = contrtact.networks[networkId];
    console.log(networkId);
    // 创建合约实例
    var instance_contract = new web3.eth.Contract(
        contrtact.abi,
        deployedNetwork.address,
    );
    // 获取账号
    const accounts = await web3.eth.getAccounts();
    console.log(accounts)
    // ----------------------------------------------------------------------------
    // 调用
    // var result = await instance_contract.methods.getUserInfo().call()
    // console.log(result);
    // 发起一个交易
    await instance_contract.methods.addData(88, 100000, 355).send({ from: accounts[0]});
    // 查询交易的结果
    console.log("---------------------------------");
    result = await instance_contract.methods.getHData(0).call()
    console.log(result);
    console.log("---------------------------------");
    result = await instance_contract.methods.getHData(1).call()
    console.log(result);
    result = await instance_contract.methods.getHData(2).call()
    console.log(result);
    console.log("---------------------------------");
}

// 这个函数可以在web服务,浏览器等地方都可以编写。
run();


上一篇 下一篇

猜你喜欢

热点阅读