-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from Laguna-Chain/seal-code
Added system-contract which exposes missing seal-apis to solidity contracts
- Loading branch information
Showing
9 changed files
with
312 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]), | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters