注意:该文档记载了一次失败的复现实验。欲跟随正确步骤进行复现,请见《8 重做调度系统复现实验》。
调度系统的复现需要在6号实验的基础上进行,故笔者新建了DispatchSystem
文件夹之后,将DeployMultiNodes
中的所有文件都全部复制到了DispatchSystem
文件夹下备用。
同时,将上一次实验中部署合约获得的地址准备好:
storeMap.address //"0x5bdcb6a89c990b0c82ba78f38716ad46a17049fc"
traffic.address // "0x1249f7546f04ac60d83f15bdfc7030affe457612"
准备好成佳壮前辈的代码仓库(位于本仓库的inves tigation-cjzhuang2020文件夹中
);这次实验的大部分操作都要在这个仓库中进行。根据复现手册中的说法,需要将cjz_underg_2021_09/mapContract.js
和cjz_underg_2021_09/trafficContract.js
中的内容进行一些修改:
// mapContract.js
var mapContractAddress = "0x5bdcb6a89c990b0c82ba78f38716ad46a17049fc";
// ...
// trafficContract.js
var trafficContractAddress = '0x1249f7546f04ac60d83f15bdfc7030affe457612';
根据复现手册,运行一个名为auto.js
的脚本可以自动地创建账户。笔者在代码仓库中搜索到很多文件,在经过比较之后,**认为成佳壮前辈的仓库中的那份文件才是文章中所说的能够创建账户的脚本。**打开一个终端,切换到该脚本所在的路径下,执行:
node auto.js
!不明晰点!报错了?
解决方法:打开auto.js
,翻到node1 = childProcess.exec(...)
发现其中的路径和二进制文件的名字都不太对!?于是将该字符串改为下面这样子(其中一串cd
的目的是将工作目录切换到00节中提到的DispatchSystem/Node1
):
"gnome-terminal -e 'bash -c \"cd ~; cd 文档; cd ReproducingBlockchain; cd DispatchSystem; cd Node1; geth1 --datadir ./gethdata --networkid 91036 --port 30303 --rpc --rpcaddr 127.0.0.1 --rpcport 8545 --rpcapi 'personal,net,eth,web3,admin' --rpccorsdomain='*' --ws --wsaddr='localhost' --wsport 8546 --wsorigins='*' --wsapi 'personal,net,eth,web3,admin' --nodiscover --allow-insecure-unlock --dev.period 1 --syncmode='full' console\"'"
解决问题之后,会看到一个启动的控制台。
!不明晰点!没有按照预想的方式执行代码,报了Unhandled promise rejection
错误。
解决方法:研究了半天,原来是因为gnome-terminal -e
这个语法已经被废弃,被当作是错误给throw出来了,导致后续代码无法执行。于是,将auto.js
其中的这个部分改为这样(注意accountConfig
的参数,它们的意义在源代码的注释中有解释):
// ...
async function (error, stdout, stderr) {
if (stderr) {
console.log(stderr);
// throw stderr;
}
console.log("ok");
console.log(stdout);
sleep(1000);
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
accountConfig(0, 4, 4);
}
!不明晰点!咋又报Unhandled promise rejection
了?
解决方法:将accountConfig
函数注释掉之后,发现报错消失,控制台不崩溃。因此,一定是accountConfig
函数出现了问题。
在该函数中上下搜索一番,发现有一句这个:
Promise.all(task).then(...)
尝试将它改为:
Promise.all(task).then(...).catch((err) => { console.log(err); })
再该一改createAccount
函数:
async function creatAccount() {
return await web3.eth.personal.newAccount('123456').then((res) => {
return res;
}).catch((err) => { console.err(err); });
}
问题解决,终端中可以看到一个警告、result1
和result2
的内容。关闭控制台。在auto.js
所在的文件夹中找到新生成的genesis.json
备用。
使用01节中获得的新genesis.json
替换DispatchSystem/Node1
下的同名文件,然后删除DispatchSystem/Node1/geth
文件夹。最后,在DispatchSystem/Node1
运行区块链初始化指令:
geth1 --identity "MyEth" --rpc --rpcaddr 127.0.0.1 --rpcport "8545" --rpccorsdomain "*" --datadir gethdata --port "30303" --nodiscover --rpcapi "eth,net,personal,web3" --networkid 91036 init genesis.json
启动控制台:
geth1 --datadir ./gethdata --networkid 91036 --port 30303 --rpc --rpcaddr 127.0.0.1 --rpcport 8545 --rpcapi 'personal,net,eth,web3,admin' --rpccorsdomain='*' --ws --wsaddr='localhost' --wsport 8546 --wsorigins='*' --wsapi 'personal,net,eth,web3,admin' --nodiscover --allow-insecure-unlock --dev.period 1 --syncmode='full' console
在控制台中,验证账户余额:
for (i = 0; i < eth.accounts.length; i++) { console.log(eth.getBalance(eth.accounts[i])) }
// 5e+40...
在打开的控制台中,输入如下的2条指令来解锁所有账户:
eth.accounts.length
// 8
for (i = 0; i < eth.accounts.length; i++) { personal.unlockAccount(eth.accounts[i],"123456",0) }
// true
随后开始挖矿:
miner.start()
来到uploadmap_cjz.js
文件(位于inves tigation-cjzhuang2020/cjz_underg_2021_09/tree_blockchain
)。
观察该文件,发现有这么一段内容:
// contract address
var myContractInstance = MyContract.at("0xbc9eed91b36c78b87dfdf0c2ca7f0c59111cd620");
var account = "0x9c20114120d1d799dbb680564756aa5a3ff1a72b";
复现手册中如下描述:
修改
myContractInstance
和account
(改为现有的一个账户就行)的值
故,笔者如下改动:将myContractInstance
改为上一个实验中部署合约StoreMap
的地址;将account
改为eth.accounts[0]
的值。
修改完成之后,在终端中运行该脚本:
node uploadmpa._cjz.js
终端不断有新输出,直到出现“地图数据上传完成”字样,关闭终端。
复现手册提及,可以使用vehicle_test.py
来初始化车辆的状态。简单观察代码后,发现需要安装如下Python库:
selenium
需要在该文件的import
部分加上一句:
from importlib import reload
注释 掉这一句:
sys.setdefaultencoding('utf8')
又注意到,代码中使用Chrome进行自动化实验,故需要下载Google Chrome和对应版本的web driver。
完成以上操作之后,使用如下指令启动脚本:
python3 vehicle_test.py
!不明晰点!报错:selenium没有叫做find_element_by_id
的属性。
解决方法:将所有find_element_by_id(...)
替换为find_element(by=By.id, value=...)
。另外,也将所有find_element_by_class_name(...)
换成find_element(by=By.CLASS_NAME, value=...)
。
做好以上准备之后,尝试先后运行vehicle_test.py
和passenger_test.py
:
python3 vehicle_test.py
python3 passenger_test.py
!不明晰点!遇到这个错误:selenium.common.exceptions.TimeoutException
。很明显,是由于网页没有按照期望的方式运行,导致wait_until
超时,进而引发的该错误。
检查vehicle_test.py
所打开的前端页面sys_vehicle_region.html
,发现其WeSocket连接的端口是8548,并非我们使用的8546(这是因为01节中给出的node = childProcess.exec(...)
中给出的代码中,有一个--wsport 8546
参数)。将其改为:
// ...
const PORT = 8546;
// ...
web3Map = new Web3(new Web3.providers.WebsocketProvider(`ws://127.0.0.1:${PORT}`));
web3Traffic = new Web3(new Web3.providers.WebsocketProvider(`ws://127.0.0.1:${PORT}`));
重新尝试运行,仍然报错,报错位置位于async function initVehicle()
。经过排查,发现全局变量vehicles
中并不是在运行auto.js
时写入vehicleAccounts.py
中的账号。尝试使用后者中存储的账号手动将vehicles.js
中的内容替换掉,重新运行测试。仍然报错,提示缺少Gas。前往DispatchSystem/Node1
,在genesis.json
中为所有账号分配余额(参考这个)。经过上述更改,在运行vehicle_test.py
时,未见其他报错。
如法炮制尝试解决运行passenger_test.py
的问题。首先是initContract()
的开头,改为:
if (typeof web3 !== 'undefined') {
web3Map = new Web3(web3.currentProvider);
web3Traffic = new Web3(web3.currentProvider);
} else {
web3Map = new Web3(new Web3.providers.WebsocketProvider("ws://127.0.0.1:8546")); // 注意这行,原文件里不是这样的
web3Traffic = new Web3(new Web3.providers.WebsocketProvider("ws://127.0.0.1:8546"));
}
接下来,将passengerAccounts.py
中内容放到passengers.js
中的对应位置。
最后,按照如下顺序启动系统:
- 保持链工作并挖矿,启动
vehicle_test.py
,按下F12打开调试工具,观察Console直至出现“车辆上传了位置”字样; - 启动
passenger_test.py
,按下F12打开调试工具,观察输出和界面的变化
**!不明晰点!当前没有合适的车辆???**根据报错提示,定位到getRoads.js
中的async function getTypes()
函数处。
!根据成佳壮的说法,上传地图只能上传一次!