1. evm虚拟机

交易的执行是区块链节点上的一个重要的功能。是把交易中的智能合约二进制代码取出来,用执行器(Executor)执行。在交易的执行过程中,会对区块链的状态(State)进行修改,形成新区块的状态储存下来(Storage)。执行器在这个过程中,类似于一个黑盒,输入是智能合约代码,输出是状态的改变.

以太坊虚拟机(environment virtual machine,简称EVM),作用是将智能合约代码编译成可在以太坊上执行的机器码,并提供智能合约的运行环境。它是一个对外完全隔离的沙盒环境,在运行期间不能访问网络、文件,即使不同合约之间也有有限的访问权限。以太坊虚拟机提供了面向合约的高级编程语言solidity,这使得开发者可以专注于应用本身,更方便、快捷的开发去中心化应用程序,同时也大大降低了开发难度。

EVM是一种基于栈的虚拟机(区别于基于寄存器的虚拟机),用于执行智能合约,同时EVM是图灵完备的,EVM操作数栈调用深度为1024,EVM机器码长度一个字节,最多可以有256个操作码,目前已经定义了144个操作码,还有100多个操作码可以扩展,每个操作码都根据其弹栈数、压栈数定义了相应的gas消耗数量。泰岳链应用了以太坊EVM机制来实现智能合约,并增加了对国密算法的支持(SM3)。

2. solidity语言

Solidity 是一门面向合约的、为实现智能合约而创建的高级编程语言。这门语言受到了 C++,Python 和 Javascript 语言的影响,设计的目的是能在 以太坊虚拟机(EVM) 上运行。

Solidity 是静态类型语言,支持继承、库和复杂的用户定义类型等特性。

直接看这里:Solidity最新中文文档

3. 使用Ganache与truffle进行合约开发

Ganache

ganache可以快速的在本机上启动一条以太坊链,用户可以方便的在上面部署合约,调用合约,完成各种与合约之间的交互。

ganache提供了Windows、Linux以及Mac三种系统的版本,直接到其官网GitHub页面下载安装即可。

安装完成后,即可以快速部署一条链 20210617132946

使用QUICKSTART模式部署的链只会在本次会话中存在,关闭当前会话或注销当前用户都会导致链的撤销,如果只是写个小demo的话,那么使用这种方式即可。
NEW WORKSPACE则会创建一条持久化的链,不会因会话结束或用户注销而撤销链。

使用QUICKSTART模式启动

20210617133515

ganache会自动创建10个测试账号,每个账号分配了100个原生币,交易需要消耗这些原生币。 20210617133647

在页面的上方,还有其他一些选项卡,可以方便的查看当前区块、交易、事件、日志等。需要注意的是在这些选项卡的下方,还标注了本链的一些信息,如它的端口,网络ID等。 20210617133846

truffle

truffle提供了合约开发、测试、部署等一系列工具,通过与Ganache配合可以十分方便的测试你的合约。

安装truffle

npm install -g truffle

新建一个truffle项目

mkdir MyContract
truffle init

20210617134412

truffle会创建如下的目录结构:

├── contracts
│   └── Migrations.sol
├── migrations
│   └── 1_initial_migration.js
├── test
└── truffle-config.js

contract目录中存放我们的合约;migrations目录中存放migrate文件,功能类似数据库migrate文件,简单来说,就是让你的应用从一个状态迁移到另一个状态;test目录中存放测试文件(还未创建);truffle-config.js是配置文件,其中配置了链的地址等信息。

根据提示我们来创建一个简单的合约模板:

truffle create contract Counter 

truffle创建了Counter.sol文件,再次查看目录结构:

├── contracts
│   ├── Counter.sol
│   └── Migrations.sol
├── migrations
│   └── 1_initial_migration.js
├── test
└── truffle-config.js

打开Counter.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

contract Counter {
  constructor() public {
  }
}

为我们提供了一个合约模板,修改合约:

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

contract counter {
  address owner;
  mapping (string => uint256) values;

  constructor() public {
    owner = msg.sender;
  }

  function increase(string memory key) public payable {
    values[key] = values[key] + 1;
  }

  function get(string memory key) view public returns (uint) {
    return values[key];
  }

  function getOwner()view public returns (address) {
    return owner;
  }
}

以上合约是一个非常简单的计数器合约,提供了increase, get, getOwner三个方法,分别用来增加计数、获取计数值、获取合约所有者。

编译合约

truffle compile

20210617135649

编辑migrate文件

vim migrations/2_deploy_contracts.js
const Counter = artifacts.require("Counter")

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

现在准备工作已经完成了,开始让我们的合约上链。

配置truffle-config.js文件,主要是要配置network:

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

networks有很详细的注释,这里的关键是需要与Ganache中显示的端口号等一致。 20210617140427

配置完成,终于到了激动人心的一步,合约上链。

truffle migrate
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.



Starting migrations...
======================
> Network name:    'development'
> Network id:      5777
> Block gas limit: 6721975 (0x6691b7)


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

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0x75050e06c2f6f1097257de17ea1370a86225c35909e8319ed62407424b21587e
   > Blocks: 0            Seconds: 0
   > contract address:    0xd366fCAF0F7A2b1A2Ebde67E89f1bbC2ED708c55
   > block number:        112
   > block timestamp:     1623909962
   > account:             0x128F3853c98671ac43e8358e3A043f3A7bD0Ca18
   > balance:             99.72713514
   > gas used:            191943 (0x2edc7)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00383886 ETH

2_deploy_contracts.js
=====================

   Deploying 'Counter'
   -------------------
   > transaction hash:    0x692c27b1b9a7f04301c10a2b3ece8c5f7737581d4de9f01e88653a907eb4b711
   > Blocks: 0            Seconds: 0
   > contract address:    0x76607048A6628A43d981811a35a629Ff049c2B11
   > block number:        114
   > block timestamp:     1623910129
   > account:             0x128F3853c98671ac43e8358e3A043f3A7bD0Ca18
   > balance:             99.72055518
   > gas used:            286660 (0x45fc4)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.0057332 ETH


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


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

从输出的日志信息中可以看到部署的合约及其消耗的资源。

查看Ganache可看到,链增长了4个区块 20210617141446

消耗的原生币也显示了出来 20210617141531

执行的所有交易 20210617141613

现在我们完成了合约编写,合约上链等步骤,但到这里只能证明合约语法的正确性,我们还需要进行一系列的测试来保证我们的合约逻辑是无误的,可以按照我们预定的逻辑执行。

生成测试代码(可选)

truffle create test Counter

cat test/counter.js
const Counter = artifacts.require("Counter");

/*
 * uncomment accounts to access the test accounts made available by the
 * Ethereum client
 * See docs: https://www.trufflesuite.com/docs/truffle/testing/writing-tests-in-javascript
 */
contract("Counter", function (/* accounts */) {
  it("should assert true", async function () {
    await Counter.deployed();
    return assert.isTrue(true);
  });
});

如果你不想自动生成代码,那么可以手动创建自己的测试文件

vim test/counter.test.js

内容同上

开始测试

$ truffle test  

Using network 'development'.

Compiling your contracts...
===========================
> Compiling ./contracts/Counter.sol
> Artifacts written to /tmp/test--10676-pZQ9laXW37D3
> Compiled successfully using:
   - solc: 0.5.16+commit.9c3226ce.Emscripten.clang



  Contract: Counter
    ✓ should assert true


  1 passing (74ms)

以上的测试文件中,断言永远为真,所以只要这个测试可以跑起来,就肯定不会fail,这只是官方给我们生成的测试代码模板,真正的测试代码还需要我们自己来编写。 修改测试代码:

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

contract("counter", async account => {

  contract("counter 1st test", async account => {
    let c;
    before("deploy contract", async () => {
      c = await Counter.deployed();
    })
    
    it("test getOwner", async () => {
      const owner = await c.getOwner();
      expectedOwner = "0x128F3853c98671ac43e8358e3A043f3A7bD0Ca18"
      assert.equal(owner, expectedOwner, "owner is wrong")
    })

    it("test increase", async () => {
      await c.increase("0x128F3853c98671ac43e8358e3A043f3A7bD0Ca18")
      const num = await c.get("0x128F3853c98671ac43e8358e3A043f3A7bD0Ca18")
      let expectedNum = 1;
      assert.equal(expectedNum, num, "error occur");
    })
  })

});

重新运行测试:

truffle test
Using network 'development'.

Compiling your contracts...
===========================
> Compiling ./contracts/Counter.sol
> Artifacts written to /tmp/test--10825-NWIGDIdS1RrG
> Compiled successfully using:
   - solc: 0.5.16+commit.9c3226ce.Emscripten.clang

  Contract: counter
    Contract: counter 1st test
      ✓ test getOwner
      ✓ test increase (81ms)

  2 passing (184ms)

测试通过,完美。

更多资料,请参考官网文档:TRUFFLE SUITE

时间处理: https://ethereum.stackexchange.com/questions/34110/compare-dates-in-solidity