交易签名是你向区块链证明自己身份的唯一途径,这也是使用以太坊的Web3开发库时需要理清的一个基本概念。在这个教程中,我们讲学习如何使用Web3.js来完成以太坊智能合约调用交易的签名与提交,适用于包括ERC20代币合约在内的所有以太坊合约的调用。
用自己熟悉的语言学习以太坊DApp开发: Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart
1、以太坊交易签名概述
有两种方法可以对交易进行签名:使用解锁账号或使用私钥。
如果你在用testRPC或Ganache开发,那么应该已经了解其创建的测试账号。这些账号默认都是解锁的,因此你可以直接用它们来签名交易。你也可以使用特殊的Web3提供器例如truffle-hdwallet-provider来生成解锁账号。
更常见的则是以用发起交易的地址对应的私钥来签名交易。考虑到安全性,当你用到私钥时需要格外小心。
2、创建以太坊智能合约调用交易
首先你需要构建一个调用合约方法的交易对象:
// 12 word mnemonic for HD Wallet Provider
// You can use any provider such as the HttpProvider if you are
// signing with private key
const mnemonic = "opinion destroy betray …";
const provider = new HDWalletProvider(mnemonic,"http://localhost:8545");
const web3 = new Web3(provider);
const myContract = new web3.eth.Contract(contractAbi,contractAddress);
const tx = {
// this could be provider.addresses[0] if it exists
from: fromAddress,
// target address, this could be a smart contract address
to: toAddress,
// optional if you want to specify the gas limit
gas: gasLimit,
// optional if you are invoking say a payable function
value: value,
// this encodes the ABI of the method and the arguements
data: myContract.methods.myMethod(arg, arg2).encodeABI()
};
接下来如何签名就取决于你使用解锁账号还是私钥了。
3、使用解锁账号签名以太坊交易
如果使用已经解锁账号,那么使用如下的代码进行签名:
const signPromise = web3.eth.signTransaction(tx, tx.from);
注意,如果你使用解锁账号,那么直接调用myContract.methods.myMethod(arg, arg2)
就可以自动完成签名,上面的代码只是为了演示这一签名过程。
4、使用私钥签名以太坊交易
不过如果这是一个锁定的账号,或者根本就不是节点管理的账号,那么你可以使用私钥签名交易:
const signPromise = web3.eth.accounts.signTransaction(tx, privateKey);
在上述两种情况下,返回的都是一个Promise对象,其解析结果为签名后的裸交易字符串。
5、广播签名后的裸交易字符串
由于签名后的裸交易字符串中已经编码了有关合约调用的全部信息,包括方法名、调用参数、gas价格等等,因此可以直接提交给以太坊节点广播:
signPromise.then((signedTx) => {
// raw transaction string may be available in .raw or
// .rawTransaction depending on which signTransaction
// function was called
const sentTx = web3.eth.sendSignedTransaction(signedTx.raw || signedTx.rawTransaction);
sentTx.on("receipt", receipt => {
// do something when receipt comes back
});
sentTx.on("error", err => {
// do something on transaction error
});
}).catch((err) => {
// do something when promise fails
});
再次提醒,上述操作中用到了私钥,因此一定要格外小心安全问题。