mirror of https://github.com/nucypher/nucypher.git
Felix expressing ideas on how to wait for chain sync using bootnodes
parent
0b304ec560
commit
8407f22ebf
|
@ -0,0 +1,7 @@
|
|||
[
|
||||
"enode://bf150c793f378775e8cf09bee4fba37ea65363fe7a41171790a80ef6462de619cad2c05f42fc58655ad317503d5da8fee898e911fdf386ac6d15da12b5e883eb@3.92.166.78:30301",
|
||||
"enode://13da3c4b5b1ca32dfb0fcd662b9c69daf6b564e6f791ddae107d57049f25952aac329de336fd393f5b42b6aa2bbb263d7aa5c426b473be611739795aa18b0212@54.173.27.77:30303",
|
||||
"enode://4f7a27820107c235bb0f8086ee1c2bad62174450ec2eec12cb29e3fa7ecb9f332710373c1d11a3115aa72f2dabbae27b73eac51f06d3df558dd9fb51007da653@52.91.112.249:30303",
|
||||
"enode://6b58a9437aa88f254b75110019c54807cf1d7da9729f2c022a2463bae86b639288909fe00ffac0599e616676eea2de3c503bacaf4be835a02195bea0b349ca80@54.88.246.77:30303",
|
||||
"enode://562051180eca42514e44b4428ed20a3cb626654631f53bbfa549de7d3b7e418376e8f784c232429d7ff01bd0597e3ce7327699bb574d39ac3b2ac1729ed0dd44@54.224.110.32:30303"
|
||||
]
|
|
@ -14,9 +14,13 @@ GNU Affero General Public License for more details.
|
|||
You should have received a copy of the GNU Affero General Public License
|
||||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
import time
|
||||
|
||||
import geth
|
||||
import maya
|
||||
from geth.chain import write_genesis_file, initialize_chain
|
||||
from twisted.logger import Logger
|
||||
from web3.exceptions import BlockNotFound
|
||||
from web3.middleware import geth_poa_middleware
|
||||
|
||||
from constant_sorrow.constants import NO_BLOCKCHAIN_AVAILABLE
|
||||
|
@ -39,11 +43,15 @@ class Blockchain:
|
|||
class ConnectionNotEstablished(RuntimeError):
|
||||
pass
|
||||
|
||||
class SyncTimeout(RuntimeError):
|
||||
pass
|
||||
|
||||
def __init__(self,
|
||||
provider_process=None,
|
||||
interface: Union[BlockchainInterface, BlockchainDeployerInterface] = None):
|
||||
|
||||
self.log = Logger("blockchain")
|
||||
|
||||
self.__provider_process = provider_process
|
||||
|
||||
# Default interface
|
||||
|
@ -66,6 +74,66 @@ class Blockchain:
|
|||
def interface(self) -> Union[BlockchainInterface, BlockchainDeployerInterface]:
|
||||
return self.__interface
|
||||
|
||||
@property
|
||||
def peers(self):
|
||||
if self._instance is NO_BLOCKCHAIN_AVAILABLE:
|
||||
raise self.ConnectionNotEstablished
|
||||
return self.interface.w3.geth.admin.peers()
|
||||
|
||||
@property
|
||||
def syncing(self):
|
||||
if self._instance is NO_BLOCKCHAIN_AVAILABLE:
|
||||
raise self.ConnectionNotEstablished
|
||||
return self.interface.w3.eth.syncing
|
||||
|
||||
def sync(self, timeout: int = 600):
|
||||
"""
|
||||
Blocking call that waits for at least one ethereum peer and a full blockchain sync.
|
||||
"""
|
||||
|
||||
# Record start time for timeout calculation
|
||||
now = maya.now()
|
||||
start_time = now
|
||||
|
||||
def check_for_timeout(timeout=timeout):
|
||||
last_update = maya.now()
|
||||
duration = (last_update - start_time).seconds
|
||||
if duration > timeout:
|
||||
raise self.SyncTimeout
|
||||
|
||||
# Check for ethereum peers
|
||||
self.log.info(f"Waiting for ethereum peers...")
|
||||
while not self.peers:
|
||||
time.sleep(0)
|
||||
check_for_timeout(timeout=5)
|
||||
|
||||
needs_sync = False
|
||||
for peer in self.peers:
|
||||
peer_block_header = peer['protocols']['eth']['head']
|
||||
try:
|
||||
self.interface.w3.eth.getBlock(peer_block_header)
|
||||
except BlockNotFound:
|
||||
needs_sync = True
|
||||
break
|
||||
|
||||
# Start
|
||||
if needs_sync:
|
||||
peers = len(self.peers)
|
||||
self.log.info(f"Waiting for sync to begin ({peers} ethereum peers)")
|
||||
while not self.syncing:
|
||||
time.sleep(0)
|
||||
check_for_timeout()
|
||||
|
||||
# Continue until done
|
||||
while self.syncing:
|
||||
current = self.syncing['currentBlock']
|
||||
total = self.syncing['highestBlock']
|
||||
self.log.info(f"Syncing {current}/{total}")
|
||||
time.sleep(1)
|
||||
check_for_timeout()
|
||||
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def connect(cls,
|
||||
provider_process=None,
|
||||
|
@ -75,7 +143,8 @@ class Blockchain:
|
|||
compile: bool = False,
|
||||
poa: bool = False,
|
||||
force: bool = True,
|
||||
fetch_registry: bool = True
|
||||
fetch_registry: bool = True,
|
||||
full_sync: bool = True,
|
||||
) -> 'Blockchain':
|
||||
|
||||
log = Logger('blockchain-init')
|
||||
|
@ -106,6 +175,7 @@ class Blockchain:
|
|||
interface.w3.middleware_onion.inject(geth_poa_middleware, layer=0)
|
||||
|
||||
cls._instance = cls(interface=interface, provider_process=provider_process)
|
||||
|
||||
else:
|
||||
|
||||
if provider_uri is not None:
|
||||
|
@ -119,6 +189,8 @@ class Blockchain:
|
|||
# but we want to connect using a different registry.
|
||||
cls._instance.interface.registry = registry
|
||||
|
||||
# Syn blockchain
|
||||
cls._instance.sync()
|
||||
return cls._instance
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import json
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
|
||||
from constant_sorrow.constants import NOT_RUNNING
|
||||
from eth_utils import to_checksum_address, is_checksum_address
|
||||
|
@ -14,23 +16,33 @@ from geth.chain import (
|
|||
from geth.process import BaseGethProcess
|
||||
from twisted.logger import Logger
|
||||
|
||||
from nucypher.config.constants import DEFAULT_CONFIG_ROOT, BASE_DIR, DEPLOY_DIR
|
||||
from nucypher.config.constants import DEFAULT_CONFIG_ROOT, BASE_DIR, DEPLOY_DIR, USER_LOG_DIR
|
||||
|
||||
NUCYPHER_CHAIN_IDS = {
|
||||
'devnet': 112358,
|
||||
}
|
||||
|
||||
|
||||
class NuCypherGethProcess(BaseGethProcess, LoggingMixin):
|
||||
class NuCypherGethProcess(LoggingMixin, BaseGethProcess):
|
||||
|
||||
IPC_PROTOCOL = 'http'
|
||||
IPC_FILENAME = 'geth.ipc'
|
||||
VERBOSITY = 5
|
||||
LOG_PATH = os.path.join(USER_LOG_DIR, 'nucypher-geth.log')
|
||||
|
||||
_CHAIN_NAME = NotImplemented
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
def __init__(self,
|
||||
geth_kwargs: dict,
|
||||
stdout_logfile_path: str = LOG_PATH,
|
||||
stderr_logfile_path: str = LOG_PATH,
|
||||
*args, **kwargs):
|
||||
|
||||
super().__init__(geth_kwargs=geth_kwargs,
|
||||
stdout_logfile_path=stdout_logfile_path,
|
||||
stderr_logfile_path=stderr_logfile_path,
|
||||
*args, **kwargs)
|
||||
|
||||
self.log = Logger('nucypher-geth')
|
||||
|
||||
@property
|
||||
|
@ -47,19 +59,20 @@ class NuCypherGethProcess(BaseGethProcess, LoggingMixin):
|
|||
uri = f"{scheme}://{location}"
|
||||
return uri
|
||||
|
||||
def start(self, timeout: int = 30):
|
||||
def start(self, timeout: int = 30, extra_delay: int = 1):
|
||||
self.log.info("STARTING GETH NOW")
|
||||
super().start()
|
||||
self.wait_for_ipc(timeout=timeout) # on for all nodes by default
|
||||
if self.IPC_PROTOCOL in ('rpc', 'http'):
|
||||
self.wait_for_rpc(timeout=timeout)
|
||||
time.sleep(extra_delay)
|
||||
|
||||
|
||||
class NuCypherGethDevProcess(NuCypherGethProcess):
|
||||
|
||||
_CHAIN_NAME = 'poa-development'
|
||||
|
||||
def __init__(self, config_root: str = None):
|
||||
def __init__(self, config_root: str = None, *args, **kwargs):
|
||||
|
||||
base_dir = config_root if config_root else DEFAULT_CONFIG_ROOT
|
||||
base_dir = os.path.join(base_dir, '.ethereum')
|
||||
|
@ -67,7 +80,7 @@ class NuCypherGethDevProcess(NuCypherGethProcess):
|
|||
|
||||
ipc_path = os.path.join(self.data_dir, 'geth.ipc')
|
||||
self.geth_kwargs = {'ipc_path': ipc_path}
|
||||
super().__init__(geth_kwargs=self.geth_kwargs)
|
||||
super().__init__(geth_kwargs=self.geth_kwargs, *args, **kwargs)
|
||||
self.geth_kwargs.update({'dev': True})
|
||||
|
||||
self.command = [*self.command, '--dev']
|
||||
|
@ -85,7 +98,8 @@ class NuCypherGethDevnetProcess(NuCypherGethProcess):
|
|||
|
||||
def __init__(self,
|
||||
config_root: str = None,
|
||||
overrides: dict = None):
|
||||
overrides: dict = None,
|
||||
*args, **kwargs):
|
||||
|
||||
log = Logger('nucypher-geth-devnet')
|
||||
|
||||
|
@ -113,6 +127,8 @@ class NuCypherGethDevnetProcess(NuCypherGethProcess):
|
|||
'verbosity': str(self.VERBOSITY),
|
||||
'data_dir': self.data_dir,
|
||||
'ipc_path': ipc_path,
|
||||
'rpc_enabled': True,
|
||||
'no_discover': True,
|
||||
}
|
||||
|
||||
# Genesis & Blockchain Init
|
||||
|
@ -130,7 +146,7 @@ class NuCypherGethDevnetProcess(NuCypherGethProcess):
|
|||
self.initialized = True
|
||||
|
||||
self.__process = NOT_RUNNING
|
||||
super().__init__(geth_kwargs) # Attaches self.geth_kwargs in super call
|
||||
super().__init__(geth_kwargs=geth_kwargs, *args, **kwargs) # Attaches self.geth_kwargs in super call
|
||||
self.command = [*self.command, '--syncmode', 'fast']
|
||||
|
||||
def get_accounts(self):
|
||||
|
@ -147,6 +163,10 @@ class NuCypherGethDevnetProcess(NuCypherGethProcess):
|
|||
log.info(f'Initializing new blockchain database and genesis block.')
|
||||
initialize_chain(genesis_data=genesis_data, **self.geth_kwargs)
|
||||
|
||||
# Write static nodes file to data dir
|
||||
bootnodes_filepath = os.path.join(DEPLOY_DIR, 'static-nodes.json')
|
||||
shutil.copy(bootnodes_filepath, os.path.join(self.data_dir))
|
||||
|
||||
def ensure_account_exists(self, password: str) -> str:
|
||||
accounts = get_accounts(**self.geth_kwargs)
|
||||
if not accounts:
|
||||
|
@ -157,3 +177,6 @@ class NuCypherGethDevnetProcess(NuCypherGethProcess):
|
|||
checksum_address = to_checksum_address(account.decode())
|
||||
assert is_checksum_address(checksum_address), f"GETH RETURNED INVALID ETH ADDRESS {checksum_address}"
|
||||
return checksum_address
|
||||
|
||||
def start(self, *args, **kwargs):
|
||||
super().start()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import os
|
||||
|
||||
import click
|
||||
from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION
|
||||
|
||||
from nucypher.blockchain.eth.clients import NuCypherGethDevnetProcess
|
||||
from nucypher.characters.banners import FELIX_BANNER
|
||||
|
@ -9,7 +10,6 @@ from nucypher.cli.config import nucypher_click_config
|
|||
from nucypher.cli.types import NETWORK_PORT, EXISTING_READABLE_FILE, EIP55_CHECKSUM_ADDRESS
|
||||
from nucypher.config.characters import FelixConfiguration
|
||||
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
|
||||
from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION
|
||||
|
||||
|
||||
@click.command()
|
||||
|
@ -197,7 +197,7 @@ ETH ........ {str(eth_balance)}
|
|||
elif action == 'run': # Start web services
|
||||
|
||||
try:
|
||||
FELIX.blockchain.connect()
|
||||
FELIX.blockchain.sync()
|
||||
FELIX.start(host=host,
|
||||
port=port,
|
||||
web_services=not dry_run,
|
||||
|
|
|
@ -302,7 +302,10 @@ class NodeConfiguration(ABC):
|
|||
def known_nodes(self):
|
||||
return self.__fleet_state
|
||||
|
||||
def connect_to_blockchain(self, enode: str = None, recompile_contracts: bool = False) -> None:
|
||||
def connect_to_blockchain(self,
|
||||
enode: str = None,
|
||||
recompile_contracts: bool = False,
|
||||
full_sync: bool = False) -> None:
|
||||
"""
|
||||
|
||||
:param enode: ETH seednode or bootnode enode address to start learning from,
|
||||
|
@ -318,7 +321,8 @@ class NodeConfiguration(ABC):
|
|||
compile=recompile_contracts,
|
||||
poa=self.poa,
|
||||
fetch_registry=True,
|
||||
provider_process=self.provider_process)
|
||||
provider_process=self.provider_process,
|
||||
full_sync=full_sync)
|
||||
|
||||
# Read Ethereum Node Keyring
|
||||
self.accounts = self.blockchain.interface.w3.eth.accounts
|
||||
|
|
Loading…
Reference in New Issue