Fix #2261
Also bumps Confutils to allow setting the hidden --web3-mode param (to allow testing the eth1 syncing without validators)
This commit is contained in:
parent
490e4c7666
commit
fa99c3b417
|
@ -42,7 +42,7 @@ type
|
||||||
## between different versions of the client and accidentally using an old
|
## between different versions of the client and accidentally using an old
|
||||||
## database.
|
## database.
|
||||||
backend: KvStoreRef
|
backend: KvStoreRef
|
||||||
preset: RuntimePreset
|
preset*: RuntimePreset
|
||||||
genesisDeposits*: DepositsSeq
|
genesisDeposits*: DepositsSeq
|
||||||
checkpoint*: proc() {.gcsafe.}
|
checkpoint*: proc() {.gcsafe.}
|
||||||
|
|
||||||
|
|
|
@ -72,3 +72,7 @@ template findIt*(s: openArray, predicate: untyped): int =
|
||||||
|
|
||||||
proc currentSlot*(node: BeaconNode): Slot =
|
proc currentSlot*(node: BeaconNode): Slot =
|
||||||
node.beaconClock.now.slotOrZero
|
node.beaconClock.now.slotOrZero
|
||||||
|
|
||||||
|
template runtimePreset*(node: BeaconNode): RuntimePreset =
|
||||||
|
node.db.preset
|
||||||
|
|
||||||
|
|
|
@ -47,9 +47,9 @@ type
|
||||||
test = "Test a web3 provider"
|
test = "Test a web3 provider"
|
||||||
|
|
||||||
Web3Mode* {.pure.} = enum
|
Web3Mode* {.pure.} = enum
|
||||||
auto = "Enabled only when validators are attached"
|
auto # Enabled only when validators are attached
|
||||||
enabled = "Always enabled"
|
enabled # Always enabled
|
||||||
disabled = "Always disabled"
|
disabled # Always disabled
|
||||||
|
|
||||||
DoppelgangerProtectionMode* {.pure.} = enum
|
DoppelgangerProtectionMode* {.pure.} = enum
|
||||||
dontcheck
|
dontcheck
|
||||||
|
@ -177,8 +177,6 @@ type
|
||||||
desc: "SSZ file specifying a recent finalized block"
|
desc: "SSZ file specifying a recent finalized block"
|
||||||
name: "finalized-checkpoint-block" }: Option[InputFile]
|
name: "finalized-checkpoint-block" }: Option[InputFile]
|
||||||
|
|
||||||
runtimePreset* {.hidden.}: RuntimePreset
|
|
||||||
|
|
||||||
nodeName* {.
|
nodeName* {.
|
||||||
defaultValue: ""
|
defaultValue: ""
|
||||||
desc: "A name for this node that will appear in the logs. " &
|
desc: "A name for this node that will appear in the logs. " &
|
||||||
|
|
|
@ -49,7 +49,6 @@ type
|
||||||
timestamp*: Eth1BlockTimestamp
|
timestamp*: Eth1BlockTimestamp
|
||||||
deposits*: seq[DepositData]
|
deposits*: seq[DepositData]
|
||||||
voteData*: Eth1Data
|
voteData*: Eth1Data
|
||||||
voteDataVerified*: bool
|
|
||||||
|
|
||||||
when hasGenesisDetection:
|
when hasGenesisDetection:
|
||||||
activeValidatorsCount*: uint64
|
activeValidatorsCount*: uint64
|
||||||
|
@ -61,6 +60,8 @@ type
|
||||||
blocksByHash: Table[BlockHash, Eth1Block]
|
blocksByHash: Table[BlockHash, Eth1Block]
|
||||||
finalizedBlockHash: Eth2Digest
|
finalizedBlockHash: Eth2Digest
|
||||||
finalizedDepositsMerkleizer: DepositsMerkleizer
|
finalizedDepositsMerkleizer: DepositsMerkleizer
|
||||||
|
hasConsensusViolation: bool
|
||||||
|
## The local chain contradicts the observed consensus on the network
|
||||||
|
|
||||||
Eth1MonitorState = enum
|
Eth1MonitorState = enum
|
||||||
Initialized
|
Initialized
|
||||||
|
@ -541,6 +542,7 @@ proc pruneOldBlocks(chain: var Eth1Chain, depositIndex: uint64) =
|
||||||
lastBlock = blk
|
lastBlock = blk
|
||||||
|
|
||||||
if chain.finalizedDepositsMerkleizer.getChunkCount > initialChunks:
|
if chain.finalizedDepositsMerkleizer.getChunkCount > initialChunks:
|
||||||
|
chain.finalizedBlockHash = lastBlock.voteData.block_hash
|
||||||
chain.db.putEth2FinalizedTo DepositContractSnapshot(
|
chain.db.putEth2FinalizedTo DepositContractSnapshot(
|
||||||
eth1Block: lastBlock.voteData.block_hash,
|
eth1Block: lastBlock.voteData.block_hash,
|
||||||
depositContractState: chain.finalizedDepositsMerkleizer.toDepositContractState)
|
depositContractState: chain.finalizedDepositsMerkleizer.toDepositContractState)
|
||||||
|
@ -624,7 +626,6 @@ proc trackFinalizedState*(chain: var Eth1Chain,
|
||||||
let matchingBlock = chain.lowerBound(finalizedEth1Data.deposit_count)
|
let matchingBlock = chain.lowerBound(finalizedEth1Data.deposit_count)
|
||||||
result = if matchingBlock != nil:
|
result = if matchingBlock != nil:
|
||||||
if matchingBlock.voteData.deposit_root == finalizedEth1Data.deposit_root:
|
if matchingBlock.voteData.deposit_root == finalizedEth1Data.deposit_root:
|
||||||
matchingBlock.voteDataVerified = true
|
|
||||||
true
|
true
|
||||||
else:
|
else:
|
||||||
error "Corrupted deposits history detected",
|
error "Corrupted deposits history detected",
|
||||||
|
@ -632,6 +633,7 @@ proc trackFinalizedState*(chain: var Eth1Chain,
|
||||||
taretDepositsCount = finalizedEth1Data.deposit_count,
|
taretDepositsCount = finalizedEth1Data.deposit_count,
|
||||||
ourDepositsRoot = matchingBlock.voteData.deposit_root,
|
ourDepositsRoot = matchingBlock.voteData.deposit_root,
|
||||||
targetDepositsRoot = finalizedEth1Data.deposit_root
|
targetDepositsRoot = finalizedEth1Data.deposit_root
|
||||||
|
chain.hasConsensusViolation = true
|
||||||
false
|
false
|
||||||
else:
|
else:
|
||||||
error "The Eth1 chain is in inconsistent state",
|
error "The Eth1 chain is in inconsistent state",
|
||||||
|
@ -639,6 +641,7 @@ proc trackFinalizedState*(chain: var Eth1Chain,
|
||||||
checkpointDeposits = finalizedEth1Data.deposit_count,
|
checkpointDeposits = finalizedEth1Data.deposit_count,
|
||||||
localChainStart = shortLog(chain.blocks.peekFirst),
|
localChainStart = shortLog(chain.blocks.peekFirst),
|
||||||
localChainEnd = shortLog(chain.blocks.peekLast)
|
localChainEnd = shortLog(chain.blocks.peekLast)
|
||||||
|
chain.hasConsensusViolation = true
|
||||||
false
|
false
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
|
@ -787,6 +790,7 @@ proc safeCancel(fut: var Future[void]) =
|
||||||
proc clear(chain: var Eth1Chain) =
|
proc clear(chain: var Eth1Chain) =
|
||||||
chain.blocks.clear()
|
chain.blocks.clear()
|
||||||
chain.blocksByHash.clear()
|
chain.blocksByHash.clear()
|
||||||
|
chain.hasConsensusViolation = false
|
||||||
|
|
||||||
proc resetState(m: Eth1Monitor) {.async.} =
|
proc resetState(m: Eth1Monitor) {.async.} =
|
||||||
safeCancel m.runFut
|
safeCancel m.runFut
|
||||||
|
@ -907,8 +911,6 @@ proc syncBlockRange(m: Eth1Monitor,
|
||||||
of DepositRootIncorrect, DepositCountIncorrect:
|
of DepositRootIncorrect, DepositCountIncorrect:
|
||||||
raise newException(CorruptDataProvider,
|
raise newException(CorruptDataProvider,
|
||||||
"The deposit log events disagree with the deposit contract state")
|
"The deposit log events disagree with the deposit contract state")
|
||||||
of VerifiedCorrect:
|
|
||||||
lastBlock.voteDataVerified = true
|
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
@ -1057,6 +1059,9 @@ proc startEth1Syncing(m: Eth1Monitor, delayBeforeStart: Duration) {.async.} =
|
||||||
await m.stop()
|
await m.stop()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if m.eth1Chain.hasConsensusViolation:
|
||||||
|
raise newException(CorruptDataProvider, "Eth1 chain contradicts Eth2 consensus")
|
||||||
|
|
||||||
await m.eth1Progress.wait()
|
await m.eth1Progress.wait()
|
||||||
m.eth1Progress.clear()
|
m.eth1Progress.clear()
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,7 @@ func enrForkIdFromState(state: BeaconState): ENRForkID =
|
||||||
next_fork_epoch: FAR_FUTURE_EPOCH)
|
next_fork_epoch: FAR_FUTURE_EPOCH)
|
||||||
|
|
||||||
proc init*(T: type BeaconNode,
|
proc init*(T: type BeaconNode,
|
||||||
|
runtimePreset: RuntimePreset,
|
||||||
rng: ref BrHmacDrbgContext,
|
rng: ref BrHmacDrbgContext,
|
||||||
conf: BeaconNodeConf,
|
conf: BeaconNodeConf,
|
||||||
depositContractAddress: Eth1Address,
|
depositContractAddress: Eth1Address,
|
||||||
|
@ -83,7 +84,7 @@ proc init*(T: type BeaconNode,
|
||||||
genesisStateContents: ref string,
|
genesisStateContents: ref string,
|
||||||
genesisDepositsSnapshotContents: ref string): Future[BeaconNode] {.async.} =
|
genesisDepositsSnapshotContents: ref string): Future[BeaconNode] {.async.} =
|
||||||
let
|
let
|
||||||
db = BeaconChainDB.init(conf.runtimePreset, conf.databaseDir)
|
db = BeaconChainDB.init(runtimePreset, conf.databaseDir)
|
||||||
|
|
||||||
var
|
var
|
||||||
genesisState, checkpointState: ref BeaconState
|
genesisState, checkpointState: ref BeaconState
|
||||||
|
@ -143,7 +144,7 @@ proc init*(T: type BeaconNode,
|
||||||
# TODO Could move this to a separate "GenesisMonitor" process or task
|
# TODO Could move this to a separate "GenesisMonitor" process or task
|
||||||
# that would do only this - see Paul's proposal for this.
|
# that would do only this - see Paul's proposal for this.
|
||||||
let eth1MonitorRes = await Eth1Monitor.init(
|
let eth1MonitorRes = await Eth1Monitor.init(
|
||||||
conf.runtimePreset,
|
runtimePreset,
|
||||||
db,
|
db,
|
||||||
conf.web3Url,
|
conf.web3Url,
|
||||||
depositContractAddress,
|
depositContractAddress,
|
||||||
|
@ -209,7 +210,7 @@ proc init*(T: type BeaconNode,
|
||||||
let
|
let
|
||||||
chainDagFlags = if conf.verifyFinalization: {verifyFinalization}
|
chainDagFlags = if conf.verifyFinalization: {verifyFinalization}
|
||||||
else: {}
|
else: {}
|
||||||
chainDag = ChainDAGRef.init(conf.runtimePreset, db, chainDagFlags)
|
chainDag = ChainDAGRef.init(runtimePreset, db, chainDagFlags)
|
||||||
beaconClock = BeaconClock.init(chainDag.headState.data.data)
|
beaconClock = BeaconClock.init(chainDag.headState.data.data)
|
||||||
quarantine = QuarantineRef.init(rng)
|
quarantine = QuarantineRef.init(rng)
|
||||||
databaseGenesisValidatorsRoot =
|
databaseGenesisValidatorsRoot =
|
||||||
|
@ -257,7 +258,7 @@ proc init*(T: type BeaconNode,
|
||||||
let genesisDepositsSnapshot = SSZ.decode(genesisDepositsSnapshotContents[],
|
let genesisDepositsSnapshot = SSZ.decode(genesisDepositsSnapshotContents[],
|
||||||
DepositContractSnapshot)
|
DepositContractSnapshot)
|
||||||
eth1Monitor = Eth1Monitor.init(
|
eth1Monitor = Eth1Monitor.init(
|
||||||
conf.runtimePreset,
|
runtimePreset,
|
||||||
db,
|
db,
|
||||||
conf.web3Url,
|
conf.web3Url,
|
||||||
depositContractAddress,
|
depositContractAddress,
|
||||||
|
@ -1440,6 +1441,7 @@ programMain:
|
||||||
eth1Network: Option[Eth1Network]
|
eth1Network: Option[Eth1Network]
|
||||||
depositContractAddress: Option[Eth1Address]
|
depositContractAddress: Option[Eth1Address]
|
||||||
depositContractDeployedAt: Option[BlockHashOrNumber]
|
depositContractDeployedAt: Option[BlockHashOrNumber]
|
||||||
|
runtimePreset: RuntimePreset
|
||||||
|
|
||||||
setupStdoutLogging(config.logLevel)
|
setupStdoutLogging(config.logLevel)
|
||||||
|
|
||||||
|
@ -1473,7 +1475,7 @@ programMain:
|
||||||
|
|
||||||
if config.eth2Network.isSome:
|
if config.eth2Network.isSome:
|
||||||
let metadata = getMetadataForNetwork(config.eth2Network.get)
|
let metadata = getMetadataForNetwork(config.eth2Network.get)
|
||||||
config.runtimePreset = metadata.runtimePreset
|
runtimePreset = metadata.runtimePreset
|
||||||
|
|
||||||
if config.cmd == noCommand:
|
if config.cmd == noCommand:
|
||||||
for node in metadata.bootstrapNodes:
|
for node in metadata.bootstrapNodes:
|
||||||
|
@ -1489,7 +1491,7 @@ programMain:
|
||||||
depositContractDeployedAt = some metadata.depositContractDeployedAt
|
depositContractDeployedAt = some metadata.depositContractDeployedAt
|
||||||
eth1Network = metadata.eth1Network
|
eth1Network = metadata.eth1Network
|
||||||
else:
|
else:
|
||||||
config.runtimePreset = defaultRuntimePreset
|
runtimePreset = defaultRuntimePreset
|
||||||
when const_preset == "mainnet":
|
when const_preset == "mainnet":
|
||||||
if config.cmd == noCommand:
|
if config.cmd == noCommand:
|
||||||
depositContractAddress = some mainnetMetadata.depositContractAddress
|
depositContractAddress = some mainnetMetadata.depositContractAddress
|
||||||
|
@ -1533,7 +1535,7 @@ programMain:
|
||||||
else: (waitFor getEth1BlockHash(config.web3Url, blockId("latest"))).asEth2Digest
|
else: (waitFor getEth1BlockHash(config.web3Url, blockId("latest"))).asEth2Digest
|
||||||
var
|
var
|
||||||
initialState = initialize_beacon_state_from_eth1(
|
initialState = initialize_beacon_state_from_eth1(
|
||||||
config.runtimePreset, eth1Hash, startTime, deposits, {skipBlsValidation})
|
runtimePreset, eth1Hash, startTime, deposits, {skipBlsValidation})
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start#create-genesis-state
|
# https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start#create-genesis-state
|
||||||
initialState.genesis_time = startTime
|
initialState.genesis_time = startTime
|
||||||
|
@ -1594,7 +1596,9 @@ programMain:
|
||||||
# letting the default Ctrl+C handler exit is safe, since we only read from
|
# letting the default Ctrl+C handler exit is safe, since we only read from
|
||||||
# the db.
|
# the db.
|
||||||
var node = waitFor BeaconNode.init(
|
var node = waitFor BeaconNode.init(
|
||||||
rng, config,
|
runtimePreset,
|
||||||
|
rng,
|
||||||
|
config,
|
||||||
depositContractAddress.get,
|
depositContractAddress.get,
|
||||||
depositContractDeployedAt.get,
|
depositContractDeployedAt.get,
|
||||||
eth1Network,
|
eth1Network,
|
||||||
|
@ -1663,7 +1667,7 @@ programMain:
|
||||||
quit QuitFailure
|
quit QuitFailure
|
||||||
|
|
||||||
let deposits = generateDeposits(
|
let deposits = generateDeposits(
|
||||||
config.runtimePreset,
|
runtimePreset,
|
||||||
rng[],
|
rng[],
|
||||||
seed,
|
seed,
|
||||||
walletPath.wallet.nextAccount,
|
walletPath.wallet.nextAccount,
|
||||||
|
@ -1682,7 +1686,7 @@ programMain:
|
||||||
config.outValidatorsDir / "deposit_data-" & $epochTime() & ".json"
|
config.outValidatorsDir / "deposit_data-" & $epochTime() & ".json"
|
||||||
|
|
||||||
let launchPadDeposits =
|
let launchPadDeposits =
|
||||||
mapIt(deposits.value, LaunchPadDeposit.init(config.runtimePreset, it))
|
mapIt(deposits.value, LaunchPadDeposit.init(runtimePreset, it))
|
||||||
|
|
||||||
Json.saveFile(depositDataPath, launchPadDeposits)
|
Json.saveFile(depositDataPath, launchPadDeposits)
|
||||||
echo "Deposit data written to \"", depositDataPath, "\""
|
echo "Deposit data written to \"", depositDataPath, "\""
|
||||||
|
|
|
@ -176,7 +176,7 @@ proc installBeaconApiHandlers*(rpcServer: RpcServer, node: BeaconNode) =
|
||||||
genesis_time: node.chainDag.headState.data.data.genesis_time,
|
genesis_time: node.chainDag.headState.data.data.genesis_time,
|
||||||
genesis_validators_root:
|
genesis_validators_root:
|
||||||
node.chainDag.headState.data.data.genesis_validators_root,
|
node.chainDag.headState.data.data.genesis_validators_root,
|
||||||
genesis_fork_version: node.config.runtimePreset.GENESIS_FORK_VERSION
|
genesis_fork_version: node.runtimePreset.GENESIS_FORK_VERSION
|
||||||
)
|
)
|
||||||
|
|
||||||
rpcServer.rpc("get_v1_beacon_states_root") do (stateId: string) -> Eth2Digest:
|
rpcServer.rpc("get_v1_beacon_states_root") do (stateId: string) -> Eth2Digest:
|
||||||
|
|
|
@ -39,13 +39,13 @@ proc installConfigApiHandlers*(rpcServer: RpcServer, node: BeaconNode) =
|
||||||
"CHURN_LIMIT_QUOTIENT": $CHURN_LIMIT_QUOTIENT,
|
"CHURN_LIMIT_QUOTIENT": $CHURN_LIMIT_QUOTIENT,
|
||||||
"SHUFFLE_ROUND_COUNT": $SHUFFLE_ROUND_COUNT,
|
"SHUFFLE_ROUND_COUNT": $SHUFFLE_ROUND_COUNT,
|
||||||
"MIN_GENESIS_ACTIVE_VALIDATOR_COUNT":
|
"MIN_GENESIS_ACTIVE_VALIDATOR_COUNT":
|
||||||
$node.config.runtimePreset.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT,
|
$node.runtimePreset.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT,
|
||||||
"MIN_GENESIS_TIME": $node.config.runtimePreset.MIN_GENESIS_TIME,
|
"MIN_GENESIS_TIME": $node.runtimePreset.MIN_GENESIS_TIME,
|
||||||
"HYSTERESIS_QUOTIENT": $HYSTERESIS_QUOTIENT,
|
"HYSTERESIS_QUOTIENT": $HYSTERESIS_QUOTIENT,
|
||||||
"HYSTERESIS_DOWNWARD_MULTIPLIER": $HYSTERESIS_DOWNWARD_MULTIPLIER,
|
"HYSTERESIS_DOWNWARD_MULTIPLIER": $HYSTERESIS_DOWNWARD_MULTIPLIER,
|
||||||
"HYSTERESIS_UPWARD_MULTIPLIER": $HYSTERESIS_UPWARD_MULTIPLIER,
|
"HYSTERESIS_UPWARD_MULTIPLIER": $HYSTERESIS_UPWARD_MULTIPLIER,
|
||||||
"SAFE_SLOTS_TO_UPDATE_JUSTIFIED": $SAFE_SLOTS_TO_UPDATE_JUSTIFIED,
|
"SAFE_SLOTS_TO_UPDATE_JUSTIFIED": $SAFE_SLOTS_TO_UPDATE_JUSTIFIED,
|
||||||
"ETH1_FOLLOW_DISTANCE": $node.config.runtimePreset.ETH1_FOLLOW_DISTANCE,
|
"ETH1_FOLLOW_DISTANCE": $node.runtimePreset.ETH1_FOLLOW_DISTANCE,
|
||||||
"TARGET_AGGREGATORS_PER_COMMITTEE": $TARGET_AGGREGATORS_PER_COMMITTEE,
|
"TARGET_AGGREGATORS_PER_COMMITTEE": $TARGET_AGGREGATORS_PER_COMMITTEE,
|
||||||
"RANDOM_SUBNETS_PER_VALIDATOR": $RANDOM_SUBNETS_PER_VALIDATOR,
|
"RANDOM_SUBNETS_PER_VALIDATOR": $RANDOM_SUBNETS_PER_VALIDATOR,
|
||||||
"EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION":
|
"EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION":
|
||||||
|
@ -59,9 +59,9 @@ proc installConfigApiHandlers*(rpcServer: RpcServer, node: BeaconNode) =
|
||||||
"EJECTION_BALANCE": $EJECTION_BALANCE,
|
"EJECTION_BALANCE": $EJECTION_BALANCE,
|
||||||
"EFFECTIVE_BALANCE_INCREMENT": $EFFECTIVE_BALANCE_INCREMENT,
|
"EFFECTIVE_BALANCE_INCREMENT": $EFFECTIVE_BALANCE_INCREMENT,
|
||||||
"GENESIS_FORK_VERSION":
|
"GENESIS_FORK_VERSION":
|
||||||
"0x" & $node.config.runtimePreset.GENESIS_FORK_VERSION,
|
"0x" & $node.runtimePreset.GENESIS_FORK_VERSION,
|
||||||
"BLS_WITHDRAWAL_PREFIX": "0x" & ncrutils.toHex([BLS_WITHDRAWAL_PREFIX]),
|
"BLS_WITHDRAWAL_PREFIX": "0x" & ncrutils.toHex([BLS_WITHDRAWAL_PREFIX]),
|
||||||
"GENESIS_DELAY": $node.config.runtimePreset.GENESIS_DELAY,
|
"GENESIS_DELAY": $node.runtimePreset.GENESIS_DELAY,
|
||||||
"SECONDS_PER_SLOT": $SECONDS_PER_SLOT,
|
"SECONDS_PER_SLOT": $SECONDS_PER_SLOT,
|
||||||
"MIN_ATTESTATION_INCLUSION_DELAY": $MIN_ATTESTATION_INCLUSION_DELAY,
|
"MIN_ATTESTATION_INCLUSION_DELAY": $MIN_ATTESTATION_INCLUSION_DELAY,
|
||||||
"SLOTS_PER_EPOCH": $SLOTS_PER_EPOCH,
|
"SLOTS_PER_EPOCH": $SLOTS_PER_EPOCH,
|
||||||
|
|
|
@ -261,7 +261,7 @@ proc makeBeaconBlockForHeadAndSlot*(node: BeaconNode,
|
||||||
assign(poolPtr.tmpState, poolPtr.headState)
|
assign(poolPtr.tmpState, poolPtr.headState)
|
||||||
|
|
||||||
makeBeaconBlock(
|
makeBeaconBlock(
|
||||||
node.config.runtimePreset,
|
node.runtimePreset,
|
||||||
hashedState,
|
hashedState,
|
||||||
validator_index,
|
validator_index,
|
||||||
head.root,
|
head.root,
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 5d8d1ea648884a460518e7819be2d406e3a4ab0f
|
Subproject commit cfa95661913b0ff8b1609e3954894f8ab31bbf3e
|
Loading…
Reference in New Issue