Skip to content

Commit

Permalink
Merge pull request #85 from wonkr/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
kevin-w-du authored Jul 13, 2022
2 parents 2e3c973 + 3155112 commit 7b0a2fa
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 7 deletions.
4 changes: 3 additions & 1 deletion examples/B06-blockchain/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,15 @@ We use `go-ethereum;geth` software to run blockchains on emulator.
When the containers are up, they will install `geth` and run it with the command
which is generated from EthereumService Class. We can customized the
`geth start command` with the following methods.
The `base start command` is `geth --datadir /root/.ethereum --identity="NODE_5" --networkid=10 --syncmode full --verbosity=2 --allow-insecure-unlock --port 30303`
The `base start command` is `geth --datadir {datadir} --identity="NODE_{node_id}" --networkid=10 --syncmode {syncmode} --snapshot={snapshot} --verbosity=2 --allow-insecure-unlock --port 30303 `

- `setNoDiscover()` = --nodiscover
- `enableGethHttp()` = --http --http.addr 0.0.0.0 --http.port 8545 --http.corsdomain "*" --http.api web3,eth,debug,personal,net,clique
- `enableGethWs()` = --ws --ws.addr 0.0.0.0 --ws.port 8546 --ws.origins "*" --ws.api web3,eth,debug,personal,net,clique
- `unlockAccounts()` = --unlock "{accounts}" --password "{accounts_passwords}"
- `startMiner()` = --mine --miner.threads=1
- `setSyncmode()` = --syncmode (snap|full|light)
- `setSnapshot()` = --snapshot (true|false)

You can also set further options using `setCustomGethCommandOption()` method.
The options will append to the `base start command`.
Expand Down
68 changes: 68 additions & 0 deletions examples/B10-dhcp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# DHCP

In this example, we show how to deploy a dhcp server inside the
SEED Emulator and how to set a host's ip with the installed dhcp server.
We first create dhcp servers on `AS151` and `AS152` controller.
We then 2 hosts in each AS to get ip address from the installed dhcp server.

See the comments in the code for detailed explanation.

We can utilize DHCP on `C03-bring-your-own-internet`. When we connect external
devices such as computer, smartphone, and IoT device to a internet emulator,
the installed dhcp will assign ip address to the newly attached devices so that
they can communicate with the nodes inside the emulator and use internet service.

## Step 1) Deploy a dhcp

```python
# Create a DHCP server (virtual node).
dhcp = DHCPService()

# Default DhcpIpRange : x.x.x.101 ~ x.x.x.120
# Set DhcpIpRange : x.x.x.125 ~ x.x.x.140
dhcp.install('dhcp-01').setIpRange(125, 140)
dhcp.install('dhcp-02')


# Customize the display name (for visualization purpose)
emu.getVirtualNode('dhcp-01').setDisplayName('DHCP Server 1')
emu.getVirtualNode('dhcp-02').setDisplayName('DHCP Server 2')


# Create new host in AS-151 and AS-161, use them to host the DHCP servers.
# We can also host it on an existing node.
as151 = base.getAutonomousSystem(151)
as151.createHost('dhcp-server-01').joinNetwork('net0')

as161 = base.getAutonomousSystem(161)
as161.createHost('dhcp-server-02').joinNetwork('net0')

# Bind the DHCP virtual node to the physical node.
emu.addBinding(Binding('dhcp-01', filter = Filter(asn=151, nodeName='dhcp-server-01')))
emu.addBinding(Binding('dhcp-02', filter = Filter(asn=161, nodeName='dhcp-server-02')))
```

Use method `DHCPServer:setIpRange` to set the ip range to assign.
The default IP range of Emulator is as below.
- host ip range : 71-99
- dhcp ip range : 101-120
- router ip range : 254-200

`DHCPServer:setIpRange` can change dhcp ip range. To change entire ip range,
we can use `Network:setHostIpRange()`, `Network:setDhcpIpRange()`, and
`Network:setRouterIpRange()`.


### Step 2) Set host to use dhcp

```python
# Create new hosts in AS-151, use it to host the Host which use dhcp instead of static ip
as151.createHost('dhcp-client-01').joinNetwork('net0', address = "dhcp")
as151.createHost('dhcp-client-02').joinNetwork('net0', address = "dhcp")

# Create new hosts in AS-161, use it to host the Host which use dhcp instead of static ip
as161.createHost('dhcp-client-01').joinNetwork('net0', address = "dhcp")
as161.createHost('dhcp-client-01').joinNetwork('net0', address = "dhcp")

```

55 changes: 55 additions & 0 deletions examples/B10-dhcp/dhcp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env python3
# encoding: utf-8

from seedemu import *

emu = Emulator()

# Load the pre-built component
emu.load('../B00-mini-internet/base-component.bin')

base:Base = emu.getLayer('Base')

# Create a DHCP server (virtual node).
dhcp = DHCPService()

# Default DhcpIpRange : x.x.x.101 ~ x.x.x.120
# Set DhcpIpRange : x.x.x.125 ~ x.x.x.140
dhcp.install('dhcp-01').setIpRange(125, 140)
dhcp.install('dhcp-02')


# Customize the display name (for visualization purpose)
emu.getVirtualNode('dhcp-01').setDisplayName('DHCP Server 1')
emu.getVirtualNode('dhcp-02').setDisplayName('DHCP Server 2')


# Create new hosts in AS-151 and AS-161, use them to host the DHCP servers.
# We can also host it on an existing node.
as151 = base.getAutonomousSystem(151)
as151.createHost('dhcp-server-01').joinNetwork('net0')

as161 = base.getAutonomousSystem(161)
as161.createHost('dhcp-server-02').joinNetwork('net0')

# Bind the DHCP virtual node to the physical node.
emu.addBinding(Binding('dhcp-01', filter = Filter(asn=151, nodeName='dhcp-server-01')))
emu.addBinding(Binding('dhcp-02', filter = Filter(asn=161, nodeName='dhcp-server-02')))


# Create new hosts in AS-151 and AS-161
# Make them to use dhcp instead of static ip
as151.createHost('dhcp-client-01').joinNetwork('net0', address = "dhcp")
as151.createHost('dhcp-client-02').joinNetwork('net0', address = "dhcp")

as161.createHost('dhcp-client-03').joinNetwork('net0', address = "dhcp")
as161.createHost('dhcp-client-04').joinNetwork('net0', address = "dhcp")

# Add the dhcp layer
emu.addLayer(dhcp)

# Render the emulation
emu.render()

# Compil the emulation
emu.compile(Docker(), './output', override=True)
Empty file.
53 changes: 47 additions & 6 deletions seedemu/services/EthereumService.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from __future__ import annotations
from enum import Enum
from os import mkdir, path, makedirs, rename
from shutil import rmtree
from seedemu.core import Node, Service, Server, Emulator
from typing import Dict, List, Tuple

Expand Down Expand Up @@ -116,7 +115,7 @@ class ConsensusMechanism(Enum):
0x0000000000000000000000000000000000000000000000000000000000000000{signer_addresses}0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'''

GethCommandTemplates['base'] = '''\
nice -n 19 geth --datadir {datadir} --identity="NODE_5" --networkid=10 --syncmode full --verbosity=2 --allow-insecure-unlock --port 30303 '''
nice -n 19 geth --datadir {datadir} --identity="NODE_{node_id}" --networkid=10 --syncmode {syncmode} --snapshot={snapshot} --verbosity=2 --allow-insecure-unlock --port 30303 '''

GethCommandTemplates['mine'] = '''\
--miner.etherbase "{coinbase}" --mine --miner.threads={num_of_threads} '''
Expand All @@ -135,6 +134,7 @@ class ConsensusMechanism(Enum):

GethCommandTemplates['bootnodes'] = '''\
--bootnodes "$(cat /tmp/eth-node-urls)" '''

class ConsensusMechanism(Enum):
"""!
@brief Consensus Mechanism Enum.
Expand All @@ -145,6 +145,14 @@ class ConsensusMechanism(Enum):
# POW for Proof of Work
POW = 'POW'

class Syncmode(Enum):
"""!
@brief geth syncmode Enum.
"""
SNAP = 'snap'
FULL = 'full'
LIGHT = 'light'


class Genesis():
"""!
Expand Down Expand Up @@ -249,14 +257,14 @@ def __init__(self, alloc_balance:int = 0,password:str = "admin", keyfilePath: st
assert alloc_balance >= 0, "EthAccount::__init__: balance cannot have a negative value. Requested Balance Value : {}".format(alloc_balance)

self.__alloc_balance = alloc_balance
self.__password = password

encrypted = self.lib_eth_account.encrypt(self.__account.key, password=password)
encrypted = self.encryptAccount()
self.__keystore_content = json.dumps(encrypted)

# generate the name of the keyfile
datastr = datetime.now(timezone.utc).isoformat().replace("+00:00", "000Z").replace(":","-")
self.__keystore_filename = "UTC--"+datastr+"--"+encrypted["address"]
self.__password = password

def __validate_balance(self, alloc_balance:int):
"""
Expand Down Expand Up @@ -290,6 +298,12 @@ def getAllocBalance(self) -> str:
def getKeyStoreFileName(self) -> str:
return self.__keystore_filename

def encryptAccount(self):
while True:
keystore = self.lib_eth_account.encrypt(self.__account.key, password=self.__password)
if len(keystore['crypto']['cipherparams']['iv']) == 32:
return keystore

def getKeyStoreContent(self) -> str:
return self.__keystore_content

Expand Down Expand Up @@ -374,6 +388,8 @@ class EthereumServer(Server):
__custom_geth_command_option: str

__data_dir: str
__syncmode: Syncmode
__snapshot: bool
__no_discover: bool
__enable_http: bool
__geth_http_port: int
Expand Down Expand Up @@ -403,6 +419,8 @@ def __init__(self, id: int):
self.__custom_geth_command_option = None

self.__data_dir = "/root/.ethereum"
self.__syncmode = Syncmode.FULL
self.__snapshot = False
self.__no_discover = False
self.__enable_ws = False
self.__enable_http = False
Expand All @@ -419,7 +437,7 @@ def __generateGethStartCommand(self):
@returns geth command.
"""
geth_start_command = GethCommandTemplates['base'].format(datadir=self.__data_dir)
geth_start_command = GethCommandTemplates['base'].format(node_id=self.__id, datadir=self.__data_dir, syncmode=self.__syncmode.value, snapshot=self.__snapshot)

if self.__no_discover:
geth_start_command += GethCommandTemplates['nodiscover']
Expand Down Expand Up @@ -562,13 +580,35 @@ def setGenesis(self, genesis:str) -> EthereumServer:

return self

def setSyncmode(self, syncmode:Syncmode) -> EthereumServer:
"""
@brief setting geth syncmode (default: snap)
@param syncmode use Syncmode enum options.
Syncmode.SNAP, Syncmode.FULL, Syncmode.LIGHT
@returns self, for chaining API calls.
"""
self.__syncmode = syncmode
return self

def setNoDiscover(self, noDiscover:bool = True) -> EthereumServer:
"""
@brief setting the automatic peer discovery to true/false
"""
self.__no_discover = noDiscover
return self

def setSnapshot(self, snapshot:bool = True) -> EthereumServer:
"""!
@breif set geth snapshot
@param snapshot bool
@returns self, for chainging API calls.
"""
self.__snapshot = snapshot
return self

def setConsensusMechanism(self, consensusMechanism:ConsensusMechanism) -> EthereumServer:
'''
Expand Down Expand Up @@ -801,6 +841,7 @@ def startMiner(self) -> EthereumServer:
@returns self, for chaining API calls.
"""
self.__start_mine = True
self.__syncmode = Syncmode.FULL

return self

Expand Down

0 comments on commit 7b0a2fa

Please sign in to comment.