Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

ngui: QT-based block / beacon node explorer PoC #2304

Draft
wants to merge 1 commit into
base: unstable
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@
path = vendor/nim-toml-serialization
url = https://github.com/status-im/nim-toml-serialization.git
ignore = untracked
[submodule "vendor/DOtherSide"]
path = vendor/DOtherSide
url = https://github.com/filcuc/DOtherSide.git
branch = master
[submodule "vendor/nimqml"]
path = vendor/nimqml
url = https://github.com/status-im/nimqml.git
branch = master
[submodule "vendor/gnosis-chain-configs"]
path = vendor/gnosis-chain-configs
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,10 @@ force_build_alone_tools: | $(FORCE_BUILD_ALONE_TOOLS_DEPS)
# https://www.gnu.org/software/make/manual/html_node/Multiple-Rules.html#Multiple-Rules
# Already defined as a reult
nimbus_beacon_node: force_build_alone_tools
ngui/ngui: | build deps
+ echo -e $(BUILD_MSG) "build/$@" && \
MAKE="$(MAKE)" V="$(V)" $(ENV_SCRIPT) scripts/compile_nim_program.sh $@ "ngui.ngui.nim" $(NIM_PARAMS) && \
echo -e $(BUILD_END_MSG) "ngui/ngui"

GOERLI_TESTNETS_PARAMS := \
--tcp-port=$$(( $(BASE_PORT) + $(NODE_ID) )) \
Expand Down
23 changes: 23 additions & 0 deletions beacon_chain/spec/eth2_apis/rest_validator_calls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ import

export client, rest_types, eth2_rest_serialization

proc getAttesterDuties*(
epoch: Epoch,
body: seq[ValidatorIndex]
): RestResponse[GetAttesterDutiesResponse] {.
rest, endpoint: "/eth/v1/validator/duties/attester/{epoch}",
meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Validator/getAttesterDuties

proc getAttesterDutiesPlain*(
epoch: Epoch,
body: seq[ValidatorIndex]
Expand All @@ -21,13 +29,28 @@ proc getAttesterDutiesPlain*(
meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Validator/getAttesterDuties

proc getProposerDuties*(
epoch: Epoch
): RestResponse[GetProposerDutiesResponse] {.
rest, endpoint: "/eth/v1/validator/duties/proposer/{epoch}",
meth: MethodGet.}
## https://ethereum.github.io/beacon-APIs/#/Validator/getProposerDuties

proc getProposerDutiesPlain*(
epoch: Epoch
): RestPlainResponse {.
rest, endpoint: "/eth/v1/validator/duties/proposer/{epoch}",
meth: MethodGet.}
## https://ethereum.github.io/beacon-APIs/#/Validator/getProposerDuties

proc getSyncCommitteeDuties*(
epoch: Epoch,
body: seq[ValidatorIndex]
): RestResponse[GetSyncCommitteeDutiesResponse] {.
rest, endpoint: "/eth/v1/validator/duties/sync/{epoch}",
meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Validator/getSyncCommitteeDuties

proc getSyncCommitteeDutiesPlain*(
epoch: Epoch,
body: seq[ValidatorIndex]
Expand Down
25 changes: 19 additions & 6 deletions beacon_chain/spec/forks.nim
Original file line number Diff line number Diff line change
Expand Up @@ -979,28 +979,41 @@ template getForkedBlockField*(
of ConsensusFork.Capella: unsafeAddr x.capellaData.message.y
of ConsensusFork.Deneb: unsafeAddr x.denebData.message.y)[]

template signature*(x: ForkedSignedBeaconBlock |
template getForkedBodyField*(
x: ForkedSignedBeaconBlock |
ForkedMsgTrustedSignedBeaconBlock |
ForkedTrustedSignedBeaconBlock,
y: untyped): untyped =
# unsafeAddr avoids a copy of the field in some cases
(case x.kind
of ConsensusFork.Phase0: unsafeAddr x.phase0Data.message.body.y
of ConsensusFork.Altair: unsafeAddr x.altairData.message.body.y
of ConsensusFork.Bellatrix: unsafeAddr x.bellatrixData.message.body.y
of ConsensusFork.Capella: unsafeAddr x.capellaData.message.body.y
of ConsensusFork.Deneb: unsafeAddr x.denebData.message.body.y)[]

func signature*(x: ForkedSignedBeaconBlock |
ForkedMsgTrustedSignedBeaconBlock |
ForkedSignedBlindedBeaconBlock): ValidatorSig =
withBlck(x): forkyBlck.signature

template signature*(x: ForkedTrustedSignedBeaconBlock): TrustedSig =
func signature*(x: ForkedTrustedSignedBeaconBlock): TrustedSig =
withBlck(x): forkyBlck.signature

template root*(x: ForkedSignedBeaconBlock |
func root*(x: ForkedSignedBeaconBlock |
ForkedMsgTrustedSignedBeaconBlock |
ForkedTrustedSignedBeaconBlock): Eth2Digest =
withBlck(x): forkyBlck.root

template slot*(x: ForkedSignedBeaconBlock |
func slot*(x: ForkedSignedBeaconBlock |
ForkedMsgTrustedSignedBeaconBlock |
ForkedTrustedSignedBeaconBlock): Slot =
withBlck(x): forkyBlck.message.slot

template shortLog*(x: ForkedBeaconBlock | ForkedBlindedBeaconBlock): auto =
func shortLog*(x: ForkedBeaconBlock | ForkedBlindedBeaconBlock): auto =
withBlck(x): shortLog(forkyBlck)

template shortLog*(x: ForkedSignedBeaconBlock |
func shortLog*(x: ForkedSignedBeaconBlock |
ForkedMsgTrustedSignedBeaconBlock |
ForkedTrustedSignedBeaconBlock |
ForkedSignedBlindedBeaconBlock): auto =
Expand Down
1 change: 1 addition & 0 deletions beacon_chain/spec/helpers.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import
# Status libraries
std/times,
stew/[byteutils, endians2, objects, saturation_arith],
chronicles,
eth/common/[eth_types, eth_types_rlp],
Expand Down
1 change: 1 addition & 0 deletions ngui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
resources.cpp
65 changes: 65 additions & 0 deletions ngui/attestationlist.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import
std/[sequtils, tables],
NimQml,
../beacon_chain/spec/eth2_apis/rest_beacon_client,
../beacon_chain/spec/[eth2_merkleization, helpers],
./objecttablemodel, ./utils

type
AttestationInfo* = object
slot*: int
index*: int
beacon_block_root*: string
source_epoch*: int
source_root*: string
target_epoch*: int
target_root*: string
aggregation_bits*: string

proc toAttestationInfo*(v: Attestation): AttestationInfo =
AttestationInfo(
slot: v.data.slot.int,
index: v.data.index.int,
beacon_block_root: toBlockLink(v.data.beacon_block_root),
source_epoch: v.data.source.epoch.int,
source_root: toBlockLink(v.data.source.root),
target_epoch: v.data.target.epoch.int,
target_root: toBlockLink(v.data.target.root),
aggregation_bits: $v.aggregation_bits,
)

QtObject:
type AttestationList* = ref object of QAbstractTableModel
# TODO this could be a generic ObjectTableModel, except generics + method don't work..
data: ObjectTableModelImpl[AttestationInfo]

proc setup(self: AttestationList) = self.QAbstractTableModel.setup

proc delete(self: AttestationList) =
self.QAbstractTableModel.delete

proc newAttestationList*(data: seq[Attestation]): AttestationList =
new(result, delete)
result.data = ObjectTableModelImpl[AttestationInfo](items: data.mapIt(it.toAttestationInfo()))
result.setup

method rowCount(self: AttestationList, index: QModelIndex = nil): int =
self.data.rowCount(index)

method columnCount(self: AttestationList, index: QModelIndex = nil): int =
self.data.columnCount(index)

method headerData*(self: AttestationList, section: int, orientation: QtOrientation, role: int): QVariant =
self.data.headerData(section, orientation, role)

method data(self: AttestationList, index: QModelIndex, role: int): QVariant =
self.data.data(index, role)

method roleNames(self: AttestationList): Table[int, string] =
self.data.roleNames()

proc setNewData*(self: AttestationList, v: seq[Attestation]) =
self.data.setNewData(self, v.mapIt(it.toAttestationInfo()))

proc sort*(self: AttestationList, section: int) {.slot.} =
self.data.sort(self, section)
51 changes: 51 additions & 0 deletions ngui/attesterslashinglist.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import
std/[sequtils, tables],
NimQml,
../beacon_chain/spec/eth2_apis/rest_beacon_client,
../beacon_chain/spec/[helpers],
./objecttablemodel, ./utils

type
AttesterSlashingInfo* = object
info*: string

proc toAttesterSlashingInfo*(v: AttesterSlashing): AttesterSlashingInfo =
AttesterSlashingInfo(
info: $v
)

QtObject:
type AttesterSlashingList* = ref object of QAbstractTableModel
# TODO this could be a generic ObjectTableModel, except generics + method don't work..
data: ObjectTableModelImpl[AttesterSlashingInfo]

proc setup(self: AttesterSlashingList) = self.QAbstractTableModel.setup

proc delete(self: AttesterSlashingList) =
self.QAbstractTableModel.delete

proc newAttesterSlashingList*(data: openArray[AttesterSlashing]): AttesterSlashingList =
new(result, delete)
result.data = ObjectTableModelImpl[AttesterSlashingInfo](items: data.mapIt(it.toAttesterSlashingInfo()))
result.setup

method rowCount(self: AttesterSlashingList, index: QModelIndex = nil): int =
self.data.rowCount(index)

method columnCount(self: AttesterSlashingList, index: QModelIndex = nil): int =
self.data.columnCount(index)

method headerData*(self: AttesterSlashingList, section: int, orientation: QtOrientation, role: int): QVariant =
self.data.headerData(section, orientation, role)

method data(self: AttesterSlashingList, index: QModelIndex, role: int): QVariant =
self.data.data(index, role)

method roleNames(self: AttesterSlashingList): Table[int, string] =
self.data.roleNames()

proc setNewData*(self: AttesterSlashingList, v: openArray[AttesterSlashing]) =
self.data.setNewData(self, v.mapIt(it.toAttesterSlashingInfo()))

proc sort*(self: AttesterSlashingList, section: int) {.slot.} =
self.data.sort(self, section)
93 changes: 93 additions & 0 deletions ngui/blockmodel.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import
std/[sequtils, times],
NimQml,
../beacon_chain/spec/eth2_apis/rest_beacon_client,
"."/[
attestationlist, depositlist, attesterslashinglist, proposerslashinglist,
voluntaryexitlist, utils]

QtObject:
type
BlockModel* = ref object of QObject
blck: ForkedSignedBeaconBlock
attestationsx: AttestationList
depositsx: DepositList
attester_slashingsx: AttesterSlashingList
proposer_slashingsx: ProposerSlashingList
voluntary_exitsx: VoluntaryExitList
genesis_time*: uint64

proc delete*(self: BlockModel) =
self.QObject.delete

proc setup*(self: BlockModel) =
self.QObject.setup

proc newBlockModel*(forked: ForkedSignedBeaconBlock, genesis_time: uint64): BlockModel =
let res = withBlck(forked): BlockModel(
blck: forked,
attestationsx: newAttestationList(forkyBlck.message.body.attestations.asSeq()),
depositsx: newDepositList(forkyBlck.message.body.deposits.mapIt(it.toDepositInfo())),
attester_slashingsx: newAttesterSlashingList(forkyBlck.message.body.attester_slashings.asSeq()),
proposer_slashingsx: newProposerSlashingList(forkyBlck.message.body.proposer_slashings.asSeq()),
voluntary_exitsx: newVoluntaryExitList(forkyBlck.message.body.voluntary_exits.asSeq()),
genesis_time: genesis_time,
)
res.setup()
res

proc `blck=`*(self: BlockModel, forked: ForkedSignedBeaconBlock) =
self.blck = forked
withBlck(forked):
self.attestationsx.setNewData(forkyBlck.message.body.attestations.asSeq())
self.depositsx.setNewData(forkyBlck.message.body.deposits.mapIt(it.toDepositInfo()))
self.attester_slashingsx.setNewData(forkyBlck.message.body.attester_slashings.asSeq())
self.proposer_slashingsx.setNewData(forkyBlck.message.body.proposer_slashings.asSeq())
self.voluntary_exitsx.setNewData(forkyBlck.message.body.voluntary_exits.asSeq())

proc slot*(self: BlockModel): int {.slot.} = getForkedBlockField(self.blck, slot).int
QtProperty[int] slot: read = slot

proc time*(self: BlockModel): string {.slot.} =
let t = self.genesis_time + getForkedBlockField(self.blck, slot) * SECONDS_PER_SLOT
$fromUnix(t.int64).utc
QtProperty[string] time: read = time

proc root*(self: BlockModel): string {.slot.} = toDisplayHex(self.blck.root.data)
QtProperty[string] root: read = root

proc proposer_index*(self: BlockModel): int {.slot.} = getForkedBlockField(self.blck, proposer_index).int
QtProperty[int] proposer_index: read = proposer_index

proc parent_root*(self: BlockModel): string {.slot.} = toBlockLink(getForkedBlockField(self.blck, parent_root))
QtProperty[string] parent_root: read = parent_root

proc state_root*(self: BlockModel): string {.slot.} = toDisplayHex(getForkedBlockField(self.blck, state_root).data)
QtProperty[string] state_root: read = state_root

proc randao_reveal*(self: BlockModel): string {.slot.} = toDisplayHex(getForkedBodyField(self.blck, randao_reveal))
QtProperty[string] randao_reveal: read = randao_reveal

proc eth1_data*(self: BlockModel): string {.slot.} = RestJson.encode(getForkedBodyField(self.blck, eth1_data), pretty=true)
QtProperty[string] eth1_data: read = eth1_data

proc graffiti*(self: BlockModel): string {.slot.} = $getForkedBodyField(self.blck, graffiti)
QtProperty[string] graffiti: read = graffiti

proc proposer_slashings*(self: BlockModel): QVariant {.slot.} = newQVariant(self.proposer_slashingsx)
QtProperty[QVariant] proposer_slashings: read = proposer_slashings

proc attester_slashings*(self: BlockModel): QVariant {.slot.} = newQVariant(self.attester_slashingsx)
QtProperty[QVariant] attester_slashings: read = attester_slashings

proc attestations*(self: BlockModel): QVariant {.slot.} = newQVariant(self.attestationsx)
QtProperty[QVariant] attestations: read = attestations

proc deposits*(self: BlockModel): QVariant {.slot.} = newQVariant(self.depositsx)
QtProperty[QVariant] deposits: read = deposits

proc voluntary_exits*(self: BlockModel): QVariant {.slot.} = newQVariant(self.voluntary_exitsx)
QtProperty[QVariant] voluntary_exits: read = voluntary_exits

proc signature*(self: BlockModel): string {.slot.} = toDisplayHex(self.blck.signature)
QtProperty[string] signature: read = signature
56 changes: 56 additions & 0 deletions ngui/depositlist.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import
std/[tables],
NimQml,
../beacon_chain/spec/datatypes/base,
./objecttablemodel, ./utils

type
DepositInfo* = object
pubkey*: string
withdrawal_credentials*: string
amount*: Gwei
signature*: string

proc toDepositInfo*(v: Deposit): DepositInfo =
DepositInfo(
pubkey: toDisplayHex(v.data.pubkey.toRaw()),
withdrawal_credentials: toDisplayHex(v.data.withdrawal_credentials),
amount: v.data.amount,
signature: toDisplayHex(v.data.signature),
)

QtObject:
type DepositList* = ref object of QAbstractTableModel
# TODO this could be a generic ObjectTableModel, except generics + method don't work..
data: ObjectTableModelImpl[DepositInfo]

proc setup(self: DepositList) = self.QAbstractTableModel.setup

proc delete(self: DepositList) =
self.QAbstractTableModel.delete

proc newDepositList*(data: seq[DepositInfo]): DepositList =
new(result, delete)
result.data = ObjectTableModelImpl[DepositInfo](items: data)
result.setup

method rowCount(self: DepositList, index: QModelIndex = nil): int =
self.data.rowCount(index)

method columnCount(self: DepositList, index: QModelIndex = nil): int =
self.data.columnCount(index)

method headerData*(self: DepositList, section: int, orientation: QtOrientation, role: int): QVariant =
self.data.headerData(section, orientation, role)

method data(self: DepositList, index: QModelIndex, role: int): QVariant =
self.data.data(index, role)

method roleNames(self: DepositList): Table[int, string] =
self.data.roleNames()

proc setNewData*(self: DepositList, v: seq[DepositInfo]) =
self.data.setNewData(self, v)

proc sort*(self: DepositList, section: int) {.slot.} =
self.data.sort(self, section)