从零开始使用Solidity编写以太坊智能合约并使用Nodejs SDK访问以太坊网络

Nodejs安装

Nodejs 版本建议8.0以上
官网:https://nodejs.org/en/
官网下载安装包:https://nodejs.org/dist/v8.12.0/node-v8.12.0-x64.msi
安装参考:https://blog.csdn.net/qq_26562641/article/details/72235585

配置淘宝镜像:

npm config set registry https://registry.npm.taobao.org

新建一个Hello World只能合约的访问

  1. 使用IDEA安装nodejs插件,在插件列表搜索

  2. 新建一个node工程项目,File>New Project 选择Node.js and NPM

    默认有很多其他的目录,我们删除其他目录,只保留如下的目录:

    或者使用npm init命令初始化一个nodejs工程

  3. 安装solidity编译器依赖,用于编译Solidity,才能被nodejs使用到

    npm install --save solc
  4. 安装以太坊的访问SDK web3.js

     npm install --save web3

    报错解决:

    1. gyp ERR! configure error gyp ERR! stack Error: Command failed: C:\Users\yan6\AppData\Local\Programs\Pytho n\Python37-32\python.EXE -c import sys; print "%s.%s.%s" % sys.version_info[:3];
      原因:安装脚本中用到了Python2的语法,你的环境变量中配置的Python3所以报这个错
      解决:修改python的环境变量,将Python3改成Python2,如果没有到官网下载一个Python2
    2. error MSB3428: 未能加载 Visual C++ 组件“VCBuild.exe”。要解决此问题,1)
      MSBUILD : error MSB3428: 未能加载 Visual C++ 组件“VCBuild.exe”。要解决此问题,1) 安装 .NET Framework 2.0 SDK;2) 安装 Microsoft Visual
      Studio 2005;或 3) 如果将
      该组件安装到了其他位置,请将其位置添加到系统路径中。 [D:\workspace\idea_workspace\blockchian1\node_modules\scrypt\build\binding.sln]
      MSBUILD : error MSB3428: 未能加载 Visual C++ 组件“VCBuild.exe”。要解决此问题,1) 安装 .NET Framework 2.0 SDK;2) 安装 Microsoft Visual
      Studio 2005;或 3) 如果将
      该组件安装到了其他位置,请将其位置添加到系统路径中。 [D:\workspace\idea_workspace\blockchian1\node_modules\scrypt\build\binding.sln]
      解决办法,按装全局windows相关组件:
      npm install --global --production windows-build-tools 
  5. 安装ganache,ganache是用来在本地测试用的测试以太坊网络

     npm install -g ganache-cli
  6. 新建一个Solidity脚本,Hello.sol

     pragma solidity ^0.4.17;
    
     contract Hello {
         string public name;
    
         function Hello(string _name) public {
             name = _name;
         }
    
         function setName(string _name) public {
             name = _name;
         }
    
         function getName() public view returns(string ) {
             return name;
         }
     }
    
  7. 编写一个solidity的编译脚本compile.js

     const path = require('path');
     const  fs = require('fs');
    
     const solc = require('solc');
    
     const srcpath = path.resolve(__dirname,'contracts', 'Hello.sol');
     const source = fs.readFileSync(srcpath, 'utf-8');
     //console.log(source);
    
     const result = solc.compile(source,1);
     //console.log(result);
     module.exports = result.contracts[':Hello'];
  8. 在tests下新建一个本地Hello World测试类,Web3Test.test.js

     const  assert = require('assert');
     //约定规范,如果变量是大写
     const Web3 = require('web3');
     //内存里面的以太坊测试环境
     const  ganache = require('ganache-cli');
     const web3 = new Web3(ganache.provider());
    
     //执行编译脚本,并将编译结果引入进来
     const {interface,bytecode} = require('../compile');
    
     /**
      * 测试一个Hello World智能合约
      * @returns {Promise.<void>}
      */
     testGetSet = async ()=> {
         let accounts = await web3.eth.getAccounts();
         //部署也是一个交易命令,所以需要花gas
         const abi = JSON.parse(interface);
         const contract = new web3.eth.Contract(abi);
    
         const result = await contract.deploy({
             data:bytecode,
             arguments:['Hello World']
         }).send({
             from:accounts[0],
             gas: 1500000,
             gasPrice: '30000'
         });
         console.log('deploy success:' + result.options.address);
    
         //测试查询
         assert.equal(await result.methods.getName().call(),'Hello World');
    
         await result.methods.setName('hahaha').send({
             from:accounts[0],
             gas:100000
         });
         assert.equal(await result.methods.getName().call(),'hahaha');
         console.log('测试智能合约成功');
     }
    
    
/**
 * 测试以太坊转账
 * @returns {Promise.<void>}
 */
testTrade = async ()=> {
    let accounts = await web3.eth.getAccounts();
    let b0 = await web3.eth.getBalance(accounts[0]);
    let b1 = await web3.eth.getBalance(accounts[1]);
    //发送交易
    console.log('开始转账:account0:' + b0 + ' account1:' + b1);
    await web3.eth.sendTransaction({
        from:accounts[0],
        to:accounts[1],
        value:'1000000000000000'
    });

    b0 = await web3.eth.getBalance(accounts[0]);
    b1 = await web3.eth.getBalance(accounts[1]);
    //发送交易
    console.log('转账成功:account0:' + b0 + ' account1:' + b1);
}

testGetSet();
testTrade();

```

以上代码const {interface,bytecode} = require('../compile');
这句话的意思是将Hello.sol编译后的导入到当前的node上下文,interface就是编译后的一些方法定义,bytecode就是最终部署到以太坊网络的二进制数据

  1. 测试运行
     node `Web3Test.test.js
    运行结果:

将代码提交到以太坊rankeby测试网络

  1. 安装truffle-hdwallet-provider
    组件官方文档:https://www.npmjs.com/package/truffle-hdwallet-provider

      npm install truffle-hdwallet-provider
  2. 使用truffle-hdwallet-provider:

     //线上的测试环境
     var HDWalletProvider = require("truffle-hdwallet-provider");
     var mnemonic = "这里是你的以太坊钱包私钥助记词"; // 12 word mnemonic
     //使用infura在线的provider
     var provider = new HDWalletProvider(mnemonic, "https://rinkeby.infura.io/v3/02b9371103e54ed6bb4ccb91651497f5");
     const web3 = new Web3(provider);

    上面用到的provider_url:https://rinkeby.infura.io/v3/02b9371103e54ed6bb4ccb91651497f5是infura的在线url,到https://rinkeby.infura.io注册一个账号并添加一个rinkeby的测试PROJECT就可以得到一个测试provider_url了,网络不好可能需要翻墙

  3. 新增一个测试代码进行测试交易,EtherOnlieRinkebyTest.test.js

     //约定规范,如果变量是大写
     const Web3 = require('web3');
    
     //线上的测试环境
     var HDWalletProvider = require("truffle-hdwallet-provider");
     var mnemonic = "这里是你的以太坊钱包私钥助记词"; // 12 word mnemonic
     //使用infura在线的provider
     var provider = new HDWalletProvider(mnemonic, "https://rinkeby.infura.io/v3/02b9371103e54ed6bb4ccb91651497f5");
     const web3 = new Web3(provider);
    
     /**
      * 测试web3
      */
     testSend = async ()=> {
    
             let accounts = await web3.eth.getAccounts();
    
             console.log(accounts);
             let account0 = accounts[0];
             let account1 = '0x5828eb46D40795Da76429553845DfA622F062CB2';
    
             let b0 = await web3.eth.getBalance(account0);
             let b1 = await web3.eth.getBalance(account1);
             console.log('开始转账:address0:' + account0 + ' :' + b0 + ' address1:'+account1 + ' account1:' + b1);
             const tx = web3.eth.sendTransaction({
                 from:account0,
                 to:account1,
                 value: web3.utils.toWei('1', 'ether'),
                 data: web3.utils.toHex('I love you ,xiao man ju')
             },async (err,address)=>  {
                 console.log("转账成功,address:" + address);
                 b0 = await web3.eth.getBalance(account0);
                 b1 = await web3.eth.getBalance(account1);
    
                 let tx = await web3.eth.getTransaction(address);
                 console.log('tx:'+ JSON.stringify(tx) +' 转账成功:address0:'+account0+':' + b0 + ' address1:'+account1+' account1:' + b1);
             });
            }
    
     testSend();
    
  4. 使用nodejs运行测试代码

     node EtherOnlieRinkebyTest.test.js

    输出结果如下:

到此从开发到上传到访问以太坊rinkeby测试网络已经完成。

Nodejs测试框架

上面我们测试一个node脚本是直接使用node命令直接运行,对于实际开发应用中如果想做到自动化测试用例的运行,需要用到类型java里面Junit测试框架的东西,这个东西在node里面叫Mocha

  1. 安装mocha

     npm install --save mocha
  2. 修改package.json,将scripts.test改成mocha

     {
       "name": "blockchian1",
       "version": "1.0.0",
       "description": "",
       "main": "app.js",
       "directories": {
         "test": "test"
       },
       "dependencies": {
         "mocha": "^5.2.0",
         "solc": "^0.4.25"
       },
       "devDependencies": {},
       "scripts": {
         "test": "mocha"
       },
       "author": "",
       "license": "ISC"
     }

    配置了scripts.test 为mocha命令,npm run test访问的就是mocha的测框架

  3. mocha测试,MochaTest.test.js, describe就是基本的mocha测试骨架,it是测试用例

     const assert = require('assert');
     /**
      * ecs6 mocha测试
      */
     class Test {
         say() {
             return 'hello';
         }
    
         happy() {
             return 'haha';
         }
     }
    
     //开始写mocha测试框架
    
     let dog;
     beforeEach(()=>{
         dog = new Test();
     })
    
     describe('第一个mocha测试用例',()=> {
         it('测试hello()',()=>{
             //const  dog = new Test();
             let say = dog.say();
             console.log(say);
             assert.equal(say,'hello');
    
         })
    
         it('测试happy()',()=>{
             let happy = dog.happy();
             console.log(happy);
             assert.equal(happy,'haha');
         })
     })

    以上用到了assert组件,这个类似java里面的Assert断言,默认在node上下文已将安装,直接依赖使用即可

  4. 运行测试用例

     npm run test

    这个test访问的就是我们之前修改的package.json里面的test命令mocha,类似maven构建时的测试,它将运行项目上下文中的所有实现了mocha的测试用例


 上一篇
动态壁纸接口 动态壁纸接口
动态壁纸接口
2019-11-28
下一篇 
区块链技术之比特币运作原理 区块链技术之比特币运作原理
什么是比特币点对点的传输的一个去中心化的电子现金系统。每个节点都共同维护一个区块链形式存储的交易记录,每个比特币节点遵守同一个比特币网络协议,并基于密码学原理加密每一笔交易记录和区块,实现每一笔交易不可逆、防篡改、去中心化的、数据可监管溯源
2019-08-21
  目录