Skip to content

Commit

Permalink
Merge pull request #35 from stakewise/refactor-operator-onboarding
Browse files Browse the repository at this point in the history
Refactor operator onboarding
  • Loading branch information
tsudmi authored Dec 19, 2021
2 parents 0acb3ec + a7aab27 commit ee1e8c2
Show file tree
Hide file tree
Showing 16 changed files with 221 additions and 338 deletions.
11 changes: 0 additions & 11 deletions deploy/.env.example
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
# COMMON
LOG_LEVEL=INFO
# mainnet or goerli
NETWORK=mainnet
ENABLE_HEALTH_SERVER=true
HEALTH_SERVER_PORT=8080
HEALTH_SERVER_HOST=0.0.0.0
ETH1_CONFIRMATION_BLOCKS=15
ORACLE_PRIVATE_KEY=0x<private_key>

# ORACLE
IPFS_PIN_ENDPOINTS=/dns/ipfs.infura.io/tcp/5001/https,/dns/ipfs/tcp/5001/http
IPFS_FETCH_ENDPOINTS=https://gateway.pinata.cloud,http://cloudflare-ipfs.com,https://ipfs.io
IPFS_PINATA_PIN_ENDPOINT=https://api.pinata.cloud/pinning/pinJSONToIPFS
IPFS_PINATA_API_KEY=""
IPFS_PINATA_SECRET_KEY=""
ETH2_ENDPOINT=http://lighthouse:5052
# lighthouse, prysm or teku
ETH2_CLIENT=lighthouse
AWS_ACCESS_KEY_ID=""
AWS_SECRET_ACCESS_KEY=""
ORACLE_PROCESS_INTERVAL=180

# Uncomment below lines if use self-hosted graph node
# STAKEWISE_SUBGRAPH_URL=http://graph-node:8000/subgraphs/name/stakewise/stakewise
Expand All @@ -28,10 +21,6 @@ ORACLE_PROCESS_INTERVAL=180

# KEEPER
WEB3_ENDPOINT=""
KEEPER_PROCESS_INTERVAL=180
TRANSACTION_TIMEOUT=900
# 0.1 ETH
KEEPER_MIN_BALANCE_WEI=100000000000000000

# GRAPH
postgres_host=postgres
Expand Down
6 changes: 3 additions & 3 deletions deploy/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ volumes:
services:
oracle:
container_name: oracle
image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.0.0-rc.1
image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.0.0-rc.2
restart: always
entrypoint: ["python"]
command: ["oracle/oracle/main.py"]
Expand All @@ -30,7 +30,7 @@ services:

keeper:
container_name: keeper
image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.0.0-rc.1
image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.0.0-rc.2
restart: always
entrypoint: ["python"]
command: ["oracle/keeper/main.py"]
Expand Down Expand Up @@ -77,7 +77,7 @@ services:

subgraphs:
container_name: subgraphs
image: europe-west4-docker.pkg.dev/stakewiselabs/public/subgraphs:v1.0.1
image: europe-west4-docker.pkg.dev/stakewiselabs/public/subgraphs:v1.0.2
command: >
/bin/sh -c "until nc -vz graph-node 8020; do echo 'Waiting graph-node'; sleep 2; done
&& yarn prepare:${NETWORK}
Expand Down
3 changes: 1 addition & 2 deletions oracle/common/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

REWARD_VOTE_FILENAME = "reward-vote.json"
DISTRIBUTOR_VOTE_FILENAME = "distributor-vote.json"
INIT_VALIDATOR_VOTE_FILENAME = "init-validator-vote.json"
FINALIZE_VALIDATOR_VOTE_FILENAME = "finalize-validator-vote.json"
VALIDATOR_VOTE_FILENAME = "validator-vote.json"

# supported networks
MAINNET = "mainnet"
Expand Down
4 changes: 2 additions & 2 deletions oracle/keeper/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

ORACLE_PRIVATE_KEY = config("ORACLE_PRIVATE_KEY")

KEEPER_PROCESS_INTERVAL = config("KEEPER_PROCESS_INTERVAL", default=180, cast=int)
KEEPER_PROCESS_INTERVAL = config("KEEPER_PROCESS_INTERVAL", default=30, cast=int)

KEEPER_MIN_BALANCE_WEI = config(
"KEEPER_MIN_BALANCE_WEI", default=Web3.toWei(0.1, "ether"), cast=int
Expand All @@ -24,7 +24,7 @@
)
elif NETWORK == GOERLI:
ORACLES_CONTRACT_ADDRESS = Web3.toChecksumAddress(
"0x06b0C9476315634dCc59AA3F3f7d5Df6feCbAa90"
"0x4bBaA17eFd71683dCb9C769DD38E7674994FE38d"
)
MULTICALL_CONTRACT_ADDRESS = Web3.toChecksumAddress(
"0x77dCa2C955b15e9dE4dbBCf1246B4B85b651e50e"
Expand Down
3 changes: 1 addition & 2 deletions oracle/keeper/typings.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,4 @@ class Parameters(NamedTuple):
class OraclesVotes(NamedTuple):
rewards: List[RewardVote]
distributor: List[DistributorVote]
initialize_validator: List[ValidatorVote]
finalize_validator: List[ValidatorVote]
validator: List[ValidatorVote]
117 changes: 60 additions & 57 deletions oracle/keeper/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@
AWS_S3_REGION,
DISTRIBUTOR_VOTE_FILENAME,
ETH1_CONFIRMATION_BLOCKS,
FINALIZE_VALIDATOR_VOTE_FILENAME,
INIT_VALIDATOR_VOTE_FILENAME,
REWARD_VOTE_FILENAME,
VALIDATOR_VOTE_FILENAME,
)
from oracle.keeper.clients import web3_client
from oracle.keeper.contracts import multicall_contract, oracles_contract
Expand Down Expand Up @@ -141,8 +140,13 @@ def check_validator_vote(vote: ValidatorVote, oracle: ChecksumAddress) -> bool:
"""Checks whether oracle's validator vote is correct."""
try:
encoded_data: bytes = web3_client.codec.encode_abi(
["uint256", "bytes", "address"],
[int(vote["nonce"]), vote["public_key"], vote["operator"]],
["uint256", "bytes", "address", "bytes32"],
[
int(vote["nonce"]),
vote["public_key"],
vote["operator"],
vote["validators_count"],
],
)
return validate_vote_signature(encoded_data, oracle, vote["signature"])
except: # noqa: E722
Expand All @@ -154,9 +158,7 @@ def get_oracles_votes(
rewards_nonce: int, validators_nonce: int, oracles: List[ChecksumAddress]
) -> OraclesVotes:
"""Fetches oracle votes that match current nonces."""
votes = OraclesVotes(
rewards=[], distributor=[], initialize_validator=[], finalize_validator=[]
)
votes = OraclesVotes(rewards=[], distributor=[], validator=[])

for oracle in oracles:
for arr, filename, correct_nonce, vote_checker in [
Expand All @@ -168,14 +170,8 @@ def get_oracles_votes(
check_distributor_vote,
),
(
votes.initialize_validator,
INIT_VALIDATOR_VOTE_FILENAME,
validators_nonce,
check_validator_vote,
),
(
votes.finalize_validator,
FINALIZE_VALIDATOR_VOTE_FILENAME,
votes.validator,
VALIDATOR_VOTE_FILENAME,
validators_nonce,
check_validator_vote,
),
Expand Down Expand Up @@ -319,48 +315,55 @@ def submit_votes(votes: OraclesVotes, total_oracles: int) -> None:
)
logger.info("Merkle Distributor has been successfully updated")

for validator_votes, func_name in [
(votes.initialize_validator, "initializeValidator"),
(votes.finalize_validator, "finalizeValidator"),
]:
counter = Counter(
[(vote["public_key"], vote["operator"]) for vote in validator_votes]
)
most_voted = counter.most_common(1)
if most_voted and can_submit(most_voted[0][1], total_oracles):
public_key, operator = most_voted[0][0]

signatures = []
i = 0
while not can_submit(len(signatures), total_oracles):
vote = validator_votes[i]
if (public_key, operator) == (
vote["public_key"],
vote["operator"],
):
signatures.append(vote["signature"])
i += 1

validator_vote: ValidatorVote = next(
vote
for vote in validator_votes
if (vote["public_key"], vote["operator"]) == (public_key, operator)
)
counter = Counter(
[
(vote["public_key"], vote["operator"], vote["validators_count"])
for vote in votes.validator
]
)
most_voted = counter.most_common(1)
if most_voted and can_submit(most_voted[0][1], total_oracles):
public_key, operator, validators_count = most_voted[0][0]
signatures = []
i = 0
while not can_submit(len(signatures), total_oracles):
vote = votes.validator[i]
if (public_key, operator, validators_count) == (
vote["public_key"],
vote["operator"],
vote["validators_count"],
):
signatures.append(vote["signature"])
i += 1

logger.info(
f"Submitting {func_name}: operator={operator}, public key={public_key}"
validator_vote: ValidatorVote = next(
vote
for vote in votes.validator
if (public_key, operator, validators_count)
== (
vote["public_key"],
vote["operator"],
vote["validators_count"],
)
submit_update(
getattr(oracles_contract.functions, func_name)(
dict(
operator=validator_vote["operator"],
withdrawalCredentials=validator_vote["withdrawal_credentials"],
depositDataRoot=validator_vote["deposit_data_root"],
publicKey=validator_vote["public_key"],
signature=validator_vote["deposit_data_signature"],
),
validator_vote["proof"],
signatures,
)
logger.info(
f"Submitting validator registration: "
f"operator={operator}, "
f"public key={public_key}, "
f"validator count={validators_count}"
)
submit_update(
oracles_contract.functions.registerValidator(
dict(
operator=validator_vote["operator"],
withdrawalCredentials=validator_vote["withdrawal_credentials"],
depositDataRoot=validator_vote["deposit_data_root"],
publicKey=validator_vote["public_key"],
signature=validator_vote["deposit_data_signature"],
),
)
logger.info(f"{func_name} has been successfully executed")
validator_vote["proof"],
validators_count,
signatures,
),
)
logger.info("Validator registration has been successfully submitted")
60 changes: 21 additions & 39 deletions oracle/oracle/eth1.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,51 @@
from .distributor.types import DistributorVote, DistributorVotingParameters
from .graphql_queries import (
FINALIZED_BLOCK_QUERY,
LATEST_BLOCK_QUERY,
ORACLE_QUERY,
VOTING_PARAMETERS_QUERY,
)
from .rewards.types import RewardsVotingParameters, RewardVote
from .validators.types import (
FinalizeValidatorVotingParameters,
InitializeValidatorVotingParameters,
ValidatorVote,
)
from .validators.types import ValidatorVote

logger = logging.getLogger(__name__)


class FinalizedBlock(TypedDict):
class Block(TypedDict):
block_number: BlockNumber
timestamp: Timestamp


class VotingParameters(TypedDict):
rewards: RewardsVotingParameters
distributor: DistributorVotingParameters
initialize_validator: InitializeValidatorVotingParameters
finalize_validator: FinalizeValidatorVotingParameters


@backoff.on_exception(backoff.expo, Exception, max_time=900)
async def get_finalized_block() -> FinalizedBlock:
async def get_finalized_block() -> Block:
"""Gets the finalized block number and its timestamp."""
result: Dict = await execute_ethereum_gql_query(
query=FINALIZED_BLOCK_QUERY,
variables=dict(
confirmation_blocks=ETH1_CONFIRMATION_BLOCKS,
),
)
return FinalizedBlock(
return Block(
block_number=BlockNumber(int(result["blocks"][0]["id"])),
timestamp=Timestamp(int(result["blocks"][0]["timestamp"])),
)


@backoff.on_exception(backoff.expo, Exception, max_time=900)
async def get_latest_block() -> Block:
"""Gets the latest block number and its timestamp."""
result: Dict = await execute_ethereum_gql_query(
query=LATEST_BLOCK_QUERY,
variables=dict(
confirmation_blocks=ETH1_CONFIRMATION_BLOCKS,
),
)
return Block(
block_number=BlockNumber(int(result["blocks"][0]["id"])),
timestamp=Timestamp(int(result["blocks"][0]["timestamp"])),
)
Expand All @@ -66,17 +76,6 @@ async def get_voting_parameters(block_number: BlockNumber) -> VotingParameters:
network = result["networks"][0]
distributor = result["merkleDistributors"][0]
reward_eth_token = result["rewardEthTokens"][0]
pool = result["pools"][0]

validators = result["validators"]
if validators:
operator = validators[0].get("operator", {}).get("id", None)
if operator is not None:
operator = Web3.toChecksumAddress(operator)
public_key = validators[0].get("id", None)
else:
operator = None
public_key = None

rewards = RewardsVotingParameters(
rewards_nonce=int(network["oraclesRewardsNonce"]),
Expand All @@ -95,24 +94,7 @@ async def get_voting_parameters(block_number: BlockNumber) -> VotingParameters:
protocol_reward=Wei(int(reward_eth_token["protocolPeriodReward"])),
distributor_reward=Wei(int(reward_eth_token["distributorPeriodReward"])),
)
initialize_validator = InitializeValidatorVotingParameters(
validator_index=int(pool["pendingValidators"])
+ int(pool["activatedValidators"]),
validators_nonce=int(network["oraclesValidatorsNonce"]),
pool_balance=Wei(int(pool["balance"])),
finalizing_validator=public_key is not None,
)
finalize_validator = FinalizeValidatorVotingParameters(
validators_nonce=int(network["oraclesValidatorsNonce"]),
operator=operator,
public_key=public_key,
)
return VotingParameters(
rewards=rewards,
distributor=distributor,
initialize_validator=initialize_validator,
finalize_validator=finalize_validator,
)
return VotingParameters(rewards=rewards, distributor=distributor)


@backoff.on_exception(backoff.expo, Exception, max_time=900)
Expand Down
Loading

0 comments on commit ee1e8c2

Please sign in to comment.