Skip to content

Commit

Permalink
Merge pull request #7 from Laguna-Chain/seal-code
Browse files Browse the repository at this point in the history
Added system-contract which exposes missing seal-apis to solidity contracts
  • Loading branch information
realnimish authored Oct 20, 2022
2 parents dbf7b30 + 2670425 commit d628bd3
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ jobs:
run: |
docker run --rm -v $(pwd)/contracts:/mounted --entrypoint /bin/bash us-docker.pkg.dev/laguna-chain/laguna-chain/solang:ink_metadata -c "solang compile --target substrate /mounted/solidity/test/*.sol -o /mounted/"
docker run --rm -v $(pwd)/contracts:/mounted --entrypoint /bin/bash us-docker.pkg.dev/laguna-chain/laguna-chain/solang:ink_metadata -c "solang compile --target substrate /mounted/solidity/*.sol -o /mounted/"
docker run --rm -v $(pwd)/contracts:/mounted --entrypoint /bin/bash us-docker.pkg.dev/laguna-chain/laguna-chain/solang:ink_metadata -c "solang compile --target substrate /mounted/example-system-contracts/*.sol -o /mounted/"
- name: start laguna-chain:devnet
run: docker run -d -p 9944:9944 us-docker.pkg.dev/laguna-chain/laguna-chain/laguna-chain:0.1.1 --dev --ws-external
- name: Run tests
Expand Down
32 changes: 32 additions & 0 deletions contracts/example-system-contracts/TestSpecialEnvFn.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
import "../system-contracts/special_env_fn/interface.sol";

contract SpecialEnvFn{
special_env_fn sys_contract;

constructor(address sys_contract_addr) {
sys_contract = special_env_fn(sys_contract_addr);
}

function is_contract(address account) external view returns(bool) {
return sys_contract.is_contract(account);
}

function code_hash(address account) external view returns(bool, bytes32) {
return sys_contract.code_hash(account);
}

function own_code_hash() external view returns(bytes32) {
return sys_contract.own_code_hash();
}

function ecdsa_to_eth_address(uint8[33] calldata pubkey) external view returns(bool, bytes20) {
return sys_contract.ecdsa_to_eth_address(pubkey);
}

function ecdsa_recover(uint8[65] calldata signature, bytes32 message_hash) external view returns(bool, uint8[33] memory) {
return sys_contract.ecdsa_recover(signature, message_hash);
}
}
9 changes: 9 additions & 0 deletions contracts/system-contracts/special_env_fn/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Ignore build artifacts from the local tests sub-crate.
/target/

# Ignore backup files creates by cargo fmt.
**/*.rs.bk

# Remove Cargo.lock when creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
Cargo.lock
36 changes: 36 additions & 0 deletions contracts/system-contracts/special_env_fn/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
name = "special_env_fn"
version = "3.3.1"
authors = ["[your_name] <[your_email]>"]
edition = "2021"

[dependencies]
ink_primitives = { version = "3.3", default-features = false }
ink_metadata = { version = "3.3", default-features = false, features = ["derive"], optional = true }
ink_env = { version = "3.3", default-features = false }
ink_storage = { version = "3.3", default-features = false }
ink_lang = { version = "3.3", default-features = false }

scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2", default-features = false, features = ["derive"], optional = true }

[lib]
name = "special_env_fn"
path = "lib.rs"
crate-type = [
# Used for normal contract Wasm blobs.
"cdylib",
"rlib",
]

[features]
default = ["std"]
std = [
"ink_metadata/std",
"ink_env/std",
"ink_storage/std",
"ink_primitives/std",
"scale/std",
"scale-info/std",
]
ink-as-dependency = []
22 changes: 22 additions & 0 deletions contracts/system-contracts/special_env_fn/interface.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface special_env_fn {
/// Checks whether the specified account is a contract.
function is_contract(address account) external view returns(bool);

/// Retrieves the code hash of the contract at the specified account (if it exists)
function code_hash(address account) external view returns(bool, bytes32);

/// Retrieves the code hash of the currently executing contract.
function own_code_hash() external view returns(bytes32);

/// Returns an Ethereum address from the ECDSA compressed public key
/// if valid pubkey provided else returns (false, bytes20(0))
function ecdsa_to_eth_address(uint8[33] calldata pubkey) external view returns(bool, bytes20);

/// Recovers the compressed ECDSA public key for given signature and message_hash
/// Incase of error, (false, bytes33(0)) is returned
function ecdsa_recover(uint8[65] calldata signature, bytes32 message_hash) external view returns(bool, uint8[33] memory);
}
57 changes: 57 additions & 0 deletions contracts/system-contracts/special_env_fn/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#![cfg_attr(not(feature = "std"), no_std)]

use ink_lang as ink;

#[ink::contract]
mod special_env_fn {

#[ink(storage)]
pub struct SpecialEnvFn {}

impl SpecialEnvFn {
#[ink(constructor, selector = 0x861731d5)]
pub fn new() -> Self {
Self {}
}

#[ink(message, selector = 0x649c07d5)]
pub fn is_contract(&self, account: AccountId) -> bool {
self.env().is_contract(&account)
}

#[ink(message, selector = 0x9804bf43)]
pub fn code_hash(&self, account: AccountId) -> (bool, Hash) {
match self.env().code_hash(&account) {
Ok(h) => (true, h),
Err(_) => (false, Hash::from([0u8; 32])),
}
}

#[ink(message, selector = 0x4ed65189)]
pub fn own_code_hash(&self) -> Hash {
let caller = self.env().caller();
assert!(self.is_contract(caller), "Caller is not a contract");
self.code_hash(caller).1
}

#[ink(message, selector = 0x7143b598)]
pub fn ecdsa_to_eth_address(&self, pubkey: [u8; 33]) -> (bool, [u8; 20]) {
match self.env().ecdsa_to_eth_address(&pubkey) {
Ok(addr) => (true, addr),
Err(_) => (false, [0u8; 20]),
}
}

#[ink(message, selector = 0xe1e0e895)]
pub fn ecdsa_recover(
&self,
signature: [u8; 65],
message_hash: [u8; 32],
) -> (bool, [u8; 33]) {
match self.env().ecdsa_recover(&signature, &message_hash) {
Ok(h) => (true, h),
Err(_) => (false, [0u8; 33]),
}
}
}
}
1 change: 1 addition & 0 deletions subxt-tests/src/cases/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod msg_sender;
mod native_token_wrapper;
mod primitives;
mod randomizer;
mod special_env_fn;
mod store;
mod structs;
mod uniswapv2_erc20;
Expand Down
151 changes: 151 additions & 0 deletions subxt-tests/src/cases/special_env_fn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
use crate::Contract;
use contract_transcode::ContractMessageTranscoder;
use parity_scale_codec::Decode;
use sp_core::hexdisplay::AsBytesRef;

#[tokio::test]
async fn access_special_env_from_solidity() -> anyhow::Result<()> {
const ALICE: sp_keyring::AccountKeyring = sp_keyring::AccountKeyring::Alice;

let api = crate::API::from_url(
std::env::var("END_POINT").unwrap_or_else(|_| "ws://127.0.0.1:9944".to_string()),
)
.await?;

// 1A. Deploy the system-contract (special_env_fn)
let mut system_contract = Contract::new("../contracts/special_env_fn.contract")?;
system_contract
.deploy_as_system_contract(&api, None, 0, &|t: ContractMessageTranscoder<'_>| {
t.encode::<_, String>("new", []).unwrap()
})
.await?;

let system_contract_addr = system_contract.address.unwrap();

// 1B. Deploy the sample solidity contract
let mut contract = Contract::new("../contracts/SpecialEnvFn.contract")?;
contract
.deploy(&api, ALICE, 0, &|t: ContractMessageTranscoder<'_>| {
t.encode::<_, String>("new", [format!("0x{}", hex::encode(&system_contract_addr))])
.unwrap()
})
.await?;

let contract_addr = contract.address.as_ref().unwrap();

// 2. Test API -> is_contract

// A. Passing non-contract address (ALICE); Should return false
let rv = contract
.try_call(&api, ALICE, 0, &|t: ContractMessageTranscoder<'_>| {
t.encode::<_, String>(
"is_contract",
[format!("0x{}", hex::encode(&ALICE.to_account_id()))],
)
.unwrap()
})
.await?;
assert!(<bool>::decode(&mut rv.as_bytes_ref())? == false);

// B. Passing contract address (SELF); Should return true
let rv = contract
.try_call(&api, ALICE, 0, &|t: ContractMessageTranscoder<'_>| {
t.encode::<_, String>(
"is_contract",
[format!("0x{}", hex::encode(&contract_addr))],
)
.unwrap()
})
.await?;
assert!(<bool>::decode(&mut rv.as_bytes_ref())?);

// 3. Test API -> code_hash

// A. Passing non-contract address should return (false, bytes32(0))
let rv = contract
.try_call(&api, ALICE, 0, &|t: ContractMessageTranscoder<'_>| {
t.encode::<_, String>(
"code_hash",
[format!("0x{}", hex::encode(&ALICE.to_account_id()))],
)
.unwrap()
})
.await?;

let res = <(bool, [u8; 32])>::decode(&mut rv.as_bytes_ref())?;
assert_eq!(res, (false, [0u8; 32]));

// B. Passing contract address should return (true, code_hash)
let rv = contract
.try_call(&api, ALICE, 0, &|t: ContractMessageTranscoder<'_>| {
t.encode::<_, String>("code_hash", [format!("0x{}", hex::encode(&contract_addr))])
.unwrap()
})
.await?;

let res = <(bool, [u8; 32])>::decode(&mut rv.as_bytes_ref())?;
assert_eq!(res, (true, contract.code_hash.0));

// 4. Test API -> own_code_hash
let rv = contract
.try_call(&api, ALICE, 0, &|t: ContractMessageTranscoder<'_>| {
t.encode::<_, String>("own_code_hash", []).unwrap()
})
.await?;

let res = <[u8; 32]>::decode(&mut rv.as_bytes_ref())?;
assert_eq!(res, contract.code_hash.0);

// 5. Test API -> ecdsa_to_eth_address
let pubkey: [u8; 33] = [
3, 110, 192, 35, 209, 24, 189, 55, 218, 250, 100, 89, 40, 76, 222, 208, 202, 127, 31, 13,
58, 51, 242, 179, 13, 63, 19, 22, 252, 164, 226, 248, 98,
];

let expected_eth_addr = [
253, 240, 181, 194, 143, 66, 163, 109, 18, 211, 78, 49, 177, 94, 159, 79, 207, 37, 21, 191,
];

let rv = contract
.try_call(&api, ALICE, 0, &|t: ContractMessageTranscoder<'_>| {
t.encode::<_, String>("ecdsa_to_eth_address", [format!("{:?}", pubkey)])
.unwrap()
})
.await?;

let res = <(bool, [u8; 20])>::decode(&mut rv.as_bytes_ref())?;
assert_eq!(res, (true, expected_eth_addr));

// 6. Test API -> ecdsa_recover
let signature: [u8; 65] = [
195, 218, 227, 165, 226, 17, 25, 160, 37, 92, 142, 238, 4, 41, 244, 211, 18, 94, 131, 116,
231, 116, 255, 164, 252, 248, 85, 233, 173, 225, 26, 185, 119, 235, 137, 35, 204, 251, 134,
131, 186, 215, 76, 112, 17, 192, 114, 243, 102, 166, 176, 140, 180, 124, 213, 102, 117,
212, 89, 89, 92, 209, 116, 17, 28,
];

let msg_hash: [u8; 32] = [
167, 124, 116, 195, 220, 156, 244, 20, 243, 69, 1, 98, 189, 205, 79, 108, 213, 78, 65, 65,
230, 30, 17, 37, 184, 220, 237, 135, 1, 209, 101, 229,
];

let expected_compressed_pubkey: [u8; 33] = [
3, 110, 192, 35, 209, 24, 189, 55, 218, 250, 100, 89, 40, 76, 222, 208, 202, 127, 31, 13,
58, 51, 242, 179, 13, 63, 19, 22, 252, 164, 226, 248, 98,
];

let rv = contract
.try_call(&api, ALICE, 0, &|t: ContractMessageTranscoder<'_>| {
t.encode::<_, String>(
"ecdsa_recover",
[format!("{:?}", signature), format!("{:?}", msg_hash)],
)
.unwrap()
})
.await?;

let res = <(bool, [u8; 33])>::decode(&mut rv.as_bytes_ref())?;
assert_eq!(res, (true, expected_compressed_pubkey));

Ok(())
}
3 changes: 3 additions & 0 deletions subxt-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ struct Contract {
project: InkProject,
blob: Vec<u8>,
address: Option<AccountId32>,
code_hash: contract_metadata::CodeHash,
}

impl Contract {
Expand All @@ -429,6 +430,7 @@ impl Contract {

let contract: ContractMetadata = serde_json::from_reader(r)?;
let project = load_versioned_metadata(&contract)?;
let code_hash = contract.source.hash;

let blob = contract
.source
Expand All @@ -441,6 +443,7 @@ impl Contract {
project,
blob,
address: None,
code_hash,
})
}

Expand Down

0 comments on commit d628bd3

Please sign in to comment.