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

feat: add config and messaging base of contracts #1

Merged
merged 18 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
scarb 2.5.0
scarb 2.4.4
starknet-foundry 0.16.0
53 changes: 50 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
<div align="center">
<img src="book/src/assets/piltover.png" height="300"/>

[![Check Workflow Status](https://github.com/keep-starknet-strange/piltover/actions/workflows/check.yml/badge.svg)](https://github.com/keep-starknet-strange/piltover/actions/workflows/check.yml)
[![Build Workflow Status](https://github.com/keep-starknet-strange/piltover/actions/workflows/build.yml/badge.svg)](https://github.com/keep-starknet-strange/piltover/actions/workflows/build.yml)
[![Check Workflow Status](https://github.com/keep-starknet-strange/piltover/actions/workflows/check.yml/badge.svg)](https://github.com/keep-starknet-strange/piltover/actions/workflows/check.yml)
[![Build Workflow Status](https://github.com/keep-starknet-strange/piltover/actions/workflows/build.yml/badge.svg)](https://github.com/keep-starknet-strange/piltover/actions/workflows/build.yml)

[![Exploration_Team](https://img.shields.io/badge/Exploration_Team-29296E.svg?&style=for-the-badge&logo=data:image/svg%2bxml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIGlkPSJhIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxODEgMTgxIj48ZGVmcz48c3R5bGU+LmJ7ZmlsbDojZmZmO308L3N0eWxlPjwvZGVmcz48cGF0aCBjbGFzcz0iYiIgZD0iTTE3Ni43Niw4OC4xOGwtMzYtMzcuNDNjLTEuMzMtMS40OC0zLjQxLTIuMDQtNS4zMS0xLjQybC0xMC42MiwyLjk4LTEyLjk1LDMuNjNoLjc4YzUuMTQtNC41Nyw5LjktOS41NSwxNC4yNS0xNC44OSwxLjY4LTEuNjgsMS44MS0yLjcyLDAtNC4yN0w5Mi40NSwuNzZxLTEuOTQtMS4wNC00LjAxLC4xM2MtMTIuMDQsMTIuNDMtMjMuODMsMjQuNzQtMzYsMzcuNjktMS4yLDEuNDUtMS41LDMuNDQtLjc4LDUuMThsNC4yNywxNi41OGMwLDIuNzIsMS40Miw1LjU3LDIuMDcsOC4yOS00LjczLTUuNjEtOS43NC0xMC45Ny0xNS4wMi0xNi4wNi0xLjY4LTEuODEtMi41OS0xLjgxLTQuNCwwTDQuMzksODguMDVjLTEuNjgsMi4zMy0xLjgxLDIuMzMsMCw0LjUzbDM1Ljg3LDM3LjNjMS4zNiwxLjUzLDMuNSwyLjEsNS40NCwxLjQybDExLjQtMy4xMSwxMi45NS0zLjYzdi45MWMtNS4yOSw0LjE3LTEwLjIyLDguNzYtMTQuNzYsMTMuNzNxLTMuNjMsMi45OC0uNzgsNS4zMWwzMy40MSwzNC44NGMyLjIsMi4yLDIuOTgsMi4yLDUuMTgsMGwzNS40OC0zNy4xN2MxLjU5LTEuMzgsMi4xNi0zLjYsMS40Mi01LjU3LTEuNjgtNi4wOS0zLjI0LTEyLjMtNC43OS0xOC4zOS0uNzQtMi4yNy0xLjIyLTQuNjItMS40Mi02Ljk5LDQuMyw1LjkzLDkuMDcsMTEuNTIsMTQuMjUsMTYuNzEsMS42OCwxLjY4LDIuNzIsMS42OCw0LjQsMGwzNC4zMi0zNS43NHExLjU1LTEuODEsMC00LjAxWm0tNzIuMjYsMTUuMTVjLTMuMTEtLjc4LTYuMDktMS41NS05LjE5LTIuNTktMS43OC0uMzQtMy42MSwuMy00Ljc5LDEuNjhsLTEyLjk1LDEzLjg2Yy0uNzYsLjg1LTEuNDUsMS43Ni0yLjA3LDIuNzJoLS42NWMxLjMtNS4zMSwyLjcyLTEwLjYyLDQuMDEtMTUuOGwxLjY4LTYuNzNjLjg0LTIuMTgsLjE1LTQuNjUtMS42OC02LjA5bC0xMi45NS0xNC4xMmMtLjY0LS40NS0xLjE0LTEuMDgtMS40Mi0xLjgxbDE5LjA0LDUuMTgsMi41OSwuNzhjMi4wNCwuNzYsNC4zMywuMTQsNS43LTEuNTVsMTIuOTUtMTQuMzhzLjc4LTEuMDQsMS42OC0xLjE3Yy0xLjgxLDYuNi0yLjk4LDE0LjEyLTUuNDQsMjAuNDYtMS4wOCwyLjk2LS4wOCw2LjI4LDIuNDYsOC4xNiw0LjI3LDQuMTQsOC4yOSw4LjU1LDEyLjk1LDEyLjk1LDAsMCwxLjMsLjkxLDEuNDIsMi4wN2wtMTMuMzQtMy42M1oiLz48L3N2Zz4=)](https://github.com/keep-starknet-strange)
[![Exploration_Team](https://img.shields.io/badge/Exploration_Team-29296E.svg?&style=for-the-badge&logo=data:image/svg%2bxml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIGlkPSJhIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxODEgMTgxIj48ZGVmcz48c3R5bGU+LmJ7ZmlsbDojZmZmO308L3N0eWxlPjwvZGVmcz48cGF0aCBjbGFzcz0iYiIgZD0iTTE3Ni43Niw4OC4xOGwtMzYtMzcuNDNjLTEuMzMtMS40OC0zLjQxLTIuMDQtNS4zMS0xLjQybC0xMC42MiwyLjk4LTEyLjk1LDMuNjNoLjc4YzUuMTQtNC41Nyw5LjktOS41NSwxNC4yNS0xNC44OSwxLjY4LTEuNjgsMS44MS0yLjcyLDAtNC4yN0w5Mi40NSwuNzZxLTEuOTQtMS4wNC00LjAxLC4xM2MtMTIuMDQsMTIuNDMtMjMuODMsMjQuNzQtMzYsMzcuNjktMS4yLDEuNDUtMS41LDMuNDQtLjc4LDUuMThsNC4yNywxNi41OGMwLDIuNzIsMS40Miw1LjU3LDIuMDcsOC4yOS00LjczLTUuNjEtOS43NC0xMC45Ny0xNS4wMi0xNi4wNi0xLjY4LTEuODEtMi41OS0xLjgxLTQuNCwwTDQuMzksODguMDVjLTEuNjgsMi4zMy0xLjgxLDIuMzMsMCw0LjUzbDM1Ljg3LDM3LjNjMS4zNiwxLjUzLDMuNSwyLjEsNS40NCwxLjQybDExLjQtMy4xMSwxMi45NS0zLjYzdi45MWMtNS4yOSw0LjE3LTEwLjIyLDguNzYtMTQuNzYsMTMuNzNxLTMuNjMsMi45OC0uNzgsNS4zMWwzMy40MSwzNC44NGMyLjIsMi4yLDIuOTgsMi4yLDUuMTgsMGwzNS40OC0zNy4xN2MxLjU5LTEuMzgsMi4xNi0zLjYsMS40Mi01LjU3LTEuNjgtNi4wOS0zLjI0LTEyLjMtNC43OS0xOC4zOS0uNzQtMi4yNy0xLjIyLTQuNjItMS40Mi02Ljk5LDQuMyw1LjkzLDkuMDcsMTEuNTIsMTQuMjUsMTYuNzEsMS42OCwxLjY4LDIuNzIsMS42OCw0LjQsMGwzNC4zMi0zNS43NHExLjU1LTEuODEsMC00LjAxWm0tNzIuMjYsMTUuMTVjLTMuMTEtLjc4LTYuMDktMS41NS05LjE5LTIuNTktMS43OC0uMzQtMy42MSwuMy00Ljc5LDEuNjhsLTEyLjk1LDEzLjg2Yy0uNzYsLjg1LTEuNDUsMS43Ni0yLjA3LDIuNzJoLS42NWMxLjMtNS4zMSwyLjcyLTEwLjYyLDQuMDEtMTUuOGwxLjY4LTYuNzNjLjg0LTIuMTgsLjE1LTQuNjUtMS42OC02LjA5bC0xMi45NS0xNC4xMmMtLjY0LS40NS0xLjE0LTEuMDgtMS40Mi0xLjgxbDE5LjA0LDUuMTgsMi41OSwuNzhjMi4wNCwuNzYsNC4zMywuMTQsNS43LTEuNTVsMTIuOTUtMTQuMzhzLjc4LTEuMDQsMS42OC0xLjE3Yy0xLjgxLDYuNi0yLjk4LDE0LjEyLTUuNDQsMjAuNDYtMS4wOCwyLjk2LS4wOCw2LjI4LDIuNDYsOC4xNiw0LjI3LDQuMTQsOC4yOSw4LjU1LDEyLjk1LDEyLjk1LDAsMCwxLjMsLjkxLDEuNDIsMi4wN2wtMTMuMzQtMy42M1oiLz48L3N2Zz4=)](https://github.com/keep-starknet-strange)

</div>

## Overview

Starknet Core Contract components in Cairo.

## Repository architecture

The different tasks of the Starknet Core Contract are here mapped to what we can call an "Appchain" Core Contract.

The functionalities of the core contract are split over several components. Each component is usually placed into a directory, event if it is simple. Doing so allow a good separation of additional files that may be written in the context of a component.

Due to a limitation of `starknet foundry`, we can't declare a contract that is defined under the `tests` directory. For this reason, `mock` contract are defined in their respective component location. This is also a good way for the component write to illustrate the minimum required to use the component.

- `appchain.cairo`: core contract of the appchain on Starknet.

- `config`: base configuration for the core contract.

- `messaging`: messaging between Appchain - Starknet.

## Build

To build the project, run:
Expand All @@ -27,3 +41,36 @@ To test the project, run:
```bash
snforge test
```

## Code style (cairo)

- Use `snake_case` for module name and not `PascalCase`.

- Don't import directly a function from a module.

```rust
// Prefer that:
let addr = starknet::contract_address_const::<0>();

// Instead of:
use starknet::contract_address_const;
let addr = contract_address_const::<0>();
```

- Order import as much as you can (and avoid relative import, if not inside tests).
drspacemn marked this conversation as resolved.
Show resolved Hide resolved

```rust
// Core or built-in plugins like starknet.
use starknet::...;

// Third party modules
use openzeppelin::...;

// Package
use piltover::...;

// Relative
super::...;
```

- Document functions inside the trait, and add details if needed in the implementation.
6 changes: 6 additions & 0 deletions Scarb.lock
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "openzeppelin"
version = "0.8.1"
source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.8.1#5c7a022f333dca721fe490c2191a811fa2566a89"

[[package]]
name = "piltover"
version = "0.1.0"
dependencies = [
"openzeppelin",
"snforge_std",
]

Expand Down
4 changes: 3 additions & 1 deletion Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ edition = "2023_10"

[dependencies]
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.16.0" }
starknet = "2.5.0"
starknet = "=2.4.4"
openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.8.1" }

drspacemn marked this conversation as resolved.
Show resolved Hide resolved
[[target.starknet-contract]]
casm = true
sierra = true
51 changes: 51 additions & 0 deletions src/appchain.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//! SPDX-License-Identifier: MIT
//!
//!

mod errors {
const INVALID_ADDRESS: felt252 = 'Config: invalid address';
}

/// Appchain settlement contract on starknet.
#[starknet::contract]
mod appchain {
use starknet::ContractAddress;
use openzeppelin::access::ownable::{OwnableComponent as ownable_cpt, interface::IOwnable};

use piltover::config::{config_cpt, config_cpt::InternalTrait as ConfigInternal, IConfig};

component!(path: ownable_cpt, storage: ownable, event: OwnableEvent);
component!(path: config_cpt, storage: config, event: ConfigEvent);

#[abi(embed_v0)]
impl ConfigImpl = config_cpt::ConfigImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
ownable: ownable_cpt::Storage,
#[substorage(v0)]
config: config_cpt::Storage,
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
OwnableEvent: ownable_cpt::Event,
#[flat]
ConfigEvent: config_cpt::Event,
}

/// Initializes the contract.
///
/// # Arguments
///
/// * `address` - The contract address of the owner.
#[constructor]
fn constructor(ref self: ContractState, owner: ContractAddress) {
self.ownable.transfer_ownership(owner);
drspacemn marked this conversation as resolved.
Show resolved Hide resolved

assert(self.config.is_owner_or_operator(owner), 'bad');
}
}
102 changes: 102 additions & 0 deletions src/config/component.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//! SPDX-License-Identifier: MIT
//!
//! Base configuration for appchain contract.

/// Errors.
mod errors {
const INVALID_CALLER: felt252 = 'Config: not owner or operator';
}

/// Configuration component.
///
/// Depends on `ownable` to ensure the configuration is
/// only editable by contract's owner.
#[starknet::component]
mod config_cpt {
use starknet::ContractAddress;

use openzeppelin::access::ownable::{
OwnableComponent as ownable_cpt, OwnableComponent::InternalTrait as OwnableInternal,
interface::IOwnable,
};

use piltover::config::interface::IConfig;

use super::errors;

#[storage]
struct Storage {
/// Appchain operator that is allowed to update the state.
operator: ContractAddress,
drspacemn marked this conversation as resolved.
Show resolved Hide resolved
/// Program info (StarknetOS), with program hash and config hash.
program_info: (felt252, felt252),
/// Facts registry contract address.
facts_registry: ContractAddress,
drspacemn marked this conversation as resolved.
Show resolved Hide resolved
}

#[embeddable_as(ConfigImpl)]
impl Config<
TContractState,
+HasComponent<TContractState>,
impl Ownable: ownable_cpt::HasComponent<TContractState>,
+Drop<TContractState>
> of IConfig<ComponentState<TContractState>> {
fn set_operator(ref self: ComponentState<TContractState>, address: ContractAddress) {
get_dep_component!(self, Ownable).assert_only_owner();
self.operator.write(address)
}

fn get_operator(self: @ComponentState<TContractState>) -> ContractAddress {
self.operator.read()
}

fn set_program_info(
ref self: ComponentState<TContractState>, program_hash: felt252, config_hash: felt252
) {
self.assert_only_owner_or_operator();
self.program_info.write((program_hash, config_hash))
}

fn get_program_info(self: @ComponentState<TContractState>) -> (felt252, felt252) {
self.program_info.read()
}

fn set_facts_registry(ref self: ComponentState<TContractState>, address: ContractAddress) {
self.assert_only_owner_or_operator();
self.facts_registry.write(address)
}

fn get_facts_registry(self: @ComponentState<TContractState>) -> ContractAddress {
self.facts_registry.read()
}
}

#[generate_trait]
impl InternalImpl<
TContractState,
+HasComponent<TContractState>,
impl Ownable: ownable_cpt::HasComponent<TContractState>,
+Drop<TContractState>
> of InternalTrait<TContractState> {
/// Asserts if the caller is the owner of the contract or
/// the authorized operator. Reverts otherwise.
fn assert_only_owner_or_operator(ref self: ComponentState<TContractState>) {
assert(
self.is_owner_or_operator(starknet::get_caller_address()), errors::INVALID_CALLER
);
}

/// Verifies if the given address is the owner of the contract
/// or the authorized operator.
///
/// # Arguments
///
/// * `address` - The contrat address to verify.
fn is_owner_or_operator(
ref self: ComponentState<TContractState>, address: ContractAddress
) -> bool {
let owner = get_dep_component!(self, Ownable).owner();
address == owner || address == self.operator.read()
}
}
}
53 changes: 53 additions & 0 deletions src/config/interface.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//! SPDX-License-Identifier: MIT
//!
//! Interface for appchain settlement contract configuration.
use starknet::ContractAddress;

#[starknet::interface]
trait IConfig<T> {
/// Sets the operator that is in charge to push state updates.
///
/// # Arguments
///
/// * `address` - The operator account address.
fn set_operator(ref self: T, address: ContractAddress);

/// Gets the operator address.
///
/// # Returns
///
/// The operator's address.
fn get_operator(self: @T) -> ContractAddress;

/// Sets the information of the program that generates the
/// state transition trace (namely StarknetOS).
///
/// # Arguments
///
/// * `program_hash` - The program hash.
/// * `config_hash` - The program's config hash.
fn set_program_info(ref self: T, program_hash: felt252, config_hash: felt252);

/// Gets the information of the program that generates the
/// state transition trace (namely StarknetOS).
///
/// # Returns
///
/// The program hash and it's configuration hash.
fn get_program_info(self: @T) -> (felt252, felt252);

/// Sets the facts registry contract address, which is already
/// initialized with the verifier information.
///
/// # Arguments
///
/// * `address` - The facts registry contract's address.
fn set_facts_registry(ref self: T, address: ContractAddress);

/// Gets the facts registry contract address.
///
/// # Returns
///
/// The contract address of the facts registry.
fn get_facts_registry(self: @T) -> ContractAddress;
}
38 changes: 38 additions & 0 deletions src/config/mock.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#[starknet::contract]
mod config_mock {
use starknet::ContractAddress;

use openzeppelin::access::ownable::{
OwnableComponent as ownable_cpt, OwnableComponent::InternalTrait as OwnableInternal
};

use piltover::config::{config_cpt, config_cpt::InternalTrait as ConfigInternal, IConfig};

component!(path: ownable_cpt, storage: ownable, event: OwnableEvent);
component!(path: config_cpt, storage: config, event: ConfigEvent);

#[abi(embed_v0)]
impl ConfigImpl = config_cpt::ConfigImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
ownable: ownable_cpt::Storage,
#[substorage(v0)]
config: config_cpt::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
OwnableEvent: ownable_cpt::Event,
#[flat]
ConfigEvent: config_cpt::Event
}

#[constructor]
fn constructor(ref self: ContractState, owner: ContractAddress) {
self.ownable.initializer(owner);
}
}
Loading