Skip to content

Commit 12e6b65

Browse files
Program Configurations MVP (#56)
* adds configs to WIT, evaluate function signatures, and examples * adds a config to example-basic-transaction * Update README.md Co-authored-by: Hernando Castano <[email protected]> * PR fixes --------- Co-authored-by: Hernando Castano <[email protected]>
1 parent eaa9fbc commit 12e6b65

File tree

15 files changed

+82
-48
lines changed

15 files changed

+82
-48
lines changed

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ cargo install cargo-component --version 0.2.0 &&
2020
cargo install wasm-tools
2121
```
2222

23-
## Example Program: `template-barebones`
23+
## A Barebones Program: [`template-barebones`](./examples/barebones/src/lib.rs)
2424

2525
An example of a barebones program is at [`examples/barebones/src/lib.rs`](./examples/barebones/src/lib.rs). This example does a simple check on the length of the message to be signed.
2626

@@ -32,6 +32,16 @@ cargo component build --release -p template-barebones --target wasm32-unknown-un
3232

3333
This builds the program as a Wasm component at `target/wasm32-unknown-unknown/release/template_barebones.wasm`.
3434

35+
## Example Custody Program with Config: [`example-basic-transaction`](./examples/basic-transaction/src/lib.rs)
36+
37+
This example validates that an an EVM transaction request recipient exists on a list of allowlisted addresses. It also uses a configuration, which allows the user to modify the allowlisted addresses without having to recompile the program (i.e. update the allowlist from the browser).
38+
39+
You can compile the program by running:
40+
41+
```bash
42+
cargo component build --release -p example-basic-transaction --target wasm32-unknown-unknown
43+
```
44+
3545
## Running Tests
3646

3747
Before running the runtime tests, you need to build the `template-barebones` and `infinite-loop` components. To do this, execute:

acl/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ use serde::{Deserialize, Serialize};
2626
Serialize,
2727
Deserialize,
2828
)]
29-
// TODO: Make const; Change Vec<Address> to something like <const Address, const N: usize>
3029
pub struct Acl<Address> {
3130
pub addresses: Vec<Address>,
3231
pub kind: AclKind,

acl/src/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ fn test_acl_functions_properly() {
5858
..Default::default()
5959
};
6060

61-
// should only block whitelisted and null recipient txs
61+
// should only block allowlisted and null recipient txs
6262
assert!(denylisted_acl
6363
.clone()
6464
.is_satisfied_by(to_address_2_tx.clone())
@@ -105,7 +105,7 @@ fn test_acl_functions_properly() {
105105
allow_null_recipient: true,
106106
};
107107

108-
// should only block whitelisted
108+
// should only block allowlisted
109109
assert!(denylisted_acl_with_null_recipient
110110
.clone()
111111
.is_satisfied_by(to_address_2_tx.clone())

examples/barebones-with-auxilary/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub struct BarebonesWithAuxilary;
1616
impl Program for BarebonesWithAuxilary {
1717
/// This is the only function required by the program runtime. `signature_request` includes the message to be
1818
/// signed, eg. RLP-serialized Ethereum transaction request, raw x86_64 executable, etc.
19-
fn evaluate(signature_request: SignatureRequest) -> Result<(), Error> {
19+
fn evaluate(signature_request: SignatureRequest, _config: Option<Vec<u8>>) -> Result<(), Error> {
2020
let SignatureRequest {
2121
message,
2222
auxilary_data,
@@ -57,7 +57,7 @@ mod tests {
5757
auxilary_data: Some(vec![0x00]),
5858
};
5959

60-
assert!(BarebonesWithAuxilary::evaluate(signature_request).is_ok());
60+
assert!(BarebonesWithAuxilary::evaluate(signature_request, None).is_ok());
6161
}
6262

6363
/// Note, the program is written s.t. if `message` is less than 10 bytes, the program will error.
@@ -69,7 +69,7 @@ mod tests {
6969
auxilary_data: Some(vec![0x00]),
7070
};
7171

72-
assert!(BarebonesWithAuxilary::evaluate(signature_request).is_err());
72+
assert!(BarebonesWithAuxilary::evaluate(signature_request, None).is_err());
7373
}
7474

7575
/// Note, the program is written s.t. if `auxilary_data` is `None`, the program will error.
@@ -81,6 +81,6 @@ mod tests {
8181
auxilary_data: None,
8282
};
8383

84-
assert!(BarebonesWithAuxilary::evaluate(signature_request).is_err());
84+
assert!(BarebonesWithAuxilary::evaluate(signature_request, None).is_err());
8585
}
8686
}

examples/barebones/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub struct BarebonesProgram;
1616
impl Program for BarebonesProgram {
1717
/// This is the only function required by the program runtime. `message` is the preimage of the curve element to be
1818
/// signed, eg. RLP-serialized Ethereum transaction request, raw x86_64 executable, etc.
19-
fn evaluate(signature_request: SignatureRequest) -> Result<(), Error> {
19+
fn evaluate(signature_request: SignatureRequest, _config: Option<Vec<u8>>) -> Result<(), Error> {
2020
let message: Vec<u8> = signature_request.message;
2121

2222
// our program just checks that the length of the message is greater than 10
@@ -49,7 +49,7 @@ mod tests {
4949
auxilary_data: None,
5050
};
5151

52-
assert!(BarebonesProgram::evaluate(signature_request).is_ok());
52+
assert!(BarebonesProgram::evaluate(signature_request, None).is_ok());
5353
}
5454

5555
#[test]
@@ -60,6 +60,6 @@ mod tests {
6060
auxilary_data: None,
6161
};
6262

63-
assert!(BarebonesProgram::evaluate(signature_request).is_err());
63+
assert!(BarebonesProgram::evaluate(signature_request, None).is_err());
6464
}
6565
}

examples/basic-transaction/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ crate-type = ["cdylib"]
1515
entropy-programs = { workspace = true }
1616
# TODO move hex parsing into the entropy-programs-evm crate
1717
hex = { version = "0.4.3", default-features = false }
18+
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
19+
serde_json = { version = "1.0", default-features = false, features = ["alloc"]}
1820

1921
# These are used by `cargo component`
2022
[package.metadata.component]

examples/basic-transaction/src/lib.rs

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,44 @@ use entropy_programs::{
77
programs::acl::*,
88
};
99

10-
use alloc::{vec, vec::Vec};
10+
use alloc::{vec::Vec, string::{String, ToString}, format};
11+
12+
use serde_json;
13+
use serde::{Serialize, Deserialize};
1114

1215
pub struct BasicTransaction;
1316

17+
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
18+
pub struct BasicTransactionConfig {
19+
pub allowlisted_addresses: Vec<String>,
20+
}
21+
1422
// TODO confirm this isn't an issue for audit
1523
register_custom_getrandom!(always_fail);
1624

1725
impl Program for BasicTransaction {
1826
/// This is the function that the programs engine will runtime esecute. signature_request is the preimage of the curve element to be
1927
/// signed, eg. RLP-serialized Ethereum transaction request, raw x86_64 executable, etc.
2028
// #[no_mangle]
21-
fn evaluate(state: SignatureRequest) -> Result<(), CoreError> {
22-
// parse the raw tx into some type
29+
fn evaluate(signature_request: SignatureRequest, config: Option<Vec<u8>>) -> Result<(), CoreError> {
30+
// parse the raw tx into some type supported by the Acl check
2331
let parsed_tx =
24-
<Evm as Architecture>::TransactionRequest::try_parse(state.message.as_slice())?;
25-
26-
// construct a whitelist ACL
27-
// TODO can we just use Address instead of AddressRaw?
28-
let whitelisted_address: <Evm as Architecture>::AddressRaw =
29-
hex::decode("772b9a9e8aa1c9db861c6611a82d251db4fac990")
30-
.unwrap()
31-
.try_into()
32-
.unwrap();
32+
<Evm as Architecture>::TransactionRequest::try_parse(signature_request.message.as_slice())?;
33+
34+
// construct a allowlist ACL from the config
35+
let typed_config = serde_json::from_slice::<BasicTransactionConfig>(
36+
config.ok_or(CoreError::Evaluation("No config provided.".to_string()))?.as_slice()
37+
).map_err(|e| CoreError::Evaluation(format!("Failed to parse config: {}", e)))?;
38+
39+
let addresses: Vec<<Evm as Architecture>::AddressRaw> =
40+
typed_config
41+
.allowlisted_addresses
42+
.iter()
43+
.map(|a| hex::decode(a).unwrap().try_into().unwrap())
44+
.collect();
45+
3346
let allowlisted_acl = Acl::<<Evm as Architecture>::AddressRaw> {
34-
addresses: vec![whitelisted_address],
47+
addresses,
3548
..Default::default()
3649
};
3750

@@ -53,27 +66,36 @@ export_program!(BasicTransaction);
5366
#[cfg(test)]
5467
mod tests {
5568
use super::*;
56-
use alloc::string::ToString;
69+
70+
const EVM_TX_WITH_ALLOWLISTED_RECIPIENT: &[u8] = b"0xef01808094772b9a9e8aa1c9db861c6611a82d251db4fac990019243726561746564204f6e20456e74726f7079018080";
71+
const EVM_TX_WITH_NONALLOWLISTED_RECIPIENT: &[u8] = b"0xef01808094772b9a9e8aa1c9db861c6611a82d251db4fac991019243726561746564204f6e20456e74726f7079018080";
72+
const CONFIG: &[u8] = r#"
73+
{
74+
"allowlisted_addresses": [
75+
"772b9a9e8aa1c9db861c6611a82d251db4fac990"
76+
]
77+
}
78+
"#.as_bytes();
5779

5880
#[test]
5981
fn test_evaluate() {
6082
let signature_request = SignatureRequest {
6183
// `data` is an RLP serialized ETH transaction with the recipient set to `0x772b9a9e8aa1c9db861c6611a82d251db4fac990`
62-
message: "0xef01808094772b9a9e8aa1c9db861c6611a82d251db4fac990019243726561746564204f6e20456e74726f7079018080".to_string().into_bytes(),
84+
message: EVM_TX_WITH_ALLOWLISTED_RECIPIENT.to_vec(),
6385
auxilary_data: None
6486
};
6587

66-
assert!(BasicTransaction::evaluate(signature_request).is_ok());
88+
assert!(BasicTransaction::evaluate(signature_request, Some(CONFIG.to_vec())).is_ok());
6789
}
6890

6991
#[test]
7092
fn test_start_fail() {
7193
let signature_request = SignatureRequest {
7294
// `data` is the same as previous test, but recipient address ends in `1` instead of `0`, so it should fail
73-
message: "0xef01808094772b9a9e8aa1c9db861c6611a82d251db4fac991019243726561746564204f6e20456e74726f7079018080".to_string().into_bytes(),
95+
message: EVM_TX_WITH_NONALLOWLISTED_RECIPIENT.to_vec(),
7496
auxilary_data: None
7597
};
7698

77-
assert!(BasicTransaction::evaluate(signature_request).is_err());
99+
assert!(BasicTransaction::evaluate(signature_request, Some(CONFIG.to_vec())).is_err());
78100
}
79101
}

examples/custom-hash/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ register_custom_getrandom!(always_fail);
1616
pub struct CustomHashExample;
1717

1818
impl Program for CustomHashExample {
19-
fn evaluate(signature_request: SignatureRequest) -> Result<(), Error> {
19+
fn evaluate(signature_request: SignatureRequest, _config: Option<Vec<u8>>) -> Result<(), Error> {
2020
if signature_request.message.len() < 1 {
2121
return Err(Error::Evaluation(
2222
"You need to give me SOME data to sign!".to_string(),

examples/infinite-loop/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub struct InfiniteLoop;
1212
impl Program for InfiniteLoop {
1313
/// This is the only function required by the program runtime. `message` is the preimage of the curve element to be
1414
/// signed, eg. RLP-serialized Ethereum transaction request, raw x86_64 executable, etc.
15-
fn evaluate(_signature_request: SignatureRequest) -> Result<(), Error> {
15+
fn evaluate(_signature_request: SignatureRequest, _config: Option<Vec<u8>>) -> Result<(), Error> {
1616
loop {}
1717
#[allow(unreachable_code)]
1818
Ok(())

examples/private-acl/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ register_custom_getrandom!(always_fail);
2424
impl Program for PrivateTransactionAcl {
2525
/// Allow any address given in the pre-defined list (addresses.txt)
2626
// #[no_mangle]
27-
fn evaluate(signature_request: SignatureRequest) -> Result<(), CoreError> {
27+
fn evaluate(signature_request: SignatureRequest, _config: Option<Vec<u8>>) -> Result<(), CoreError> {
2828
// parse the raw tx into some type
2929
let parsed_tx = <Evm as Architecture>::TransactionRequest::try_parse(
3030
signature_request.message.as_slice(),
@@ -72,7 +72,7 @@ mod tests {
7272
auxilary_data: None,
7373
};
7474

75-
assert!(PrivateTransactionAcl::evaluate(signature_request).is_ok());
75+
assert!(PrivateTransactionAcl::evaluate(signature_request, None).is_ok());
7676
}
7777

7878
#[test]
@@ -83,6 +83,6 @@ mod tests {
8383
auxilary_data: None,
8484
};
8585

86-
assert!(PrivateTransactionAcl::evaluate(signature_request).is_err());
86+
assert!(PrivateTransactionAcl::evaluate(signature_request, None).is_err());
8787
}
8888
}

0 commit comments

Comments
 (0)