diff --git a/docker/hive/Dockerfile b/docker/hive/Dockerfile new file mode 100644 index 000000000..6b100f894 --- /dev/null +++ b/docker/hive/Dockerfile @@ -0,0 +1,112 @@ +# trunk-ignore-all(terrascan/AC_DOCKER_0047) + +# CairoVM Chain +FROM ghcr.io/dojoengine/dojo:v0.5.1 as katana + +# Indexer service +### Apibara DNA indexer and indexer +FROM quay.io/apibara/starknet:1.2.0 as apibara +FROM quay.io/apibara/sink-mongo as indexer +FROM debian:bookworm as apibara-build +RUN apt-get update && apt-get install -y patchelf && rm -rf /var/lib/apt/lists/* +# Run `docker image inspect apibara/starknet:1.2.0` to get the exact path +COPY --from=apibara /nix/store/swzxd9vbhhj92qzh16j48vb011q57v59-apibara-starknet-1.2.0/bin/apibara-starknet /usr/local/bin/starknet +COPY --from=indexer /nix/store/3iqnrcirqpg4s7zdy1wdh0dq17jwzmlc-apibara-sink-mongo-0.5.3/bin/apibara-sink-mongo /usr/local/bin/sink-mongo +# Change the interpreter path. +ARG BUILDPLATFORM +RUN case $BUILDPLATFORM in \ + "linux/amd64") \ + patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 /usr/local/bin/starknet && \ + patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 /usr/local/bin/sink-mongo; \ + ;; \ + "linux/arm64") \ + patchelf --set-interpreter /lib/ld-linux-aarch64.so.1 /usr/local/bin/starknet && \ + patchelf --set-interpreter /lib/ld-linux-aarch64.so.1 /usr/local/bin/sink-mongo; \ + ;; \ + *) \ + echo "Unknown BUILDPLATFORM: $BUILDPLATFORM"; \ + exit 1; \ + ;; \ + esac + +### Indexer transform plugin +#### First, clone the indexer repository +FROM docker.io/alpine/git:latest as indexer-cloner +WORKDIR /code +RUN git clone -v "https://github.com/kkrt-labs/kakarot-indexer.git" + +#### MongoDB +FROM mongo:6.0.8 as mongo + +# Ethereum RPC Server +FROM ghcr.io/kkrt-labs/kakarot-rpc/node:v0.2.3-2 as rpc + +FROM debian:bookworm-slim as base +# Install any necessary dependencies +RUN apt-get update && apt-get install -y tini curl jq && rm -rf /var/lib/apt/lists/* + +# Environment +############# +### Indexer environment variables +#### Indexer environment variables +ENV ALLOW_ENV_FROM_ENV=DEBUG,APIBARA_AUTH_TOKEN,STARTING_BLOCK,STREAM_URL,SINK_TYPE,MONGO_CONNECTION_STRING,MONGO_DATABASE_NAME +ENV DEBUG="" +ENV APIBARA_AUTH_TOKEN="" +ENV MONGO_CONNECTION_STRING=mongodb://localhost:27017 +ENV MONGO_DATABASE_NAME=kakarot-local +ENV STARTING_BLOCK=0 +ENV STREAM_URL=http://localhost:7171 +ENV SINK_TYPE=mongo +### Kakarot RPC environment variables +### Port 8545: https://github.com/ethereum/hive/blob/master/simulators/ethereum/rpc/helper.go#L50 +ENV KATANA_ACCOUNT_ADDRESS=0x01b44e32c8edbcada35fa7ae6f1f44bbc16657f442b7b19286812e1976c34d83 +ENV KATANA_PRIVATE_KEY=0x05b8e27acc53020ed612507af8245cbffafd8aa588b79decaee1acf99a1c5cc2 +ENV KAKAROT_RPC_URL=0.0.0.0:8545 +ENV STARKNET_NETWORK=http://localhost:5050 +ENV RUST_LOG=kakarot_rpc=info + +HEALTHCHECK --interval=10s --timeout=10s --start-period=15s --retries=5 \ + CMD response=$(curl --silent --request POST \ + --header "Content-Type: application/json" \ + --data '{"jsonrpc": "2.0", "method": "eth_getBlockByNumber", "params": ["latest", true], "id": 1}' \ + http://${KAKAROT_RPC_URL} | jq -e '.result != null') && echo $response && [ "$response" = "true" ] || exit 1 + + +# Ports +####### +# 8545 Ethereum RPC +# 27017 MongoDB +EXPOSE 8545 27017 + +# Copy binaries and dependencies +################################ +### CairoVM chain service +COPY --from=katana /usr/local/bin/katana /usr/local/bin + +### Indexer service +#### We need the DNA indexer binary +COPY --from=apibara-build /usr/local/bin/starknet /usr/local/bin/starknet + +#### We need the indexer typescript code and the binary that knows how to run it +COPY --from=indexer-cloner /code /usr/src/app/code +COPY --from=apibara-build /usr/local/bin/sink-mongo /usr/local/bin/sink-mongo + +#### We need the mongo binary +COPY --from=mongo /bin/mongod /usr/local/bin +RUN mkdir -p /data/db + + +# Generate the genesis +COPY --from=rpc /usr/local/bin/kakarot-rpc-hive /usr/local/bin/kakarot-rpc +COPY --from=rpc /usr/local/bin/hive_genesis /usr/local/bin +RUN mkdir -p /genesis/contracts +COPY ./lib/kakarot/build /genesis/contracts +COPY ./docker/hive/genesis.json /genesis/hive-genesis.json + +# Copy start script +COPY docker/hive/start.sh /start.sh +RUN chmod +x /start.sh + +ENTRYPOINT ["/usr/bin/tini", "--", "/start.sh"] + +CMD [] diff --git a/docker/hive/docs/hive_strategy.png b/docker/hive/docs/hive_strategy.png new file mode 100644 index 000000000..4add1c897 Binary files /dev/null and b/docker/hive/docs/hive_strategy.png differ diff --git a/docker/hive/genesis.json b/docker/hive/genesis.json new file mode 100644 index 000000000..9af819f46 --- /dev/null +++ b/docker/hive/genesis.json @@ -0,0 +1,45 @@ +{ + "config": { + "chainId": 7, + "homesteadBlock": 0, + "eip150Block": 0, + "eip150Hash": "0x5de1ee4135274003348e80b788e5afa4b18b18d320a5622218d5c493fedf5689", + "eip155Block": 0, + "eip158Block": 0 + }, + "coinbase": "0x0000000000000000000000000000000000000000", + "difficulty": "0x20000", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x2fefd8", + "nonce": "0x0000000000000000", + "timestamp": "0x1234", + "alloc": { + "cf49fda3be353c69b41ed96333cd24302da4556f": { + "balance": "0x123450000000000000000" + }, + "0161e041aad467a890839d5b08b138c1e6373072": { + "balance": "0x123450000000000000000" + }, + "87da6a8c6e9eff15d703fc2773e32f6af8dbe301": { + "balance": "0x123450000000000000000" + }, + "b97de4b8c857e4f6bc354f226dc3249aaee49209": { + "balance": "0x123450000000000000000" + }, + "c5065c9eeebe6df2c2284d046bfc906501846c51": { + "balance": "0x123450000000000000000" + }, + "0000000000000000000000000000000000000314": { + "balance": "0x0", + "code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x1234", + "0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9": "0x01" + } + }, + "0000000000000000000000000000000000000315": { + "balance": "0x9999999999999999999999999999999", + "code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef2769ca1461003e575b610000565b3461000057610078600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061007a565b005b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051809050600060405180830381858888f1935050505015610106578173ffffffffffffffffffffffffffffffffffffffff167f30a3c50752f2552dcc2b93f5b96866280816a986c0c0408cb6778b9fa198288f826040518082815260200191505060405180910390a25b5b50505600a165627a7a72305820637991fabcc8abad4294bf2bb615db78fbec4edff1635a2647d3894e2daf6a610029" + } + } +} diff --git a/docker/hive/start.sh b/docker/hive/start.sh new file mode 100644 index 000000000..39562ff5c --- /dev/null +++ b/docker/hive/start.sh @@ -0,0 +1,51 @@ +# 1. Create the genesis file +echo "Creating the genesis file..." +KAKAROT_CONTRACTS_PATH="genesis/contracts" \ + HIVE_GENESIS_PATH="genesis/hive-genesis.json" \ + GENESIS_OUTPUT="genesis.json" \ + MANIFEST_OUTPUT="manifest.json" \ + hive_genesis +mv /genesis/hive-genesis.json /hive-genesis.json && rm -fr /genesis + +# 2. Start Katana +echo "Launching Katana..." +RUST_LOG=warn katana --block-time 2000 --disable-fee --chain-id=0x$(jq -r '.config.chainId' hive-genesis.json) --genesis genesis.json & +###### 2.5. Await Katana to be healthy +# Loop until the curl command succeeds +until + curl --silent --request POST \ + --header "Content-Type: application/json" \ + --data '{ + "jsonrpc": "2.0", + "method": "starknet_blockNumber", + "params": [], + "id": 1 + }' \ + "${STARKNET_NETWORK}" # Use the provided network address +do + echo "Waiting for Katana to start..." + sleep 1 +done + +# 3. Start the Indexer service: DNA Indexer, Indexer transformer, and MongoDB +## MongoDB +echo "Launching mongo..." +mongod --bind_ip 0.0.0.0 --noauth & +## DNA +echo "Launching DNA..." +starknet start --rpc=http://localhost:5050 --wait-for-rpc --head-refresh-interval-ms=500 --data=/data & +# ## Indexer +echo "Launching indexer..." +sink-mongo run /usr/src/app/code/kakarot-indexer/src/main.ts & + +### 3.5. Await the Indexer to be healthy +echo "Waiting for the indexer to start..." +sleep 3 + +# 4. Start the Kakarot RPC service +echo "Launching Kakarot RPC..." +export PROXY_ACCOUNT_CLASS_HASH=$(jq -r '.declarations.proxy' manifest.json) +export CONTRACT_ACCOUNT_CLASS_HASH=$(jq -r '.declarations.contract_account' manifest.json) +export EXTERNALLY_OWNED_ACCOUNT_CLASS_HASH=$(jq -r '.declarations.externally_owned_account' manifest.json) +export KAKAROT_ADDRESS=$(jq -r '.deployments.kakarot_address' manifest.json) +kakarot-rpc diff --git a/src/eth_provider/constant.rs b/src/eth_provider/constant.rs index 11382c3db..be71acad7 100644 --- a/src/eth_provider/constant.rs +++ b/src/eth_provider/constant.rs @@ -33,7 +33,7 @@ lazy_static! { )), FieldElement::from_str(&var("KATANA_ACCOUNT_ADDRESS").expect("Missing deployer address")) .expect("Failed to parse deployer address"), - *CHAIN_ID.get().expect("Missing chain ID"), + *CHAIN_ID.get().expect("Failed to get chain id"), ExecutionEncoding::New ); pub static ref DEPLOY_WALLET_NONCE: Arc> = Arc::new(Mutex::new(FieldElement::ZERO)); diff --git a/src/eth_provider/provider.rs b/src/eth_provider/provider.rs index 0cfd396e0..fb00b6ce6 100644 --- a/src/eth_provider/provider.rs +++ b/src/eth_provider/provider.rs @@ -456,7 +456,7 @@ where // If the contract is not found, we need to deploy it. #[cfg(feature = "hive")] { - use crate::eth_provider::constant::{CHAIN_ID, DEPLOY_WALLET, DEPLOY_WALLET_NONCE}; + use crate::eth_provider::constant::{DEPLOY_WALLET, DEPLOY_WALLET_NONCE}; use starknet::accounts::Call; use starknet::accounts::Execution; use starknet::core::types::BlockTag; @@ -467,8 +467,6 @@ where proxy.get_implementation().block_id(StarknetBlockId::Tag(BlockTag::Latest)).call().await; if contract_not_found(&maybe_class_hash) { - let chain_id = self.starknet_provider.chain_id().await?; - let _ = CHAIN_ID.set(chain_id); let execution = Execution::new( vec![Call { to: *KAKAROT_ADDRESS, diff --git a/src/eth_rpc/servers/eth_rpc.rs b/src/eth_rpc/servers/eth_rpc.rs index 3e7e998f3..3f47cb2c4 100644 --- a/src/eth_rpc/servers/eth_rpc.rs +++ b/src/eth_rpc/servers/eth_rpc.rs @@ -40,7 +40,7 @@ where #[tracing::instrument(skip_all, ret, err)] async fn syncing(&self) -> Result { - Ok(self.syncing().await?) + Ok(self.eth_provider.syncing().await?) } async fn coinbase(&self) -> Result
{ diff --git a/src/main.rs b/src/main.rs index 0c9696336..40a8e865f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,8 +53,16 @@ async fn main() -> Result<()> { // Get the deployer nonce and set the value in the DEPLOY_WALLET_NONCE #[cfg(feature = "hive")] { - use kakarot_rpc::eth_provider::constant::{DEPLOY_WALLET, DEPLOY_WALLET_NONCE}; + use kakarot_rpc::eth_provider::constant::{CHAIN_ID, DEPLOY_WALLET, DEPLOY_WALLET_NONCE}; use starknet::accounts::ConnectedAccount; + use starknet::providers::Provider; + let provider = JsonRpcClient::new(HttpTransport::new( + starknet_config.network.provider_url().expect("Incorrect provider URL"), + )); + + let chain_id = provider.chain_id().await?; + CHAIN_ID.set(chain_id).expect("Failed to set chain id"); + let deployer_nonce = DEPLOY_WALLET.get_nonce().await?; let mut nonce = DEPLOY_WALLET_NONCE.lock().await; *nonce = deployer_nonce; diff --git a/tests/eth_provider.rs b/tests/eth_provider.rs index cc4c633d4..978a42793 100644 --- a/tests/eth_provider.rs +++ b/tests/eth_provider.rs @@ -265,6 +265,7 @@ async fn test_fee_history(#[future] katana: Katana, _setup: ()) { #[cfg(feature = "hive")] async fn test_predeploy_eoa(#[future] katana: Katana, _setup: ()) { use futures::future::join_all; + use kakarot_rpc::eth_provider::constant::CHAIN_ID; use kakarot_rpc::test_utils::eoa::KakarotEOA; use reth_primitives::B256; use starknet::providers::Provider; @@ -275,6 +276,8 @@ async fn test_predeploy_eoa(#[future] katana: Katana, _setup: ()) { let starknet_provider = eth_provider.starknet_provider(); let other_eoa_1 = KakarotEOA::new(B256::from_str(&format!("0x{:0>64}", "0abde1")).unwrap(), eth_provider.clone()); let other_eoa_2 = KakarotEOA::new(B256::from_str(&format!("0x{:0>64}", "0abde2")).unwrap(), eth_provider.clone()); + let chain_id = starknet_provider.chain_id().await.unwrap(); + CHAIN_ID.set(chain_id).expect("Failed to set chain id"); let evm_address = eoa.evm_address().unwrap(); let balance_before = eth_provider.balance(eoa.evm_address().unwrap(), None).await.unwrap();