Skip to content

Commit c28d69b

Browse files
authored
Feat: API get_l2_block_by_id (#146)
* Feat: Adds API `get_l2_block_by_id`. * Adds `get_l2_block_by_id.ts` to integration test. * Updates dependency `common-rs`. * Fixes the failed asserts.
1 parent d5e8812 commit c28d69b

File tree

12 files changed

+470
-38
lines changed

12 files changed

+470
-38
lines changed

.env

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
RUN_MODE=development # development or staging
22
RUST_BACKTRACE=1
3-
RUST_LOG=rollup_state_manager=debug
3+
RUST_LOG=rollup_state_manager=debug,info
44

55
NTXS=2
66
BALANCELEVELS=3

.github/workflows/integration-test.yml

+1
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,5 @@ jobs:
8787
- name: Run tests
8888
run: |
8989
cd ./examples/js/
90+
npx ts-node get_l2_block_by_id.ts
9091
npx ts-node get_token_balance.ts

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/js/get_l2_block_by_id.ts

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { grpcClient } from "./grpc_client";
2+
import { kafkaProducer } from "./kafka_producer";
3+
import { sleep } from "./util";
4+
import { strict as assert } from "assert";
5+
6+
const blockId = 0;
7+
const userId = 0;
8+
9+
const kafkaUserValue = {
10+
user_id: userId,
11+
l1_address: "0x6286d0A2FC1d4C12a4ACc274018b401c68157Fdb",
12+
l2_pubkey:
13+
"0x5d182c51bcfe99583d7075a7a0c10d96bef82b8a059c4bf8c5f6e7124cf2bba3"
14+
};
15+
16+
const kafkaBalanceValue = {
17+
timestamp: 16264463600,
18+
user_id: userId,
19+
asset: "ETH",
20+
business: "deposit",
21+
change: "3",
22+
balance: "3",
23+
balance_available: "3",
24+
balance_frozen: "0",
25+
detail: JSON.stringify({ id: 0 })
26+
};
27+
28+
async function main() {
29+
try {
30+
await mainTest();
31+
} catch (error) {
32+
console.error("Caught error:", error);
33+
process.exit(1);
34+
}
35+
}
36+
37+
async function mainTest() {
38+
await kafkaProducer.Init();
39+
40+
await registerUser();
41+
await depositBalance();
42+
await sleep(1000);
43+
await kafkaProducer.Stop();
44+
45+
await getL2BlockByIdTest();
46+
}
47+
48+
async function registerUser() {
49+
await kafkaProducer.send([
50+
{
51+
key: "registeruser",
52+
value: JSON.stringify(kafkaUserValue)
53+
}
54+
]);
55+
}
56+
57+
async function depositBalance() {
58+
await kafkaProducer.send([
59+
{
60+
key: "balances",
61+
value: JSON.stringify(kafkaBalanceValue)
62+
}
63+
]);
64+
}
65+
66+
async function getL2BlockByIdTest() {
67+
const res = await grpcClient.l2BlockQuery(blockId);
68+
assert(res["created_time"]);
69+
assert.equal(res["txs"].length, 2);
70+
assert.equal(res["tx_num"], "2");
71+
assert.equal(res["real_tx_num"], "2");
72+
assert.equal(res["status"], "INITED");
73+
assert.equal(
74+
res["new_root"],
75+
"Fr(0x1cd19866300cc822c6ead46a420ecf2b4dacd26b49d3a5cbf737761f3fa2dd01)"
76+
);
77+
78+
console.log("getL2BlockByIdTest passed");
79+
}
80+
81+
main();

examples/js/get_token_balance.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { sleep } from "./util";
44
import { strict as assert } from "assert";
55

66
const tokenId = 0;
7-
const userId = 0;
7+
const userId = 1;
88

99
const kafkaUserValue = {
1010
user_id: userId,

examples/js/grpc_client.ts

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ class Client {
1717
this.client = caller(`${server}`, { file, load }, "RollupState");
1818
}
1919

20+
async l2BlockQuery(block_id): Promise<Map<string, any>> {
21+
return (await this.client.l2BlockQuery({ block_id }));
22+
}
2023
async tokenBalanceQuery(account_id, token_id, token_address, token_name): Promise<Map<string, any>> {
2124
return (await this.client.tokenBalanceQuery({
2225
account_id,

proto/rollup_state.proto

+35-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ package rollup_state;
55
import "google/api/annotations.proto";
66

77
service RollupState {
8+
rpc L2BlockQuery(L2BlockQueryRequest) returns (L2BlockQueryResponse) {
9+
option (google.api.http) = {
10+
post : "/api/l2_block/{block_id}"
11+
body : "*"
12+
};
13+
}
14+
815
rpc TokenBalanceQuery(TokenBalanceQueryRequest) returns (TokenBalanceQueryResponse) {
916
option (google.api.http) = {
1017
post : "/api/token_balance/{account_id}/{token_id}"
@@ -13,6 +20,23 @@ service RollupState {
1320
}
1421
}
1522

23+
message L2BlockQueryRequest {
24+
int64 block_id = 1;
25+
}
26+
27+
message L2BlockQueryResponse {
28+
string new_root = 1;
29+
double created_time = 2;
30+
uint64 tx_num = 3;
31+
uint64 real_tx_num = 4;
32+
TaskStatus status = 5;
33+
repeated Tx txs = 6;
34+
// TODO: Adds `l1_tx_hash: string`.
35+
message Tx {
36+
repeated string detail = 1; // TODO: Fixes to decoding TX in issue #132.
37+
}
38+
}
39+
1640
message TokenBalanceQueryRequest {
1741
uint32 account_id = 1;
1842
uint32 token_id = 2;
@@ -21,7 +45,15 @@ message TokenBalanceQueryRequest {
2145
}
2246

2347
message TokenBalanceQueryResponse {
24-
string balance = 1;
25-
string balance_raw = 2;
26-
uint32 precision = 3;
48+
string balance = 1;
49+
string balance_raw = 2;
50+
uint32 precision = 3;
51+
}
52+
53+
enum TaskStatus {
54+
INITED = 0;
55+
WITGENING = 1;
56+
READY = 2;
57+
ASSIGNED = 3;
58+
PROVED = 4;
2759
}

src/grpc/controller.rs

+79-3
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,56 @@
1+
use crate::config::Settings;
12
use crate::grpc::rpc::*;
23
use crate::state::global::GlobalState;
34
use crate::test_utils::types::{get_token_id_by_name, prec_token_id};
5+
use crate::types::l2::L2BlockSerde;
6+
use fluidex_common::db::models::{l2_block, tablenames, task};
7+
use fluidex_common::db::DbType;
48
use fluidex_common::types::FrExt;
9+
use fluidex_common::utils::timeutil::FTimestamp;
510
use std::sync::{Arc, RwLock};
6-
use tonic::Status;
11+
use tonic::{Code, Status};
712

813
pub struct Controller {
14+
db_pool: sqlx::Pool<DbType>,
915
state: Arc<RwLock<GlobalState>>,
1016
}
1117

1218
impl Controller {
13-
pub fn new(state: Arc<RwLock<GlobalState>>) -> Self {
14-
Self { state }
19+
pub async fn new(state: Arc<RwLock<GlobalState>>) -> Self {
20+
let db_pool = sqlx::postgres::PgPool::connect(Settings::db()).await.unwrap();
21+
Self { db_pool, state }
22+
}
23+
24+
pub async fn l2_block_query(&self, request: L2BlockQueryRequest) -> Result<L2BlockQueryResponse, Status> {
25+
let block_id = request.block_id;
26+
let l2_block = get_l2_block_by_id(&self.db_pool, block_id).await?;
27+
28+
let status = match get_task_status_by_block_id(&self.db_pool, block_id).await? {
29+
task::TaskStatus::Inited => TaskStatus::Inited,
30+
task::TaskStatus::Witgening => TaskStatus::Witgening,
31+
task::TaskStatus::Ready => TaskStatus::Ready,
32+
task::TaskStatus::Assigned => TaskStatus::Assigned,
33+
task::TaskStatus::Proved => TaskStatus::Proved,
34+
};
35+
36+
let witness: L2BlockSerde = serde_json::from_value(l2_block.witness).unwrap();
37+
let tx_num = witness.encoded_txs.len() as u64;
38+
let txs = witness
39+
.encoded_txs
40+
.iter()
41+
.map(|tx| l2_block_query_response::Tx {
42+
detail: tx.iter().map(|fr_str| fr_str.0.to_decimal_string()).collect(),
43+
})
44+
.collect();
45+
46+
Ok(L2BlockQueryResponse {
47+
new_root: l2_block.new_root,
48+
created_time: FTimestamp::from(&l2_block.created_time).0,
49+
tx_num,
50+
real_tx_num: tx_num, // TODO: Needs to decode and filter out NOP txs.
51+
status: status as i32,
52+
txs,
53+
})
1554
}
1655

1756
pub fn token_balance_query(&self, request: TokenBalanceQueryRequest) -> Result<TokenBalanceQueryResponse, Status> {
@@ -33,3 +72,40 @@ impl Controller {
3372
})
3473
}
3574
}
75+
76+
async fn get_l2_block_by_id(db_pool: &sqlx::Pool<DbType>, block_id: i64) -> Result<l2_block::L2Block, Status> {
77+
let stmt = format!(
78+
"select block_id, new_root, witness, created_time
79+
from {}
80+
where block_id = $1
81+
order by created_time desc limit 1",
82+
tablenames::L2_BLOCK,
83+
);
84+
match sqlx::query_as::<_, l2_block::L2Block>(&stmt)
85+
.bind(block_id)
86+
.fetch_one(db_pool)
87+
.await
88+
{
89+
Ok(l2_block) => Ok(l2_block),
90+
Err(sqlx::Error::RowNotFound) => Err(Status::new(Code::NotFound, "db l2_block record not found")),
91+
Err(err) => {
92+
println!("{:?}", err);
93+
Err(Status::new(Code::Internal, "db table l2_block fetch error"))
94+
}
95+
}
96+
}
97+
98+
async fn get_task_status_by_block_id(db_pool: &sqlx::Pool<DbType>, block_id: i64) -> Result<task::TaskStatus, Status> {
99+
let stmt = format!(
100+
"select status
101+
from {}
102+
where block_id = $1
103+
order by created_time desc limit 1",
104+
tablenames::TASK,
105+
);
106+
match sqlx::query_as(&stmt).bind(block_id).fetch_one(db_pool).await {
107+
Ok((task_status,)) => Ok(task_status),
108+
Err(sqlx::Error::RowNotFound) => Err(Status::new(Code::NotFound, "db task record not found")),
109+
Err(_) => Err(Status::new(Code::Internal, "db table task fetch error")),
110+
}
111+
}

src/grpc/handler.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@ pub struct Handler {
99
}
1010

1111
impl Handler {
12-
pub fn new(state: Arc<RwLock<GlobalState>>) -> Self {
12+
pub async fn new(state: Arc<RwLock<GlobalState>>) -> Self {
1313
Self {
14-
controller: Controller::new(state),
14+
controller: Controller::new(state).await,
1515
}
1616
}
1717
}
1818

1919
#[tonic::async_trait]
2020
impl rollup_state_server::RollupState for Handler {
21+
async fn l2_block_query(&self, request: Request<L2BlockQueryRequest>) -> Result<Response<L2BlockQueryResponse>, Status> {
22+
Ok(Response::new(self.controller.l2_block_query(request.into_inner()).await?))
23+
}
24+
2125
async fn token_balance_query(&self, request: Request<TokenBalanceQueryRequest>) -> Result<Response<TokenBalanceQueryResponse>, Status> {
2226
Ok(Response::new(self.controller.token_balance_query(request.into_inner())?))
2327
}

src/grpc/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub fn run_grpc_server(addr: SocketAddr, state: Arc<RwLock<GlobalState>>) -> any
2222
tx.send(()).ok();
2323
});
2424

25-
let handler = Handler::new(state);
25+
let handler = Handler::new(state).await;
2626

2727
tonic::transport::Server::builder()
2828
.add_service(RollupStateServer::new(handler))

0 commit comments

Comments
 (0)