Skip to content

Commit b7a310b

Browse files
proxy: c-frontend (#3745)
* builds * download per slot * format * multiple endpoints in config * reviews * formatting * init * add multiple links * getBalance * rebase fix * all methods * add docs * format * copyright year fix * add instruction for compiling the example * switch to release * remove rebase residual * improve example
1 parent 15ff468 commit b7a310b

File tree

11 files changed

+1353
-224
lines changed

11 files changed

+1353
-224
lines changed

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ EXCLUDED_NIM_PACKAGES := \
2525
vendor/nimbus-eth2/vendor/nim-eth \
2626
vendor/nimbus-eth2/vendor/nim-faststreams \
2727
vendor/nimbus-eth2/vendor/nim-http-utils \
28+
vendor/nimbus-eth2/vendor/nim-ngtcp2 \
29+
vendor/nimbus-eth2/vendor/nim-quic \
2830
vendor/nimbus-eth2/vendor/nim-json-rpc \
2931
vendor/nimbus-eth2/vendor/nim-json-serialization \
3032
vendor/nimbus-eth2/vendor/nim-libbacktrace \
@@ -89,11 +91,14 @@ PORTAL_TOOLS_CSV := $(subst $(SPACE),$(COMMA),$(FLUFFY_TOOLS))
8991
OS_PLATFORM = $(shell $(CC) -dumpmachine)
9092
ifneq (, $(findstring darwin, $(OS_PLATFORM)))
9193
SHAREDLIBEXT = dylib
94+
STATICLIBEXT = a
9295
else
9396
ifneq (, $(findstring mingw, $(OS_PLATFORM))$(findstring cygwin, $(OS_PLATFORM))$(findstring msys, $(OS_PLATFORM)))
9497
SHAREDLIBEXT = dll
98+
STATICLIBEXT = lib
9599
else
96100
SHAREDLIBEXT = so
101+
STATICLIBEXT = a
97102
endif
98103
endif
99104

@@ -350,7 +355,8 @@ nimbus-verified-proxy-test: | build deps
350355
libverifproxy: | build deps
351356
+ echo -e $(BUILD_MSG) "build/$@" && \
352357
$(ENV_SCRIPT) nim --version && \
353-
$(ENV_SCRIPT) nim c --app:lib -d:"libp2p_pki_schemes=secp256k1" --noMain:on --threads:on --nimcache:nimcache/libverifproxy -o:$(VERIF_PROXY_OUT_PATH)/$@.$(SHAREDLIBEXT) $(NIM_PARAMS) nimbus_verified_proxy/libverifproxy/verifproxy.nim
358+
echo $(NIM_PARAMS) && \
359+
$(ENV_SCRIPT) nim c --app:staticlib -d:"libp2p_pki_schemes=secp256k1" --noMain:on --out:$(VERIF_PROXY_OUT_PATH)/$@.$(STATICLIBEXT) $(NIM_PARAMS) nimbus_verified_proxy/libverifproxy/verifproxy.nim
354360
cp nimbus_verified_proxy/libverifproxy/verifproxy.h $(VERIF_PROXY_OUT_PATH)/
355361
echo -e $(BUILD_END_MSG) "build/$@"
356362

nimbus_verified_proxy/lc/lc.nim

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
import
1111
chronicles,
1212
chronos,
13+
eth/common/keys, # used for keys.rng
1314
beacon_chain/gossip_processing/light_client_processor,
14-
beacon_chain/beacon_clock,
15+
beacon_chain/[beacon_clock, conf],
1516
./lc_manager # use the modified light client manager
1617

1718
type
@@ -20,8 +21,8 @@ type
2021
) {.gcsafe, raises: [].}
2122

2223
LightClient* = ref object
23-
cfg: RuntimeConfig
24-
forkDigests: ref ForkDigests
24+
cfg*: RuntimeConfig
25+
forkDigests*: ref ForkDigests
2526
getBeaconTime*: GetBeaconTimeFn
2627
store*: ref ForkedLightClientStore
2728
processor*: ref LightClientProcessor
@@ -144,16 +145,59 @@ proc new*(
144145

145146
lightClient
146147

148+
proc new*(
149+
T: type LightClient, chain: Option[string], trustedBlockRoot: Option[Eth2Digest]
150+
): T =
151+
let metadata = loadEth2Network(chain)
152+
153+
# just for short hand convenience
154+
template cfg(): auto =
155+
metadata.cfg
156+
157+
# initialize beacon node genesis data, beacon clock and forkDigests
158+
let
159+
genesisState =
160+
try:
161+
template genesisData(): auto =
162+
metadata.genesis.bakedBytes
163+
164+
newClone(
165+
readSszForkedHashedBeaconState(
166+
cfg, genesisData.toOpenArray(genesisData.low, genesisData.high)
167+
)
168+
)
169+
except CatchableError as err:
170+
raiseAssert "Invalid baked-in state: " & err.msg
171+
172+
# getStateField reads seeks info directly from a byte array
173+
# get genesis time and instantiate the beacon clock
174+
genesisTime = getStateField(genesisState[], genesis_time)
175+
beaconClock = BeaconClock.init(cfg.timeParams, genesisTime).valueOr:
176+
error "Invalid genesis time in state", genesisTime
177+
quit QuitFailure
178+
179+
# get the function that itself get the current beacon time
180+
getBeaconTime = beaconClock.getBeaconTimeFn()
181+
genesis_validators_root = getStateField(genesisState[], genesis_validators_root)
182+
forkDigests = newClone ForkDigests.init(cfg, genesis_validators_root)
183+
184+
rng = keys.newRng()
185+
186+
# light client is set to optimistic finalization mode
187+
lightClient = LightClient.new(
188+
rng, cfg, forkDigests, getBeaconTime, genesis_validators_root,
189+
LightClientFinalizationMode.Optimistic,
190+
)
191+
192+
lightClient.trustedBlockRoot = trustedBlockRoot
193+
lightClient
194+
147195
proc setBackend*(lightClient: LightClient, backend: EthLCBackend) =
148196
lightClient.manager.backend = backend
149197

150-
proc start*(lightClient: LightClient) =
198+
proc start*(lightClient: LightClient) {.async: (raises: [CancelledError]).} =
151199
info "Starting beacon light client", trusted_block_root = lightClient.trustedBlockRoot
152-
lightClient.manager.start()
153-
154-
proc stop*(lightClient: LightClient) {.async: (raises: []).} =
155-
info "Stopping beacon light client"
156-
await lightClient.manager.stop()
200+
await lightClient.manager.start()
157201

158202
proc resetToFinalizedHeader*(
159203
lightClient: LightClient,

nimbus_verified_proxy/lc/lc_manager.nim

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ type
3737
UpdateVerifier* = ValueVerifier[ForkedLightClientUpdate]
3838
FinalityUpdateVerifier* = ValueVerifier[ForkedLightClientFinalityUpdate]
3939
OptimisticUpdateVerifier* = ValueVerifier[ForkedLightClientOptimisticUpdate]
40-
4140
GetTrustedBlockRootCallback* = proc(): Option[Eth2Digest] {.gcsafe, raises: [].}
4241
GetBoolCallback* = proc(): bool {.gcsafe, raises: [].}
4342
GetSlotCallback* = proc(): Slot {.gcsafe, raises: [].}
@@ -293,6 +292,7 @@ proc query[E](
293292

294293
# cancel all workers
295294
for i in 0 ..< NUM_WORKERS:
295+
assert workers[i] != nil
296296
workers[i].cancelSoon()
297297

298298
return success
@@ -385,13 +385,6 @@ proc loop(self: LightClientManager) {.async: (raises: [CancelledError]).} =
385385
# check for updates every slot
386386
await sleepAsync(self.timeParams.SLOT_DURATION)
387387

388-
proc start*(self: var LightClientManager) =
388+
proc start*(self: LightClientManager) {.async: (raises: [CancelledError]).} =
389389
## Start light client manager's loop.
390-
doAssert self.loopFuture == nil
391-
self.loopFuture = self.loop()
392-
393-
proc stop*(self: var LightClientManager) {.async: (raises: []).} =
394-
## Stop light client manager's loop.
395-
if self.loopFuture != nil:
396-
await noCancel self.loopFuture.cancelAndWait()
397-
self.loopFuture = nil
390+
await self.loop()
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
### Quick Instructions
2+
3+
*tested on MacOS (ARM64)*
4+
5+
> NOTE: change the hashes under `makeCalls` functions in `example.c` to any recent blockhash and transaction hash because verify proxy cannot query more than 1000 blocks in the history (`maxBlockWalk`) under default configuration
6+
7+
> NOTE: update the `trustedBlockRoot` before compiling the example
8+
9+
```bash
10+
./env.sh make -j12 nimbus_verified_proxy
11+
gcc -I./build/libverifproxy/ -L./build/libverifproxy/ -lverifproxy -lstdc++ -o proxy_from_c ./nimbus_verified_proxy/libverifproxy/example.c
12+
./proxy_from_c
13+
```
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/**
2+
* nimbus_verified_proxy
3+
* Copyright (c) 2025 Status Research & Development GmbH
4+
* Licensed and distributed under either of
5+
* * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
6+
* * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
7+
* at your option. This file may not be copied, modified, or distributed except according to those terms.
8+
*/
9+
10+
#include "./verifproxy.h"
11+
#include <stdio.h>
12+
#include <stdlib.h>
13+
#include <string.h>
14+
#include <unistd.h>
15+
#include <time.h>
16+
#include <stdbool.h>
17+
18+
char filterId[67];
19+
bool filterCreated = false;
20+
21+
void onBlockNumber(Context *ctx, int status, char *res) {
22+
printf("Blocknumber: %s\n", res);
23+
freeResponse(res);
24+
}
25+
26+
void onStart(Context *ctx, int status, char *res) {
27+
if (status < 0){ // callback onStart is called only for errors
28+
printf("Problem while starting verified proxy\n");
29+
stopVerifProxy(ctx);
30+
freeContext(ctx);
31+
exit(EXIT_FAILURE);
32+
}
33+
}
34+
35+
void onStorage(Context *ctx, int status, char *res) {
36+
printf("Storage: %s\n", res);
37+
freeResponse(res);
38+
}
39+
40+
void onBalance(Context *ctx, int status, char *res) {
41+
printf("Balance: %s\n", res);
42+
freeResponse(res);
43+
}
44+
45+
void onNonce(Context *ctx, int status, char *res) {
46+
printf("Nonce: %s\n", res);
47+
freeResponse(res);
48+
}
49+
50+
void onCode(Context *ctx, int status, char *res) {
51+
printf("Code: %s\n", res);
52+
freeResponse(res);
53+
}
54+
55+
void genericCallback(Context *ctx, int status, char *res) {
56+
printf("Status: %d\n", status);
57+
if (status < 0) printf("Error: %s\n", res);
58+
freeResponse(res);
59+
}
60+
61+
void onFilterCreate(Context *ctx, int status, char *res) {
62+
if (status == RET_SUCCESS) {
63+
strncpy(filterId, &res[1], strlen(res) - 2); // remove quotes
64+
filterId[strlen(res) - 2] = '\0';
65+
filterCreated = true;
66+
}
67+
freeResponse(res);
68+
}
69+
70+
void onCallComplete(Context *ctx, int status, char *res) {
71+
if (status == RET_SUCCESS) {
72+
printf("Call Complete: %s\n", res);
73+
} else {
74+
printf("Call Error: %s\n", res);
75+
}
76+
freeResponse(res);
77+
}
78+
79+
void onLogs(Context *ctx, int status, char *res) {
80+
if (status == RET_SUCCESS) {
81+
printf("Logs fetch successful\n");
82+
} else {
83+
printf("Logs Fetch Error: %s\n", res);
84+
}
85+
freeResponse(res);
86+
}
87+
88+
void makeCalls(Context *ctx) {
89+
char *BLOCK_HASH = "0xc62fa4cbdd48175b1171d8b7cede250ac1bea47ace4d19db344b922cd1e63111";
90+
char *TX_HASH = "0xbbcd3d9bc70874c03453caa19fd91239abb0eef84dc61ca33e2110df81df330c";
91+
char *CALL_ARGS = "{\"to\": \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\",\"data\": \"0x70a08231000000000000000000000000De5ae63A348C4d63343C8E20Fb6286909418c8A4\"}";
92+
char *FILTER_OPTIONS = "{\"fromBlock\": \"latest\", \"toBlock\": \"latest\", \"topics\":[\"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\"]}";
93+
94+
eth_blockNumber(ctx, onBlockNumber);
95+
eth_getBalance(ctx, "0x954a86C613fd1fBaC9C7A43a071A68254C75E4AC", "latest", onBalance);
96+
eth_getStorageAt(ctx, "0x954a86C613fd1fBaC9C7A43a071A68254C75E4AC", "0x0", "latest", onStorage);
97+
eth_getTransactionCount(ctx, "0x954a86C613fd1fBaC9C7A43a071A68254C75E4AC", "latest", onNonce);
98+
eth_getCode(ctx, "0x954a86C613fd1fBaC9C7A43a071A68254C75E4AC", "latest", onCode);
99+
100+
/* -------- Blocks & Uncles -------- */
101+
102+
eth_getBlockByHash(ctx, BLOCK_HASH, false, genericCallback);
103+
eth_getBlockByNumber(ctx, "latest", false, genericCallback);
104+
eth_getUncleCountByBlockNumber(ctx, "latest", genericCallback);
105+
eth_getUncleCountByBlockHash(ctx, BLOCK_HASH, genericCallback);
106+
107+
eth_getBlockTransactionCountByNumber(ctx, "latest", genericCallback);
108+
eth_getBlockTransactionCountByHash(ctx, BLOCK_HASH, genericCallback);
109+
110+
/* -------- Transactions -------- */
111+
eth_getTransactionByBlockNumberAndIndex(ctx, "latest", 0ULL, genericCallback);
112+
eth_getTransactionByBlockHashAndIndex(ctx, BLOCK_HASH, 0ULL, genericCallback);
113+
114+
eth_getTransactionByHash(ctx, TX_HASH, genericCallback);
115+
eth_getTransactionReceipt(ctx, TX_HASH, genericCallback);
116+
117+
eth_getBlockReceipts(ctx, "latest", genericCallback);
118+
119+
/* -------- Calls, Access Lists, Gas Estimation -------- */
120+
eth_call(ctx, CALL_ARGS, "latest", true, onCallComplete);
121+
eth_createAccessList(ctx, CALL_ARGS, "latest", false, onCallComplete);
122+
eth_estimateGas(ctx, CALL_ARGS, "latest", false, onCallComplete);
123+
124+
/* -------- Logs & Filters -------- */
125+
eth_getLogs(ctx, FILTER_OPTIONS, onLogs);
126+
if (filterCreated) {
127+
eth_getFilterLogs(ctx, filterId, onLogs);
128+
eth_getFilterChanges(ctx, filterId, onLogs);
129+
} else {
130+
eth_newFilter(ctx, FILTER_OPTIONS, onFilterCreate);
131+
}
132+
}
133+
134+
int main() {
135+
NimMain();
136+
137+
char* jsonConfig =
138+
"{"
139+
"\"eth2Network\": \"mainnet\","
140+
"\"trustedBlockRoot\": \"0x2558d82e8b29c4151a0683e4f9d480d229d84b27b51a976f56722e014227e723\","
141+
"\"backendUrl\": \"https://eth.blockrazor.xyz\","
142+
"\"beaconApiUrls\": \"http://testing.mainnet.beacon-api.nimbus.team,http://www.lightclientdata.org\","
143+
"\"logLevel\": \"FATAL\","
144+
"\"logStdout\": \"None\""
145+
"}";
146+
147+
Context *ctx = startVerifProxy(jsonConfig, onStart);
148+
149+
time_t start = time(NULL);
150+
151+
makeCalls(ctx);
152+
153+
while(true) {
154+
if ((time(NULL) - start) > 12) { //all 24 methods should return
155+
printf("\n\n Executing all eth api methods\n\n");
156+
makeCalls(ctx);
157+
start = time(NULL);
158+
}
159+
processVerifProxyTasks(ctx);
160+
}
161+
printf("it is here and this is the problem");
162+
stopVerifProxy(ctx);
163+
freeContext(ctx);
164+
}

0 commit comments

Comments
 (0)