node开发以太坊程序

记录用node开发以太坊程序的一些关键点,通过web3js-1.0

提示

nodejs是写后端的,它提供的很多模块在前端js上不能运行(因为浏览器没有环境),
可通过browserify在本地打包对应的模块成.js 文件,在浏览器页面上运行。
安装:

1
npm install -g browserify

打包自己写的模块

1
browserify packagename.js -o name.js

打包系统模块

1
browserify packagename > compiled.js


编译智能合约

编译合约的方式有多种,包括用remix直接编译、终端运行solc。这里不展开,主要介绍node代码中编译合约。
以下是代码:

1
2
3
4
solc = require('solc')
compiledCode = solc.compile(code)
abi = JSON.parse(compiledCode.contracts[':Grades'].interface) // 返回abi
byteCode = compiledCode.contracts[':Grades'].bytecode // 返回二进制

编译出来重要的部分也就是abi和二进制,对应用solc编译后得到的.abi和.bin。

初始化web3

官方推荐的写法:

1
2
3
4
5
6
let Web3 = require('web3');
if(typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider);
} else {
web3 = new Web3(new Web3.providers.HttpProvider(Address)); // Address这里放以太坊节点的ip
}

加载已经部署好的合约

1
let contract = new web3.eth.Contract(abi, address);

第一个参数是abi,第二个参数是合约地址,后面的参数都可以通过myContract.options进行设置。

部署合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
myContract.deploy({
data: '0x12345...',
arguments: [123, 'My String']
})
.send({
from: '0x1234567890123456789012345678901234567891',
gas: 1500000,
gasPrice: '30000000000000'
}, function(error, transactionHash){ ... })
.on('error', function(error){ ... })
.on('transactionHash', function(transactionHash){ ... })
.on('receipt', function(receipt){
console.log(receipt.contractAddress) // contains the new contract address
})
.on('confirmation', function(confirmationNumber, receipt){ ... })
.then(function(newContractInstance){
console.log(newContractInstance.options.address) // instance with the new contract address
});


// When the data is already set as an option to the contract itself
myContract.options.data = '0x12345...';

myContract.deploy({
arguments: [123, 'My String']
})
.send({
from: '0x1234567890123456789012345678901234567891',
gas: 1500000,
gasPrice: '30000000000000'
})
.then(function(newContractInstance){
console.log(newContractInstance.options.address) // instance with the new contract address
});


// Simply encoding
myContract.deploy({
data: '0x12345...',
arguments: [123, 'My String']
})
.encodeABI();
> '0x12345...0000012345678765432'


// Gas estimation
myContract.deploy({
data: '0x12345...',
arguments: [123, 'My String']
})
.estimateGas(function(err, gas){
console.log(gas);
});

解密钱包

1
2
3
4
let walletJson = JSON.parse(walletBuffer); //walletBuffer是以字符串读入的钱包文件
let wallets = new Array(walletJson);
walletDecrypted = web3.eth.accounts.wallet.decrypt(wallets, passwordVal);
let privateKey = walletDecrypted[0].privateKey; //提取私钥

调用非交易方法

1
2
3
4
5
6
7
8
9
function getUsers() {
contract.methods.getUsers().call({from: '1062024529684b1890b2fa5964334d8db7da2512'}, function(err, res) {
if(err) {
alert(err);
} else {
alert(res);
}
});
}

调用交易方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
contract.methods.saveEvidence(digest, 4444444444).send({
from: '1062024529684b1890b2fa5964334d8db7da2512',
gasPrice: '0x3B9ACA00',
gas: 4000000,
})
// .on('confirmation', function(confirmationNumber, receipt) {
// alert(confirmationNumber);
// alert(JSON.stringify(receipt));
// })
.on('receipt', function(receipt) {
// alert(JSON.stringify(receipt));
let saveEvidenceOutput = document.getElementById('saveEvidenceOutput');
saveEvidenceOutput.value = JSON.stringify(receipt);
})
.on('error', alert);
}

js读本地文件

1
2
let reader = new FileReader();
reader.readAsBinaryString(myWallet); // myWallet文件路径

读出的对象是一个object,可通过json序列化查看内容

源代码

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<!doctype html>

<html>

<head>

<title>ConnectToContract</title>

<script type = 'text/javascript' src = 'web3.js'></script>

<script type = 'text/javascript' src = 'crypto.js'></script>

<script type = 'text/javascript' src = 'main.js'></script>

</head>



<body>

<form id="test-register" action="#" target="_blank" onsubmit="return checkRegisterForm()">

<span style="line-height: 40px;">

<p id="test-error" style="color:red"></p>



<p>

choose wallet file: <input type = 'file' name = 'file' id = 'walletfile'/><br>

password: <input type='text' id='password' name='password'><br>

<button type = 'button' onclick = 'authentication()'>Authentication</button>

<hr>

</p>

<p>

choose evidence file: <input type = 'file' name = 'file' id = 'evidencefile'/><br>

<button type = 'button' onclick = 'getEvidence()'>GetEvidence</button>

<input type='text' id='getEvidenceOutput' name='getEvidenceOutput' size='100'>

<br>

<button type = 'button' onclick = 'saveEvidence()'>SaveEvidence</button>

<input type='text' id='saveEvidenceOutput' name='saveEvidenceOutput' size='100'>

<hr>

</p>



<p>

<button type = 'button' onclick = 'getUsers()'>GetAllUsers</button>

</p>



<p>

<button type = 'button' onclick = 'test()'>test</button>

</p>

</span>

</form>



</body>

</html>

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
const infuraAddress = "https://rinkeby.infura.io/v3/2f85c22a29994320b52da33bec96968d";

let Web3 = require('web3');
if(typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider);
} else {
web3 = new Web3(new Web3.providers.HttpProvider(infuraAddress));
}

let abi = [{"constant":true,"inputs":[],"name":"getUsers","outputs":[{"name":"users","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"userList","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"fileHash","type":"bytes"}],"name":"getEvidence","outputs":[{"name":"code","type":"uint256"},{"name":"fHash","type":"bytes"},{"name":"fUpLoadTime","type":"uint256"},{"name":"saverAddress","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"fileHash","type":"bytes"},{"name":"fileUploadTime","type":"uint256"}],"name":"saveEvidence","outputs":[{"name":"code","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"}];
let address = "0x5b11477f2f8b1efaf46b60044e5d51e37a6fc76c";
let contract = new web3.eth.Contract(abi, address);

//==============================================================================================

function authentication() {
let password = document.getElementById('password');
let passwordVal = password.value;

let walletFile = document.getElementById('walletfile');
let myWallet = walletFile.files[0];
let reader = new FileReader();
let walletDecrypted;

reader.onload = function(e) {
let walletBuffer = e.target.result;
let walletJson = JSON.parse(walletBuffer);
let wallets = new Array(walletJson);

try {
walletDecrypted = web3.eth.accounts.wallet.decrypt(wallets, passwordVal);
alert('Authentication success');
} catch(e) {
alert('Error: please try again');
}

let privateKey = walletDecrypted[0].privateKey;
//alert(privateKey);
}
reader.readAsBinaryString(myWallet);

}

function getUsers() {
contract.methods.getUsers().call({from: '1062024529684b1890b2fa5964334d8db7da2512'}, function(err, res) {
if(err) {
alert(err);
} else {
alert(res);
}
});
}

function getEvidence() {
let walletFile = document.getElementById('evidencefile');
let myWallet = walletFile.files[0];
let reader = new FileReader();

reader.onload = function(e) {
let fileSource = e.target.result;
let crypto = require('crypto');
let md5Hash = crypto.createHash('md5');
md5Hash.update(fileSource);
let digest = md5Hash.digest('hex');
digest = '0x' + digest;
alert(digest);
// callback funciton can not return the result
contract.methods.getEvidence(digest).call({from: '1062024529684b1890b2fa5964334d8db7da2512'}, function(err, res) {
if(err) {
alert('aaaaaa');
alert(JSON.stringify(err));
} else {
let evidenceOutput = document.getElementById('getEvidenceOutput');
let retRes = '';
if(res.code == '0'){
retRes = 'SUCCESS: ';
} else {
retRes = 'FAIL: ';
}
evidenceOutput.value = retRes + JSON.stringify(res);
}
});
}
reader.readAsBinaryString(myWallet);

}


function saveEvidence() {
let walletFile = document.getElementById('evidencefile');
let myWallet = walletFile.files[0];
let reader = new FileReader();

reader.onload = function(e) {
let fileSource = e.target.result;
let crypto = require('crypto');
let md5Hash = crypto.createHash('md5');
md5Hash.update(fileSource);
let digest = md5Hash.digest('hex');
digest = '0x' + digest;
//alert(digest);
// callback funciton can not return the result

contract.methods.saveEvidence(digest, 4444444444).send({
from: '1062024529684b1890b2fa5964334d8db7da2512',
gasPrice: '0x3B9ACA00',
gas: 4000000,
})
// .on('confirmation', function(confirmationNumber, receipt) {
// alert(confirmationNumber);
// alert(JSON.stringify(receipt));
// })
.on('receipt', function(receipt) {
// alert(JSON.stringify(receipt));
let saveEvidenceOutput = document.getElementById('saveEvidenceOutput');
saveEvidenceOutput.value = JSON.stringify(receipt);
})
.on('error', alert);
}
reader.readAsBinaryString(myWallet);

}