Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor operator onboarding #35

Merged
merged 3 commits into from
Dec 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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