Ganache Cli私有网络账户ETH莫名归零问题求助
我使用以下命令启动Ganache CLI私有网络,为3个账户初始化了大额ETH:
ganache-cli -h "159.89.119.189" -a 3 -e "1000000000000000000000000000" --secure -u 0 -u 1 -u 2 -s 20
但运行数分钟后,所有账户的ETH全部归零,无法执行任何交易或合约调用。我的DApp通过以下app.js代码连接该网络,请求帮忙排查问题:
App = { web3Provider: null, contracts: {}, account: 0x0, coinbase: 0x0, coinbase_amount: 0, loading: false, init: function () { return App.initWeb3(); }, initWeb3: function () { // initialize web3 if (typeof web3 !== 'undefined') { //reuse the provider of the Web3 object injected by Metamask App.web3Provider = web3.currentProvider; } else { //create a new provider and plug it directly into our local node //App.web3Provider = new Web3.providers.HttpProvider('http://localhost:8545'); App.web3Provider = new Web3.providers.HttpProvider('http://159.89.119.189:8545'); } web3 = new Web3(App.web3Provider); App.getCoinbase(); return App.initContract(); }, hostname: function () { return window.location.origin; }, setAccount: function (address) { App.account = address; }, displayAccountInfo: function () { // console.log(App); if (App.account != 0) { toastr.remove(); toastr.info('Getting Account Info', {timeOut: 300000}); $('#account').text(App.account); App.getBalance(); } }, getBalance: function() { web3.eth.getBalance(App.account, function (err, balance) { if (err === null) { if(web3.fromWei(balance, "ether") == 0){ setTimeout(App.getBalance(), 60000); } else { console.log(web3.fromWei(balance, "ether")); toastr.remove(); $('#accountBalance').text(web3.fromWei(balance, "ether") + " ETH"); } } }) }, getCoinbase: function () { web3.eth.getCoinbase(function (err, account) { if (err === null) { App.coinbase = account; // $('#account').text(account); web3.eth.getBalance(account, function (err, balance) { if (err === null) { App.coinbase_amount = web3.fromWei(balance, "ether").toNumber(); console.log(App.coinbase, App.coinbase_amount) } }) } }); }, transfer: function() { web3.personal.unlockAccount(App.coinbase, "pass12345", 100000, function (err, result) { console.log(result) console.log(err) web3.personal.unlockAccount(App.account, "pass@123", 100000, function (err, result) { web3.eth.sendTransaction({ from: App.coinbase, to: App.account, value: web3.toWei(10, "ether") }, function (err, result) { if (err == null) { console.log("sent money"); console.log(result) console.log(err) web3.eth.getBalance(App.account, function (err, balance) { if (err === null) { console.log(web3.fromWei(balance, "ether") + " ETH"); } }) } else { console.log(err); } }) }); }); }, register: function () { let email = $('#inputEmail'); let fname = $('#inputFname'); let lname = $('#inputLname'); let password = $('#inputPassword'); let btnRegister = $('#btnRegister'); if (email.val() == "" || fname.val() == "" || lname.val() == "" || password.val() == "") { toastr.error('Please fill all fields'); return false; } btnRegister.attr("disabled", 'disabled'); web3.personal.newAccount("pass@123", function (err, data) { let address = data; if (err === null) { let postData = { email: email.val(), fname: fname.val(), lname: lname.val(), password: password.val(), address: data } $.ajax({ method: "POST", url: App.hostname() + "/register", data: postData, success: function (data) { console.log(data) if (data.status == "success") { web3.personal.unlockAccount(address, "pass@123", 1000, function (err, result) { web3.eth.sendTransaction({ from: App.coinbase, to: address, value: web3.toWei(10, "ether") }, function (err, result) { if (err == null) { console.log("sent money"); console.log(result) web3.eth.getBalance(App.coinbase, function (err, balance) { if (err === null) { console.log("coinbase "+web3.fromWei(balance, "ether") + " ETH"); } }) web3.eth.getBalance(address, function (err, balance) { if (err === null) { console.log(web3.fromWei(balance, "ether") + " ETH"); } }) } else { console.log(err); } toastr.success("Success."); window.location.href = App.hostname(); }) }); } else { toastr.error(data.data); } btnRegister.attr("disabled", false); }, error: function (err) { toastr.error('Error Registering'); btnRegister.attr("disabled", false); } }); } else { toastr.error('Error Registering'); btnRegister.attr("disabled", false); return false; } }) }, login: function () { let email = $('#inputEmail'); let password = $('#inputPassword'); let btnLogin = $('#btnLogin'); if (email.val() == "" || password.val() == "") { toastr.error('Please fill all fields'); return false; } btnLogin.attr("disabled", 'disabled'); let postData = { email: email.val(), password: password.val(), } $.ajax({ method: "POST", url: App.hostname() + "/login", data: postData, success: function (data) { console.log(data) if (data.status == "success") { toastr.success("Success."); window.location.href = App.hostname(); } else { toastr.error(data.data); } btnLogin.attr("disabled", false); }); } }
我帮你梳理几个可能导致账户余额归零的原因,以及对应的解决办法:
--secure参数与账户解锁的冲突
你启动Ganache时加了--secure参数,这个参数会强制所有交易必须签名,同时默认禁用账户解锁功能。虽然你用-u 0 -u 1 -u 2解锁了前三个账户,但--secure可能会覆盖或干扰这个设置,甚至在运行一段时间后重置账户状态。建议先去掉
--secure参数重新启动Ganache,因为-u已经帮你解锁了指定账户,--secure在这里不仅多余,还可能引发异常。修改后的启动命令如下:ganache-cli -h "159.89.119.189" -a 3 -e "1000000000000000000000000000" -u 0 -u 1 -u 2 -s 20内存链状态丢失(进程意外终止)
Ganache CLI默认是内存区块链,所有数据只存放在内存中。如果Ganache进程因为服务器资源不足(比如内存耗尽被系统杀死)、网络波动或其他意外原因终止,再重启的话会重新生成全新的账户和状态,之前的余额自然就没了。你可以先检查服务器上Ganache进程是否还在运行,或者查看系统日志(比如
/var/log/syslog或dmesg)有没有进程被终止的记录。如果要持久化数据,加上--db参数指定一个存储目录:ganache-cli -h "159.89.119.189" -a 3 -e "1000000000000000000000000000" -u 0 -u 1 -u 2 -s 20 --db ./ganache-data这样即使重启Ganache,也会从指定目录加载之前的区块链数据。
DApp代码中的潜在问题
看你的app.js代码,有几个细节需要修正:getBalance中的延迟调用错误:当余额为0时,你写的setTimeout(App.getBalance(), 60000);会立即执行getBalance,而不是延迟60秒执行,这会导致频繁递归调用,可能触发Ganache的限制或者消耗大量资源。应该改成:setTimeout(App.getBalance, 60000);- 账户解锁的冗余操作:你在
transfer和register中反复调用unlockAccount,如果Ganache的解锁状态是持久的(默认解锁是永久的,除非进程重启),多次解锁可能没必要,甚至引发异常。可以先检查账户是否已经解锁,再决定是否调用解锁函数。 - 交易后余额查询的时机:发送交易后立即查询余额,可能交易还没被打包上链,导致余额显示不准确。建议监听交易回执,确认交易上链后再查询余额。
服务器资源限制
如果你的服务器内存不足,Ganache作为内存链,运行一段时间后可能因为内存耗尽被系统的OOM Killer终止。你可以通过free -h命令查看服务器内存使用情况,或者检查系统日志中的OOM相关记录。如果是内存不足,建议升级服务器配置,或者限制Ganache的内存使用。
内容的提问来源于stack exchange,提问作者Dike Jude

