〇、准备工作
安装node.js
安装solc
安装web3.js
安装mocha
启动私链或公网区块链节点
创建文件目录
......
mkdir contract_workflow
cd contract_workflow
mkdir contracts #存放智能合约.sol文件
mkdir compiled #存放编译后的json文件
mkdir scripts #存放脚本js文件
mkdir tests #存放测试调用脚本js文件
一、智能合约开发
通过solidity语言,实现一份简单的智能合约.sol文件,示例:
pragma solidity ^0.4.22;
contract Car{
string public brand;
constructor(string _brand) public {
brand = _brand;
}
function setBrand(string _brand)public {
brand = _brand;
}
}
二、智能合约编译
源代码通过编译成字节码(Bytecode),同时会产生二进制接口规范(ABI);另外编译能够处理编译时抛出的错误,确保不会在包含错误的源代码上进行编译。
const fs = require('fs-extra');//fs-extra替换fs,目的是把编译结果保存在文件系统中
const solc = require('solc');
const path = require('path');
//改进2:每次编译前清空
const compiledPath = path.resolve(__dirname,'../compiled');
fs.removeSync(compiledPath);//同步移除目录
fs.ensureDirSync(compiledPath);//确保文件存在,不存在则新建
//对合约进行编译
const contractPath = path.resolve(__dirname,'../contracts','Car.sol');//解析路径
const contractSource = fs.readFileSync(contractPath,'utf-8');//从路径中读取合约源码
let compileResult = solc.compile(contractSource,1);//solc编译器同步编译,1表示打开solc中的优化器
// console.log(compileResult);//打印编译结果
//改进3:编译阶段处理异常
if(Array.isArray(compileResult.errors) && compileResult.errors.length){
throw new Error(compiledResult.errors[0]);
}
//改进1:把编译结果保存在文件系统中
Object.keys(compileResult.contracts).forEach(name=>{//contracts中每个元素提出来
let contractName = name.replace(/^:/,'');
let filePath = path.resolve(__dirname,'../compiled',`${contractName}.json`);
fs.outputJsonSync(filePath,compileResult.contracts[name]);//fs-extra中方法
console.log("saving json file to ",filePath);
})
执行脚本成功后,会在对应目录生成Car.json文件。
node ./scripts/compile.js
compiled目录下会生成Car.json
三、部署智能合约
通过交易将字节码部署到以太坊网络,部署成功会产生一个智能合约账户,示例是部署在私链节点上
//1.0.0
const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
const path = require('path');
//1.拿到abi和bytecode
const filePath = path.resolve(__dirname,'../compiled/Car.json');
//{interface,bytecode}名称与文件内一致,不可自定义
const {interface,bytecode} = require(filePath);
(async () => {
//2.获取钱包里的账户
let accounts = await web3.eth.getAccounts();
console.log('部署合约的账户:',accounts[0]);
// console.time('deploy time');
//3.创建合约实例并部署
let result = await new web3.eth.Contract(JSON.parse(interface))//创建新合约
.deploy({data: bytecode,arguments: ['BMW']})//部署操作-交易对象
.send({from: accounts[0],gas: 5000000});
// console.timeEnd('deploy time');
console.log("contract_address: ",result.options.address);
})();
执行脚本
node ./scripts/deploy.js
四、调用测试合约
通过web3.js+ABI去调用智能合约中的函数来实现数据的读取和修改。
//mocha测试框架
const path = require('path');
const Web3 = require('web3');
const assert = require('assert');
const ganache = require('ganache-cli');
//1.配置provider
const web3 = new Web3(ganache.provider());
//2.拿到abi和bytecode
const contractPath = path.resolve(__dirname,'../compiled/Car.json');
const {interface,bytecode} = require(contractPath);
let accounts;
let contract;
const initBrand = 'BMW';
describe('contract',()=>{
before(()=>{
console.log('测试开始');
});
//3.每次跑单测时需要部署全新的合约实例,起到隔离的作用
beforeEach(async()=>{//异步测试,保证顺序
accounts = await web3.eth.getAccounts();
contract = await new web3.eth.Contract(JSON.parse(interface))
.deploy({data: bytecode,arguments: [initBrand]})//部署操作-交易对象
.send({from: accounts[0],gas: 5000000});
console.log('合约已部署');
});
after(()=>{
console.log('测试结束');
});
afterEach(()=>{
console.log('当前测试完成');
});
it('deployed contract successfully',()=>{
assert.ok(contract.options.address);
});
it('should has a initial brand',async()=>{
let brand = await contract.methods.brand().call();
assert.equal(brand,initBrand);
});
it('should set a new brand',async()=>{
const newBrand = 'Audi';
await contract.methods.setBrand(newBrand)
.send({from: accounts[0]});
let brand = await contract.methods.brand().call();
assert.equal(brand,newBrand);
});
});
执行脚本
node ./tests/car.js