| PolarSPARC |
Hyperledger Besu Private Network using Docker
| Bhaskar S | *UPDATED*07/04/2025 |
Overview
Hyperledger Besu is a Java-based, open source Ethereum client that is designed for the Enterprise use-cases and can be run in both the public (permissionless) and the private (permissioned) scenarios.
Hyperledger Besu includes the following capabilities:
Execution Environment :: The Ethereum Virtual Machine (EVM) that is responsible for the processing of transactions and the execution of contracts that are coded as business logic (Smart Contracts)
Persistence Storage :: The blockchain store for persisting the state of the blockchain (account balances, etc) as well as the immutable blocks of transactions that led to the current state. Under the hood, it uses RocksDB (a key-value database) for storage of data on each Ethereum node
Consensus Algorithms :: The mechanism used by the Ethereum nodes distributed across the network to achieve agreement on the current state of the blockchain as well as the validity of the transactions. For the public (permissionless) network, it uses the Proof of Stake (PoS) algorithm, and for the private (permissioned) network, it uses the Proof of Authority (PoA) algorithm. Currently, there are three PoA algorithms supported - Clique, Istanbul Byzantine Fault Tolerance (IBFT 2.0), and Quorum Byzantine Fault Tolerance ( QBFT)
Peer-2-Peer Networking :: The nodes in the for Ethereum network communicate with each other to discover other nodes in the network and to synchronize the state of the blockchain on each node
Application Connectivity :: The use of JSON based messages over HTTP to allow business applications to interact with the Ethereum blockchain
Monitoring :: The mechanism to monitor the health and performance of the nodes in the Ethereum network
The following diagram illustrates the high-level architecture of Hyperledger Besu:
In the article Introduction to Ethereum - Part 1, we introduced some basic concepts of Ethereum, got our hands dirty in setting up a private network with 4 accounts (one for each of the entities - the bank, the buyer, the dealer, & the dmv), and demonstrated sending a sample transaction between two of the accounts.
In this article, we will demonstrate the same transaction behavior, but with a different setup using the official Docker image for Hyperledger Besu.
Installation
The setup will be on a Ubuntu 24.04 LTS based Linux desktop.
Ensure Docker is installed and setup (Reference).
Also, ensure the Python programming language (version 3.1x) is installed and setup.
Assuming that we are logged in as polarsparc and the current working directory is the home directory /home/polarsparc, we will setup a directory structure by executing the following commands in a terminal window:
$ mkdir -p HyperledgerBesu/conf
$ mkdir -p HyperledgerBesu/data{/bank/keystore,/buyer/keystore,/dealer/keystore,/dmv/keystore}
Now, change the current working directory to the directory /home/polarsparc/HyperledgerBesu. In the following paragraphs we will refer to this location as $BESU_HOME.
Visit the link Hyperledger Besu to determine the current version of the docker image. At the time of this article, the current stable version was 25.7.0.
To pull and download the docker image for Hyperledger Besu, execute the following command:
docker pull hyperledger/besu:25.7.0
The following should be the typical output:
25.7.0: Pulling from hyperledger/besu b08e2ff4391e: Pull complete 65b4faee11fb: Pull complete 4f4fb700ef54: Pull complete 8c5b0b547769: Pull complete dcc6ce8db6bf: Pull complete bc0fb53ff1a9: Pull complete Digest: sha256:09317071e2742ca0530b504ad40f3efeba3a0ef85a0038917bc056865fce7c50 Status: Downloaded newer image for hyperledger/besu:25.7.0 docker.io/hyperledger/besu:25.7.0
Once the download is complete, execute the following command to check everything was ok with the image for Hyperledger Besu:
$ docker run --rm --name besu hyperledger/besu:25.7.0 --version
The following would be the typical output:
besu/v25.7.0/linux-x86_64/openjdk-java-21
Next step is to download and install the Python module for Web3. Execute the following command:
python -m pip install web3
This completes the installation part. Moving on to setup the private network.
Private Network Setup
For this demonstration, we will need 4 accounts, one for each of the entities - the bank, the buyer, the dealer, and the dmv.
We will create the 4 accounts using MyEtherWallet.
First, we will create an account for the entity bank. Click on Create a new wallet as shown in the illustration below:
Next, click on Other Methods as shown in the illustration below:
Next, click on Create a software wallet as shown in the illustration below:
Next, click on Keystore File as shown in the illustration below:
Next, enter a secure password in the text box Password, re-enter the same password in the text box Confirm Password, and then click on Create Wallet as shown in the illustration below:
Next, click on Acknowledge & Download as shown in the illustration below:
This action will take a few seconds and the keystore file UTC--{timestamp}--{address} will be downloaded to the directory $HOME/Downloads.
Move the keystore file we just created for the bank to the appropriate directory by executing the following command:
$ mv $HOME/Downloads/UTC--* $BESU_HOME/data/bank/keystore
Perform the above steps for creating a new keystore for each of the remaining entities - the buyer, the dealer, and the dmv.
Once the 4 keystore files are created and moved to the appropriate location, execute the following command at the location $BESU_HOME:
$ tree ./data
The following would be the typical output:
./data
|-- bank
| |-- keystore
| |-- UTC--2025-07-04T19-35-44.627Z--4ed5d88f55c4715298d577d17d3ffc57bf3dc981
|-- buyer
| |-- keystore
| |-- UTC--2025-07-04T19-39-12.796Z--5a4240acd295d673c13e197b99d0fa70bbd64ca6
|-- dealer
| |-- keystore
| |-- UTC--2025-07-04T19-41-36.877Z--3b66024245bc6b4c4f183d203170b6fa78754948
|-- dmv
|-- keystore
|-- UTC--2025-07-04T19-42-07.826Z--6a571605bf60218e3d155eca6d6f507e83e2ca14
9 directories, 4 files
From the Output.3 above, we have the 4 accounts and their respective addresses as follows:
bank :: 4ed5d88f55c4715298d577d17d3ffc57bf3dc981
buyer :: 5a4240acd295d673c13e197b99d0fa70bbd64ca6
dealer :: 3b66024245bc6b4c4f183d203170b6fa78754948
dmv :: 6a571605bf60218e3d155eca6d6f507e83e2ca14
In order to setup a private Ethereum blockchain network, we need to initialize and create the Genesis block. The Genesis block is the first block of the blockchain that has no previous block. We will use the following template to create the genesis file:
{
"config":{
"chainId": 21,
"berlinBlock": 0,
"clique":{
"blockperiodseconds": 10,
"epochlength": 30000
}
},
"coinbase":"0x0000000000000000000000000000000000000000",
"difficulty":"0x1",
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000bank-address0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit":"0xa00000",
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce":"0x0",
"timestamp":"0x5c51a607",
"alloc": {
"bank-address": {
"balance": "20000000000000000000"
},
"buyer-address": {
"balance": "10000000000000000000"
},
"dealer-address": {
"balance": "5000000000000000000"
},
"dmv-address": {
"balance": "3000000000000000000"
}
},
"gasUsed": "0x0",
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000"
}
Create a file called genesis.json with above contents in the directory $BESU_HOME/conf.
For this demonstration, we will be using the Clique algorithm, which is one of the supported Proof of Authority (PoA) consensus algorithm.
The PoA is most suitable for Enterprise scenarios where the participants know and trust each other. As a result, the PoA consensus algorithms have faster block creation times and higher transaction throughput.
Clique is a reputation-based algorithm in which only approved and trusted accounts (known as the Signers) can validate transactions and create blocks. If there are more than one Signers, they take turns to create the next block.
We will use the bank as the trusted Signers in our private Ethereum network. The address of the Signers is specified in the extraData field.
The following illustration shows the template for the extraData field:
Using the bank's address as the Signer (which is a 40 character hex string) f1da86e0f5288f0537865e7b51edd302089ae06f, the following illustration shows the final extraData field:
After modifying the contents of the file genesis.json (located in the directory $BESU_HOME/conf) with the Signer and the 4 account addresses, the following would be the contents of the genesis file:
{
"config":{
"chainId": 21,
"berlinBlock": 0,
"clique":{
"blockperiodseconds": 10,
"epochlength": 30000
}
},
"coinbase":"0x0000000000000000000000000000000000000000",
"difficulty":"0x1",
"extraData":"0x00000000000000000000000000000000000000000000000000000000000000004ed5d88f55c4715298d577d17d3ffc57bf3dc9810000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit":"0xa00000",
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce":"0x0",
"timestamp":"0x5c51a607",
"alloc": {
"4ed5d88f55c4715298d577d17d3ffc57bf3dc981": {
"balance": "20000000000000000000"
},
"5a4240acd295d673c13e197b99d0fa70bbd64ca6": {
"balance": "10000000000000000000"
},
"3b66024245bc6b4c4f183d203170b6fa78754948": {
"balance": "5000000000000000000"
},
"6a571605bf60218e3d155eca6d6f507e83e2ca14": {
"balance": "3000000000000000000"
}
},
"gasUsed": "0x0",
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000"
}
In the following section, we will explain some of the important parameters used in the genysis file shown above:
config :: Describes this private blockchain in terms of the network identifier, the milestone block number, and the consensus engine used
chainId :: It specifies the blockchain network identifier. A value of 1, 2, 3, or 4 refer to the real-world public Ethereum networks currently in use. Since we desire a private network, we will choose an arbitrary value of 21 for our network
berlinBlock :: It specifies the block at which the network changed protocols. Since our network is private and we are starting from scratch, we set the value to 0 (zero) indicating no hard fork
clique :: Indicates we are using the specified PoA consensus algorithm
blockperiodseconds :: Indicates the time interval between the creation of the next block
epochlength :: Indicates the number of blocks after which to checkpoint and reset the pending votes
coinbase :: Indicates the address of the account that will receive the rewards for successful mining of a block
difficulty :: Indicates the level of effort before generating a new block. Higher value indicates more effort on the part of the miner nodes. For our private network, we will keep this value low
extraData :: Indicates the concatenated list of authorized signers. At least one initial signer MUST be specified
gasLimit :: Indicates the limit of the gas fees that will be charged for processing a block
nonce :: Indicates the 64-bit hash corresponding to amount of mining computation effort performed on a block
mixHash :: Indicates the 256-bit hash, that combined with nonce, indicates the amount of computation performed to generate a block
timestamp :: Indicates the creation date and time of the block
alloc :: Indicates the accounts with pre-allocated ethers in our private network. At least one account MUST be specified
gasUsed :: Indicates the total gas used by all transactions in this block. For the genesis block it is ZERO
parentHash :: Indicates the 256-bit hash of the parent block. For the genesis block it is all-ZEROs
Moving along, the next step is to extract the private key for the 4 accounts. For this, we will leverage the web3 module we installed for Python.
Open a new terminal window, change the current directory to $BESU_HOME, and launch the Python interpreter.
To extract the private key for bank, execute the following commands at the Python interpreter prompt:
>>> from web3.auto import w3
>>> with open(''./data/bank/keystore/UTC--2025-07-04T19-35-44.627Z--4ed5d88f55c4715298d577d17d3ffc57bf3dc981') as key_file:
... encrypted_key = key_file.read()
... bank_priv_key = w3.eth.account.decrypt(encrypted_key, 'Bank_Secret')
>>> bank_priv_key
The following would be the typical output:
HexBytes('0x712c1cc999b24753768bad4b3e1371d530abd9693803b627a974009b64b0dbf1')
Copy the private key 712c1cc999b24753768bad4b3e1371d530abd9693803b627a974009b64b0dbf1 (without the 0x) and save it to a file called key in the directory $BESU_HOME/data/bank.
We will repeat the above steps for each of the other accounts buyer, dealer , and dmv.
To extract the private key for buyer, execute the following commands at the Python interpreter prompt:
>>> with open('./data/buyer/keystore/UTC--2025-07-04T19-39-12.796Z--5a4240acd295d673c13e197b99d0fa70bbd64ca6') as key_file:
... encrypted_key = key_file.read()
... buyer_priv_key = w3.eth.account.decrypt(encrypted_key, 'Buyer_Secret')
>>> buyer_priv_key
The following would be the typical output:
HexBytes('0x1680ff9e0ad288d507d04b569ea2d1aa9e5e3b58458e8a5dea7df12d2c41a1b9')
Copy the private key 1680ff9e0ad288d507d04b569ea2d1aa9e5e3b58458e8a5dea7df12d2c41a1b9 (without the 0x) and save it to a file called key in the directory $BESU_HOME/data/buyer.
To extract the private key for dealer, execute the following commands at the Python interpreter prompt:
>>> with open('./data/dealer/keystore/UTC--2025-07-04T19-41-36.877Z--3b66024245bc6b4c4f183d203170b6fa78754948') as key_file:
... encrypted_key = key_file.read()
... dealer_priv_key = w3.eth.account.decrypt(encrypted_key, 'Dealer_Secret')
>>> dealer_priv_key
The following would be the typical output:
HexBytes('0xdb35217c01ade41b401fb2616a83af102ce8c0178fbca298f8af42100bc5aab5')
Copy the private key db35217c01ade41b401fb2616a83af102ce8c0178fbca298f8af42100bc5aab5 (without the 0x) and save it to a file called key in the directory $BESU_HOME/data/dealer.
Finally, to extract the private key for dmv, execute the following commands at the Python interpreter prompt:
>>> with open('./data/dmv/keystore/UTC--2025-07-04T19-42-07.826Z--6a571605bf60218e3d155eca6d6f507e83e2ca14') as key_file:
... encrypted_key = key_file.read()
... dmv_priv_key = w3.eth.account.decrypt(encrypted_key, 'Dmv_Secret')
>>> dmv_priv_key
The following would be the typical output:
HexBytes('0x308c9f3ab1124c392c5977b073597313f6500ddf55cec824bff72aad0abd6999')
Copy the private key 308c9f3ab1124c392c5977b073597313f6500ddf55cec824bff72aad0abd6999 (without the 0x) and save it to a file called key in the directory $BESU_HOME/data/dmv.
The next step is to extract the public address of the bootnode and store it in a file named bootnode_pubkey that will be located at $BESU_HOME/data/bank. A bootnode is used for initial discovery of the nodes (peers) in the network. We will designate the node running the bank as the bootnode.
To extract the public address for bank, execute the following command in the terminal prompt:
$ docker run --rm --name bank -u $(id -u $USER):$(id -g $USER) -v $BESU_HOME:/root hyperledger/besu:25.7.0 --data-path=/root/data/bank --node-private-key-file=/root/data/bank/key public-key export-address --to=/root/data/bank/bootnode_pubkey
The following would be the typical output:
2025-07-05 00:22:12.764+00:00 | main | INFO | KeyPairUtil | Attempting to load public key from /root/data/bank/key 2025-07-05 00:22:12.789+00:00 | main | INFO | KeyPairUtil | Loaded public key 0x02746850aeb12435cbcc9698f6d12d2ebbac104ea635cf09ead27676faf798c8c6735139b74165157f9e027e89fcc777a9875d43e49d436c9da823a0e3003c77 from /root/data/bank/key
For simplicity, the nodes of our Hyperledger Besu based private Ethereum network will on localhost 127.0.0.1 on different ports.
The next step is to create a configuration file with various options for the bank node. The configuration file will be used by Hyperledger Besu to start the Ethereum node.
The following will be the contents of the configuration file bank-config.toml (located in the directory $BESU_HOME/conf):
logging="INFO" identity="bank" data-path="/opt/besu/data" genesis-file="/config/genesis.json" node-private-key-file="/opt/besu/keys/key" nat-method="DOCKER" host-allowlist=["*"] # P2P related p2p-host="127.0.0.1" p2p-port=30303 # RPC related rpc-http-enabled=true rpc-http-host="127.0.0.1" rpc-http-port=8545 rpc-http-api=["ADMIN", "CLIQUE", "ETH", "NET", "TXPOOL", "WEB3"] rpc-http-cors-origins=["*"]
In the following section, we will explain some of the configuration options used above:
identity :: Indicates the name of the Ethereum node
data-path :: Specifies the path to the data directory used by Hyperledger Besu
genesis-file :: Specifies the genesis file to be used for the private Ethereum network
node-private-key-file :: Specifies the private key file for the Ethereum node
nat-method :: Specifies the method to be used in a NAT environment so other nodes can discovery each other
host-allowlist :: Specifies a comma-separated list of host addresses to make API calls. The "*" (asterisk) here means allow all hosts
p2p-host :: Specifies the host on which to listen for peer-2-peer discovery
p2p-port :: Specifies the network port on which to listen for peer-2-peer discovery
rpc-http-enabled :: If true, enables API access via HTTP
rpc-http-host :: Specifies the host on which to listen for API request(s)
rpc-http-port :: Specifies the network port on which to listen for API request(s)
rpc-http-api :: Specifies a comma-separated list of APIs enabled for access
rpc-http-cors-origins :: Specifies a comma-separated list of domains allowed for CORS validation. The "*" (asterisk) here means accept request(s) from all domains
Open 4 terminal windows, each representing the 4 entities, namely, the bank, the buyer, the dealer, and the dmv. We will refer to these 4 windows corresponding to the entity they refer. For example, the first terminal will be referred to as the bank , the second terminal as the buyer, the third terminal as the dealer and the fourth terminal as the dmv.
In each of the 4 windows, change the current working directory to $BESU_HOME.
In the bank terminal, start the Ethereum node by executing the following command:
$ docker run --rm --name bank --network host -e BESU_OPTS="--add-opens java.base/jdk.internal.misc=ALL-UNNAMED -Dio.netty.tryReflectionSetAccessible=true" -u $(id -u $USER):$(id -g $USER) -v $BESU_HOME/data/bank:/opt/besu/data -v $BESU_HOME/data/bank:/opt/besu/keys -v $BESU_HOME/data/bank:/opt/besu/public-keys -v $BESU_HOME/conf/bank-config.toml:/config/config.toml -v $BESU_HOME/conf/genesis.json:/config/genesis.json hyperledger/besu:25.7.0 --config-file=/config/config.toml
The following would be the typical trimmed output:
Setting logging level to INFO
2025-07-05 00:35:37.080+00:00 | main | INFO | Besu | Starting Besu
2025-07-05 00:35:37.191+00:00 | main | WARN | Besu | --rpc-ws-host has been ignored because --rpc-ws-enabled was not defined on the command line.
2025-07-05 00:35:37.194+00:00 | main | WARN | Besu | --graphql-http-host has been ignored because --graphql-http-enabled was not defined on the command line.
2025-07-05 00:35:37.280+00:00 | main | WARN | Besu | Forcing --bonsai-limit-trie-logs-enabled=false, since it cannot be enabled with --sync-mode=FULL and --data-storage-format=BONSAI.
2025-07-05 00:35:37.280+00:00 | main | INFO | Besu | Connecting to 0 static nodes.
.....[TRIM].....
2025-07-05 00:35:37.337+00:00 | main | INFO | Besu | Security Module: localfile
2025-07-05 00:35:37.337+00:00 | main | INFO | VersionMetadata | Lookup version metadata file in data directory: /opt/besu/data
2025-07-05 00:35:37.338+00:00 | main | INFO | VersionMetadata | No version data detected. Writing Besu version 25.7.0 to metadata file
2025-07-05 00:35:37.369+00:00 | main | INFO | Besu | Using the native implementation of alt bn128
2025-07-05 00:35:37.369+00:00 | main | INFO | Besu | Using the Java implementation of modexp
2025-07-05 00:35:37.369+00:00 | main | INFO | Besu | Using the native implementation of the signature algorithm
2025-07-05 00:35:37.370+00:00 | main | INFO | Besu | Using the native implementation of the blake2bf algorithm
2025-07-05 00:35:37.425+00:00 | main | INFO | KeyPairUtil | Attempting to load public key from /opt/besu/keys/key
2025-07-05 00:35:37.438+00:00 | main | INFO | KeyPairUtil | Loaded public key 0x02746850aeb12435cbcc9698f6d12d2ebbac104ea635cf09ead27676faf798c8c6735139b74165157f9e027e89fcc777a9875d43e49d436c9da823a0e3003c77 from /opt/besu/keys/key
2025-07-05 00:35:37.507+00:00 | main | INFO | ProtocolScheduleBuilder | Protocol schedule created with milestones: [Berlin:0]
2025-07-05 00:35:37.510+00:00 | main | INFO | RocksDBKeyValueStorageFactory | No existing database at /opt/besu/data. Using default metadata for new db versionedStorageFormat=BaseVersionedStorageFormat{format=BONSAI, version=3}
2025-07-05 00:35:37.673+00:00 | main | INFO | FlatDbStrategyProvider | Flat db mode found FULL
2025-07-05 00:35:37.674+00:00 | main | INFO | FlatDbStrategyProvider | DB mode with code stored using code hash enabled = true
2025-07-05 00:35:37.680+00:00 | main | INFO | FlatDbStrategyProvider | Flat db mode found FULL
2025-07-05 00:35:37.681+00:00 | main | INFO | FlatDbStrategyProvider | DB mode with code stored using code hash enabled = true
2025-07-05 00:35:37.735+00:00 | main | INFO | TransactionPoolFactory | Enabling transaction pool
2025-07-05 00:35:37.744+00:00 | main | INFO | BesuControllerBuilder | TTD difficulty is not present, creating initial sync phase for PoW
2025-07-05 00:35:37.761+00:00 | main | INFO | RunnerBuilder | Resolved 0 bootnodes.
2025-07-05 00:35:37.766+00:00 | main | INFO | RunnerBuilder | Detecting NAT service.
2025-07-05 00:35:37.830+00:00 | main | INFO | Runner | Starting external services ...
2025-07-05 00:35:37.830+00:00 | main | INFO | JsonRpcHttpService | Starting JSON-RPC service on 0.0.0.0:8545
2025-07-05 00:35:37.888+00:00 | vert.x-eventloop-thread-1 | INFO | JsonRpcHttpService | JSON-RPC service started and listening on 0.0.0.0:8545
2025-07-05 00:35:37.890+00:00 | main | INFO | AutoTransactionLogBloomCachingService | Starting auto transaction log bloom caching service.
2025-07-05 00:35:37.891+00:00 | main | INFO | LogBloomCacheMetadata | Lookup cache metadata file in data directory: /opt/besu/data/caches
2025-07-05 00:35:37.896+00:00 | main | INFO | Runner | Starting Ethereum main loop ...
2025-07-05 00:35:37.896+00:00 | main | INFO | DockerNatManager | Starting docker NAT manager.
2025-07-05 00:35:37.898+00:00 | main | INFO | NetworkRunner | Starting Network.
2025-07-05 00:35:37.904+00:00 | nioEventLoopGroup-2-1 | INFO | RlpxAgent | P2P RLPx agent started and listening on /[0:0:0:0:0:0:0:0]:30303.
2025-07-05 00:35:37.904+00:00 | main | INFO | PeerDiscoveryAgent | Starting peer discovery agent on host=0.0.0.0, port=30303. IPv6 available.
2025-07-05 00:35:37.911+00:00 | vert.x-eventloop-thread-1 | INFO | VertxPeerDiscoveryAgent | Started peer discovery agent successfully, on effective host=0:0:0:0:0:0:0:0 and port=30303
2025-07-05 00:35:37.913+00:00 | vert.x-eventloop-thread-1 | INFO | PeerDiscoveryAgent | P2P peer discovery agent started and listening on /[0:0:0:0:0:0:0:0]:30303
2025-07-05 00:35:37.938+00:00 | vert.x-eventloop-thread-1 | INFO | PeerDiscoveryAgent | Writing node record to disk. NodeRecord{seq=1, publicKey=0x0302746850aeb12435cbcc9698f6d12d2ebbac104ea635cf09ead27676faf798c8, udpAddress=Optional[/127.0.0.1:30303], tcpAddress=Optional[/127.0.0.1:30303], udp6Address=Optional.empty, tcp6Address=Optional.empty, asBase64=-Je4QGDSNn261CGC4kWGeQv3gQ726TJkQRC6BZhq6VfPk29zcn7DVzdATLO2flH0qhTDA0NjfpkczY2kH3lBa2mueKcBg2V0aMfGhFgzXyCAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQMCdGhQrrEkNcvMlpj20S0uu6wQTqY1zwnq0nZ2-veYyIN0Y3CCdl-DdWRwgnZf, nodeId=0x487968a8aa662889f99da14e4ed5d88f55c4715298d577d17d3ffc57bf3dc981, customFields={tcp=30303, udp=30303, eth=[[0x58335f20, 0x]], id=V4, ip=0x7f000001, secp256k1=0x0302746850aeb12435cbcc9698f6d12d2ebbac104ea635cf09ead27676faf798c8}}
2025-07-05 00:35:37.945+00:00 | main | INFO | DefaultP2PNetwork | Enode URL enode://02746850aeb12435cbcc9698f6d12d2ebbac104ea635cf09ead27676faf798c8c6735139b74165157f9e027e89fcc777a9875d43e49d436c9da823a0e3003c77@127.0.0.1:30303
2025-07-05 00:35:37.946+00:00 | main | INFO | DefaultP2PNetwork | Node address 0x4ed5d88f55c4715298d577d17d3ffc57bf3dc981
2025-07-05 00:35:37.950+00:00 | main | INFO | NetworkRunner | Supported capabilities: [eth/66, eth/67, eth/68, eth/69], [snap/1]
2025-07-05 00:35:37.950+00:00 | main | INFO | DefaultSynchronizer | Starting synchronizer.
2025-07-05 00:35:37.951+00:00 | main | INFO | TransactionPoolFactory | Enabling transaction handling following initial sync
2025-07-05 00:35:37.951+00:00 | main | INFO | FullSyncDownloader | Starting full sync.
2025-07-05 00:35:37.951+00:00 | main | INFO | FullSyncTargetManager | Unable to find sync target. Waiting for 5 peers minimum. Currently checking 0 peers for usefulness
2025-07-05 00:35:37.955+00:00 | main | INFO | Runner | Ethereum main loop is up.
From the Output.8 above, we need to make a note of the bootnode address from the line that contains the sub-string Enode URL.
We will now create the configuration files (with various options including the address of the bootnode) for the remaining 3 entities.
The docker option --network host was the *ONLY* way to get the Ethereum private network work, with all the nodes discovered and connected.
The following will be the contents of the configuration file buyer-config.toml (located in the directory $BESU_HOME/conf):
logging="INFO" identity="buyer" data-path="/opt/besu/data" genesis-file="/config/genesis.json" node-private-key-file="/opt/besu/keys/key" nat-method="DOCKER" host-allowlist=["*"] bootnodes=["enode://02746850aeb12435cbcc9698f6d12d2ebbac104ea635cf09ead27676faf798c8c6735139b74165157f9e027e89fcc777a9875d43e49d436c9da823a0e3003c77@127.0.0.1:30303"] # P2P related p2p-host="127.0.0.1" p2p-port=30304 # RPC related rpc-http-enabled=false rpc-http-host="127.0.0.1" rpc-http-port=8546 rpc-http-api=["ADMIN", "CLIQUE", "ETH", "NET", "TXPOOL", "WEB3"] rpc-http-cors-origins=["*"]
The following will be the contents of the configuration file dealer-config.toml (located in the directory $BESU_HOME/conf):
logging="INFO" identity="dealer" data-path="/opt/besu/data" genesis-file="/config/genesis.json" node-private-key-file="/opt/besu/keys/key" nat-method="DOCKER" host-allowlist=["*"] bootnodes=["enode://02746850aeb12435cbcc9698f6d12d2ebbac104ea635cf09ead27676faf798c8c6735139b74165157f9e027e89fcc777a9875d43e49d436c9da823a0e3003c77@127.0.0.1:30303"] # P2P related p2p-host="127.0.0.1" p2p-port=30305 # RPC related rpc-http-enabled=false rpc-http-host="127.0.0.1" rpc-http-port=8547 rpc-http-api=["ADMIN", "CLIQUE", "ETH", "NET", "TXPOOL", "WEB3"] rpc-http-cors-origins=["*"]
The following will be the contents of the configuration file dmv-config.toml (located in the directory $BESU_HOME/conf):
logging="INFO" identity="dmv" data-path="/opt/besu/data" genesis-file="/config/genesis.json" node-private-key-file="/opt/besu/keys/key" nat-method="DOCKER" host-allowlist=["*"] bootnodes=["enode://02746850aeb12435cbcc9698f6d12d2ebbac104ea635cf09ead27676faf798c8c6735139b74165157f9e027e89fcc777a9875d43e49d436c9da823a0e3003c77@127.0.0.1:30303"] # P2P related p2p-host="127.0.0.1" p2p-port=30306 # RPC related rpc-http-enabled=false rpc-http-host="127.0.0.1" rpc-http-port=8548 rpc-http-api=["ADMIN", "CLIQUE", "ETH", "NET", "TXPOOL", "WEB3"] rpc-http-cors-origins=["*"]
The values for the configuration options p2p-port and the rpc-http-port have to be unique for every Ethereum node in the private network.
In the buyer terminal, start the Ethereum node by executing the following command:
$ docker run --rm --name buyer --network host -e BESU_OPTS="--add-opens java.base/jdk.internal.misc=ALL-UNNAMED -Dio.netty.tryReflectionSetAccessible=true" -u $(id -u $USER):$(id -g $USER) -v $BESU_HOME/data/buyer:/opt/besu/data -v $BESU_HOME/data/buyer:/opt/besu/keys -v $BESU_HOME/conf/buyer-config.toml:/config/config.toml -v $BESU_HOME/conf/genesis.json:/config/genesis.json hyperledger/besu:25.7.0 --config-file=/config/config.toml
The following would be the typical trimmed output:
Setting logging level to INFO
2025-07-05 00:44:23.235+00:00 | main | INFO | Besu | Starting Besu
2025-07-05 00:44:23.347+00:00 | main | WARN | Besu | --rpc-http-host, --rpc-http-port, --rpc-http-cors-origins and --rpc-http-api has been ignored because --rpc-http-enabled was not defined on the command line.
2025-07-05 00:44:23.348+00:00 | main | WARN | Besu | --rpc-ws-host has been ignored because --rpc-ws-enabled was not defined on the command line.
2025-07-05 00:44:23.351+00:00 | main | WARN | Besu | --graphql-http-host has been ignored because --graphql-http-enabled was not defined on the command line.
2025-07-05 00:44:23.421+00:00 | main | WARN | Besu | Forcing --bonsai-limit-trie-logs-enabled=false, since it cannot be enabled with --sync-mode=FULL and --data-storage-format=BONSAI.
2025-07-05 00:44:23.422+00:00 | main | INFO | Besu | Connecting to 0 static nodes.
.....[TRIM].....
2025-07-05 00:44:23.477+00:00 | main | INFO | Besu | Security Module: localfile
2025-07-05 00:44:23.477+00:00 | main | INFO | VersionMetadata | Lookup version metadata file in data directory: /opt/besu/data
2025-07-05 00:44:23.478+00:00 | main | INFO | VersionMetadata | No version data detected. Writing Besu version 25.7.0 to metadata file
2025-07-05 00:44:23.509+00:00 | main | INFO | Besu | Using the native implementation of alt bn128
2025-07-05 00:44:23.510+00:00 | main | INFO | Besu | Using the Java implementation of modexp
2025-07-05 00:44:23.510+00:00 | main | INFO | Besu | Using the native implementation of the signature algorithm
2025-07-05 00:44:23.511+00:00 | main | INFO | Besu | Using the native implementation of the blake2bf algorithm
2025-07-05 00:44:23.569+00:00 | main | INFO | KeyPairUtil | Attempting to load public key from /opt/besu/keys/key
2025-07-05 00:44:23.582+00:00 | main | INFO | KeyPairUtil | Loaded public key 0x16ea033659ac96ec08b16202c4994c991a33e7dab6aba6c4abf801a81a84da442fdcc829163ccf521ae661d6ba74e63f4369318cbf69201a8152e82f8b57495e from /opt/besu/keys/key
2025-07-05 00:44:23.652+00:00 | main | INFO | ProtocolScheduleBuilder | Protocol schedule created with milestones: [Berlin:0]
2025-07-05 00:44:23.657+00:00 | main | INFO | RocksDBKeyValueStorageFactory | No existing database at /opt/besu/data. Using default metadata for new db versionedStorageFormat=BaseVersionedStorageFormat{format=BONSAI, version=3}
2025-07-05 00:44:23.819+00:00 | main | INFO | FlatDbStrategyProvider | Flat db mode found FULL
2025-07-05 00:44:23.820+00:00 | main | INFO | FlatDbStrategyProvider | DB mode with code stored using code hash enabled = true
2025-07-05 00:44:23.827+00:00 | main | INFO | FlatDbStrategyProvider | Flat db mode found FULL
2025-07-05 00:44:23.827+00:00 | main | INFO | FlatDbStrategyProvider | DB mode with code stored using code hash enabled = true
2025-07-05 00:44:23.884+00:00 | main | INFO | TransactionPoolFactory | Enabling transaction pool
2025-07-05 00:44:23.894+00:00 | main | INFO | BesuControllerBuilder | TTD difficulty is not present, creating initial sync phase for PoW
2025-07-05 00:44:23.910+00:00 | main | INFO | RunnerBuilder | Resolved 1 bootnodes.
2025-07-05 00:44:23.915+00:00 | main | INFO | RunnerBuilder | Detecting NAT service.
2025-07-05 00:44:23.954+00:00 | main | INFO | Runner | Starting external services ...
2025-07-05 00:44:23.956+00:00 | main | INFO | AutoTransactionLogBloomCachingService | Starting auto transaction log bloom caching service.
2025-07-05 00:44:23.957+00:00 | main | INFO | LogBloomCacheMetadata | Lookup cache metadata file in data directory: /opt/besu/data/caches
2025-07-05 00:44:23.963+00:00 | main | INFO | Runner | Starting Ethereum main loop ...
2025-07-05 00:44:23.964+00:00 | main | INFO | DockerNatManager | Starting docker NAT manager.
2025-07-05 00:44:23.965+00:00 | main | INFO | NetworkRunner | Starting Network.
2025-07-05 00:44:23.996+00:00 | nioEventLoopGroup-2-1 | INFO | RlpxAgent | P2P RLPx agent started and listening on /[0:0:0:0:0:0:0:0]:30304.
2025-07-05 00:44:23.997+00:00 | main | INFO | PeerDiscoveryAgent | Starting peer discovery agent on host=0.0.0.0, port=30304. IPv6 available.
2025-07-05 00:44:24.006+00:00 | vert.x-eventloop-thread-1 | INFO | VertxPeerDiscoveryAgent | Started peer discovery agent successfully, on effective host=0:0:0:0:0:0:0:0 and port=30304
2025-07-05 00:44:24.007+00:00 | vert.x-eventloop-thread-1 | INFO | PeerDiscoveryAgent | P2P peer discovery agent started and listening on /[0:0:0:0:0:0:0:0]:30304
2025-07-05 00:44:24.030+00:00 | vert.x-eventloop-thread-1 | INFO | PeerDiscoveryAgent | Writing node record to disk. NodeRecord{seq=1, publicKey=0x0216ea033659ac96ec08b16202c4994c991a33e7dab6aba6c4abf801a81a84da44, udpAddress=Optional[/127.0.0.1:30304], tcpAddress=Optional[/127.0.0.1:30304], udp6Address=Optional.empty, tcp6Address=Optional.empty, asBase64=-Je4QM5XF1fURPVPoZfw30eHppag21fnGLv_UWcpNAw-3IcROPrbHvMAX51WORaQjrBVaH3wLgrB_rSMjc6mlBURvFsBg2V0aMfGhFgzXyCAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQIW6gM2WayW7AixYgLEmUyZGjPn2rarpsSr-AGoGoTaRIN0Y3CCdmCDdWRwgnZg, nodeId=0x8aff6771f979f0a9fc9e36d65a4240acd295d673c13e197b99d0fa70bbd64ca6, customFields={tcp=30304, udp=30304, eth=[[0x58335f20, 0x]], id=V4, ip=0x7f000001, secp256k1=0x0216ea033659ac96ec08b16202c4994c991a33e7dab6aba6c4abf801a81a84da44}}
2025-07-05 00:44:24.042+00:00 | main | INFO | DefaultP2PNetwork | Enode URL enode://16ea033659ac96ec08b16202c4994c991a33e7dab6aba6c4abf801a81a84da442fdcc829163ccf521ae661d6ba74e63f4369318cbf69201a8152e82f8b57495e@127.0.0.1:30304
2025-07-05 00:44:24.042+00:00 | main | INFO | DefaultP2PNetwork | Node address 0x5a4240acd295d673c13e197b99d0fa70bbd64ca6
2025-07-05 00:44:24.047+00:00 | main | INFO | NetworkRunner | Supported capabilities: [eth/66, eth/67, eth/68, eth/69], [snap/1]
2025-07-05 00:44:24.047+00:00 | main | INFO | DefaultSynchronizer | Starting synchronizer.
2025-07-05 00:44:24.048+00:00 | main | INFO | TransactionPoolFactory | Enabling transaction handling following initial sync
2025-07-05 00:44:24.048+00:00 | main | INFO | FullSyncDownloader | Starting full sync.
2025-07-05 00:44:24.048+00:00 | main | INFO | FullSyncTargetManager | Unable to find sync target. Waiting for 5 peers minimum. Currently checking 0 peers for usefulness
2025-07-05 00:44:24.053+00:00 | main | INFO | Runner | Ethereum main loop is up.
2025-07-05 00:44:24.246+00:00 | nioEventLoopGroup-3-1 | INFO | TransactionPoolFactory | Node out of sync, disabling transaction handling
2025-07-05 00:44:29.141+00:00 | EthScheduler-Services-5 (importBlock) | INFO | TransactionPoolFactory | Node is in sync, enabling transaction handling
In the dealer terminal, start the Ethereum node by executing the following command:
$ docker run --rm --name dealer --network host -e BESU_OPTS="--add-opens java.base/jdk.internal.misc=ALL-UNNAMED -Dio.netty.tryReflectionSetAccessible=true" -u $(id -u $USER):$(id -g $USER) -v $BESU_HOME/data/dealer:/opt/besu/data -v $BESU_HOME/data/dealer:/opt/besu/keys -v $BESU_HOME/conf/dealer-config.toml:/config/config.toml -v $BESU_HOME/conf/genesis.json:/config/genesis.json hyperledger/besu:25.7.0 --config-file=/config/config.toml
In the dmv terminal, start the Ethereum node by executing the following command:
$ docker run --rm --name dmv --network host -e BESU_OPTS="--add-opens java.base/jdk.internal.misc=ALL-UNNAMED -Dio.netty.tryReflectionSetAccessible=true" -u $(id -u $USER):$(id -g $USER) -v $BESU_HOME/data/dmv:/opt/besu/data -v $BESU_HOME/data/dmv:/opt/besu/keys -v $BESU_HOME/conf/dmv-config.toml:/config/config.toml -v $BESU_HOME/conf/genesis.json:/config/genesis.json hyperledger/besu:25.7.0 --config-file=/config/config.toml
To make API requests on our private blockchain network, we will issue the following commands at the Python interpreter prompt:
>>> import web3
>>> from web3 import Web3
>>> from web3.middleware import ExtraDataToPOAMiddleware
>>> provider = Web3.HTTPProvider('http://127.0.0.1:8545')
>>> w3 = Web3(provider)
>>> w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
To check if we have successfully connected to the bank node, execute the following command at the Python interpreter prompt:
>>> w3.is_connected()
The following would be the typical output:
True
To get information about the bank node, execute the following command at the Python interpreter prompt:
>>> w3.geth.admin.node_info()
The following would be the typical output:
AttributeDict({'enode': 'enode://02746850aeb12435cbcc9698f6d12d2ebbac104ea635cf09ead27676faf798c8c6735139b74165157f9e027e89fcc777a9875d43e49d436c9da823a0e3003c77@127.0.0.1:30303', 'listenAddr': '127.0.0.1:30303', 'activeFork': 'Berlin', 'ip': '127.0.0.1', 'name': 'besu/bank/v25.7.0/linux-x86_64/openjdk-java-21', 'id': '02746850aeb12435cbcc9698f6d12d2ebbac104ea635cf09ead27676faf798c8c6735139b74165157f9e027e89fcc777a9875d43e49d436c9da823a0e3003c77', 'ports': AttributeDict({'discovery': 30303, 'listener': 30303}), 'protocols': AttributeDict({'eth': AttributeDict({'config': AttributeDict({'chainId': 21, 'berlinBlock': 0, 'clique': AttributeDict({'epochLength': 30000, 'blockPeriodSeconds': 10, 'createemptyblocks': True})}), 'difficulty': 393, 'genesis': '0x9fe2eac5405ea5af672e3d6843c40f2a925280e08dabc90099559d9e42c2dc56', 'head': '0xca43c0a4ff43972c94e98342c924a01a1844be3ef8eaa22ac63ed2f4da9d7141', 'network': 21})})})
The trimmed string value enode://0274685...@127.0.0.1:30303 is the endpoint address for the bank node.
To verify the peers connected to the bank node, execute the following command at the Python interpreter prompt:
>>> w3.geth.admin.peers()
The following would be the typical output:
[AttributeDict({'version': '0x5', 'name': 'besu/dmv/v25.7.0/linux-x86_64/openjdk-java-21', 'caps': ['eth/66', 'eth/67', 'eth/68', 'eth/69', 'snap/1'], 'network': AttributeDict({'localAddress': '127.0.0.1:40760', 'remoteAddress': '127.0.0.1:30306', 'inbound': False}), 'port': '0x7662', 'id': '0xfa14a71f42599c6574640977b80e0fc1116e5044b8a86cd92b0b193b1c1cc66ded1c10c988db83ae01941c3ada28d5effec9f99c470290017aea848b0cd25610', 'protocols': AttributeDict({'eth': AttributeDict({'difficulty': '0x187', 'head': '0xd080a3426bd1f62a17bb758a599eee5275a13b5fc3d8aba874c75f284a8a5406', 'version': 69})}), 'enode': 'enode://fa14a71f42599c6574640977b80e0fc1116e5044b8a86cd92b0b193b1c1cc66ded1c10c988db83ae01941c3ada28d5effec9f99c470290017aea848b0cd25610@127.0.0.1:30306'}), AttributeDict({'version': '0x5', 'name': 'besu/dealer/v25.7.0/linux-x86_64/openjdk-java-21', 'caps': ['eth/66', 'eth/67', 'eth/68', 'eth/69', 'snap/1'], 'network': AttributeDict({'localAddress': '127.0.0.1:56606', 'remoteAddress': '127.0.0.1:30305', 'inbound': False}), 'port': '0x7661', 'id': '0x4f320374381cf02087afa4b52b1d8f98f01d696814b4ce26c027bfc56247f09c57d25e5924f4189071e002732938db11f8341edcdda1ec7c0536bf7bb3c8f813', 'protocols': AttributeDict({'eth': AttributeDict({'difficulty': '0x187', 'head': '0xd080a3426bd1f62a17bb758a599eee5275a13b5fc3d8aba874c75f284a8a5406', 'version': 69})}), 'enode': 'enode://4f320374381cf02087afa4b52b1d8f98f01d696814b4ce26c027bfc56247f09c57d25e5924f4189071e002732938db11f8341edcdda1ec7c0536bf7bb3c8f813@127.0.0.1:30305'}), AttributeDict({'version': '0x5', 'name': 'besu/buyer/v25.7.0/linux-x86_64/openjdk-java-21', 'caps': ['eth/66', 'eth/67', 'eth/68', 'eth/69', 'snap/1'], 'network': AttributeDict({'localAddress': '127.0.0.1:30303', 'remoteAddress': '127.0.0.1:51698', 'inbound': True}), 'port': '0x7660', 'id': '0x16ea033659ac96ec08b16202c4994c991a33e7dab6aba6c4abf801a81a84da442fdcc829163ccf521ae661d6ba74e63f4369318cbf69201a8152e82f8b57495e', 'protocols': AttributeDict({'eth': AttributeDict({'difficulty': '0x187', 'head': '0xd080a3426bd1f62a17bb758a599eee5275a13b5fc3d8aba874c75f284a8a5406', 'version': 69})}), 'enode': 'enode://16ea033659ac96ec08b16202c4994c991a33e7dab6aba6c4abf801a81a84da442fdcc829163ccf521ae661d6ba74e63f4369318cbf69201a8152e82f8b57495e@127.0.0.1:30304?discport=0'})]
To retrieve the details of a block by the block number 0 (zero), execute the following command at the Python interpreter prompt:
>>> json.dumps(json.loads(w3.to_json(w3.eth.get_block(0))), indent=2)
The following would be the typical output:
{
"number": 0,
"hash": "0x9fe2eac5405ea5af672e3d6843c40f2a925280e08dabc90099559d9e42c2dc56",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot": "0x550dcc5cabd0941cfcf95ba88e9e36eea4fbd08b33dfac815fd995ab6c4dc971",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"miner": "0x0000000000000000000000000000000000000000",
"difficulty": 1,
"totalDifficulty": 1,
"proofOfAuthorityData": "0x00000000000000000000000000000000000000000000000000000000000000004ed5d88f55c4715298d577d17d3ffc57bf3dc9810000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"size": 626,
"gasLimit": 10485760,
"gasUsed": 0,
"timestamp": 1548854791,
"uncles": [],
"transactions": []
}
To display account balance for the buyer account, execute the following command at the Python interpreter prompt:
>>> w3.eth.getBalance(Web3.to_checksum_address('0x5a4240acd295d673c13e197b99d0fa70bbd64ca6'))
The following would be the typical output:
10000000000000000000
If we do not use Web3.to_checksum_address on the address to get the account balance, we will encounter an error
Similarly, to display account balance for the dealer account, execute the following command at the Python interpreter prompt:
>>> w3.eth.getBalance(Web3.to_checksum_address('0x3b66024245bc6b4c4f183d203170b6fa78754948'))
The following would be the typical output:
5000000000000000000
We will now send a transaction to transfer 1 ether from the buyer to the dealer. For this we will need to create a send transaction message. Enter the following data at the Python interpreter prompt to create the message structure called buyer_dealer_txn:
>>> buyer_dealer_txn = {
... 'from': Web3.to_checksum_address('0x5a4240acd295d673c13e197b99d0fa70bbd64ca6'),
... 'to': Web3.to_checksum_address('0x3b66024245bc6b4c4f183d203170b6fa78754948'),
... 'value': w3.to_wei(1, 'ether'),
... 'gas': 90000,
... 'gasPrice': 18000000000,
... 'nonce': 0,
... 'chainId': 21
...}
Before sending the transaction message buyer_dealer_txn, it needs to be digitally signed using the private key of the buyer. To do that, execute the following commands at the Python interpreter prompt:
>>> buyer_priv_key = '0x1680ff9e0ad288d507d04b569ea2d1aa9e5e3b58458e8a5dea7df12d2c41a1b9'
>>> signed_txn = w3.eth.account.sign_transaction(buyer_dealer_txn, buyer_priv_key)
To send the signed transaction to transfer 1 ether from the buyer to the dealer, execute the following command at the Python interpreter prompt:
>>> w3.eth.send_raw_transaction(signed_txn.raw_transaction)
The following would be the typical output:
HexBytes('0x0be38c14e1e23665970c115517d1c318ed3ab5b0395992ada7d656d1e5485544')
The w3.eth.send_raw_transaction method will return a transaction hash as a hex string (similar to a transaction id).
To display all the details of a transaction on this private network, execute the following command at the Python interpreter prompt:
>>> json.dumps(json.loads(w3.to_json(w3.eth.get_transaction('0x0be38c14e1e23665970c115517d1c318ed3ab5b0395992ada7d656d1e5485544'))), indent=2)
The following would be the typical output:
{
"blockHash": "0x4a8d86a720be419cfa2be08c4fdd7a299c0c85f1e18d239d8d7a2202f4f101aa",
"blockNumber": 224,
"chainId": 21,
"from": "0x5A4240ACD295D673C13E197B99D0FA70bBd64ca6",
"gas": 90000,
"gasPrice": 18000000000,
"hash": "0x7141a3260cf678c3e6c2d723cbaf163b31a6a141d346c88dc8f61a74e54f3afa",
"input": "0x",
"nonce": 0,
"to": "0x3B66024245bC6b4C4f183d203170B6Fa78754948",
"transactionIndex": 0,
"type": 0,
"value": 1000000000000000000,
"v": 78,
"r": "0x63d9c66ae4a2a97a88cbdc846c0f3f18ac56ab4fe4dd45556f246639a623dd71",
"s": "0x314290da53e71a8d6ad118067f25e33f1c6d6a821cfaa2ac83e2395e018f7b24"
}
From the Output.18 above, we see that transaction is processed in block number 224.
To display account balance for the buyer account, execute the following command at the Python interpreter prompt:
>>> w3.from_wei(w3.eth.get_balance(Web3.to_checksum_address('0x5a4240acd295d673c13e197b99d0fa70bbd64ca6')), 'ether')
The following would be the typical output:
8.999622
Similarly, to display account balance for the dealer account, execute the following command at the Python interpreter prompt:
>>> w3.from_wei(w3.eth.get_balance(Web3.to_checksum_address('0x3b66024245bc6b4c4f183d203170b6fa78754948')), 'ether')
The following would be the typical output:
6
As is evident from the Output.19 and Output.20 above, the account balance for the buyer has decreased, while the account balance for the dealer has increased.
BINGO !!! We have successfully demonstrated a 4-node Hyperledger Besu based private Ethereum network using Docker.
References