From fdac32242c11c792ffa82e0c5b97578c87e4ac42 Mon Sep 17 00:00:00 2001 From: Antoine Le Calvez Date: Mon, 10 Mar 2025 16:08:08 +0100 Subject: [PATCH 01/12] Add new HTTP endpoint that returns total supply as of given state root --- beacon_node/http_api/src/lib.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 368faae6129..d6bc62ab881 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -4448,6 +4448,23 @@ pub fn serve( }) }); + // GET lighthouse/supply/{state_root} + let get_lighthouse_supply = warp::path("lighthouse") + .and(warp::path("supply")) + .and(warp::path::param::()) + .and(warp::path::end()) + .and(task_spawner_filter.clone()) + .and(chain_filter.clone()) + .then(|state_id: StateId, task_spawner: TaskSpawner, chain: Arc>| { + task_spawner.blocking_json_task(Priority::P1, move || { + state_id + .map_state_and_execution_optimistic_and_finalized(&chain, |state, execution_optimistic, finalized| { + Ok((state.balances().iter().sum::(), execution_optimistic, finalized)) + }) + .map(api_types::GenericResponse::from) + }) + }); + // GET lighthouse/analysis/attestation_performance/{index} let get_lighthouse_attestation_performance = warp::path("lighthouse") .and(warp::path("analysis")) @@ -4720,6 +4737,7 @@ pub fn serve( .uor(get_lighthouse_staking) .uor(get_lighthouse_database_info) .uor(get_lighthouse_block_rewards) + .uor(get_lighthouse_supply) .uor(get_lighthouse_attestation_performance) .uor(get_beacon_light_client_optimistic_update) .uor(get_beacon_light_client_finality_update) From 2ceb554dc402ec6fac98bd75b7a8ebc3242ba424 Mon Sep 17 00:00:00 2001 From: Antoine Le Calvez Date: Thu, 20 Mar 2025 09:43:50 +0100 Subject: [PATCH 02/12] Add doc in book --- book/src/api_lighthouse.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/book/src/api_lighthouse.md b/book/src/api_lighthouse.md index b65bef47628..d7b934c96e4 100644 --- a/book/src/api_lighthouse.md +++ b/book/src/api_lighthouse.md @@ -775,6 +775,28 @@ Caveats: This is because the state *prior* to the `start_epoch` needs to be loaded from the database, and loading a state on a boundary is most efficient. +## `/lighthouse/supply/{state_root}` + +Returns the sum of all validator balances for a given state root. + +```bash +curl -X GET "http://localhost:5052/lighthouse/supply/0x7e76880eb67bbdc86250aa578958e9d0675e64e714337855204fb5abaaf82c2b" -H "accept: application/json" | jq +``` + +```json +{ + "data": [ + 674144000000000, + false, + true + ] +} +``` + +The two boolean flags in the response are respectively: +* `execution_optimism`: whether the response was computed using optimistically synced data +* `finalized`: whether the response was computed over finalized dasta + ## `/lighthouse/logs` This is a Server Side Event subscription endpoint. This allows a user to read From c8849f8264cb31d254243b234004b676cc2701e4 Mon Sep 17 00:00:00 2001 From: Antoine Le Calvez Date: Thu, 27 Mar 2025 12:59:11 +0100 Subject: [PATCH 03/12] Apply review suggestions --- beacon_node/http_api/src/lib.rs | 38 ++++++++++++++++++++++----------- book/src/api_lighthouse.md | 6 +++--- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index d6bc62ab881..75401561984 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -4448,22 +4448,34 @@ pub fn serve( }) }); - // GET lighthouse/supply/{state_root} - let get_lighthouse_supply = warp::path("lighthouse") - .and(warp::path("supply")) + // GET lighthouse/analysis/global_validator_supply/{state_root} + let get_lighthouse_global_validator_supply = warp::path("lighthouse") + .and(warp::path("analysis")) + .and(warp::path("global_validator_supply")) .and(warp::path::param::()) .and(warp::path::end()) .and(task_spawner_filter.clone()) .and(chain_filter.clone()) - .then(|state_id: StateId, task_spawner: TaskSpawner, chain: Arc>| { - task_spawner.blocking_json_task(Priority::P1, move || { - state_id - .map_state_and_execution_optimistic_and_finalized(&chain, |state, execution_optimistic, finalized| { - Ok((state.balances().iter().sum::(), execution_optimistic, finalized)) - }) - .map(api_types::GenericResponse::from) - }) - }); + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_json_task(Priority::P1, move || { + state_id + .map_state_and_execution_optimistic_and_finalized( + &chain, + |state, execution_optimistic, finalized| { + Ok(( + state.balances().iter().sum::(), + execution_optimistic, + finalized, + )) + }, + ) + .map(api_types::GenericResponse::from) + }) + }, + ); // GET lighthouse/analysis/attestation_performance/{index} let get_lighthouse_attestation_performance = warp::path("lighthouse") @@ -4737,7 +4749,7 @@ pub fn serve( .uor(get_lighthouse_staking) .uor(get_lighthouse_database_info) .uor(get_lighthouse_block_rewards) - .uor(get_lighthouse_supply) + .uor(get_lighthouse_global_validator_supply) .uor(get_lighthouse_attestation_performance) .uor(get_beacon_light_client_optimistic_update) .uor(get_beacon_light_client_finality_update) diff --git a/book/src/api_lighthouse.md b/book/src/api_lighthouse.md index d7b934c96e4..2bd4bbe0f70 100644 --- a/book/src/api_lighthouse.md +++ b/book/src/api_lighthouse.md @@ -775,12 +775,12 @@ Caveats: This is because the state *prior* to the `start_epoch` needs to be loaded from the database, and loading a state on a boundary is most efficient. -## `/lighthouse/supply/{state_root}` +## `lighthouse/analysis/global_validator_supply/{state_root}` Returns the sum of all validator balances for a given state root. ```bash -curl -X GET "http://localhost:5052/lighthouse/supply/0x7e76880eb67bbdc86250aa578958e9d0675e64e714337855204fb5abaaf82c2b" -H "accept: application/json" | jq +curl -X GET "http://localhost:5052/lighthouse/analysis/global_validator_supply/0x7e76880eb67bbdc86250aa578958e9d0675e64e714337855204fb5abaaf82c2b" -H "accept: application/json" | jq ``` ```json @@ -794,7 +794,7 @@ curl -X GET "http://localhost:5052/lighthouse/supply/0x7e76880eb67bbdc86250aa578 ``` The two boolean flags in the response are respectively: -* `execution_optimism`: whether the response was computed using optimistically synced data +* `execution_optimistic`: whether the response was computed using optimistically synced data * `finalized`: whether the response was computed over finalized dasta ## `/lighthouse/logs` From 9238b7312a4a2b57a9af7bcdd58e6dbf58a4369a Mon Sep 17 00:00:00 2001 From: Antoine Le Calvez Date: Thu, 27 Mar 2025 15:14:30 +0100 Subject: [PATCH 04/12] Fix YAML linting --- book/src/api_lighthouse.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/book/src/api_lighthouse.md b/book/src/api_lighthouse.md index 2bd4bbe0f70..6e112ceac09 100644 --- a/book/src/api_lighthouse.md +++ b/book/src/api_lighthouse.md @@ -794,8 +794,9 @@ curl -X GET "http://localhost:5052/lighthouse/analysis/global_validator_supply/0 ``` The two boolean flags in the response are respectively: -* `execution_optimistic`: whether the response was computed using optimistically synced data -* `finalized`: whether the response was computed over finalized dasta + +- `execution_optimistic`: whether the response was computed using optimistically synced data +- `finalized`: whether the response was computed over finalized dasta ## `/lighthouse/logs` From dd16ee2d084d8eab959298d93677a209df930cf8 Mon Sep 17 00:00:00 2001 From: Antoine Le Calvez Date: Thu, 27 Mar 2025 15:17:01 +0100 Subject: [PATCH 05/12] Check Markdown linting earlier in the test suite --- .github/workflows/test-suite.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index 817fd9524df..29fd40d6de5 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -348,6 +348,8 @@ jobs: bins: cargo-audit - name: Check formatting with cargo fmt run: make cargo-fmt + - name: Markdown-linter + run: make mdlint - name: Lint code for quality and style with Clippy run: make lint-full - name: Certify Cargo.lock freshness @@ -360,8 +362,6 @@ jobs: run: make audit-CI - name: Run cargo vendor to make sure dependencies can be vendored for packaging, reproducibility and archival purpose run: CARGO_HOME=$(readlink -f $HOME) make vendor - - name: Markdown-linter - run: make mdlint - name: Spell-check uses: rojopolis/spellcheck-github-actions@v0 check-msrv: From e4055402e9a4f324323055885f44f1e14ccfa55d Mon Sep 17 00:00:00 2001 From: Antoine Le Calvez Date: Thu, 27 Mar 2025 15:18:50 +0100 Subject: [PATCH 06/12] Fix typo --- book/src/api_lighthouse.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/api_lighthouse.md b/book/src/api_lighthouse.md index 6e112ceac09..8902b2a76bd 100644 --- a/book/src/api_lighthouse.md +++ b/book/src/api_lighthouse.md @@ -796,7 +796,7 @@ curl -X GET "http://localhost:5052/lighthouse/analysis/global_validator_supply/0 The two boolean flags in the response are respectively: - `execution_optimistic`: whether the response was computed using optimistically synced data -- `finalized`: whether the response was computed over finalized dasta +- `finalized`: whether the response was computed over finalized data ## `/lighthouse/logs` From 008b2924f160e95b5a8c4344bee38aaf3ee98006 Mon Sep 17 00:00:00 2001 From: Antoine Le Calvez Date: Fri, 25 Apr 2025 13:42:30 +0200 Subject: [PATCH 07/12] Add test, change output format to conform with other endpoints --- beacon_node/http_api/src/lib.rs | 17 ++++++++-------- beacon_node/http_api/tests/tests.rs | 31 +++++++++++++++++++++++++++++ book/src/api_lighthouse.md | 13 +++--------- common/eth2/src/lib.rs | 17 ++++++++++++++++ 4 files changed, 59 insertions(+), 19 deletions(-) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 14a9a4a99c7..99620a4add7 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -4527,18 +4527,17 @@ pub fn serve( task_spawner: TaskSpawner, chain: Arc>| { task_spawner.blocking_json_task(Priority::P1, move || { - state_id - .map_state_and_execution_optimistic_and_finalized( - &chain, - |state, execution_optimistic, finalized| { - Ok(( - state.balances().iter().sum::(), + state_id.map_state_and_execution_optimistic_and_finalized( + &chain, + |state, execution_optimistic, finalized| { + let response = state.balances().iter().sum::(); + Ok(api_types::GenericResponse::from(response) + .add_execution_optimistic_finalized( execution_optimistic, finalized, )) - }, - ) - .map(api_types::GenericResponse::from) + }, + ) }) }, ); diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 6d407d27421..7227b78a3ca 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -5740,6 +5740,35 @@ impl ApiTester { self } + pub async fn test_get_lighthouse_global_validator_supply(self) -> Self { + for state_id in self.interesting_state_ids() { + let state_opt = state_id + .state(&self.chain) + .ok() + .map(|(state, _execution_optimistic, _finalized)| state); + + let global_validator_supply = match state_opt.as_ref() { + Some(state) => Some(state.balances().iter().sum::()), + None => None, + }; + + let api_global_validator_supply = self + .client + .get_global_validator_supply(state_id.0) + .await + .unwrap() + .map(|res| res.data); + + assert_eq!( + global_validator_supply, api_global_validator_supply, + "{:?}", + state_id + ); + } + + self + } + pub async fn test_post_lighthouse_database_reconstruct(self) -> Self { let response = self .client @@ -7337,6 +7366,8 @@ async fn lighthouse_endpoints() { .test_post_lighthouse_liveness() .await .test_post_lighthouse_add_remove_peer() + .await + .test_get_lighthouse_global_validator_supply() .await; } diff --git a/book/src/api_lighthouse.md b/book/src/api_lighthouse.md index 8902b2a76bd..9018e65eebf 100644 --- a/book/src/api_lighthouse.md +++ b/book/src/api_lighthouse.md @@ -785,19 +785,12 @@ curl -X GET "http://localhost:5052/lighthouse/analysis/global_validator_supply/0 ```json { - "data": [ - 674144000000000, - false, - true - ] + "execution_optimistic": false, + "finalized": true, + "data": 674144000000000 } ``` -The two boolean flags in the response are respectively: - -- `execution_optimistic`: whether the response was computed using optimistically synced data -- `finalized`: whether the response was computed over finalized data - ## `/lighthouse/logs` This is a Server Side Event subscription endpoint. This allows a user to read diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index 1c55220b507..65a29e19490 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -2639,6 +2639,23 @@ impl BeaconNodeHttpClient { Ok(()) } + /// `GET lighthouse/analysis/global_validator_supply/{state_root}` + pub async fn get_global_validator_supply( + &self, + state_id: StateId, + ) -> Result>, Error> { + let mut path = self.server.full.clone(); + + path.path_segments_mut() + .map_err(|()| Error::InvalidUrl(self.server.clone()))? + .push("lighthouse") + .push("analysis") + .push("global_validator_supply") + .push(&state_id.to_string()); + + self.get_opt(path).await + } + /// `GET events?topics` pub async fn get_events( &self, From e2b34f7905f9dca9d3a7b012607cb241c7887927 Mon Sep 17 00:00:00 2001 From: Antoine Le Calvez Date: Fri, 25 Apr 2025 21:43:42 +0200 Subject: [PATCH 08/12] Fix linting issues --- beacon_node/http_api/tests/tests.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 7227b78a3ca..7baba03631f 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -5747,10 +5747,7 @@ impl ApiTester { .ok() .map(|(state, _execution_optimistic, _finalized)| state); - let global_validator_supply = match state_opt.as_ref() { - Some(state) => Some(state.balances().iter().sum::()), - None => None, - }; + let global_validator_supply = state_opt.as_ref().map(|state| state.balances().iter().sum::()); let api_global_validator_supply = self .client @@ -7340,7 +7337,7 @@ async fn post_validator_liveness_epoch() { } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn lighthouse_endpoints() { +async fn lighthouse_get_endpoints() { ApiTester::new() .await .test_get_lighthouse_health() @@ -7360,14 +7357,20 @@ async fn lighthouse_endpoints() { .test_get_lighthouse_eth1_deposit_cache() .await .test_get_lighthouse_staking() + .await + .test_get_lighthouse_global_validator_supply() + .await; +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn lighthouse_post_endpoints() { + ApiTester::new() .await .test_post_lighthouse_database_reconstruct() .await .test_post_lighthouse_liveness() .await .test_post_lighthouse_add_remove_peer() - .await - .test_get_lighthouse_global_validator_supply() .await; } From 79bc2ce117de3710bf806faf3982d9615a7f3c69 Mon Sep 17 00:00:00 2001 From: Antoine Le Calvez Date: Mon, 19 May 2025 10:55:19 +0200 Subject: [PATCH 09/12] Fix formatting --- beacon_node/http_api/tests/tests.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 5c0ba735988..ed0de41e42d 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -5850,7 +5850,9 @@ impl ApiTester { .ok() .map(|(state, _execution_optimistic, _finalized)| state); - let global_validator_supply = state_opt.as_ref().map(|state| state.balances().iter().sum::()); + let global_validator_supply = state_opt + .as_ref() + .map(|state| state.balances().iter().sum::()); let api_global_validator_supply = self .client From 8ed54f675cf457c6fe1f1c8ad75132e904d93989 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:15:12 -0700 Subject: [PATCH 10/12] rename global_validator_supply to total_supply --- beacon_node/http_api/src/lib.rs | 8 ++++---- beacon_node/http_api/tests/tests.rs | 12 ++++++------ book/src/api_lighthouse.md | 4 ++-- common/eth2/src/lib.rs | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index dae68a18644..6562aca57db 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -4697,10 +4697,10 @@ pub fn serve( }) }); - // GET lighthouse/analysis/global_validator_supply/{state_root} - let get_lighthouse_global_validator_supply = warp::path("lighthouse") + // GET lighthouse/analysis/total_supply/{state_root} + let get_lighthouse_total_supply = warp::path("lighthouse") .and(warp::path("analysis")) - .and(warp::path("global_validator_supply")) + .and(warp::path("total_supply")) .and(warp::path::param::()) .and(warp::path::end()) .and(task_spawner_filter.clone()) @@ -5002,7 +5002,7 @@ pub fn serve( .uor(get_lighthouse_staking) .uor(get_lighthouse_database_info) .uor(get_lighthouse_block_rewards) - .uor(get_lighthouse_global_validator_supply) + .uor(get_lighthouse_total_supply) .uor(get_lighthouse_attestation_performance) .uor(get_beacon_light_client_optimistic_update) .uor(get_beacon_light_client_finality_update) diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 6046a193e99..9ed5383252e 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -5989,26 +5989,26 @@ impl ApiTester { self } - pub async fn test_get_lighthouse_global_validator_supply(self) -> Self { + pub async fn test_get_lighthouse_total_supply(self) -> Self { for state_id in self.interesting_state_ids() { let state_opt = state_id .state(&self.chain) .ok() .map(|(state, _execution_optimistic, _finalized)| state); - let global_validator_supply = state_opt + let total_supply = state_opt .as_ref() .map(|state| state.balances().iter().sum::()); - let api_global_validator_supply = self + let api_total_supply = self .client - .get_global_validator_supply(state_id.0) + .get_total_supply(state_id.0) .await .unwrap() .map(|res| res.data); assert_eq!( - global_validator_supply, api_global_validator_supply, + total_supply, api_total_supply, "{:?}", state_id ); @@ -7736,7 +7736,7 @@ async fn lighthouse_get_endpoints() { .await .test_get_lighthouse_staking() .await - .test_get_lighthouse_global_validator_supply() + .test_get_lighthouse_total_supply() .await; } diff --git a/book/src/api_lighthouse.md b/book/src/api_lighthouse.md index 9018e65eebf..67cc43495cc 100644 --- a/book/src/api_lighthouse.md +++ b/book/src/api_lighthouse.md @@ -775,12 +775,12 @@ Caveats: This is because the state *prior* to the `start_epoch` needs to be loaded from the database, and loading a state on a boundary is most efficient. -## `lighthouse/analysis/global_validator_supply/{state_root}` +## `lighthouse/analysis/total_supply/{state_root}` Returns the sum of all validator balances for a given state root. ```bash -curl -X GET "http://localhost:5052/lighthouse/analysis/global_validator_supply/0x7e76880eb67bbdc86250aa578958e9d0675e64e714337855204fb5abaaf82c2b" -H "accept: application/json" | jq +curl -X GET "http://localhost:5052/lighthouse/analysis/total_supply/0x7e76880eb67bbdc86250aa578958e9d0675e64e714337855204fb5abaaf82c2b" -H "accept: application/json" | jq ``` ```json diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index 3507809c3a9..c43a2dd4b40 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -2704,8 +2704,8 @@ impl BeaconNodeHttpClient { Ok(()) } - /// `GET lighthouse/analysis/global_validator_supply/{state_root}` - pub async fn get_global_validator_supply( + /// `GET lighthouse/analysis/total_supply/{state_root}` + pub async fn get_total_supply( &self, state_id: StateId, ) -> Result>, Error> { @@ -2715,7 +2715,7 @@ impl BeaconNodeHttpClient { .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("lighthouse") .push("analysis") - .push("global_validator_supply") + .push("total_supply") .push(&state_id.to_string()); self.get_opt(path).await From ce3b7c4f0064b689e6332191e956882266c3a70f Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:19:17 -0700 Subject: [PATCH 11/12] fix: use expose_full() for private SensitiveUrl field --- common/eth2/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index aa444cd3f10..b1fd11bed93 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -2787,7 +2787,7 @@ impl BeaconNodeHttpClient { &self, state_id: StateId, ) -> Result>, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? From e5a941bfe76a37b8addef0dcb74f4a053a8e73a5 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Tue, 10 Feb 2026 00:44:09 -0700 Subject: [PATCH 12/12] fix formatting --- beacon_node/http_api/tests/tests.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index dcad4ce0798..d12fc50a9ec 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -6251,11 +6251,7 @@ impl ApiTester { .unwrap() .map(|res| res.data); - assert_eq!( - total_supply, api_total_supply, - "{:?}", - state_id - ); + assert_eq!(total_supply, api_total_supply, "{:?}", state_id); } self