将签名的交易发送到 Ropsten
Posted
技术标签:
【中文标题】将签名的交易发送到 Ropsten【英文标题】:Sending signed transactions to Ropsten 【发布时间】:2018-07-28 01:53:13 【问题描述】:使用 web3js 调用不需要签名的函数(例如不更新合约状态的函数)很容易。但是,除了手动解锁我的 MetaMask 钱包并在Remix 环境中调用函数之外,还不清楚如何调用需要签名的函数。
第一次将我的 dapp 部署到 Ropsten 后,我需要调用 createItem(string name, uint price)
100 次以最初填充 items
数组。由于我不想在 Remix 中手动执行,我想编写一个自动执行此操作的脚本。
看起来除了web3js
之外,我还需要ethereumjs-tx
才能在没有MetaMask 的情况下以编程方式签署交易。我还需要有我的account
和privateKey
。有了所有这些信息和官方 web3js 文档,我想出了以下内容:
// Call an external function programatically
const web3 = new Web3(new Web3.providers.HttpProvider("https://ropsten.infura.io"))
const account = "ACCOUNT_ADDRESS"
const privateKey = new Buffer('PRIVATE_KEY', 'hex')
const contract = new web3.eth.Contract(abi, CONTRACT_ADDRESS,
from: account,
gas: 3000000,
)
const functionAbi = contract.methods.myFunctionName(myArgument).encodeABI()
let estimatedGas
contract.methods.myFunctionNAme(myArgument).estimateGas(
from: account,
).then((gasAmount) =>
estimatedGas = gasAmount.toString(16)
)
const txParams =
gasPrice: '0x' + estimatedGas,
to: CONTRACT_ADDRESS,
data: functionAbi,
from: account,
const tx = new Tx(txParams)
tx.sign(privateKey)
const serializedTx = tx.serialize()
web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).
on('receipt', console.log)
代码运行,但txParams
实际上缺少一个键:nonce
。当你运行它时,你会得到以下错误:
Unhandled rejection Error: Returned error: nonce too low
这是我的问题:
-
这通常是我想做的事情的正确方法吗?
如果 1 为真,我如何获取已部署合约的
nonce
参数?
参考文献:
-
http://web3js.readthedocs.io/en/1.0/
https://github.com/ethereumjs/ethereumjs-tx
https://ethereum.stackexchange.com/questions/21402/web3-eth-call-how-can-i-set-data-param
https://ethereum.stackexchange.com/questions/6368/using-web3-to-sign-a-transaction-without-connecting-to-geth
更新:
感谢 Adam,现在我学会了如何获取 nonce
。所以我添加了以下代码:
let nonce
web3.eth.getTransactionCount(account).then(_nonce =>
nonce = _nonce.toString(16)
)
const txParams =
gasPrice: '0x' + gasPrice,
to: CONTRACT_ADDRESS,
data: functionAbi,
from: account,
nonce: '0x' + nonce,
但现在我一直遇到这个异常:
未处理的拒绝错误:返回错误:rlp:输入字符串太长 对于 uint64,解码为 (types.Transaction)(types.txdata).AccountNonce
除了让我找到这个具有异常处理程序的文件 (https://github.com/ethereum/go-ethereum/blob/master/rlp/decode.go) 之外,Google 搜索没有任何帮助。有谁知道如何解决这个问题?
【问题讨论】:
在您的更新中,您已经在 TX 对象中使用了异步(承诺)代码更新nonce
。该对象只有在同一个 then
块内运行时才能看到更新后的 nonce 状态。
【参考方案1】:
通常看起来是正确的。我唯一的问题是你打算如何加载私钥?您将需要提示输入私钥,或者在密钥库中导入/读取并提示输入密码。您可以使用keythereum 来完成此操作(示例代码请参见key import 部分)。
nonce
只是用于为帐户订购交易的递增数字。要获得正确的值,只需使用web3.eth.getTransactionCount(account)
编辑 - 使用带有锁定帐户的 Ganache 运行示例(--secure
选项):
SimpleContract.sol
pragma solidity ^0.4.19;
contract SimpleContract
uint public val = 4;
function increment(uint amt) public
val += amt;
function get() public constant returns (uint)
return val;
test.js
const Web3 = require('web3');
const Tx = require('ethereumjs-tx');
const provider = new Web3.providers.HttpProvider("http://localhost:8545");
const web3 = new Web3(provider);
const account = '0x9a6d82ef3912d5ab60473124bccd2f2a640769d7'; // Ganache
const privateKey = Buffer.from('70f1384b24df3d2cdaca7974552ec28f055812ca5e4da7a0ccd0ac0f8a4a9b00', 'hex');
const contractAddress = '0x6dd7c1c13df7594c27e0d191fd8cc21efbfc98b4'; // Deployed manually
const abi = ["constant":true,"inputs":[],"name":"val","outputs":["name":"","type":"uint256"],"payable":false,"stateMutability":"view","type":"function","constant":true,"inputs":[],"name":"get","outputs":["name":"","type":"uint256"],"payable":false,"stateMutability":"view","type":"function","constant":false,"inputs":["name":"amt","type":"uint256"],"name":"increment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"];
const contract = new web3.eth.Contract(abi, contractAddress,
from: account,
gasLimit: 3000000,
);
const contractFunction = contract.methods.increment(3);
const functionAbi = contractFunction.encodeABI();
let estimatedGas;
let nonce;
console.log("Getting gas estimate");
contractFunction.estimateGas(from: account).then((gasAmount) =>
estimatedGas = gasAmount.toString(16);
console.log("Estimated gas: " + estimatedGas);
web3.eth.getTransactionCount(account).then(_nonce =>
nonce = _nonce.toString(16);
console.log("Nonce: " + nonce);
const txParams =
gasPrice: '0x09184e72a000',
gasLimit: 3000000,
to: contractAddress,
data: functionAbi,
from: account,
nonce: '0x' + nonce
;
const tx = new Tx(txParams);
tx.sign(privateKey);
const serializedTx = tx.serialize();
contract.methods.get().call().then(v => console.log("Value before increment: " + v));
web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).on('receipt', receipt =>
console.log(receipt);
contract.methods.get().call().then(v => console.log("Value after increment: " + v));
)
);
);
结果:
$ npm run my_test
> hello_world_dapp@1.0.0 my_test C:\cygwin\home\adamk\eth\hello_world_dapp
> node ./test.js
Getting gas estimate
Estimated gas: 6919
Nonce: 5
Value before increment: 19
transactionHash: '0xb6fdfc36cc32cb01a2be8832a387da586a44a37c1241bbf2979745536f206ed4',
transactionIndex: 0,
blockHash: '0xb6ee8d4e45585010d9a12d48aa37a478eb6021aad78599f1719cb424ab364bb5',
blockNumber: 10,
gasUsed: 26905,
cumulativeGasUsed: 26905,
contractAddress: null,
logs: [],
status: 1
Value after increment: 22
【讨论】:
嗨,亚当,非常感谢!不过,我仍然无法发送带有 nonce 值的交易。您介意看看更新后的问题吗? 嗯....其他一切看起来都正确。似乎错误告诉您您的交易计数太大,这看起来很奇怪。txParams.nonce
的最终值是多少?
nonce
类似于21
我将其转换为十六进制并在其前面加上0x
仔细检查其余的逻辑、承诺等。我刚刚在 Ganache 中使用锁定帐户运行了一个类似的示例,它工作得很好。我将使用我的示例代码、配置和输出来更新我的答案。它也可能是您的特定配置,因为它与使用 Infura 相关,我认为这需要您创建令牌并在 Truffle 中使用特殊配置。请参阅 Truffle 的 tutorial。我建议在与 Infura 集成之前验证您的代码在本地工作。
亚当,谢谢!它在本地环境中运行良好(使用 ganache-cli 和 truffle 控制台)但使用 ropsten,它挂起并抛出这个:Transaction was not mined within 50 blocks, please make sure your transaction was properly send. Be aware that it might still be mined!
你可以尝试将你的提供者更改为const provider = new Web3.providers.HttpProvider("https://ropsten.infura.io");
并再次尝试相同的代码吗?
【参考方案2】:
这是一个用于在rinkeby上发送签名交易的code-sn-p。以类似的方式,您可以继续进行ropsten测试网络:
const Tx = require('ethereumjs-tx')
const Web3 = require('web3')
const web3 =new
Web3('https://rinkeby.infura.io/v3/9ce80a86c6c54d22aa821d0486a1a47d')
var account1 = '0xa00c70B72150D627cf53872eefD077079116B6a6'
var account2 = '0xD2a8aa318Fbc56995Db8C415BE6E40329DB1C56C'
const privateKey1 = Buffer.from(process.env.PRIVATE_KEY_1,'hex')
const privateKey2 = Buffer.from(process.env.PRIVATE_KEY_2,'hex')
web3.eth.getTransactionCount(account1,(err,txCount)=>
const txObject =
nonce:web3.utils.toHex(txCount),
to:account2,
value:web3.utils.toHex(web3.utils.toWei('0.1','ether')),
gasLimit:web3.utils.toHex(21000),
gasPrice:web3.utils.toHex(web3.utils.toWei('10','gwei')),
console.log(txObject)
const tx = new Tx(txObject)
tx.sign(privateKey1)
const serializedTransaction = tx.serialize()
const raw = '0x'+serializedTransaction.toString('hex')
web3.eth.sendSignedTransaction(raw,(err,txHash)=>
console.log(txHash)
)
)
【讨论】:
以上是关于将签名的交易发送到 Ropsten的主要内容,如果未能解决你的问题,请参考以下文章
错误:在调用 Multicall Contract ethereum 的聚合函数时发送交易需要签名者