Ethereum blockchain network deployment and verification experiment
Article directory
- Ethereum blockchain network deployment and verification experiment
Computer network experiment of the National University of Science and Technology in the fall semester of 2023. Briefly record the experiment process.
1 Go1.19 installation
Installing Go 1.19 version on Ubuntu can be done by following these steps:
1.1 Download Go 1.19
First, open a terminal. Download the latest version from the official website of the Go language. Use wget
the or curl
command to download the Go 1.19 tarball.
wget https://dl.google.com/go/go1.19.linux-amd64.tar.gz
Make sure the download link is up to date. The correct link can be found on the official Go language download page .
1.2 Unzip files
After the download is complete, unzip the file to /usr/local
the directory. This directory is usually used to store user-level software installations. Use the following command:
sudo tar -C /usr/local -xzf go1.19.linux-amd64.tar.gz
This will /usr/local
create a go
directory in .
1.3 Set environment variables
To use Go, you need to add Go's bin directory to your PATH environment variable. Set this variable in the or .profile
file . .bashrc
The following are .profile
sample commands set in the file:
echo "export PATH=\$PATH:/usr/local/go/bin" >> ~/.profile
Then, make the changes take effect:
source ~/.profile
Or, if using a bash shell, modify .bashrc
the file:
echo "export PATH=\$PATH:/usr/local/go/bin" >> ~/.bashrc
source ~/.bashrc
1.4 Verify installation
After the installation is complete, restart the terminal or use the source command to update the current session. Then, use the following command to verify that Go is installed correctly:
go version
If all is well, this will display Go's version information, similar to go version go1.19 linux/amd64
.
Precautions:
- If you have an older version of Go installed before, you may need to uninstall the old version first or make sure the path to the new version is correctly added to your PATH.
- Make sure the Go version you download matches your system architecture (such as amd64, arm64, etc.).
After installation is complete:
2 geth1.10.25 installation
Installing Geth version 1.10.25 on Ubuntu is accomplished by downloading version-specific binaries or compiling from source. Here are the specific steps:
2.1 Method 1: Download the binary file
- Visit the Geth GitHub repository :
- Open the Go-Ethereum Releases page.
- Download a specific version :
- On the Releases page, find
1.10.25
Releases.
- On the Releases page, find
- Download the file for Linux, usually a
geth-linux-amd64-1.10.25-xxxxxx.tar.gz
compressed file named .
-
unzip files :
-
Use the command line to unzip the downloaded file. For example:
tar -xvzf geth-linux-amd64-1.10.25-xxxxxx.tar.gz
-
-
Move Geth to the executable directory :
-
Move the unzipped
geth
executable to a directory in your PATH environment variable, such as/usr/local/bin
.sudo mv geth-linux-amd64-1.10.25-xxxxxx/geth /usr/local/bin/
-
-
Verify installation :
-
Execute the following command in the terminal to confirm the installation:
geth version
-
2.2 Method 2: Compile from source code
If you want to compile Geth from source, you can follow these steps:
-
Install compilation dependencies :
-
Install
build-essential
,git
,golang
and other necessary tools:sudo apt-get update sudo apt-get install -y build-essential git golang
-
-
Clone the source code repository :
-
Clone the Go-Ethereum repository:
git clone https://github.com/ethereum/go-ethereum.git
-
-
Switch to a specific version :
-
Enter the repository directory and switch to
1.10.25
version:cd go-ethereum git checkout v1.10.25
-
-
Compile Geth :
-
Execute the compilation command in the warehouse root directory:
make geth
-
-
Install Geth :
-
Copy the compiled
geth
executable file to/usr/local/bin
:sudo cp build/bin/geth /usr/local/bin/
-
-
Verify installation :
- Run
geth version
to verify the installation.
- Run
Precautions:
- Compiling from source may take some time, depending on your system capabilities.
Note: The above two methods require access to the external network.
(Actually, calsh deleted the library and ran away. I couldn’t find the Linux version of calsh 科学上网
, so I came up with method three)
2.3 Method 3: Download the compiled version directly
Find a compiled version online.
Download: https://download.csdn.net/download/yezhijing/86811420
Unzip the file:
tar -xvzf geth-linux-amd64-1.10.25-xxxxxx.tar.gz
The follow-up is the same as 方法1
4 and 5 .
Effect:
3 Deploy the Ethereum blockchain private network
3.1 Start private chain
Create the genesis block information file: genesis.json
{
"config": {
"chainId": 1001,//据说666没法进行多节点交互,不知道为啥,迷之操作
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0
},
"coinbase" : "0x0000000000000000000000000000000000000000",
"difficulty" : "0x1111111",
"extraData" : "",
"gasLimit" : "0x2fefd8",
"nonce" : "0x0000000000000042",
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp" : "0x00",
"alloc" : {}
}
Initialize an Ethereum node data directory
# 当前目录/home/network/ethdata
geth --datadir ./ init genesis.json
#node1
geth --datadir ./node2 init ./genesis.json
#node2
geth --datadir ./node3 init ./genesis.json
#node3
geth --datadir ./node3 init ./genesis.json
This command is used to initialize the Geth client of an Ethereum node. It configures the node's data storage directory and sets the initial blockchain state (genesis block). Each part of the command has a specific meaning:
-
geth
: This is the command line tool (client) for executing Ethereum nodes. -
--datadir ./ethdata
ethdata
: This parameter tells Geth to store all blockchain data in the folder under the current directory . If the folder does not exist, Geth will automatically create it. -
init
: This subcommand is used to initialize a new blockchain or reset the status of an existing blockchain. -
genesis.json
: This file defines the configuration of the genesis block, which is a JSON format file. This file contains information about the initial state of the blockchain, such as preset account balances, consensus rules, network ID, etc.
After running this command, Geth will genesis.json
create a genesis block based on the information in the file and store it in the specified data directory. This is the first step in building a new private Ethereum network.
The effect is as shown in the figure:
Start an Ethereum node
geth --datadir ./ --networkid 1001 --identity "node1" --port 30303 --http --nodiscover --verbosity 4 console 2 > node1.log
geth --datadir ./node2 --networkid 1001 --identity "node2" --port 30304 --http --http.port 8546 --authrpc.port 8547 --nodiscover --verbosity 4 console 2 > node2.log
geth --datadir ./node3 --networkid 1001 --identity "node3" --port 30305 --http --http.port 8548 --authrpc.port 8549 --nodiscover --verbosity 4 console 2 > node3.log
#==================================================================================
geth --datadir ./ --networkid 1001 --identity "node1" --port 30303 --http --http.port 8545 --http.corsdomain "https://remix.ethereum.org" --http.api "web3,eth,debug,personal,net" --vmdebug --nodiscover --verbosity 4 console 2 > node1.log
# 节点2
geth --datadir ./node2 --networkid 1001 --identity "node2" --port 30304 --http --http.port 8546 --http.corsdomain "https://remix.ethereum.org" --http.api "web3,eth,debug,personal,net" --authrpc.port 8547 --nodiscover --verbosity 4 console 2 > node2.log
# 节点3
geth --datadir ./node3 --networkid 1001 --identity "node3" --port 30305 --http --http.port 8548 --http.corsdomain "https://remix.ethereum.org" --http.api "web3,eth,debug,personal,net" --authrpc.port 8549 --nodiscover --verbosity 4 console 2 > node3.log
# 节点4
geth --datadir ./node4 --networkid 1001 --identity "node3" --port 30306 --http --http.port 8550 --http.corsdomain "https://remix.ethereum.org" --http.api "web3,eth,debug,personal,net" --authrpc.port 8549 --nodiscover --verbosity 4 console 2 > node4.log
geth --datadir /ethdata --networkid 1001 --identity "node1" --port 30303 --http --nodiscover --verbosity 4 console 2 > node1.log
Used to start a Geth node with the following configuration:
-
--datadir /ethdata
:
Specify the directory where Geth stores blockchain data/ethdata
. Please make sure this directory exists and is writable. -
--networkid 1001
:
Set the network ID of the private network to 1001. This value is necessary in private networks to differentiate between different Ethereum networks. -
--identity "node1"
:
Set an identification name "node1" for the node. -
--port 30303
:
Set the communication port between nodes to 30303, which is the default Ethereum P2P port. -
--http
:
Enables the HTTP RPC service so that you can interact with nodes through HTTP requests. -
--nodiscover
:
Disable node discovery. This means that nodes will not actively discover other nodes, which is suitable for private networks. -
--verbosity 4
:
Set the log detail level to 4. This will provide more detailed log output. -
console
:
Open Geth's JavaScript console, allowing node interaction. -
2 > node1.log
:
Redirect standard error (stderr) output to a filenode1.log
. This means errors and some log information will be written to this file.
access log file
Use tail -f node1.log
the command to view the contents of the file in real time node1.log
, which is useful for debugging and monitoring node status.
Create an account within the chainpersonal.newAccount()
-
Open a new terminal and make sure the Ethereum node is running (started with command).
-
Connect to the node's JavaScript console. Run the following command:
geth attach geth attach ipc:./geth.ipc
-
In the JavaScript console, create a new account using the following command:
personal.newAccount()
Results of the
personal.newAccount()
Passphrase:
Repeat passphrase:
“0x6b754c28176d8146dabddf3a3b3f310802761135”
You will be prompted for a password to protect the new account and generate a new Ethereum address.
Start miningminer.start()
In the JavaScript console, run the following command to start mining (mining is possible on a private network, but it should not be possible if the public network does not have strong computing power):
miner.start()
This will start the mining operation and send mining rewards to the created account.
3.2 Multi-node interaction
Create multiple folders and nodes
-
Create multiple directories, each directory will be used for a separate node
node2
,node3
,node4
. -
Within each node directory, create a new data directory to store node data
./node2
,./node3
,./node4
. -
In each node directory, use the genesis block information file
genesis.json
so that every node uses the same genesis block. -
Start different nodes with different identities, ports, and data directories. For example, for the second node:
geth --datadir ./node2 --networkid 1001 --identity "node2" --port 30304 --http --http.port 8546 --http.corsdomain "https://remix.ethereum.org" --http.api "web3,eth,debug,personal,net" --authrpc.port 8547 --nodiscover --verbosity 4 console 2 > node2.log
For the third node:
geth --datadir ./node3 --networkid 1001 --identity "node3" --port 30305 --http --http.port 8548 --http.corsdomain "https://remix.ethereum.org" --http.api "web3,eth,debug,personal,net" --authrpc.port 8549 --nodiscover --verbosity 4 console 2 > node3.log
And so on.
Establish connections between nodes
admin.addPeer()
Add node using
In each node's JavaScript console, you can use admin.addPeer()
to add additional nodes. For example, for node 2, you can execute the following command:
admin.addPeer("enode://enode-info")
Among them, enode-info
is the information of other nodes enode
.
use admin.addPeer()
1. Use the admin.nodeInfo.enode command in the first node to view the enode information, as shown below:
admin.nodeInfo.enode
"enode://2c4eb7a267b1c00a8550e0237277234ca4bf8076b48268693e689f0657d6eb40eb88ee3f4d619b2361471c72881918a300a0de2943f6a29bd2a7c78e7a820d85@127.0.0.1:30303?discport=0"
2. Use the admin.addPeer command in the new node to add a contact, as shown below:
admin.addPeer("enode://2c4eb7a267b1c00a8550e0237277234ca4bf8076b48268693e689f0657d6eb40eb88ee3f4d619b2361471c72881918a300a0de2943f6a29bd2a7c78e7a820d85@127.0.0.1:30303?discport=0")
true
3. After the addition is completed, it will appear as shown in the figure.
View information about peers that the current Ethereum node is connected to
admin.peers
--datadir
Add static-nodes.json
files in directory
--datadir
Create a file in the node's directory static-nodes.json
to let nodes know the contact information of other nodes. This file contains information about other nodes enode
.
--bootnodes
Start the node using
When starting a node, you can use --bootnodes
the parameter to specify a bootstrap node. These nodes are usually known nodes on the network and are used to guide new nodes to join the network.
For example, if you want to connect node 2 to node 1, you can use the following command:
geth --datadir ./node2 --networkid 1001 --identity "node2" --port 30304 --http --nodiscover --verbosity 4 --bootnodes "enode://enode-info-of-node1" console 2 > node2.log
Please replace enode-info-of-node1
with the actual information of node 1 enode
.
These steps create multiple nodes, allowing them to connect to each other and interact with the Ethereum private chain. Make sure to modify the configuration to suit the network and needs in practice.
enode-info-of-node1
is a placeholder representing the actual enode
information of node 1. Each Ethereum node has a unique enode
identifier that is used to identify and connect to other nodes in the network. To get node 1 information on node 1 enode
, you can follow these steps:
- Open the JavaScript console of Node 1 and connect to Node 1 as follows:
geth attach
-
In Node 1’s JavaScript console, run the following command to get Node 1’s
enode
information:admin.nodeInfo.enode
This will return
enode
information for node 1 similar to the following (sample data, actual data will vary):"enode://5a5f7d1a3f2b9c39e5e31c1e15da40a9b9853d15a8a746799d9aa8505b0912f2d22c52f9971d144f3c9d2ba1ea2bf6cb75371ccf4f93cbcd10b2f8cd8@192.168.1.100:30303"
Copy and replace this information
enode-info-of-node1
and then use this command to start other nodes so they can connect to node 1.
3.3 remix platform
https://remix.ethereum.org/
Remix is an online integrated development environment (IDE) for Ethereum smart contract development that provides a friendly interface that enables developers to easily create, test and deploy Ethereum smart contracts. Here is some information about the Remix platform:
-
Online IDE : Remix is a web-based online IDE that requires no downloading or installation of any software. Just visit the Remix official website through your browser to get started.
-
Smart contract development : Remix supports the Solidity smart contract language and provides a code editor, compiler and debugging tools to enable developers to create and test smart contracts.
-
Built-in compiler : Remix includes a built-in Solidity compiler that can compile Solidity contracts into bytecode for execution on the Ethereum Virtual Machine.
-
Deployment and testing : Remix allows smart contracts to be deployed on different Ethereum networks and provides simulators and testing tools for contract testing.
-
Interactive debugging : Remix provides interactive debugging functionality, allowing you to step through the code and view variable status during contract execution.
-
Plug-in support : Remix supports plug-ins, and you can install plug-ins to extend its functions, such as integration with hardware wallets, code static analysis, etc.
-
Multi-network support : Remix supports connecting to different Ethereum networks, including local development network, test network, main network, etc.
-
Auto-save : Remix automatically saves your project and code to ensure no progress is lost.
-
Open Source : Remix is an open source project, and its source code can be found and contributed to on GitHub.
Click deploy to start mining
Contract code
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract SimpleStorage {
uint storedData;
function set(uint x) public {
storedData = x;
}
function get() public view returns (uint) {
return storedData;
}
}
abi sentence matter
{
"accounts": {
"account{0}": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"
},
"linkReferences": {},
"transactions": [
{
"timestamp": 1702920383750,
"record": {
"value": "0",
"inputs": "()",
"parameters": [],
"name": "",
"type": "constructor",
"abi": "0x87ffd8ade60e42979d05c9f20c82da7bc3b83edd90b5e21b99f1c7fc22b3f4c4",
"contractName": "SimpleStorage",
"bytecode": "608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820e88ab6cf60239ef75ac75ee4f05782c5ba81fe8d68165ed5320bd223b26e8fb90029",
"linkReferences": {},
"from": "account{0}"
}
}
],
"abis": {
"0x87ffd8ade60e42979d05c9f20c82da7bc3b83edd90b5e21b99f1c7fc22b3f4c4": [
{
"constant": false,
"inputs": [
{
"name": "x",
"type": "uint256"
}
],
"name": "set",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "get",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]
}
}
Four node node information
node1:
{
enode: "enode://1cfeb52d4b3f16dad23bc33b26b4fd1de172ca1887449be5067edb3c2f07e362ddd174141efdccfb73f7bc4ba26de8aedd8b11c6e1b9d0689cc5084cdf798335@127.0.0.1:30303?discport=0",
enr: "enr:-Jy4QJGbI7JRI5myR2Pxqg7Xt5vao0bFSWJ1e8tYuKGndq0VIC1VYQWYUN_NPtcFoNIe5vCBe4OP3L6g7asgmF51Uu2GAYx9rlrsg2V0aMfGhJ0r3hCAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQMc_rUtSz8W2tI7wzsmtP0d4XLKGIdEm-UGfts8LwfjYoRzbmFwwIN0Y3CCdl8",
id: "af1588b49ceded64e6ffea5ff8975e5feb411566c2d083eb3e6efd2ed37e5dfa",
ip: "127.0.0.1",
listenAddr: "[::]:30303",
name: "Geth/node1/v1.10.25-stable-69568c55/linux-amd64/go1.18.5",
ports: {
discovery: 0,
listener: 30303
},
protocols: {
eth: {
config: {
chainId: 1001,
eip150Block: 0,
eip150Hash: "0x0000000000000000000000000000000000000000000000000000000000000000",
eip155Block: 0,
eip158Block: 0,
homesteadBlock: 0
},
difficulty: 17895697,
genesis: "0xcc97a15707e1a3cf64433ce47735b831073c2995ad8b0618169abb8dac9c57c9",
head: "0xcc97a15707e1a3cf64433ce47735b831073c2995ad8b0618169abb8dac9c57c9",
network: 1001
},
snap: {}
}
}
node2:
{
enode: "enode://c79420eadd69edbaa4c4d53e9039d6a063c0376be2bfb5551fd2b69b3505c1c7521efa4c25e9b3929e1bd2ac0bbbb5fbc7bf1a70e9f89744459add2869e259e7@127.0.0.1:30304?discport=0",
enr: "enr:-Jy4QAFVZFNhaOj91TF5_yBRhLWwbYd-Bq_R8X9GL9hQ6n-xddNTu-vUQpfJrvqGjWO55nWhIWleMySrJUyXsl2CKe6GAYx9tJSlg2V0aMfGhJ0r3hCAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPHlCDq3WntuqTE1T6QOdagY8A3a-K_tVUf0rabNQXBx4RzbmFwwIN0Y3CCdmA",
id: "83f9dcf2e1885cbede6e86bd098381732e678fe5b8e995c6009323b8b68cc33d",
ip: "127.0.0.1",
listenAddr: "[::]:30304",
name: "Geth/node2/v1.10.25-stable-69568c55/linux-amd64/go1.18.5",
ports: {
discovery: 0,
listener: 30304
},
protocols: {
eth: {
config: {
chainId: 1001,
eip150Block: 0,
eip150Hash: "0x0000000000000000000000000000000000000000000000000000000000000000",
eip155Block: 0,
eip158Block: 0,
homesteadBlock: 0
},
difficulty: 17895697,
genesis: "0xcc97a15707e1a3cf64433ce47735b831073c2995ad8b0618169abb8dac9c57c9",
head: "0xcc97a15707e1a3cf64433ce47735b831073c2995ad8b0618169abb8dac9c57c9",
network: 1001
},
snap: {}
}
}
node3:
{
enode: "enode://c8acdec78e2920e68526c577eb474dd6579af767ec8a00fc14a0047d8e5577794b6abba15d3deffb321983a2c81f2ca0547398248005efb8c2cccac27b875a2c@127.0.0.1:30305?discport=0",
enr: "enr:-Jy4QFVKitcbQ5k0mfigmp5AoLINiCHWq95n7-Fw0yeHPB67RgmkDclHixlEqHixQD08sEcvJHmrX3R1ENjmD4xZhfGGAYx91NwEg2V0aMfGhJ0r3hCAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQLIrN7Hjikg5oUmxXfrR03WV5r3Z-yKAPwUoAR9jlV3eYRzbmFwwIN0Y3CCdmE",
id: "d4f0afc790c004126c95bdd71e120c3d3814c2d519446b26609ab6941699d130",
ip: "127.0.0.1",
listenAddr: "[::]:30305",
name: "Geth/node3/v1.10.25-stable-69568c55/linux-amd64/go1.18.5",
ports: {
discovery: 0,
listener: 30305
},
protocols: {
eth: {
config: {
chainId: 1001,
eip150Block: 0,
eip150Hash: "0x0000000000000000000000000000000000000000000000000000000000000000",
eip155Block: 0,
eip158Block: 0,
homesteadBlock: 0
},
difficulty: 17895697,
genesis: "0xcc97a15707e1a3cf64433ce47735b831073c2995ad8b0618169abb8dac9c57c9",
head: "0xcc97a15707e1a3cf64433ce47735b831073c2995ad8b0618169abb8dac9c57c9",
network: 1001
},
snap: {}
}
}
node4:
{
enode:
"enode://5a5f7d1a3f2b9c39e5e31c1e15da40a9b9853d15a8a746799d9aa8505b0912f2d22c52f9971d144f3c9d2ba1ea2bf6cb75371ccf4f93cbcd10b2f8cd8@192.168.1.100:30303",
enr: "enr:-Jy4QAFVZFNhaOj91TF5_yBRhLWwbYd-Bq_R8X9GL9hQ6n-xddNTu-vUQpfJrvqGjWO55nWhIWleMySrJUyXsl2CKe6GAYx9tJSlg2V0aMfGhJ0r3hCAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPHlCDq3WntuqTE1T6QOdagY8A3a-K_tVUf0rabNQXBx4RzbmFwwIN0Y3CCdmA",
id: "83f9dcf2e1885cbede6e86bd098381732e678fe5b8e995c6009323b8b68cc33d",
ip: "127.0.0.1",
listenAddr: "[::]:30304",
name: "Geth/node2/v1.10.25-stable-69568c55/linux-amd64/go1.18.5",
ports: {
discovery: 0,
listener: 30304
},
protocols: {
eth: {
config: {
chainId: 1001,
eip150Block: 0,
eip150Hash: "0x0000000000000000000000000000000000000000000000000000000000000000",
eip155Block: 0,
eip158Block: 0,
homesteadBlock: 0
},
difficulty: 17895697,
genesis: "0xcc97a15707e1a3cf64433ce47735b831073c2995ad8b0618169abb8dac9c57c9",
head: "0xcc97a15707e1a3cf64433ce47735b831073c2995ad8b0618169abb8dac9c57c9",
network: 1001
},
snap: {}
}
}
3.4 Commands for storage operations
const Web3 = require('web3');
const web3 = new Web3('https://127.0.0.1:8854');
const contractABI = [0x5B38Da6a701c568545dCfcB03FcB875f56beddC4]; // 合约的ABI
const contractAddress = '0x87ffd8ade60e42979d05c9f20c82da7bc3b83edd90b5e21b99f1c7fc22b3f4c4'; // 合约地址
const privateKey = '0xbcea812a244c0ac6ce96d877a023747c20c55c91'; // 发送交易的私钥
const contract = new web3.eth.Contract(contractABI, contractAddress);
const key = '123456';
const value = '10';
const data = contract.methods.store(key, value).encodeABI();
web3.eth.accounts.signTransaction({
to: contractAddress,
data: data,
gas: '5000000', // 适当设置gas
}, privateKey).then(signedTx => {
web3.eth.sendSignedTransaction(signedTx.rawTransaction)
.on('receipt', receipt => {
console.log('Transaction receipt:', receipt);
});
});
3.5 Command to query value operation based on key
const Web3 = require('web3');
const web3 = new Web3('https://127.0.0.1:8854');
const contractABI = [0x5B38Da6a701c568545dCfcB03FcB875f56beddC4]; // 合约的ABI
const contractAddress = '0x87ffd8ade60e42979d05c9f20c82da7bc3b83edd90b5e21b99f1c7fc22b3f4c4'; // 合约地址
const contract = new web3.eth.Contract(contractABI, contractAddress);
const key = '123456';
contract.methods.getValue(key).call()
.then(result => {
console.log('Value for key', key, 'is', result);
});