Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit d453530

Browse files
committed
Integrate native dynamic contracts
1 parent 6073cd5 commit d453530

File tree

12 files changed

+495
-14
lines changed

12 files changed

+495
-14
lines changed

Cargo.toml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc", rev = "4b
8585
jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc", rev = "4b6060b" }
8686
ipnetwork = "0.12.7"
8787
itertools = "0.7.8"
88+
libc = "0.2.43"
89+
libloading = "0.5.0"
8890
log = "0.4.2"
8991
matches = "0.1.6"
9092
nix = "0.11.0"
@@ -102,7 +104,11 @@ sys-info = "0.5.6"
102104
tokio = "0.1"
103105
tokio-codec = "0.1"
104106
untrusted = "0.6.2"
105-
libc = "0.2.43"
107+
108+
[dev-dependencies]
109+
noop = { path = "contracts/noop" }
110+
print = { path = "contracts/print" }
111+
move_funds = { path = "contracts/move_funds" }
106112

107113
[[bench]]
108114
name = "bank"
@@ -118,3 +124,18 @@ name = "signature"
118124

119125
[[bench]]
120126
name = "sigverify"
127+
128+
[workspace]
129+
members = [
130+
".",
131+
"contracts/noop",
132+
"contracts/print",
133+
"contracts/move_funds",
134+
]
135+
default-members = [
136+
".",
137+
"contracts/noop",
138+
"contracts/print",
139+
"contracts/move_funds",
140+
]
141+

contracts/move_funds/Cargo.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "move_funds"
3+
version = "0.1.0"
4+
authors = [
5+
"Anatoly Yakovenko <[email protected]>",
6+
"Greg Fitzgerald <[email protected]>",
7+
"Stephen Akridge <[email protected]>",
8+
"Michael Vines <[email protected]>",
9+
"Rob Walker <[email protected]>",
10+
"Pankaj Garg <[email protected]>",
11+
"Tyera Eulberg <[email protected]>",
12+
"Jack May <[email protected]>",
13+
]
14+
15+
[dependencies]
16+
bincode = "1.0.0"
17+
generic-array = { version = "0.12.0", default-features = false, features = ["serde"] }
18+
libloading = "0.5.0"
19+
solana = { path = "../.." }
20+
21+
[lib]
22+
name = "move_funds"
23+
crate-type = ["dylib"]
24+

contracts/move_funds/src/lib.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
extern crate bincode;
2+
extern crate solana;
3+
4+
use bincode::deserialize;
5+
use solana::dynamic_contract::KeyedAccount;
6+
7+
#[no_mangle]
8+
pub extern "C" fn process(infos: &mut Vec<KeyedAccount>, data: &[u8]) {
9+
let tokens: i64 = deserialize(data).unwrap();
10+
if infos[0].account.tokens >= tokens {
11+
infos[0].account.tokens -= tokens;
12+
infos[1].account.tokens += tokens;
13+
} else {
14+
println!(
15+
"Insufficient funds, asked {}, only had {}",
16+
tokens, infos[0].account.tokens
17+
);
18+
}
19+
}
20+
21+
#[cfg(test)]
22+
mod tests {
23+
use super::*;
24+
use bincode::serialize;
25+
use solana::bank::Account;
26+
use solana::signature::Pubkey;
27+
28+
#[test]
29+
fn test_move_funds() {
30+
let tokens: i64 = 100;
31+
let data: Vec<u8> = serialize(&tokens).unwrap();
32+
let keys = vec![Pubkey::default(); 2];
33+
let mut accounts = vec![Account::default(), Account::default()];
34+
accounts[0].tokens = 100;
35+
accounts[1].tokens = 1;
36+
37+
{
38+
let mut infos: Vec<KeyedAccount> = Vec::new();
39+
for (key, account) in keys.iter().zip(&mut accounts).collect::<Vec<_>>() {
40+
infos.push(KeyedAccount { key, account });
41+
}
42+
43+
process(&mut infos, &data);
44+
}
45+
assert_eq!(0, accounts[0].tokens);
46+
assert_eq!(101, accounts[1].tokens);
47+
}
48+
}

contracts/noop/Cargo.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "noop"
3+
version = "0.1.0"
4+
authors = [
5+
"Anatoly Yakovenko <[email protected]>",
6+
"Greg Fitzgerald <[email protected]>",
7+
"Stephen Akridge <[email protected]>",
8+
"Michael Vines <[email protected]>",
9+
"Rob Walker <[email protected]>",
10+
"Pankaj Garg <[email protected]>",
11+
"Tyera Eulberg <[email protected]>",
12+
"Jack May <[email protected]>",
13+
]
14+
15+
[dependencies]
16+
libloading = "0.5.0"
17+
solana = { path = "../.." }
18+
19+
[lib]
20+
name = "noop"
21+
crate-type = ["dylib"]
22+

contracts/noop/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
extern crate solana;
2+
3+
use solana::dynamic_contract::KeyedAccount;
4+
5+
#[no_mangle]
6+
pub extern "C" fn process(_infos: &mut Vec<KeyedAccount>, _data: &[u8]) {}

contracts/print/Cargo.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "print"
3+
version = "0.1.0"
4+
authors = [
5+
"Anatoly Yakovenko <[email protected]>",
6+
"Greg Fitzgerald <[email protected]>",
7+
"Stephen Akridge <[email protected]>",
8+
"Michael Vines <[email protected]>",
9+
"Rob Walker <[email protected]>",
10+
"Pankaj Garg <[email protected]>",
11+
"Tyera Eulberg <[email protected]>",
12+
"Jack May <[email protected]>",
13+
]
14+
15+
[dependencies]
16+
libloading = "0.5.0"
17+
solana = { path = "../.." }
18+
19+
[lib]
20+
name = "print"
21+
crate-type = ["dylib"]
22+

contracts/print/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
extern crate solana;
2+
3+
use solana::dynamic_contract::KeyedAccount;
4+
5+
#[no_mangle]
6+
pub extern "C" fn process(infos: &mut Vec<KeyedAccount>, _data: &[u8]) {
7+
println!("AccountInfos: {:#?}", infos);
8+
//println!("data: {:#?}", data);
9+
}

src/bank.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use bincode::deserialize;
77
use bincode::serialize;
88
use budget_contract::BudgetContract;
99
use counter::Counter;
10+
use dynamic_contract::{DynamicContract, KeyedAccount};
1011
use entry::Entry;
1112
use hash::{hash, Hash};
1213
use itertools::Itertools;
@@ -135,6 +136,9 @@ pub struct Bank {
135136

136137
// The latest finality time for the network
137138
finality_time: AtomicUsize,
139+
140+
// loaded contracts hashed by contract_id
141+
loaded_contracts: RwLock<HashMap<Pubkey, DynamicContract>>,
138142
}
139143

140144
impl Default for Bank {
@@ -146,6 +150,7 @@ impl Default for Bank {
146150
transaction_count: AtomicUsize::new(0),
147151
is_leader: true,
148152
finality_time: AtomicUsize::new(std::usize::MAX),
153+
loaded_contracts: RwLock::new(HashMap::new()),
149154
}
150155
}
151156
}
@@ -306,6 +311,7 @@ impl Bank {
306311
Ok(called_accounts)
307312
}
308313
}
314+
309315
fn load_accounts(
310316
&self,
311317
txs: &[Transaction],
@@ -316,6 +322,7 @@ impl Bank {
316322
.map(|tx| self.load_account(tx, accounts, error_counters))
317323
.collect()
318324
}
325+
319326
pub fn verify_transaction(
320327
tx: &Transaction,
321328
pre_contract_id: &Pubkey,
@@ -340,11 +347,33 @@ impl Bank {
340347
}
341348
Ok(())
342349
}
350+
351+
fn loaded_contract(&self, tx: &Transaction, accounts: &mut [Account]) -> bool {
352+
let loaded_contracts = self.loaded_contracts.write().unwrap();
353+
match loaded_contracts.get(&tx.contract_id) {
354+
Some(dc) => {
355+
let mut infos: Vec<_> = (&tx.keys)
356+
.into_iter()
357+
.zip(accounts)
358+
.map(|(key, account)| KeyedAccount { key, account })
359+
.collect();
360+
361+
dc.call(&mut infos, &tx.userdata);
362+
true
363+
}
364+
None => false,
365+
}
366+
}
367+
343368
/// Execute a transaction.
344369
/// This method calls the contract's process_transaction method and verifies that the result of
345370
/// the contract does not violate the bank's accounting rules.
346371
/// The accounts are committed back to the bank only if this function returns Ok(_).
347-
fn execute_transaction(tx: Transaction, accounts: &mut [Account]) -> Result<Transaction> {
372+
fn execute_transaction(
373+
&self,
374+
tx: Transaction,
375+
accounts: &mut [Account],
376+
) -> Result<Transaction> {
348377
let pre_total: i64 = accounts.iter().map(|a| a.tokens).sum();
349378
let pre_data: Vec<_> = accounts
350379
.iter_mut()
@@ -354,11 +383,12 @@ impl Bank {
354383
// Call the contract method
355384
// It's up to the contract to implement its own rules on moving funds
356385
if SystemContract::check_id(&tx.contract_id) {
357-
SystemContract::process_transaction(&tx, accounts)
386+
SystemContract::process_transaction(&tx, accounts, &self.loaded_contracts)
358387
} else if BudgetContract::check_id(&tx.contract_id) {
359388
// TODO: the runtime should be checking read/write access to memory
360389
// we are trusting the hard coded contracts not to clobber or allocate
361390
BudgetContract::process_transaction(&tx, accounts)
391+
} else if self.loaded_contract(&tx, accounts) {
362392
} else {
363393
return Err(BankError::UnknownContractId(tx.contract_id));
364394
}
@@ -415,7 +445,7 @@ impl Bank {
415445
.zip(txs.into_iter())
416446
.map(|(acc, tx)| match acc {
417447
Err(e) => Err(e.clone()),
418-
Ok(ref mut accounts) => Self::execute_transaction(tx, accounts),
448+
Ok(ref mut accounts) => self.execute_transaction(tx, accounts),
419449
}).collect();
420450
let execution_elapsed = now.elapsed();
421451
let now = Instant::now();

0 commit comments

Comments
 (0)