Merge branch 'dev/etan/bd-serialization' into feat/eip-7495

This commit is contained in:
Etan Kissling 2024-06-13 13:41:04 +02:00
commit 728d7db66e
No known key found for this signature in database
GPG Key ID: B21DA824C5A3D03D
82 changed files with 622 additions and 333 deletions

View File

@ -213,7 +213,7 @@ jobs:
devbuild:
name: "Developer builds"
runs-on: ubuntu-latest
runs-on: ['self-hosted','ubuntu-22.04']
steps:
- name: Checkout
uses: actions/checkout@v4

10
.gitmodules vendored
View File

@ -5,6 +5,11 @@
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
[submodule "vendor/mainnet"]
path = vendor/mainnet
url = https://github.com/eth-clients/mainnet.git
ignore = untracked
branch = main
[submodule "vendor/nim-eth2-scenarios"]
path = vendor/nim-eth2-scenarios
url = https://github.com/status-im/nim-eth2-scenarios
@ -170,11 +175,6 @@
url = https://github.com/status-im/nim-presto.git
ignore = untracked
branch = master
[submodule "vendor/eth2-networks"]
path = vendor/eth2-networks
url = https://github.com/eth-clients/eth2-networks.git
ignore = untracked
branch = master
[submodule "vendor/nim-taskpools"]
path = vendor/nim-taskpools
url = https://github.com/status-im/nim-taskpools

View File

@ -907,12 +907,14 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
+ getAggregatedAttestationDataScore() test vectors OK
+ getAttestationDataScore() test vectors OK
+ getLiveness() response deserialization test OK
+ getProduceBlockResponseV3Score() default test OK
+ getProduceBlockResponseV3Score() test vectors OK
+ getSyncCommitteeContributionDataScore() test vectors OK
+ getSyncCommitteeMessageDataScore() test vectors OK
+ getUniqueVotes() test vectors OK
+ normalizeUri() test vectors OK
```
OK: 12/12 Fail: 0/12 Skip: 0/12
OK: 14/14 Fail: 0/14 Skip: 0/14
## Validator change pool testing suite
```diff
+ addValidatorChangeMessage/getAttesterSlashingMessage OK

View File

@ -593,7 +593,7 @@ define MAKE_DEPOSIT
build/deposit_contract sendDeposits \
$(2) \
--deposit-contract=$$(cat vendor/eth2-networks/shared/$(1)/deposit_contract.txt) \
--deposit-contract=$$(cat vendor/$(1)/metadata/deposit_contract.txt) \
--deposits-file=nbc-$(1)-deposits.json \
--min-delay=$(DEPOSITS_DELAY) \
--max-delay=$(DEPOSITS_DELAY) \

View File

@ -73,7 +73,7 @@ proc initLightClient*(
headBlockHash = blckPayload.block_hash,
safeBlockHash = beaconHead.safeExecutionBlockHash,
finalizedBlockHash = beaconHead.finalizedExecutionBlockHash,
payloadAttributes = none attributes)
payloadAttributes = Opt.none attributes)
case node.dag.cfg.consensusForkAtEpoch(
forkyBlck.message.slot.epoch)

View File

@ -1047,7 +1047,7 @@ type
monitoringType* {.
desc: "Enable block monitoring which are seen by beacon node (BETA)"
defaultValue: BlockMonitoringType.Disabled
defaultValue: BlockMonitoringType.Event
name: "block-monitor-type".}: BlockMonitoringType
SigningNodeConf* = object

View File

@ -2408,7 +2408,7 @@ proc updateHead*(
if dag.headState.kind > lastHeadKind:
case dag.headState.kind
of ConsensusFork.Phase0 .. ConsensusFork.Bellatrix, ConsensusFork.Electra:
of ConsensusFork.Phase0 .. ConsensusFork.Bellatrix:
discard
of ConsensusFork.Capella:
if dag.vanityLogs.onUpgradeToCapella != nil:
@ -2416,6 +2416,9 @@ proc updateHead*(
of ConsensusFork.Deneb:
if dag.vanityLogs.onUpgradeToDeneb != nil:
dag.vanityLogs.onUpgradeToDeneb()
of ConsensusFork.Electra:
if dag.vanityLogs.onUpgradeToElectra != nil:
dag.vanityLogs.onUpgradeToElectra()
if dag.vanityLogs.onKnownBlsToExecutionChange != nil and
checkBlsToExecutionChanges(

View File

@ -184,7 +184,7 @@ proc updateExecutionClientHead*(
headBlockHash = headExecutionBlockHash,
safeBlockHash = newHead.safeExecutionBlockHash,
finalizedBlockHash = newHead.finalizedExecutionBlockHash,
payloadAttributes = none attributes)
payloadAttributes = Opt.none attributes)
# Can't use dag.head here because it hasn't been updated yet
let
@ -374,7 +374,7 @@ proc runProposalForkchoiceUpdated*(
let (status, _) = await self.elManager.forkchoiceUpdated(
headBlockHash, safeBlockHash,
beaconHead.finalizedExecutionBlockHash,
payloadAttributes = some fcPayloadAttributes)
payloadAttributes = Opt.some fcPayloadAttributes)
debug "Fork-choice updated for proposal", status
static: doAssert high(ConsensusFork) == ConsensusFork.Electra

View File

@ -0,0 +1,28 @@

..-|\_/|
  text .-'..d :.: $
(_: __. :::::$
`-' ? :::: $
\ :::: $
\::::::$
area\ :::::$
|:.::: $
 `| :::: $..__
`.::::::.::.`-.
|:::::::::::::`--.
here `.::::.:::::::::::`---.....
  `.:::::::::::::::::::::::`\
 |:::::::::::::::::::::::::|
 |:::::::::::::::::::::::|:|
  |::::::::::::|::::::::::|:|
':::)___..----\ :::::| .`. \
|:::| | :|`.::::|: : `.`.
() () O  O |:::| |. ||:::| `.: |:|
\ / \ /   |:::| | :|`.:::`. .:`.`.
() ()()  ()  () O () |:::| |: |::::| |::|`.`.
\  / \ // / / |:::| | :||:::| | :| )))
 () ()  O() O ()  () O  () |:::||.:.| () ()::| |:..;((( ()
. \. /. \. // O. /. . \. /. \ /. . \ .'::.'|::'|. /. . /.|::|. ` :.\ `/ .
....()O...O()...\()()....()O...O.()....()|:::| |_M()()....()..:::|.()|: :()..()
\../...\./...\../....\../...\./....\../..|:::|\../....\../...|:::<...\_M/|.....
 ():::::O:::::()::::::():::::O::::::():::|_N_|:()::::::()::::|_N_|::::():::::::

View File

@ -0,0 +1,28 @@
..-|\_/|
text .-'..d :.: $
(_: __. :::::$
`-' ? :::: $
\ :::: $
\::::::$
area \ :::::$
|:.::: $
`| :::: $..__
`.::::::.::.`-.
|:::::::::::::`--.
here `.::::.:::::::::::`---.....
`.:::::::::::::::::::::::`\
|:::::::::::::::::::::::::|
|:::::::::::::::::::::::|:|
|::::::::::::|::::::::::|:|
':::)___..----\ :::::| .`. \
|:::| | :| `.::::|: : `.`.
() () O O |:::| |. | |:::| `.: |:|
\ / \ / |:::| | :| `.:::`. .:`.`.
() ()() () () O () |:::| |: | ::::| |::|`.`.
\ / \ / / / / |:::| | :| |:::| | :| )))
() () O() O () () O () |:::||.:.| () ()::| |:..;((( ()
. \. /. \. // O. /. . \. /. \ /. . \ .'::.'|::'|. /. . /.|::|. ` :.\ `/ .
....()O...O()...\()()....()O...O.()....()|:::| |_M()()....()..:::|.()|: :()..()
\../...\./...\../....\../...\./....\../..|:::|\../....\../...|:::<...\_M/|.....
():::::O:::::()::::::():::::O::::::():::|_N_|:()::::::()::::|_N_|::::():::::::

View File

@ -34,6 +34,10 @@ type
# in case of chain reorgs around the upgrade.
onUpgradeToDeneb*: LogProc
# Gets displayed on upgrade to Electra. May be displayed multiple times
# in case of chain reorgs around the upgrade.
onUpgradeToElectra*: LogProc
# Created by http://beatscribe.com/ (beatscribe#1008 on Discord)
# These need to be the main body of the log not to be reformatted or escaped.
@ -47,3 +51,6 @@ proc capellaBlink*() = notice "\n" & staticRead("capella" / "blink.ans")
proc denebMono*() = notice "\n" & staticRead("deneb" / "mono.txt")
proc denebColor*() = notice "\n" & staticRead("deneb" / "color.ans")
proc electraMono*() = notice "\n" & staticRead("electra" / "mono.txt")
proc electraColor*() = notice "\n" & staticRead("electra" / "color.ans")

View File

@ -123,25 +123,25 @@ contract(DepositContract):
proc deployContract*(web3: Web3, code: seq[byte]): Future[ReceiptObject] {.async.} =
let tr = TransactionArgs(
`from`: web3.defaultAccount.some,
data: code.some,
gas: Quantity(3000000).some,
gasPrice: Quantity(1).some)
`from`: Opt.some web3.defaultAccount,
data: Opt.some code,
gas: Opt.some Quantity(3000000),
gasPrice: Opt.some Quantity(1))
let r = await web3.send(tr)
result = await web3.getMinedTransactionReceipt(r)
proc sendEth(web3: Web3, to: Eth1Address, valueEth: int): Future[TxHash] =
let tr = TransactionArgs(
`from`: web3.defaultAccount.some,
`from`: Opt.some web3.defaultAccount,
# TODO: Force json-rpc to generate 'data' field
# should not be needed anymore, new execution-api schema
# is using `input` field
data: some(newSeq[byte]()),
gas: Quantity(3000000).some,
gasPrice: Quantity(1).some,
value: some(valueEth.u256 * 1000000000000000000.u256),
to: some(to))
data: Opt.some(newSeq[byte]()),
gas: Opt.some Quantity(3000000),
gasPrice: Opt.some Quantity(1),
value: Opt.some(valueEth.u256 * 1000000000000000000.u256),
to: Opt.some(to))
web3.send(tr)
type
@ -153,7 +153,7 @@ proc ethToWei(eth: UInt256): UInt256 =
proc initWeb3(web3Url, privateKey: string): Future[Web3] {.async.} =
result = await newWeb3(web3Url)
if privateKey.len != 0:
result.privateKey = some(PrivateKey.fromHex(privateKey)[])
result.privateKey = Opt.some(PrivateKey.fromHex(privateKey)[])
else:
let accounts = await result.provider.eth_accounts()
doAssert(accounts.len > 0)

View File

@ -106,7 +106,7 @@ type
Running, Closing, Closed
ELManager* = ref object
eth1Network: Option[Eth1Network]
eth1Network: Opt[Eth1Network]
## If this value is supplied the EL manager will check whether
## all configured EL nodes are connected to the same network.
@ -133,7 +133,7 @@ type
## also includes blocks without deposits because we must
## vote for a block only if it's part of our known history.
syncTargetBlock: Option[Eth1BlockNumber]
syncTargetBlock: Opt[Eth1BlockNumber]
chainSyncingLoopFut: Future[void]
exchangeTransitionConfigurationLoopFut: Future[void]
@ -177,7 +177,7 @@ type
depositContractSyncStatus: DepositContractSyncStatus
## Are we sure that this EL has synced the deposit contract?
lastPayloadId: Option[PayloadID]
lastPayloadId: Opt[PayloadID]
FullBlockId* = object
number: Eth1BlockNumber
@ -419,7 +419,7 @@ func asConsensusType*(payloadWithValue: BellatrixExecutionPayloadWithValue):
executionPayload: payloadWithValue.executionPayload.asConsensusType,
blockValue: payloadWithValue.blockValue)
template maybeDeref[T](o: Option[T]): T = o.get
template maybeDeref[T](o: Opt[T]): T = o.get
template maybeDeref[V](v: V): V = v
func asConsensusType*(rpcExecutionPayload: ExecutionPayloadV1OrV2|ExecutionPayloadV2):
@ -779,15 +779,15 @@ func areSameAs(expectedParams: Option[NextExpectedPayloadParams],
proc forkchoiceUpdated(rpcClient: RpcClient,
state: ForkchoiceStateV1,
payloadAttributes: Option[PayloadAttributesV1] |
Option[PayloadAttributesV2] |
Option[PayloadAttributesV3]):
payloadAttributes: Opt[PayloadAttributesV1] |
Opt[PayloadAttributesV2] |
Opt[PayloadAttributesV3]):
Future[ForkchoiceUpdatedResponse] =
when payloadAttributes is Option[PayloadAttributesV1]:
when payloadAttributes is Opt[PayloadAttributesV1]:
rpcClient.engine_forkchoiceUpdatedV1(state, payloadAttributes)
elif payloadAttributes is Option[PayloadAttributesV2]:
elif payloadAttributes is Opt[PayloadAttributesV2]:
rpcClient.engine_forkchoiceUpdatedV2(state, payloadAttributes)
elif payloadAttributes is Option[PayloadAttributesV3]:
elif payloadAttributes is Opt[PayloadAttributesV3]:
rpcClient.engine_forkchoiceUpdatedV3(state, payloadAttributes)
else:
static: doAssert false
@ -817,7 +817,7 @@ proc getPayloadFromSingleEL(
headBlockHash: headBlock.asBlockHash,
safeBlockHash: safeBlock.asBlockHash,
finalizedBlockHash: finalizedBlock.asBlockHash),
some PayloadAttributesV1(
Opt.some PayloadAttributesV1(
timestamp: Quantity timestamp,
prevRandao: FixedBytes[32] randomData.data,
suggestedFeeRecipient: suggestedFeeRecipient))
@ -827,7 +827,7 @@ proc getPayloadFromSingleEL(
headBlockHash: headBlock.asBlockHash,
safeBlockHash: safeBlock.asBlockHash,
finalizedBlockHash: finalizedBlock.asBlockHash),
some PayloadAttributesV2(
Opt.some PayloadAttributesV2(
timestamp: Quantity timestamp,
prevRandao: FixedBytes[32] randomData.data,
suggestedFeeRecipient: suggestedFeeRecipient,
@ -841,7 +841,7 @@ proc getPayloadFromSingleEL(
headBlockHash: headBlock.asBlockHash,
safeBlockHash: safeBlock.asBlockHash,
finalizedBlockHash: finalizedBlock.asBlockHash),
some PayloadAttributesV3(
Opt.some PayloadAttributesV3(
timestamp: Quantity timestamp,
prevRandao: FixedBytes[32] randomData.data,
suggestedFeeRecipient: suggestedFeeRecipient,
@ -1341,9 +1341,9 @@ proc sendNewPayload*(
proc forkchoiceUpdatedForSingleEL(
connection: ELConnection,
state: ref ForkchoiceStateV1,
payloadAttributes: Option[PayloadAttributesV1] |
Option[PayloadAttributesV2] |
Option[PayloadAttributesV3]
payloadAttributes: Opt[PayloadAttributesV1] |
Opt[PayloadAttributesV2] |
Opt[PayloadAttributesV3]
): Future[PayloadStatusV1] {.async: (raises: [CatchableError]).} =
let
rpcClient = await connection.connectedRpcClient()
@ -1363,10 +1363,10 @@ proc forkchoiceUpdatedForSingleEL(
proc forkchoiceUpdated*(
m: ELManager,
headBlockHash, safeBlockHash, finalizedBlockHash: Eth2Digest,
payloadAttributes: Option[PayloadAttributesV1] |
Option[PayloadAttributesV2] |
Option[PayloadAttributesV3]
): Future[(PayloadExecutionStatus, Option[BlockHash])] {.
payloadAttributes: Opt[PayloadAttributesV1] |
Opt[PayloadAttributesV2] |
Opt[PayloadAttributesV3]
): Future[(PayloadExecutionStatus, Opt[BlockHash])] {.
async: (raises: [CancelledError]).} =
doAssert not headBlockHash.isZero
@ -1383,16 +1383,16 @@ proc forkchoiceUpdated*(
# payload (`Hash32()` if none yet finalized)"
if m.elConnections.len == 0:
return (PayloadExecutionStatus.syncing, none BlockHash)
return (PayloadExecutionStatus.syncing, Opt.none BlockHash)
when payloadAttributes is Option[PayloadAttributesV3]:
when payloadAttributes is Opt[PayloadAttributesV3]:
template payloadAttributesV3(): auto =
if payloadAttributes.isSome:
payloadAttributes.get
else:
# As timestamp and prevRandao are both 0, won't false-positive match
(static(default(PayloadAttributesV3)))
elif payloadAttributes is Option[PayloadAttributesV2]:
elif payloadAttributes is Opt[PayloadAttributesV2]:
template payloadAttributesV3(): auto =
if payloadAttributes.isSome:
PayloadAttributesV3(
@ -1404,7 +1404,7 @@ proc forkchoiceUpdated*(
else:
# As timestamp and prevRandao are both 0, won't false-positive match
(static(default(PayloadAttributesV3)))
elif payloadAttributes is Option[PayloadAttributesV1]:
elif payloadAttributes is Opt[PayloadAttributesV1]:
template payloadAttributesV3(): auto =
if payloadAttributes.isSome:
PayloadAttributesV3(
@ -1489,7 +1489,7 @@ proc forkchoiceUpdated*(
pendingRequests.filterIt(not(it.finished())).
mapIt(it.cancelAndWait())
await noCancel allFutures(pending)
return (PayloadExecutionStatus.invalid, none BlockHash)
return (PayloadExecutionStatus.invalid, Opt.none BlockHash)
elif responseProcessor.selectedResponse.isSome:
# We spawn task which will wait for all other responses which are
# still pending, after 30.seconds all pending requests will be
@ -1504,7 +1504,7 @@ proc forkchoiceUpdated*(
pendingRequests.filterIt(not(it.finished())).
mapIt(it.cancelAndWait())
await noCancel allFutures(pending)
return (PayloadExecutionStatus.syncing, none BlockHash)
return (PayloadExecutionStatus.syncing, Opt.none BlockHash)
if len(pendingRequests) == 0:
# All requests failed, we will continue our attempts until deadline
@ -1762,7 +1762,7 @@ proc new*(T: type ELManager,
depositContractBlockHash: Eth2Digest,
db: BeaconChainDB,
engineApiUrls: seq[EngineApiUrl],
eth1Network: Option[Eth1Network]): T =
eth1Network: Opt[Eth1Network]): T =
let
eth1Chain = Eth1Chain.init(
cfg, db, depositContractBlockNumber, depositContractBlockHash)
@ -1847,8 +1847,8 @@ proc syncBlockRange(
await connection.engineApiRequest(
depositContract.getJsonLogs(
DepositEvent,
fromBlock = some blockId(currentBlock),
toBlock = some blockId(maxBlockNumberRequested)),
fromBlock = Opt.some blockId(currentBlock),
toBlock = Opt.some blockId(maxBlockNumberRequested)),
"getLogs", Moment.now(), 30.seconds)
except CancelledError as exc:
debug "Request for deposit logs was interrupted"
@ -1956,7 +1956,6 @@ proc startExchangeTransitionConfigurationLoop(
while true:
# https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.3/src/engine/paris.md#specification-3
debug "Exchange transition configuration tick"
await m.exchangeTransitionConfiguration()
await sleepAsync(60.seconds)
@ -2090,7 +2089,7 @@ proc syncEth1Chain(
latestBlockNumber = latestBlock.number
m.syncTargetBlock = some(
m.syncTargetBlock = Opt.some(
if latestBlock.number > m.cfg.ETH1_FOLLOW_DISTANCE.Eth1BlockNumber:
latestBlock.number - m.cfg.ETH1_FOLLOW_DISTANCE
else:

View File

@ -8,7 +8,6 @@
{.push raises: [].}
import
stew/results,
chronicles, chronos, metrics,
../spec/[forks, signatures, signatures_batch],
../sszdump
@ -243,7 +242,7 @@ proc expectValidForkchoiceUpdated(
headBlockHash = headBlockHash,
safeBlockHash = safeBlockHash,
finalizedBlockHash = finalizedBlockHash,
payloadAttributes = none headBlockPayloadAttributesType)
payloadAttributes = Opt.none headBlockPayloadAttributesType)
receivedExecutionBlockHash =
when typeof(receivedBlock).kind >= ConsensusFork.Bellatrix:
receivedBlock.message.body.execution_payload.block_hash
@ -685,7 +684,7 @@ proc storeBlock(
self.consensusManager[].optimisticExecutionBlockHash,
safeBlockHash = newHead.get.safeExecutionBlockHash,
finalizedBlockHash = newHead.get.finalizedExecutionBlockHash,
payloadAttributes = none attributes)
payloadAttributes = Opt.none attributes)
let consensusFork = self.consensusManager.dag.cfg.consensusForkAtEpoch(
newHead.get.blck.bid.slot.epoch)

View File

@ -9,8 +9,8 @@
import
std/tables,
stew/results,
chronicles, chronos, metrics, taskpools,
chronicles, chronos, metrics,
taskpools,
../spec/[helpers, forks],
../spec/datatypes/[altair, phase0, deneb],
../consensus_object_pools/[
@ -122,7 +122,7 @@ type
# ----------------------------------------------------------------
dag*: ChainDAGRef
attestationPool*: ref AttestationPool
validatorPool: ref ValidatorPool
validatorPool*: ref ValidatorPool
syncCommitteeMsgPool: ref SyncCommitteeMsgPool
lightClientPool: ref LightClientPool
@ -366,7 +366,8 @@ proc checkForPotentialDoppelganger(
proc processAttestation*(
self: ref Eth2Processor, src: MsgSource,
attestation: phase0.Attestation | electra.Attestation, subnet_id: SubnetId,
checkSignature: bool = true): Future[ValidationRes] {.async: (raises: [CancelledError]).} =
checkSignature, checkValidator: bool
): Future[ValidationRes] {.async: (raises: [CancelledError]).} =
var wallTime = self.getCurrentBeaconTime()
let (afterGenesis, wallSlot) = wallTime.toSlot()
@ -393,6 +394,13 @@ proc processAttestation*(
let (attester_index, sig) = v.get()
if checkValidator and (attester_index in self.validatorPool[]):
warn "A validator client has attempted to send an attestation from " &
"validator that is also managed by the beacon node",
validator_index = attester_index
errReject("An attestation could not be sent from a validator that is " &
"also managed by the beacon node")
else:
self[].checkForPotentialDoppelganger(attestation, [attester_index])
trace "Attestation validated"

View File

@ -9,7 +9,6 @@
import
std/[json, sequtils, times],
stew/saturation_arith,
eth/common/[eth_types_rlp, transaction],
eth/keys,
eth/p2p/discoveryv5/random2,
@ -1254,37 +1253,37 @@ proc ETHExecutionBlockHeaderCreateFromJson(
coinbase: distinctBase(data.miner),
stateRoot: data.stateRoot.asEth2Digest,
txRoot: data.transactionsRoot.asEth2Digest,
receiptRoot: data.receiptsRoot.asEth2Digest,
bloom: distinctBase(data.logsBloom),
receiptsRoot: data.receiptsRoot.asEth2Digest,
logsBloom: distinctBase(data.logsBloom),
difficulty: data.difficulty,
blockNumber: distinctBase(data.number).u256,
gasLimit: cast[int64](data.gasLimit),
gasUsed: cast[int64](data.gasUsed),
timestamp: EthTime(int64.saturate distinctBase(data.timestamp)),
number: distinctBase(data.number),
gasLimit: distinctBase(data.gasLimit),
gasUsed: distinctBase(data.gasUsed),
timestamp: EthTime(distinctBase(data.timestamp)),
extraData: distinctBase(data.extraData),
mixDigest: data.mixHash.asEth2Digest,
mixHash: data.mixHash.asEth2Digest,
nonce: distinctBase(data.nonce.get),
fee: data.baseFeePerGas,
baseFeePerGas: data.baseFeePerGas,
withdrawalsRoot:
if data.withdrawalsRoot.isSome:
some(data.withdrawalsRoot.get.asEth2Digest)
Opt.some(data.withdrawalsRoot.get.asEth2Digest)
else:
none(ExecutionHash256),
Opt.none(ExecutionHash256),
blobGasUsed:
if data.blobGasUsed.isSome:
some distinctBase(data.blobGasUsed.get)
Opt.some distinctBase(data.blobGasUsed.get)
else:
none(uint64),
Opt.none(uint64),
excessBlobGas:
if data.excessBlobGas.isSome:
some distinctBase(data.excessBlobGas.get)
Opt.some distinctBase(data.excessBlobGas.get)
else:
none(uint64),
Opt.none(uint64),
parentBeaconBlockRoot:
if data.parentBeaconBlockRoot.isSome:
some distinctBase(data.parentBeaconBlockRoot.get.asEth2Digest)
Opt.some distinctBase(data.parentBeaconBlockRoot.get.asEth2Digest)
else:
none(ExecutionHash256))
Opt.none(ExecutionHash256))
if rlpHash(blockHeader) != executionHash[]:
return nil
@ -1529,15 +1528,15 @@ proc ETHTransactionsCreateFromJson(
chainId: data.chainId.get(0.Quantity).ChainId,
nonce: distinctBase(data.nonce),
gasPrice: data.gasPrice.GasInt,
maxPriorityFee:
maxPriorityFeePerGas:
distinctBase(data.maxPriorityFeePerGas.get(data.gasPrice)).GasInt,
maxFee: distinctBase(data.maxFeePerGas.get(data.gasPrice)).GasInt,
maxFeePerGas: distinctBase(data.maxFeePerGas.get(data.gasPrice)).GasInt,
gasLimit: distinctBase(data.gas).GasInt,
to:
if data.to.isSome:
some(distinctBase(data.to.get))
Opt.some(distinctBase(data.to.get))
else:
none(EthAddress),
Opt.none(EthAddress),
value: data.value,
payload: data.input,
accessList:
@ -1555,7 +1554,7 @@ proc ETHTransactionsCreateFromJson(
ExecutionHash256(data: distinctBase(it)))
else:
@[],
V: data.v.int64,
V: data.v.uint64,
R: data.r,
S: data.s)
rlpBytes =
@ -1567,7 +1566,7 @@ proc ETHTransactionsCreateFromJson(
if data.hash.asEth2Digest != hash:
return nil
template isEven(x: int64): bool =
template isEven(x: uint64): bool =
(x and 1) == 0
# Compute from execution address
@ -1614,9 +1613,9 @@ proc ETHTransactionsCreateFromJson(
chainId: distinctBase(tx.chainId).u256,
`from`: ExecutionAddress(data: fromAddress),
nonce: tx.nonce,
maxPriorityFeePerGas: tx.maxPriorityFee.uint64,
maxFeePerGas: tx.maxFee.uint64,
gas: tx.gasLimit.uint64,
maxPriorityFeePerGas: tx.maxPriorityFeePerGas,
maxFeePerGas: tx.maxFeePerGas,
gas: tx.gasLimit,
destinationType: destinationType,
to: ExecutionAddress(data: toAddress),
value: tx.value,
@ -2178,7 +2177,7 @@ proc ETHReceiptsCreateFromJson(
else:
default(ExecutionHash256),
cumulativeGasUsed: distinctBase(data.cumulativeGasUsed).GasInt,
bloom: distinctBase(data.logsBloom),
logsBloom: distinctBase(data.logsBloom),
logs: data.logs.mapIt(Log(
address: distinctBase(it.address),
topics: it.topics.mapIt(distinctBase(it)),
@ -2198,7 +2197,7 @@ proc ETHReceiptsCreateFromJson(
root: rec.hash,
status: rec.status,
gasUsed: distinctBase(data.gasUsed), # Validated during sanity checks.
logsBloom: BloomLogs(data: rec.bloom),
logsBloom: BloomLogs(data: rec.logsBloom),
logs: rec.logs.mapIt(ETHLog(
address: ExecutionAddress(data: it.address),
topics: it.topics.mapIt(Eth2Digest(data: it)),

View File

@ -1 +1 @@
../../../vendor/eth2-networks/shared/mainnet/config.yaml
../../../vendor/mainnet/metadata/config.yaml

View File

@ -1 +1 @@
../../../vendor/eth2-networks/shared/mainnet/genesis.ssz
../../../vendor/mainnet/metadata/genesis.ssz

View File

@ -9,7 +9,7 @@
import
std/[algorithm, sequtils],
chronos, chronicles, stew/results,
chronos, chronicles,
eth/p2p/discoveryv5/[enr, protocol, node, random2],
../spec/datatypes/altair,
../spec/eth2_ssz_serialization,
@ -53,7 +53,7 @@ proc addBootstrapNode*(bootstrapAddr: string,
return
# Ignore comments in
# https://github.com/eth-clients/eth2-networks/blob/063f826a03676c33c95a66306916f18b690d35eb/shared/mainnet/bootstrap_nodes.txt
# https://github.com/eth-clients/mainnet/blob/main/metadata/bootstrap_nodes.txt
let enrRes = parseBootstrapAddress(bootstrapAddr.split(" # ")[0])
if enrRes.isOk:
bootstrapEnrs.add enrRes.value
@ -127,7 +127,7 @@ proc queryRandom*(
forkId: ENRForkID,
wantedAttnets: AttnetBits,
wantedSyncnets: SyncnetBits,
minScore: int): Future[seq[Node]] {.async.} =
minScore: int): Future[seq[Node]] {.async: (raises: [CancelledError]).} =
## Perform a discovery query for a random target
## (forkId) and matching at least one of the attestation subnets.
@ -143,7 +143,7 @@ proc queryRandom*(
peerForkId =
try:
SSZ.decode(eth2FieldBytes, ENRForkID)
except SszError as e:
except SerializationError as e:
debug "Could not decode the eth2 field of peer",
peer = n.record.toURI(), exception = e.name, msg = e.msg
continue
@ -156,7 +156,7 @@ proc queryRandom*(
let attnetsNode =
try:
SSZ.decode(attnetsBytes.get(), AttnetBits)
except SszError as e:
except SerializationError as e:
debug "Could not decode the attnets ERN bitfield of peer",
peer = n.record.toURI(), exception = e.name, msg = e.msg
continue
@ -170,7 +170,7 @@ proc queryRandom*(
let syncnetsNode =
try:
SSZ.decode(syncnetsBytes.get(), SyncnetBits)
except SszError as e:
except SerializationError as e:
debug "Could not decode the syncnets ENR bitfield of peer",
peer = n.record.toURI(), exception = e.name, msg = e.msg
continue

View File

@ -1550,7 +1550,7 @@ proc getLowSubnets(node: Eth2Node, epoch: Epoch): (AttnetBits, SyncnetBits) =
default(SyncnetBits)
)
proc runDiscoveryLoop(node: Eth2Node) {.async.} =
proc runDiscoveryLoop(node: Eth2Node) {.async: (raises: [CancelledError]).} =
debug "Starting discovery loop"
while true:

View File

@ -78,7 +78,7 @@ type
# additional checks to ensure we are connecting to a web3 provider
# serving data for the same network. The value can be set to `None`
# for custom networks and testing purposes.
eth1Network*: Option[Eth1Network]
eth1Network*: Opt[Eth1Network]
cfg*: RuntimeConfig
# Parsing `enr.Records` is still not possible at compile-time
@ -112,13 +112,13 @@ proc readBootEnr*(path: string): seq[string] {.raises: [IOError].} =
proc loadEth2NetworkMetadata*(
path: string,
eth1Network = none(Eth1Network),
eth1Network = Opt.none(Eth1Network),
isCompileTime = false,
downloadGenesisFrom = none(DownloadInfo),
useBakedInGenesis = none(string)
downloadGenesisFrom = Opt.none(DownloadInfo),
useBakedInGenesis = Opt.none(string)
): Result[Eth2NetworkMetadata, string] {.raises: [IOError, PresetFileError].} =
# Load data in eth2-networks format
# https://github.com/eth-clients/eth2-networks
# Load data in mainnet format
# https://github.com/eth-clients/mainnet
try:
let
@ -208,9 +208,9 @@ proc loadEth2NetworkMetadata*(
proc loadCompileTimeNetworkMetadata(
path: string,
eth1Network = none(Eth1Network),
useBakedInGenesis = none(string),
downloadGenesisFrom = none(DownloadInfo)): Eth2NetworkMetadata =
eth1Network = Opt.none(Eth1Network),
useBakedInGenesis = Opt.none(string),
downloadGenesisFrom = Opt.none(DownloadInfo)): Eth2NetworkMetadata =
if fileExists(path & "/config.yaml"):
try:
let res = loadEth2NetworkMetadata(
@ -255,13 +255,13 @@ when const_preset == "gnosis":
const
gnosisMetadata = loadCompileTimeNetworkMetadata(
vendorDir & "/gnosis-chain-configs/mainnet",
none(Eth1Network),
useBakedInGenesis = some "gnosis")
Opt.none(Eth1Network),
useBakedInGenesis = Opt.some "gnosis")
chiadoMetadata = loadCompileTimeNetworkMetadata(
vendorDir & "/gnosis-chain-configs/chiado",
none(Eth1Network),
useBakedInGenesis = some "chiado")
Opt.none(Eth1Network),
useBakedInGenesis = Opt.some "chiado")
static:
for network in [gnosisMetadata, chiadoMetadata]:
@ -292,28 +292,28 @@ elif const_preset == "mainnet":
else:
const
mainnetGenesis* = slurp(
vendorDir & "/eth2-networks/shared/mainnet/genesis.ssz")
vendorDir & "/mainnet/metadata/genesis.ssz")
sepoliaGenesis* = slurp(
vendorDir & "/sepolia/bepolia/genesis.ssz")
const
mainnetMetadata = loadCompileTimeNetworkMetadata(
vendorDir & "/eth2-networks/shared/mainnet",
some mainnet,
useBakedInGenesis = some "mainnet")
vendorDir & "/mainnet/metadata",
Opt.some mainnet,
useBakedInGenesis = Opt.some "mainnet")
holeskyMetadata = loadCompileTimeNetworkMetadata(
vendorDir & "/holesky/custom_config_data",
some holesky,
downloadGenesisFrom = some DownloadInfo(
Opt.some holesky,
downloadGenesisFrom = Opt.some DownloadInfo(
url: "https://github.com/status-im/nimbus-eth2/releases/download/v23.9.1/holesky-genesis.ssz.sz",
digest: Eth2Digest.fromHex "0x0ea3f6f9515823b59c863454675fefcd1d8b4f2dbe454db166206a41fda060a0"))
sepoliaMetadata = loadCompileTimeNetworkMetadata(
vendorDir & "/sepolia/bepolia",
some sepolia,
useBakedInGenesis = some "sepolia")
Opt.some sepolia,
useBakedInGenesis = Opt.some "sepolia")
static:
for network in [mainnetMetadata, sepoliaMetadata, holeskyMetadata]:

View File

@ -28,7 +28,7 @@
# name_size = 64-bit length in bytes
eth2_mainnet_genesis_data:
.incbin "eth2-networks/shared/mainnet/genesis.ssz"
.incbin "mainnet/metadata/genesis.ssz"
eth2_mainnet_genesis_end:
.global cdecl(eth2_mainnet_genesis_size)
.p2align 3

View File

@ -147,14 +147,16 @@ func getVanityLogs(stdoutKind: StdoutLogKind): VanityLogs =
onFinalizedMergeTransitionBlock: bellatrixBlink,
onUpgradeToCapella: capellaColor,
onKnownBlsToExecutionChange: capellaBlink,
onUpgradeToDeneb: denebColor)
onUpgradeToDeneb: denebColor,
onUpgradeToElectra: electraColor)
of StdoutLogKind.NoColors:
VanityLogs(
onMergeTransitionBlock: bellatrixMono,
onFinalizedMergeTransitionBlock: bellatrixMono,
onUpgradeToCapella: capellaMono,
onKnownBlsToExecutionChange: capellaMono,
onUpgradeToDeneb: denebMono)
onUpgradeToDeneb: denebMono,
onUpgradeToElectra: electraMono)
of StdoutLogKind.Json, StdoutLogKind.None:
VanityLogs(
onMergeTransitionBlock:
@ -166,12 +168,14 @@ func getVanityLogs(stdoutKind: StdoutLogKind): VanityLogs =
onKnownBlsToExecutionChange:
(proc() = notice "🦉 BLS to execution changed 🦉"),
onUpgradeToDeneb:
(proc() = notice "🐟 Proto-Danksharding is ON 🐟"))
(proc() = notice "🐟 Proto-Danksharding is ON 🐟"),
onUpgradeToElectra:
(proc() = notice "🦒 [PH] Electra 🦒"))
func getVanityMascot(consensusFork: ConsensusFork): string =
case consensusFork
of ConsensusFork.Electra:
" "
"🦒"
of ConsensusFork.Deneb:
"🐟"
of ConsensusFork.Capella:
@ -1767,7 +1771,8 @@ proc installMessageValidators(node: BeaconNode) =
): Future[ValidationResult] {.async: (raises: [CancelledError]).} =
return toValidationResult(
await node.processor.processAttestation(
MsgSource.gossip, attestation, subnet_id)))
MsgSource.gossip, attestation, subnet_id,
checkSignature = true, checkValidator = false)))
else:
for it in SubnetId:
closureScope: # Needed for inner `proc`; don't lift it out of loop.
@ -1778,7 +1783,8 @@ proc installMessageValidators(node: BeaconNode) =
): Future[ValidationResult] {.async: (raises: [CancelledError]).} =
return toValidationResult(
await node.processor.processAttestation(
MsgSource.gossip, attestation, subnet_id)))
MsgSource.gossip, attestation, subnet_id,
checkSignature = true, checkValidator = false)))
# beacon_aggregate_and_proof
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.4/specs/phase0/p2p-interface.md#beacon_aggregate_and_proof

View File

@ -123,7 +123,7 @@ programMain:
headBlockHash = payload.block_hash,
safeBlockHash = payload.block_hash, # stub value
finalizedBlockHash = ZERO_HASH,
payloadAttributes = none(consensusFork.PayloadAttributes))
payloadAttributes = Opt.none(consensusFork.PayloadAttributes))
else: discard
optimisticProcessor = initOptimisticProcessor(
getBeaconTime, optimisticHandler)

View File

@ -9,7 +9,7 @@
import
std/[typetraits, sequtils, sets],
stew/[results, base10],
stew/base10,
chronicles, metrics,
./rest_utils,
./state_ttl_cache,
@ -911,10 +911,12 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
when consensusFork >= ConsensusFork.Deneb:
await node.router.routeSignedBeaconBlock(
forkyBlck, Opt.some(
forkyBlck.create_blob_sidecars(kzg_proofs, blobs)))
forkyBlck.create_blob_sidecars(kzg_proofs, blobs)),
checkValidator = true)
else:
await node.router.routeSignedBeaconBlock(
forkyBlck, Opt.none(seq[BlobSidecar]))
forkyBlck, Opt.none(seq[BlobSidecar]),
checkValidator = true)
if res.isErr():
return RestApiResponse.jsonError(
@ -966,10 +968,12 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
when consensusFork >= ConsensusFork.Deneb:
await node.router.routeSignedBeaconBlock(
forkyBlck, Opt.some(
forkyBlck.create_blob_sidecars(kzg_proofs, blobs)))
forkyBlck.create_blob_sidecars(kzg_proofs, blobs)),
checkValidator = true)
else:
await node.router.routeSignedBeaconBlock(
forkyBlck, Opt.none(seq[BlobSidecar]))
forkyBlck, Opt.none(seq[BlobSidecar]),
checkValidator = true)
if res.isErr():
return RestApiResponse.jsonError(
@ -1087,7 +1091,8 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
let res = withBlck(forked):
forkyBlck.root = hash_tree_root(forkyBlck.message)
await node.router.routeSignedBeaconBlock(
forkyBlck, Opt.none(seq[BlobSidecar]))
forkyBlck, Opt.none(seq[BlobSidecar]),
checkValidator = true)
if res.isErr():
return RestApiResponse.jsonError(

View File

@ -9,7 +9,6 @@
import
std/sequtils,
stew/results,
chronicles,
chronos/apps/http/httpserver,
./rest_utils,

View File

@ -5,9 +5,11 @@
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
{.push raises: [].}
import
std/[sequtils],
stew/[results, base10],
stew/base10,
chronicles,
chronos/apps/http/httpdebug,
libp2p/[multiaddress, multicodec, peerstore],

View File

@ -4,8 +4,10 @@
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
{.push raises: [].}
import
stew/[byteutils, results],
stew/byteutils,
chronicles,
eth/p2p/discoveryv5/enr,
libp2p/[multiaddress, multicodec, peerstore],

View File

@ -7,7 +7,7 @@
{.push raises: [].}
import std/[typetraits, sets, sequtils]
import stew/[results, base10], chronicles
import stew/base10, chronicles
import ".."/[beacon_chain_db, beacon_node],
".."/networking/eth2_network,
".."/consensus_object_pools/[blockchain_dag, spec_cache,

View File

@ -7,7 +7,7 @@
{.push raises: [].}
# Types specific to Deneb (i.e. known to have changed across hard forks) - see
# Types specific to Electra (i.e. known to have changed across hard forks) - see
# `base` for types and guidelines common across forks
# TODO Careful, not nil analysis is broken / incomplete and the semantics will

View File

@ -1173,6 +1173,16 @@ template withForkyMaybeBlindedBlck*(
template forkyMaybeBlindedBlck: untyped {.inject, used.} = b.phase0Data
body
template shortLog*(x: ForkedMaybeBlindedBeaconBlock): auto =
withForkyMaybeBlindedBlck(x):
when consensusFork >= ConsensusFork.Deneb:
when isBlinded == true:
shortLog(forkyMaybeBlindedBlck)
else:
shortLog(forkyMaybeBlindedBlck.`block`)
else:
shortLog(forkyMaybeBlindedBlck)
template withStateAndBlck*(
s: ForkedHashedBeaconState,
b: ForkedBeaconBlock | ForkedSignedBeaconBlock |
@ -1360,19 +1370,19 @@ func readSszForkedHashedBeaconState*(cfg: RuntimeConfig, data: openArray[byte]):
ForkedHashedBeaconState {.raises: [SerializationError].} =
## Read a state picking the right fork by first reading the slot from the byte
## source
if data.len() < sizeof(BeaconStateHeader):
raise (ref MalformedSszError)(msg: "Not enough data for BeaconState header")
const numHeaderBytes = fixedPortionSize(BeaconStateHeader)
if data.len() < numHeaderBytes:
raise (ref MalformedSszError)(msg: "Incomplete BeaconState header")
let header = SSZ.decode(
data.toOpenArray(0, sizeof(BeaconStateHeader) - 1),
BeaconStateHeader)
data.toOpenArray(0, numHeaderBytes - 1), BeaconStateHeader)
# TODO https://github.com/nim-lang/Nim/issues/19357
result = readSszForkedHashedBeaconState(cfg, header.slot, data)
type
ForkedBeaconBlockHeader = object
message*: uint32 # message offset
signature*: ValidatorSig
message: uint32 # message offset
signature: ValidatorSig
slot: Slot # start of BeaconBlock
func readSszForkedSignedBeaconBlock*(
@ -1380,11 +1390,11 @@ func readSszForkedSignedBeaconBlock*(
ForkedSignedBeaconBlock {.raises: [SerializationError].} =
## Helper to read a header from bytes when it's not certain what kind of block
## it is
if data.len() < sizeof(ForkedBeaconBlockHeader):
raise (ref MalformedSszError)(msg: "Not enough data for SignedBeaconBlock header")
const numHeaderBytes = fixedPortionSize(ForkedBeaconBlockHeader)
if data.len() < numHeaderBytes:
raise (ref MalformedSszError)(msg: "Incomplete SignedBeaconBlock header")
let header = SSZ.decode(
data.toOpenArray(0, sizeof(ForkedBeaconBlockHeader) - 1),
ForkedBeaconBlockHeader)
data.toOpenArray(0, numHeaderBytes - 1), ForkedBeaconBlockHeader)
# TODO https://github.com/nim-lang/Nim/issues/19357
result = ForkedSignedBeaconBlock(

View File

@ -484,24 +484,24 @@ proc blockToBlockHeader*(blck: ForkyBeaconBlock): ExecutionBlockHeader =
txRoot = payload.computeTransactionsTrieRoot()
withdrawalsRoot =
when typeof(payload).kind >= ConsensusFork.Capella:
some payload.computeWithdrawalsTrieRoot()
Opt.some payload.computeWithdrawalsTrieRoot()
else:
none(ExecutionHash256)
Opt.none(ExecutionHash256)
blobGasUsed =
when typeof(payload).kind >= ConsensusFork.Deneb:
some payload.blob_gas_used
Opt.some payload.blob_gas_used
else:
none(uint64)
Opt.none(uint64)
excessBlobGas =
when typeof(payload).kind >= ConsensusFork.Deneb:
some payload.excess_blob_gas
Opt.some payload.excess_blob_gas
else:
none(uint64)
Opt.none(uint64)
parentBeaconBlockRoot =
when typeof(payload).kind >= ConsensusFork.Deneb:
some ExecutionHash256(data: blck.parent_root.data)
Opt.some ExecutionHash256(data: blck.parent_root.data)
else:
none(ExecutionHash256)
Opt.none(ExecutionHash256)
ExecutionBlockHeader(
parentHash : payload.parent_hash,
@ -509,17 +509,17 @@ proc blockToBlockHeader*(blck: ForkyBeaconBlock): ExecutionBlockHeader =
coinbase : EthAddress payload.fee_recipient.data,
stateRoot : payload.state_root,
txRoot : txRoot,
receiptRoot : payload.receipts_root,
bloom : payload.logs_bloom.data,
receiptsRoot : payload.receipts_root,
logsBloom : payload.logs_bloom.data,
difficulty : default(DifficultyInt),
blockNumber : payload.block_number.u256,
number : payload.block_number,
gasLimit : cast[GasInt](payload.gas_limit),
gasUsed : cast[GasInt](payload.gas_used),
timestamp : EthTime(int64.saturate payload.timestamp),
extraData : payload.extra_data.asSeq,
mixDigest : payload.prev_randao, # EIP-4399 `mixDigest` -> `prevRandao`
mixHash : payload.prev_randao, # EIP-4399 `mixHash` -> `prevRandao`
nonce : default(BlockNonce),
fee : some payload.base_fee_per_gas,
baseFeePerGas : Opt.some payload.base_fee_per_gas,
withdrawalsRoot : withdrawalsRoot,
blobGasUsed : blobGasUsed, # EIP-4844
excessBlobGas : excessBlobGas, # EIP-4844

View File

@ -421,3 +421,23 @@ proc verify_bls_to_execution_change_signature*(
let signing_root = compute_bls_to_execution_change_signing_root(
genesisFork, genesis_validators_root, msg.message)
blsVerify(pubkey, signing_root.data, signature)
func compute_consolidation_signing_root(
genesisFork: Fork, genesis_validators_root: Eth2Digest,
msg: Consolidation): Eth2Digest =
# Uses genesis fork version regardless
doAssert genesisFork.current_version == genesisFork.previous_version
let domain = compute_domain(
DOMAIN_CONSOLIDATION, genesisFork.current_version,
genesis_validators_root=genesis_validators_root)
compute_signing_root(msg, domain)
proc verify_consolidation_signature*(
genesisFork: Fork, genesis_validators_root: Eth2Digest,
msg: SignedConsolidation | TrustedSignedConsolidation,
pubkeys: openArray[ValidatorPubKey]): bool =
withTrust(msg.signature):
let signing_root = compute_consolidation_signing_root(
genesisFork, genesis_validators_root, msg.message)
blsFastAggregateVerify(pubkeys, signing_root.data, msg.signature)

View File

@ -361,7 +361,8 @@ func partialBeaconBlock*(
deposits: seq[Deposit],
validator_changes: BeaconBlockValidatorChanges,
sync_aggregate: SyncAggregate,
execution_payload: ForkyExecutionPayloadForSigning
execution_payload: ForkyExecutionPayloadForSigning,
consolidations: openArray[SignedConsolidation]
): auto =
const consensusFork = typeof(state).kind
@ -411,12 +412,14 @@ func partialBeaconBlock*(
deposits: seq[Deposit],
validator_changes: BeaconBlockValidatorChanges,
sync_aggregate: SyncAggregate,
execution_payload: ForkyExecutionPayloadForSigning
execution_payload: ForkyExecutionPayloadForSigning,
consolidations: seq[SignedConsolidation],
): auto =
const consensusFork = typeof(state).kind
debugComment "re-enable attester slashing packing in electra"
# https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/phase0/validator.md#preparing-for-a-beaconblock
var res = consensusFork.BeaconBlock(
consensusFork.BeaconBlock(
slot: state.data.slot,
proposer_index: proposer_index.uint64,
parent_root: state.latest_block_root,
@ -429,28 +432,14 @@ func partialBeaconBlock*(
attestations:
List[electra.Attestation, Limit MAX_ATTESTATIONS_ELECTRA](attestations),
deposits: List[Deposit, Limit MAX_DEPOSITS](deposits),
voluntary_exits: validator_changes.voluntary_exits))
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.1/specs/altair/validator.md#preparing-a-beaconblock
when consensusFork >= ConsensusFork.Altair:
res.body.sync_aggregate = sync_aggregate
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.2/specs/bellatrix/validator.md#block-proposal
when consensusFork >= ConsensusFork.Bellatrix:
res.body.execution_payload = execution_payload.executionPayload
# https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/capella/validator.md#block-proposal
when consensusFork >= ConsensusFork.Capella:
res.body.bls_to_execution_changes =
validator_changes.bls_to_execution_changes
# https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/deneb/validator.md#constructing-the-beaconblockbody
when consensusFork >= ConsensusFork.Deneb:
res.body.blob_kzg_commitments = execution_payload.blobsBundle.commitments
debugComment "either consolidate this within separate function or recombine, re when consensusFork >= foo and atts/attslashings; here to allow noninterference with pre-pectra"
res
voluntary_exits: validator_changes.voluntary_exits,
sync_aggregate: sync_aggregate,
execution_payload: execution_payload.executionPayload,
bls_to_execution_changes: validator_changes.bls_to_execution_changes,
blob_kzg_commitments: execution_payload.blobsBundle.commitments,
consolidations:
List[SignedConsolidation, Limit MAX_CONSOLIDATIONS].init(
consolidations)))
proc makeBeaconBlockWithRewards*(
cfg: RuntimeConfig,
@ -464,6 +453,7 @@ proc makeBeaconBlockWithRewards*(
validator_changes: BeaconBlockValidatorChanges,
sync_aggregate: SyncAggregate,
executionPayload: ForkyExecutionPayloadForSigning,
consolidations: seq[SignedConsolidation],
rollback: RollbackForkedHashedProc,
cache: var StateCache,
# TODO:
@ -490,7 +480,7 @@ proc makeBeaconBlockWithRewards*(
partialBeaconBlock(
cfg, state.`kind Data`, proposer_index, randao_reveal, eth1_data,
graffiti, attestations, deposits, validator_changes, sync_aggregate,
executionPayload))
executionPayload, consolidations))
let res = process_block(
cfg, state.`kind Data`.data, blck.`kind Data`.asSigVerified(),
@ -533,9 +523,8 @@ proc makeBeaconBlockWithRewards*(
forkyState.data.latest_execution_payload_header.transactions_root =
transactions_root.get
debugComment "makeBeaconBlock doesn't support Electra (i.e. check for missing beaconblock body fields)"
when executionPayload is electra.ExecutionPayloadForSigning:
# https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/deneb/beacon-chain.md#beaconblockbody
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.2/specs/electra/beacon-chain.md#beaconblockbody
forkyState.data.latest_block_header.body_root = hash_tree_root(
[hash_tree_root(randao_reveal),
hash_tree_root(eth1_data),
@ -550,7 +539,9 @@ proc makeBeaconBlockWithRewards*(
hash_tree_root(sync_aggregate),
execution_payload_root.get,
hash_tree_root(validator_changes.bls_to_execution_changes),
hash_tree_root(kzg_commitments.get)
hash_tree_root(kzg_commitments.get),
hash_tree_root(List[SignedConsolidation, Limit MAX_CONSOLIDATIONS].init(
consolidations))
])
else:
raiseAssert "Attempt to use non-Electra payload with post-Deneb state"
@ -593,6 +584,7 @@ proc makeBeaconBlock*(
validator_changes: BeaconBlockValidatorChanges,
sync_aggregate: SyncAggregate,
executionPayload: ForkyExecutionPayloadForSigning,
consolidations: seq[SignedConsolidation],
rollback: RollbackForkedHashedProc, cache: var StateCache,
verificationFlags: UpdateFlags,
transactions_root: Opt[Eth2Digest],
@ -603,8 +595,8 @@ proc makeBeaconBlock*(
? makeBeaconBlockWithRewards(
cfg, state, proposer_index, randao_reveal, eth1_data, graffiti,
attestations, deposits, validator_changes, sync_aggregate,
executionPayload, rollback, cache, verificationFlags, transactions_root,
execution_payload_root, kzg_commitments)
executionPayload, consolidations, rollback, cache, verificationFlags,
transactions_root, execution_payload_root, kzg_commitments)
ok(blockAndRewards.blck)
proc makeBeaconBlock*(
@ -616,12 +608,13 @@ proc makeBeaconBlock*(
validator_changes: BeaconBlockValidatorChanges,
sync_aggregate: SyncAggregate,
executionPayload: ForkyExecutionPayloadForSigning,
consolidations: seq[SignedConsolidation],
rollback: RollbackForkedHashedProc, cache: var StateCache):
Result[ForkedBeaconBlock, cstring] =
makeBeaconBlock(
cfg, state, proposer_index, randao_reveal, eth1_data, graffiti,
attestations, deposits, validator_changes, sync_aggregate,
executionPayload, rollback, cache,
executionPayload, consolidations, rollback, cache,
verificationFlags = {}, transactions_root = Opt.none Eth2Digest,
execution_payload_root = Opt.none Eth2Digest,
kzg_commitments = Opt.none KzgCommitments)
@ -635,13 +628,14 @@ proc makeBeaconBlock*(
validator_changes: BeaconBlockValidatorChanges,
sync_aggregate: SyncAggregate,
executionPayload: ForkyExecutionPayloadForSigning,
consolidations: seq[SignedConsolidation],
rollback: RollbackForkedHashedProc,
cache: var StateCache, verificationFlags: UpdateFlags):
Result[ForkedBeaconBlock, cstring] =
makeBeaconBlock(
cfg, state, proposer_index, randao_reveal, eth1_data, graffiti,
attestations, deposits, validator_changes, sync_aggregate,
executionPayload, rollback, cache,
executionPayload, consolidations, rollback, cache,
verificationFlags = verificationFlags,
transactions_root = Opt.none Eth2Digest,
execution_payload_root = Opt.none Eth2Digest,

View File

@ -644,19 +644,10 @@ proc process_consolidation*(
target_validator.withdrawal_credentials.data.toOpenArray(12, 31)):
return err("Consolidation: source and target don't have same withdrawal address")
debugComment "this is per spec, near-verbatim, but Nimbus generally factors this out into spec/signatures.nim. so, create verify_consolidation_signature infra there, call here"
# Verify consolidation is signed by the source and the target
let
domain = compute_domain(
DOMAIN_CONSOLIDATION, cfg.GENESIS_FORK_VERSION,
genesis_validators_root=state.genesis_validators_root)
signing_root = compute_signing_root(consolidation, domain)
pubkeys = [source_validator[].pubkey, target_validator.pubkey]
debugComment "as a good example, this trustedsig hack typically/should live in spec/signatures.nim"
when not (signed_consolidation.signature is TrustedSig):
if not blsFastAggregateVerify(
pubkeys, signing_root.data, signed_consolidation.signature):
if not verify_consolidation_signature(
cfg.genesisFork, state.genesis_validators_root, signed_consolidation,
[source_validator[].pubkey, target_validator.pubkey]):
return err("Consolidation: invalid signature")
# Initiate source validator exit and append pending consolidation
@ -667,8 +658,7 @@ proc process_consolidation*(
debugComment "check HashList add return value"
discard state.pending_consolidations.add(PendingConsolidation(
source_index: consolidation.source_index,
target_index: consolidation.target_index
))
target_index: consolidation.target_index))
ok()

View File

@ -8,7 +8,7 @@
{.push raises: [].}
import std/[strutils, sequtils, algorithm]
import stew/[results, base10], chronos, chronicles
import stew/base10, chronos, chronicles
import
../spec/datatypes/[phase0, altair],
../spec/eth2_apis/rest_types,

View File

@ -8,7 +8,7 @@
{.push raises: [].}
import std/[heapqueue, tables, strutils, sequtils, math]
import stew/[results, base10], chronos, chronicles
import stew/base10, chronos, chronicles
import
../spec/datatypes/[base, phase0, altair],
../spec/[helpers, forks],

View File

@ -8,7 +8,7 @@
{.push raises: [].}
import
stew/[base10, results],
stew/base10,
chronicles, chronos, eth/async_utils,
./sync/[light_client_sync_helpers, sync_manager],
./consensus_object_pools/[block_clearance, blockchain_dag],

View File

@ -39,14 +39,14 @@ type
status*: ApiOperation
data*: seq[ApiNodeResponse[T]]
ApiScore* = object
ApiScore*[T] = object
index*: int
score*: Opt[float64]
score*: Opt[T]
BestNodeResponse*[T] = object
BestNodeResponse*[T, X] = object
node*: BeaconNodeServerRef
data*: ApiResponse[T]
score*: float64
score*: X
const
ViableNodeStatus* = {
@ -56,7 +56,7 @@ const
RestBeaconNodeStatus.Synced
}
proc `$`*(s: ApiScore): string =
proc `$`*[T](s: ApiScore[T]): string =
var res = Base10.toString(uint64(s.index))
res.add(": ")
if s.score.isSome():
@ -65,22 +65,27 @@ proc `$`*(s: ApiScore): string =
res.add("<n/a>")
res
proc `$`*(ss: openArray[ApiScore]): string =
proc `$`*[T](ss: openArray[ApiScore[T]]): string =
"[" & ss.mapIt($it).join(",") & "]"
chronicles.formatIt(seq[ApiScore]):
$it
func init*(t: typedesc[ApiScore], node: BeaconNodeServerRef,
score: float64): ApiScore =
ApiScore(index: node.index, score: Opt.some(score))
score: float64): ApiScore[float64] =
ApiScore[float64](index: node.index, score: Opt.some(score))
func init*(t: typedesc[ApiScore], node: BeaconNodeServerRef): ApiScore =
ApiScore(index: node.index, score: Opt.none(float64))
func init*(t: typedesc[ApiScore], node: BeaconNodeServerRef,
score: UInt256): ApiScore[UInt256] =
ApiScore[UInt256](index: node.index, score: Opt.some(score))
func init*[T](t: typedesc[BestNodeResponse], node: BeaconNodeServerRef,
data: ApiResponse[T], score: float64): BestNodeResponse[T] =
BestNodeResponse[T](node: node, data: data, score: score)
func init*(tt: typedesc[ApiScore],
node: BeaconNodeServerRef, T: typedesc): ApiScore[T] =
ApiScore[T](index: node.index, score: Opt.none(T))
func init*[T, X](t: typedesc[BestNodeResponse], node: BeaconNodeServerRef,
data: ApiResponse[T], score: X): BestNodeResponse[T, X] =
BestNodeResponse[T, X](node: node, data: data, score: score)
proc lazyWaiter(node: BeaconNodeServerRef, request: FutureBase,
requestName: string, strategy: ApiStrategyKind) {.async.} =
@ -234,7 +239,7 @@ template firstSuccessParallel*(
pendingNodes.del(index)
let
node {.inject.} = beaconNode
node {.inject, used.} = beaconNode
apiResponse {.inject.} =
apiResponseOr[responseType](requestFut, timerFut,
"Timeout exceeded while awaiting for the response")
@ -283,6 +288,7 @@ template bestSuccess*(
vc: ValidatorClientRef,
responseType: typedesc,
handlerType: typedesc,
scoreType: typedesc,
timeout: Duration,
statuses: set[RestBeaconNodeStatus],
roles: set[BeaconNodeRole],
@ -301,8 +307,8 @@ template bestSuccess*(
var
retRes: ApiResponse[handlerType]
scores: seq[ApiScore]
bestResponse: Opt[BestNodeResponse[handlerType]]
scores: seq[ApiScore[scoreType]]
bestResponse: Opt[BestNodeResponse[handlerType, scoreType]]
block mainLoop:
while true:
@ -395,7 +401,7 @@ template bestSuccess*(
perfectScoreFound = true
break
else:
scores.add(ApiScore.init(node))
scores.add(ApiScore.init(node, scoreType))
if perfectScoreFound:
# lazyWait will cancel `pendingRequests` on timeout.
@ -714,16 +720,30 @@ template firstSuccessSequential*(
break
proc getErrorMessage*(response: RestPlainResponse): string =
let res = decodeBytes(RestErrorMessage, response.data,
response.contentType)
if res.isOk():
let errorObj = res.get()
if errorObj.stacktraces.isSome():
errorObj.message & ": [" & errorObj.stacktraces.get().join("; ") & "]"
let res =
decodeBytes(RestErrorMessage, response.data, response.contentType).valueOr:
return "Unable to decode error response: [" & $error & "]"
if res.stacktraces.isSome():
res.message & ": [" & res.stacktraces.get().join("; ") & "]"
else:
errorObj.message
res.message
proc unpackErrorMessage*(response: RestPlainResponse): RestIndexedErrorMessage =
decodeBytes(RestIndexedErrorMessage, response.data,
response.contentType).valueOr:
let message = "Unable to decode error response: [" & $error & "]"
return RestIndexedErrorMessage(
code: -1,
message: message,
failures: default(seq[RestIndexedErrorMessageItem]))
proc getErrorMessage*(msg: RestIndexedErrorMessage): string =
if len(msg.failures) > 0:
msg.message & ": [" &
msg.failures.mapIt($it.index & ":" & it.message).join("; ") & "]"
else:
"Unable to decode error response: [" & $res.error & "]"
msg.message
template handleCommunicationError(): untyped {.dirty.} =
let failure = ApiNodeFailure.init(ApiFailure.Communication, RequestName,
@ -755,6 +775,13 @@ template handle400(): untyped {.dirty.} =
node.updateStatus(RestBeaconNodeStatus.Incompatible, failure)
failures.add(failure)
template handle400Indexed(): untyped {.dirty.} =
let failure = ApiNodeFailure.init(ApiFailure.Invalid, RequestName,
strategy, node, response.status,
response.unpackErrorMessage().getErrorMessage())
node.updateStatus(RestBeaconNodeStatus.Incompatible, failure)
failures.add(failure)
template handle404(): untyped {.dirty.} =
let failure = ApiNodeFailure.init(ApiFailure.NotFound, RequestName,
strategy, node, response.status, response.getErrorMessage())
@ -1181,6 +1208,7 @@ proc getHeadBlockRoot*(
let res = vc.bestSuccess(
RestPlainResponse,
GetBlockRootResponse,
float64,
SlotDuration,
ViableNodeStatus,
{BeaconNodeRole.SyncCommitteeData},
@ -1413,6 +1441,7 @@ proc produceAttestationData*(
let res = vc.bestSuccess(
RestPlainResponse,
ProduceAttestationDataResponse,
float64,
OneThirdDuration,
ViableNodeStatus,
{BeaconNodeRole.AttestationData},
@ -1514,7 +1543,7 @@ proc submitPoolAttestations*(
of 200:
ApiResponse[bool].ok(true)
of 400:
handle400()
handle400Indexed()
ApiResponse[bool].err(ResponseInvalidError)
of 500:
handle500()
@ -1542,7 +1571,7 @@ proc submitPoolAttestations*(
of 200:
return true
of 400:
handle400()
handle400Indexed()
false
of 500:
handle500()
@ -1589,7 +1618,7 @@ proc submitPoolSyncCommitteeSignature*(
of 200:
ApiResponse[bool].ok(true)
of 400:
handle400()
handle400Indexed()
ApiResponse[bool].err(ResponseInvalidError)
of 500:
handle500()
@ -1618,7 +1647,7 @@ proc submitPoolSyncCommitteeSignature*(
of 200:
return true
of 400:
handle400()
handle400Indexed()
false
of 500:
handle500()
@ -1685,6 +1714,7 @@ proc getAggregatedAttestation*(
let res = vc.bestSuccess(
RestPlainResponse,
GetAggregatedAttestationResponse,
float64,
OneThirdDuration,
ViableNodeStatus,
{BeaconNodeRole.AggregatedData},
@ -1818,6 +1848,7 @@ proc produceSyncCommitteeContribution*(
let res = vc.bestSuccess(
RestPlainResponse,
ProduceSyncCommitteeContributionResponse,
float64,
OneThirdDuration,
ViableNodeStatus,
{BeaconNodeRole.SyncCommitteeData},
@ -2036,7 +2067,59 @@ proc produceBlockV3*(
var failures: seq[ApiNodeFailure]
case strategy
of ApiStrategyKind.First, ApiStrategyKind.Best:
of ApiStrategyKind.Best:
let res = vc.bestSuccess(
RestPlainResponse,
ProduceBlockResponseV3,
UInt256,
SlotDuration,
ViableNodeStatus,
{BeaconNodeRole.BlockProposalData},
produceBlockV3Plain(it, slot, randao_reveal, graffiti,
builder_boost_factor),
getProduceBlockResponseV3Score(itresponse)):
if apiResponse.isErr():
handleCommunicationError()
ApiResponse[ProduceBlockResponseV3].err(apiResponse.error)
else:
let response = apiResponse.get()
case response.status
of 200:
let
version = response.headers.getString("eth-consensus-version")
blinded =
response.headers.getString("eth-execution-payload-blinded")
executionValue =
response.headers.getString("eth-execution-payload-value")
consensusValue =
response.headers.getString("eth-consensus-block-value")
res = decodeBytes(ProduceBlockResponseV3, response.data,
response.contentType, version, blinded,
executionValue, consensusValue)
if res.isErr():
handleUnexpectedData()
ApiResponse[ProduceBlockResponseV3].err($res.error)
else:
ApiResponse[ProduceBlockResponseV3].ok(res.get())
of 400:
handle400()
ApiResponse[ProduceBlockResponseV3].err(ResponseInvalidError)
of 500:
handle500()
ApiResponse[ProduceBlockResponseV3].err(ResponseInternalError)
of 503:
handle503()
ApiResponse[ProduceBlockResponseV3].err(
ResponseNoSyncError)
else:
handleUnexpectedCode()
ApiResponse[ProduceBlockResponseV3].err(
ResponseUnexpectedError)
if res.isErr():
raise (ref ValidatorApiError)(msg: res.error, data: failures)
return res.get()
of ApiStrategyKind.First:
let res = vc.firstSuccessParallel(
RestPlainResponse,
ProduceBlockResponseV3,

View File

@ -87,8 +87,8 @@ proc pollForValidatorIndices*(service: DutiesServiceRef) {.async.} =
if validator.isNone():
missing.add(validatorLog(item.validator.pubkey, item.index))
else:
validator.get().updateValidator(Opt.some ValidatorAndIndex(
index: item.index,
vc.attachedValidators[].updateValidator(validator.get(),
Opt.some ValidatorAndIndex(index: item.index,
validator: item.validator))
updated.add(validatorLog(item.validator.pubkey, item.index))
list.add(validator.get())

View File

@ -10,6 +10,7 @@
import std/strutils
import ssz_serialization/[types, bitseqs]
import stew/endians2
import stint
import nimcrypto/hash
import "."/common
@ -24,6 +25,9 @@ const
func perfectScore*(score: float64): bool =
score == Inf
func perfectScore*(score: UInt256): bool =
score == high(UInt256)
proc shortScore*(score: float64): string =
if score == Inf:
"<perfect>"
@ -32,6 +36,9 @@ proc shortScore*(score: float64): string =
else:
formatFloat(score, ffDecimal, 4)
proc shortScore*(score: UInt256): string =
$score
func getLexicographicScore(digest: Eth2Digest): float64 =
# We calculate score on first 8 bytes of digest.
let
@ -183,3 +190,28 @@ proc getUniqueVotes*(attestations: openArray[phase0.Attestation]): int =
processVotes(attestation)
res += count
res
proc getProduceBlockResponseV3Score*(blck: ProduceBlockResponseV3): UInt256 =
let (res, cv, ev) =
block:
var score256 = UInt256.zero
let
cvalue =
if blck.consensusValue.isSome():
let value = blck.consensusValue.get()
score256 = score256 + value
$value
else:
"<missing>"
evalue =
if blck.executionValue.isSome():
let value = blck.executionValue.get()
score256 = score256 + value
$value
else:
"<missing>"
(score256, cvalue, evalue)
debug "Block score", blck = shortLog(blck), consensus_value = cv,
execution_value = ev, score = shortScore(res)
res

View File

@ -152,7 +152,7 @@ proc addValidatorsFromWeb3Signer(
gasLimit = node.consensusManager[].getGasLimit(keystore.pubkey)
v = node.attachedValidators[].addValidator(keystore, feeRecipient,
gasLimit)
v.updateValidator(data)
node.attachedValidators[].updateValidator(v, data)
proc addValidators*(node: BeaconNode) {.async: (raises: [CancelledError]).} =
info "Loading validators", validatorsDir = node.config.validatorsDir(),
@ -174,7 +174,7 @@ proc addValidators*(node: BeaconNode) {.async: (raises: [CancelledError]).} =
v = node.attachedValidators[].addValidator(keystore, feeRecipient,
gasLimit)
v.updateValidator(data)
node.attachedValidators[].updateValidator(v, data)
# We use `allFutures` because all failures are already reported as
# user-visible warnings in `queryValidatorsSource`.
@ -363,10 +363,12 @@ proc createAndSendAttestation(node: BeaconNode,
res =
if consensusFork >= ConsensusFork.Electra:
await node.router.routeAttestation(
registered.toElectraAttestation(signature), subnet_id, checkSignature = false)
registered.toElectraAttestation(signature), subnet_id,
checkSignature = false, checkValidator = false)
else:
await node.router.routeAttestation(
registered.toAttestation(signature), subnet_id, checkSignature = false)
registered.toAttestation(signature), subnet_id,
checkSignature = false, checkValidator = false)
if not res.isOk():
return
@ -537,6 +539,7 @@ proc makeBeaconBlockForHeadAndSlot*(
slot, validator_index
return err("Unable to get execution payload")
debugComment "flesh out consolidations"
let res = makeBeaconBlockWithRewards(
node.dag.cfg,
state[],
@ -549,6 +552,7 @@ proc makeBeaconBlockForHeadAndSlot*(
exits,
node.syncCommitteeMsgPool[].produceSyncAggregate(head.bid, slot),
payload,
@[], # consolidations
noRollback, # Temporary state - no need for rollback
cache,
verificationFlags = {},
@ -1292,7 +1296,8 @@ proc proposeBlockAux(
else:
Opt.none(seq[BlobSidecar])
newBlockRef = (
await node.router.routeSignedBeaconBlock(signedBlock, blobsOpt)
await node.router.routeSignedBeaconBlock(signedBlock, blobsOpt,
checkValidator = false)
).valueOr:
return head # Errors logged in router
@ -1869,7 +1874,7 @@ proc updateValidators(
let
v = node.attachedValidators[].getValidator(validators[i].pubkey).valueOr:
continue
v.index = Opt.some ValidatorIndex(i)
node.attachedValidators[].setValidatorIndex(v, ValidatorIndex(i))
node.dutyValidatorCount = validators.len
@ -1879,10 +1884,12 @@ proc updateValidators(
# Activation epoch can change after index is assigned..
let index = validator.index.get()
if index < validators.lenu64:
validator.updateValidator(
node.attachedValidators[].updateValidator(
validator,
Opt.some(ValidatorAndIndex(
index: index, validator: validators[int index]
)))
))
)
proc handleFallbackAttestations(node: BeaconNode, lastSlot, slot: Slot) =
# Neither block proposal nor sync committee duties can be done in this

View File

@ -1607,7 +1607,7 @@ proc addValidator*(
if not isNil(host.getValidatorAndIdxFn):
let data = host.getValidatorAndIdxFn(keystore.pubkey)
v.updateValidator(data)
host.validatorPool[].updateValidator(v, data)
proc generateDeposits*(cfg: RuntimeConfig,
rng: var HmacDrbgContext,

View File

@ -8,7 +8,6 @@
{.push raises: [].}
import
stew/results,
std/sequtils,
chronicles,
metrics,
@ -85,13 +84,22 @@ template getCurrentBeaconTime(router: MessageRouter): BeaconTime =
type RouteBlockResult = Result[Opt[BlockRef], string]
proc routeSignedBeaconBlock*(
router: ref MessageRouter, blck: ForkySignedBeaconBlock,
blobsOpt: Opt[seq[BlobSidecar]]):
blobsOpt: Opt[seq[BlobSidecar]], checkValidator: bool):
Future[RouteBlockResult] {.async: (raises: [CancelledError]).} =
## Validate and broadcast beacon block, then add it to the block database
## Returns the new Head when block is added successfully to dag, none when
## block passes validation but is not added, and error otherwise
let wallTime = router[].getCurrentBeaconTime()
block:
let vindex = ValidatorIndex(blck.message.proposer_index)
if checkValidator and (vindex in router.processor.validatorPool[]):
warn "A validator client attempts to send a block from " &
"validator that is also manager by beacon node",
validator_index = vindex
return err("Block could not be sent from validator that is also " &
"managed by the beacon node")
# Start with a quick gossip validation check such that broadcasting the
# block doesn't get the node into trouble
block:
@ -193,13 +201,14 @@ proc routeSignedBeaconBlock*(
proc routeAttestation*(
router: ref MessageRouter,
attestation: phase0.Attestation | electra.Attestation,
subnet_id: SubnetId, checkSignature: bool):
subnet_id: SubnetId, checkSignature, checkValidator: bool):
Future[SendResult] {.async: (raises: [CancelledError]).} =
## Process and broadcast attestation - processing will register the it with
## the attestation pool
block:
let res = await router[].processor.processAttestation(
MsgSource.api, attestation, subnet_id, checkSignature)
MsgSource.api, attestation, subnet_id,
checkSignature = checkSignature, checkValidator = checkValidator)
if not res.isGoodForSending:
warn "Attestation failed validation",
@ -250,7 +259,7 @@ proc routeAttestation*(
committee_index)
return await router.routeAttestation(
attestation, subnet_id, checkSignature = true)
attestation, subnet_id, checkSignature = true, checkValidator = true)
proc routeSignedAggregateAndProof*(
router: ref MessageRouter, proof: phase0.SignedAggregateAndProof,

View File

@ -143,7 +143,8 @@ proc unblindAndRouteBlockMEV*(
blck = shortLog(signedBlock)
let newBlockRef =
(await node.router.routeSignedBeaconBlock(signedBlock, blobsOpt)).valueOr:
(await node.router.routeSignedBeaconBlock(
signedBlock, blobsOpt, checkValidator = false)).valueOr:
# submitBlindedBlock has run, so don't allow fallback to run
return err("routeSignedBeaconBlock error") # Errors logged in router

View File

@ -8,7 +8,7 @@
{.push raises: [].}
import
std/[tables, json, streams, sequtils, uri],
std/[tables, json, streams, sequtils, uri, sets],
chronos, chronicles, metrics,
json_serialization/std/net,
presto/client,
@ -93,6 +93,7 @@ type
ValidatorPool* = object
validators*: Table[ValidatorPubKey, AttachedValidator]
indexSet*: HashSet[ValidatorIndex]
slashingProtection*: SlashingProtectionDB
doppelgangerDetectionEnabled*: bool
@ -223,10 +224,24 @@ func contains*(pool: ValidatorPool, pubkey: ValidatorPubKey): bool =
## Returns ``true`` if validator with key ``pubkey`` present in ``pool``.
pool.validators.contains(pubkey)
proc contains*(pool: ValidatorPool, index: ValidatorIndex): bool =
## Returns ``true`` if validator with index ``index`` present in ``pool``.
pool.indexSet.contains(index)
proc setValidatorIndex*(pool: var ValidatorPool, validator: AttachedValidator,
index: ValidatorIndex) =
pool.indexSet.incl(index)
validator.index = Opt.some(index)
proc removeValidatorIndex(pool: var ValidatorPool, index: ValidatorIndex) =
pool.indexSet.excl(index)
proc removeValidator*(pool: var ValidatorPool, pubkey: ValidatorPubKey) =
## Delete validator with public key ``pubkey`` from ``pool``.
let validator = pool.validators.getOrDefault(pubkey)
if not(isNil(validator)):
if validator.index.isSome():
pool.removeValidatorIndex(validator.index.get)
pool.validators.del(pubkey)
case validator.kind
of ValidatorKind.Local:
@ -243,8 +258,9 @@ proc removeValidator*(pool: var ValidatorPool, pubkey: ValidatorPubKey) =
func needsUpdate*(validator: AttachedValidator): bool =
validator.index.isNone() or validator.activationEpoch == FAR_FUTURE_EPOCH
proc updateValidator*(
validator: AttachedValidator, validatorData: Opt[ValidatorAndIndex]) =
proc updateValidator*(pool: var ValidatorPool,
validator: AttachedValidator,
validatorData: Opt[ValidatorAndIndex]) =
defer: validator.updated = true
let
@ -259,6 +275,7 @@ proc updateValidator*(
## Update activation information for a validator
if validator.index != Opt.some data.index:
pool.setValidatorIndex(validator, data.index)
validator.index = Opt.some data.index
validator.validator = Opt.some data.validator

View File

@ -289,7 +289,7 @@ template `as`(address: Eth1Address, T: type bellatrix.ExecutionAddress): T =
template `as`(address: BlockHash, T: type Eth2Digest): T =
asEth2Digest(address)
func getOrDefault[T](x: Option[T]): T =
func getOrDefault[T](x: Opt[T]): T =
if x.isSome:
x.get
else:
@ -505,25 +505,25 @@ proc doCreateTestnet*(config: CliConfig,
proc deployContract(web3: Web3, code: seq[byte]): Future[ReceiptObject] {.async.} =
let tr = TransactionArgs(
`from`: web3.defaultAccount.some,
data: code.some,
gas: Quantity(3000000).some,
gasPrice: Quantity(1).some)
`from`: Opt.some web3.defaultAccount,
data: Opt.some code,
gas: Opt.some Quantity(3000000),
gasPrice: Opt.some Quantity(1))
let r = await web3.send(tr)
result = await web3.getMinedTransactionReceipt(r)
proc sendEth(web3: Web3, to: Eth1Address, valueEth: int): Future[TxHash] =
let tr = TransactionArgs(
`from`: web3.defaultAccount.some,
`from`: Opt.some web3.defaultAccount,
# TODO: Force json-rpc to generate 'data' field
# should not be needed anymore, new execution-api schema
# is using `input` field
data: some(newSeq[byte]()),
gas: Quantity(3000000).some,
gasPrice: Quantity(1).some,
value: some(valueEth.u256 * 1000000000000000000.u256),
to: some(to))
data: Opt.some(newSeq[byte]()),
gas: Opt.some Quantity(3000000),
gasPrice: Opt.some Quantity(1),
value: Opt.some(valueEth.u256 * 1000000000000000000.u256),
to: Opt.some(to))
web3.send(tr)
type
@ -535,7 +535,7 @@ func ethToWei(eth: UInt256): UInt256 =
proc initWeb3(web3Url, privateKey: string): Future[Web3] {.async.} =
result = await newWeb3(web3Url)
if privateKey.len != 0:
result.privateKey = some(keys.PrivateKey.fromHex(privateKey)[])
result.privateKey = Opt.some(keys.PrivateKey.fromHex(privateKey)[])
else:
let accounts = await result.provider.eth_accounts()
doAssert(accounts.len > 0)

View File

@ -79,7 +79,7 @@ stack-data==0.1.4
terminado==0.12.1
testpath==0.5.0
tomli==1.2.3
tornado==6.3.3
tornado==6.4.1
traitlets==5.1.1
typing_extensions==4.0.1
wcwidth==0.2.5

View File

@ -85,7 +85,7 @@ proc makeSimulationBlock(
var blck = partialBeaconBlock(
cfg, state, proposer_index, randao_reveal, eth1_data, graffiti,
attestations, deposits, exits, sync_aggregate, execution_payload)
attestations, deposits, exits, sync_aggregate, execution_payload, @[])
let res = process_block(
cfg, state.data, blck.asSigVerified(), verificationFlags, cache)
@ -128,7 +128,7 @@ proc makeSimulationBlock(
var blck = partialBeaconBlock(
cfg, state, proposer_index, randao_reveal, eth1_data, graffiti,
attestations, deposits, exits, sync_aggregate, execution_payload)
attestations, deposits, exits, sync_aggregate, execution_payload, @[])
let res = process_block(
cfg, state.data, blck.asSigVerified(), verificationFlags, cache)

View File

@ -57,7 +57,7 @@ proc setupEngineAPI*(server: RpcServer) =
# https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.3/src/engine/paris.md#engine_forkchoiceupdatedv1
server.rpc("engine_forkchoiceUpdatedV1") do(
update: ForkchoiceStateV1,
payloadAttributes: Option[PayloadAttributesV1]) -> ForkchoiceUpdatedResponse:
payloadAttributes: Opt[PayloadAttributesV1]) -> ForkchoiceUpdatedResponse:
info "engine_forkchoiceUpdatedV1",
update,
payloadAttributes
@ -68,7 +68,7 @@ proc setupEngineAPI*(server: RpcServer) =
# https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.3/src/engine/shanghai.md#engine_forkchoiceupdatedv2
server.rpc("engine_forkchoiceUpdatedV2") do(
forkchoiceState: ForkchoiceStateV1, payloadAttributes: Option[PayloadAttributesV2]) -> ForkchoiceUpdatedResponse:
forkchoiceState: ForkchoiceStateV1, payloadAttributes: Opt[PayloadAttributesV2]) -> ForkchoiceUpdatedResponse:
info "engine_forkchoiceUpdatedV2",
forkchoiceState, payloadAttributes

View File

@ -134,7 +134,7 @@ cli do(validatorsDir: string, secretsDir: string,
headBlockHash = payload.block_hash,
safeBlockHash = payload.block_hash,
finalizedBlockHash = ZERO_HASH,
payloadAttributes = none(consensusFork.PayloadAttributes))
payloadAttributes = Opt.none(consensusFork.PayloadAttributes))
if status != PayloadExecutionStatus.valid:
continue
@ -295,6 +295,7 @@ cli do(validatorsDir: string, secretsDir: string,
BeaconBlockValidatorChanges(),
syncAggregate,
payload,
@[], # consolidations
noRollback,
cache).get()

View File

@ -21,7 +21,7 @@ source "${SCRIPTS_DIR}/bash_utils.sh"
download_geth_stable() {
if [[ ! -e "${STABLE_GETH_BINARY}" ]]; then
GETH_VERSION="1.14.0-87246f3c" # https://geth.ethereum.org/downloads
GETH_VERSION="1.14.5-0dd173a7" # https://geth.ethereum.org/downloads
GETH_URL="https://gethstore.blob.core.windows.net/builds/"
case "${OS}-${ARCH}" in

View File

@ -12,7 +12,6 @@ import
# Utilities
chronicles,
unittest2,
stew/results,
# Beacon chain internals
../../../beacon_chain/spec/[beaconstate, state_transition_block],
../../../beacon_chain/spec/datatypes/altair,

View File

@ -12,7 +12,6 @@ import
# Utilities
chronicles,
unittest2,
stew/results,
# Beacon chain internals
../../../beacon_chain/spec/state_transition_block,
../../../beacon_chain/spec/datatypes/bellatrix,

View File

@ -12,7 +12,6 @@ import
# Utilities
chronicles,
unittest2,
stew/results,
# Beacon chain internals
../../../beacon_chain/spec/state_transition_block,
../../../beacon_chain/spec/datatypes/capella,

View File

@ -12,7 +12,6 @@ import
# Utilities
chronicles,
unittest2,
stew/results,
# Beacon chain internals
../../../beacon_chain/spec/state_transition_block,
../../../beacon_chain/spec/datatypes/deneb,

View File

@ -12,7 +12,6 @@ import
# Utilities
chronicles,
unittest2,
stew/results,
# Beacon chain internals
../../../beacon_chain/spec/state_transition_block,
../../../beacon_chain/spec/datatypes/electra,

View File

@ -12,7 +12,6 @@ import
# Utilities
chronicles,
unittest2,
stew/results,
# Beacon chain internals
../../../beacon_chain/spec/[beaconstate, state_transition_block],
../../../beacon_chain/spec/datatypes/phase0,

View File

@ -10,7 +10,7 @@
import
# Status libraries
stew/[byteutils, results], chronicles,
stew/byteutils, chronicles,
taskpools,
# Internals
../../beacon_chain/spec/[helpers, forks, state_transition_block],

View File

@ -12,7 +12,7 @@ import
std/json,
yaml,
kzg4844/kzg_ex,
stew/[byteutils, results],
stew/byteutils,
../testutil,
./fixtures_utils, ./os_ops

View File

@ -10,7 +10,6 @@
import
# Status lib
stew/results,
chronicles,
# Internal
../../beacon_chain/validators/[slashing_protection, slashing_protection_v2],

View File

@ -10,10 +10,10 @@
import
# Standard library
std/[os],
std/os,
# Status lib
eth/db/[kvstore, kvstore_sqlite3],
stew/[results, endians2],
stew/endians2,
# Internal
../../beacon_chain/validators/slashing_protection,
../../beacon_chain/spec/[helpers],

View File

@ -10,7 +10,8 @@
import
std/[json, os, random, sequtils, strutils, times],
chronos, stew/[base10, results], chronicles, unittest2,
chronos,
stew/base10, chronicles, unittest2,
yaml,
../beacon_chain/beacon_chain_db,
../beacon_chain/spec/deposit_snapshots,
@ -39,7 +40,7 @@ proc ifNecessaryMigrateDCS(db: BeaconChainDB,
db.putDepositContractSnapshot upgradeProc(oldSnapshot.get)
# Hexlified copy of
# eth2-networks/shared/mainnet/genesis_deposit_contract_snapshot.ssz
# mainnet/metadata/genesis_deposit_contract_snapshot.ssz
let ds1: seq[byte] = hexToSeqByte(
"""
eeea1373d4aa9e099d7c9deddb694db9aeb4577755ef83f9b6345ce4357d9abfca3bfce2c

View File

@ -9,7 +9,7 @@
{.used.}
import
stew/results, presto/client,
presto/client,
testutils/unittests, chronicles,
../beacon_chain/spec/eth2_apis/[eth2_rest_serialization, rest_types],
./testutil

View File

@ -389,6 +389,7 @@ const
("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01", "0.9995"),
("0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101", "0.0005"),
]
ContributionDataVectors = [
("0xffffffffffffffffffffffffffff7f7f", "0.9844"),
("0xffffffffffffffffffffffff7f7f7f7f", "0.9688"),
@ -446,6 +447,15 @@ const
([("0xff01", Slot(0), 0'u64), ("0xff01", Slot(0), 1'u64)], 16)
]
UInt256ScoreVectors = [
("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0"),
("0x10",
"0x10",
"32")
]
proc init(t: typedesc[Eth2Digest], data: string): Eth2Digest =
let length = len(data)
var dst = Eth2Digest()
@ -755,6 +765,25 @@ suite "Validator Client test suite":
score == "0.0000"
isLowestScoreAggregatedAttestation(adata.data) == true
test "getProduceBlockResponseV3Score() default test":
let
bdata1 = ProduceBlockResponseV3()
bdata2 = ProduceBlockResponseV3(
consensusValue: Opt.some(UInt256.zero)
)
bdata3 = ProduceBlockResponseV3(
executionValue: Opt.some(UInt256.zero),
)
bdata4 = ProduceBlockResponseV3(
consensusValue: Opt.some(UInt256.zero),
executionValue: Opt.some(UInt256.zero)
)
check:
shortScore(getProduceBlockResponseV3Score(bdata1)) == "0"
shortScore(getProduceBlockResponseV3Score(bdata2)) == "0"
shortScore(getProduceBlockResponseV3Score(bdata3)) == "0"
shortScore(getProduceBlockResponseV3Score(bdata4)) == "0"
test "getSyncCommitteeContributionDataScore() test vectors":
for vector in ContributionDataVectors:
let
@ -773,11 +802,24 @@ suite "Validator Client test suite":
check:
score == vector[5]
test "getProduceBlockResponseV3Score() test vectors":
for vector in UInt256ScoreVectors:
let
value1 = strictParse(vector[0], UInt256, 16).get()
value2 = strictParse(vector[1], UInt256, 16).get()
bdata = ProduceBlockResponseV3(
executionValue: Opt.some(value1),
consensusValue: Opt.some(value2)
)
check shortScore(getProduceBlockResponseV3Score(bdata)) == vector[2]
test "getUniqueVotes() test vectors":
for vector in AttestationBitsVectors:
let
a1 = phase0.Attestation.init(vector[0][0][0], vector[0][0][1], vector[0][0][2])
a2 = phase0.Attestation.init(vector[0][1][0], vector[0][1][1], vector[0][1][2])
a1 = phase0.Attestation.init(vector[0][0][0], vector[0][0][1],
vector[0][0][2])
a2 = phase0.Attestation.init(vector[0][1][0], vector[0][1][1],
vector[0][1][2])
check getUniqueVotes([a1, a2]) == vector[1]
asyncTest "firstSuccessParallel() API timeout test":
@ -850,6 +892,7 @@ suite "Validator Client test suite":
let response = vc.bestSuccess(
RestPlainResponse,
uint64,
float64,
100.milliseconds,
AllBeaconNodeStatuses,
{BeaconNodeRole.Duties},

View File

@ -71,13 +71,14 @@ func checkResponse(a, b: openArray[KeystoreData]): bool =
suite "Validator pool":
test "Doppelganger for genesis validator":
let
pool = newClone(ValidatorPool())
v = AttachedValidator(activationEpoch: FAR_FUTURE_EPOCH)
check:
not v.triggersDoppelganger(GENESIS_EPOCH) # no check
not v.doppelgangerReady(GENESIS_EPOCH.start_slot) # no activation
v.updateValidator(makeValidatorAndIndex(ValidatorIndex(1), GENESIS_EPOCH))
pool[].updateValidator(v, makeValidatorAndIndex(ValidatorIndex(1), GENESIS_EPOCH))
check:
not v.triggersDoppelganger(GENESIS_EPOCH) # no check
@ -94,6 +95,7 @@ suite "Validator pool":
test "Doppelganger for validator that activates in same epoch as check":
let
pool = newClone(ValidatorPool())
v = AttachedValidator(activationEpoch: FAR_FUTURE_EPOCH)
now = Epoch(10).start_slot()
@ -104,7 +106,7 @@ suite "Validator pool":
not v.doppelgangerReady(GENESIS_EPOCH.start_slot)
not v.doppelgangerReady(now)
v.updateValidator(makeValidatorAndIndex(ValidatorIndex(5), FAR_FUTURE_EPOCH))
pool[].updateValidator(v, makeValidatorAndIndex(ValidatorIndex(5), FAR_FUTURE_EPOCH))
check: # We still don't know when validator activates so we wouldn't trigger
not v.triggersDoppelganger(GENESIS_EPOCH)
@ -113,7 +115,7 @@ suite "Validator pool":
not v.doppelgangerReady(GENESIS_EPOCH.start_slot)
not v.doppelgangerReady(now)
v.updateValidator(makeValidatorAndIndex(ValidatorIndex(5), now.epoch()))
pool[].updateValidator(v, makeValidatorAndIndex(ValidatorIndex(5), now.epoch()))
check: # No check done yet
not v.triggersDoppelganger(GENESIS_EPOCH)

View File

@ -118,8 +118,6 @@ proc build_empty_merge_execution_payload(state: bellatrix.BeaconState):
bellatrix.ExecutionPayloadForSigning(executionPayload: payload,
blockValue: Wei.zero)
from stew/saturating_arith import saturate
proc build_empty_execution_payload(
state: bellatrix.BeaconState,
feeRecipient: Eth1Address): bellatrix.ExecutionPayloadForSigning =
@ -129,8 +127,8 @@ proc build_empty_execution_payload(
latest = state.latest_execution_payload_header
timestamp = compute_timestamp_at_slot(state, state.slot)
randao_mix = get_randao_mix(state, get_current_epoch(state))
base_fee = calcEip1599BaseFee(GasInt.saturate latest.gas_limit,
GasInt.saturate latest.gas_used,
base_fee = calcEip1599BaseFee(latest.gas_limit,
latest.gas_used,
latest.base_fee_per_gas)
var payload = bellatrix.ExecutionPayloadForSigning(
@ -172,6 +170,8 @@ proc addTestBlock*(
cfg, state, getStateField(state, slot) + 1, cache, info, flags).expect(
"can advance 1")
debugComment "add consolidations support to addTestBlock"
let
proposer_index = get_beacon_proposer_index(
state, cache, getStateField(state, slot)).expect("valid proposer index")
@ -208,8 +208,6 @@ proc addTestBlock*(
else:
default(bellatrix.ExecutionPayloadForSigning)
debugComment "addTestBlock Electra attestation support"
makeBeaconBlock(
cfg,
state,
@ -229,6 +227,7 @@ proc addTestBlock*(
BeaconBlockValidatorChanges(),
sync_aggregate,
execution_payload,
@[],
noRollback,
cache,
verificationFlags = {skipBlsValidation})

@ -1 +0,0 @@
Subproject commit ab581251bcda11e3cc120cc9e9ad1ad679340949

@ -1 +1 @@
Subproject commit c115f3688c19eb6153e22c1e76477db4ed27fae3
Subproject commit 9ed6c63314899d17e2c3f669adbe2bc915610982

1
vendor/mainnet vendored Submodule

@ -0,0 +1 @@
Subproject commit f6b7882618a5ad2c1d2731ae35e5d16a660d5bb7

2
vendor/nim-blscurve vendored

@ -1 +1 @@
Subproject commit d091a579a2e7c4668140e675a6fb2c78b8c6dc57
Subproject commit f29698d2e9a59453d99db7315a5af58add3c8715

2
vendor/nim-eth vendored

@ -1 +1 @@
Subproject commit c482b4c5b658a77cc96b49d4a397aa6d98472ac7
Subproject commit 9b6497ed8a05ba25ee47142f3fc1f61742b51a6c

@ -1 +1 @@
Subproject commit 1ac1d69f9512d55d15e8218a9dbdff129bf96ddb
Subproject commit 4d0b0662ed960ab2c5a1ddbd08f77048bac13ae7

2
vendor/nim-kzg4844 vendored

@ -1 +1 @@
Subproject commit 4fbcfbe4c452313bd440936318a87ed708987d8b
Subproject commit f12616d0675d9f6346141ca95f0840ab227eb213

2
vendor/nim-libp2p vendored

@ -1 +1 @@
Subproject commit 21cbe3a91a70811522554e89e6a791172cebfef2
Subproject commit 2fa2c4425f4bb835c0517efc03009925dcd28239

@ -1 +1 @@
Subproject commit 4c4fc6f1436b5e0468a6b3a7929bb603f0b43f33
Subproject commit 9c7dc8c58ff9c3dfb11c2d333171b47659ed824c

@ -1 +1 @@
Subproject commit 2bc945cc9ebfae1b688f72ea59f78fd23873d1d4
Subproject commit 194b715b16766e383b5aef92dd779fb182faf45d

@ -1 +1 @@
Subproject commit afae13adac25b6fa98bacf4b9f38458dc64317b0
Subproject commit 005ee90cb6aa563cdd690910455ea05f916ead3f

2
vendor/nim-snappy vendored

@ -1 +1 @@
Subproject commit aaef74113cadaaca690c6144eae0cf2c2a10db28
Subproject commit 913c426d571cf82601452642e01cd11ea26f7ac6

2
vendor/nim-stew vendored

@ -1 +1 @@
Subproject commit 104132fd0217e846b04dd26a5fbe3e43a4929a05
Subproject commit a0a53c911606cace989074f6b806eb0546a64ef6

2
vendor/nim-stint vendored

@ -1 +1 @@
Subproject commit 3c238df6cd4b9c1f37a9f103383e7d2bbd420c13
Subproject commit 9d2b382c5dc34f0d6bbd93b2a5d65dde85067e0f

2
vendor/nim-web3 vendored

@ -1 +1 @@
Subproject commit 9aed14a7373f51715730712adfde39546708296f
Subproject commit b705f816439f0068ece8c234336bc7093222d00f