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

Code Review: Smart Contract VM 02/13/19 #921

Merged
merged 50 commits into from Feb 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
b664716
extending list type information with max_len
kantai Jan 30, 2019
fccd156
use type _admission_ for database checks, rather than type equality
kantai Jan 31, 2019
3d46937
cleanup "use" statements, use a generic result type instead of Interp…
kantai Jan 31, 2019
db254b4
more test coverage, test list type admission in datamaps
kantai Jan 31, 2019
4c7f7e5
more test coverage.
kantai Jan 31, 2019
0ef94c3
eliminate unneccessary boxing
kantai Feb 4, 2019
11b1a31
enforce max stack depth (currently 128). use u8 instead of char for b…
kantai Feb 6, 2019
0add35c
do literal lexing in the actual lexer. use something like a munch lexer.
kantai Feb 6, 2019
2ba047a
get_data_map / get_mut_data_map for the contract db interface
kantai Feb 6, 2019
47ddc2c
catch potential dimension overflow in list construction
kantai Feb 6, 2019
fe8e394
move environment initialization out of eval_all
kantai Feb 6, 2019
02f15cb
issue #914 -- enforce name legality at (define...)
kantai Feb 6, 2019
898c1ac
missing source file
kantai Feb 6, 2019
b53e515
handle define legality in functions/define.rs
kantai Feb 6, 2019
a7391fd
check for reserved names in let-expressions
kantai Feb 6, 2019
7852400
tests for maximum stack depth
kantai Feb 6, 2019
9d88516
use splitn for a bounded number of splits
kantai Feb 6, 2019
393428e
enforce maximum context depth
kantai Feb 6, 2019
046ec96
Merge branch 'review/smart-contract-013019' into feature/smart-contra…
kantai Feb 6, 2019
594d0cc
merge reorg of src/vm
kantai Feb 6, 2019
09f31e3
Merge branch 'review/smart-contract-013019' into feature/smart-contra…
kantai Feb 7, 2019
e929979
Merge branch 'review/smart-contract-013019' into feature/smart-contra…
kantai Feb 7, 2019
4ed2a7d
add fmt::Display impl for Values. add CLI command (mostly to get rid …
kantai Feb 7, 2019
06e1f04
use 256 for max stack, context depth. use drain() in parser to avoid …
kantai Feb 7, 2019
ec2af76
Merge branch 'develop' into feature/smart-contract-vm
kantai Feb 7, 2019
ba2d703
u8 is too small for 256!
kantai Feb 7, 2019
53f6e3f
refactor value construction to allow for value size enforcement.
kantai Feb 8, 2019
6c48a5b
environments now have immutable ref to global context. this is in pre…
kantai Feb 8, 2019
388743c
update tests to work with env constructions
kantai Feb 8, 2019
c981aca
add execute_transaction impl for skeletal contract interface
kantai Feb 8, 2019
856c56d
instantiating contract
kantai Feb 8, 2019
7193467
add define-public and a test for contract environment execution
kantai Feb 9, 2019
7a5e1bb
add test for non-public contract invocation failure
kantai Feb 12, 2019
961886b
drop the string-based type descriptors in favor of the structural des…
kantai Feb 12, 2019
233a26d
do value size checks in type instantiations
kantai Feb 13, 2019
bc24cc3
oops, type forcing
kantai Feb 13, 2019
1f6aac5
update admits() logic for buffers. add test for more complex type sig…
kantai Feb 13, 2019
59de936
use loose type admission for buffers and tuples as well as lists
kantai Feb 14, 2019
0056fe9
require type parameters in define-public
kantai Feb 14, 2019
96c6085
add Principal type. implement c32 address decoding/encoding.
kantai Feb 15, 2019
d062cd9
return error from size() instead of panics on overflow.
kantai Feb 18, 2019
c62297d
handle the sender argument to contract execution
kantai Feb 18, 2019
476cd7f
use [u8;20] array instead of vec for principal type
kantai Feb 18, 2019
8791851
use two types for public and private functions
kantai Feb 18, 2019
241b3cc
separate two kinds of contexts (local and global) into two types
kantai Feb 18, 2019
f3177ee
implement equality for Value enum
kantai Feb 19, 2019
74e45cb
implement partialeq, hash for ListData and TupleData manually: allows…
kantai Feb 19, 2019
bb20395
rename MultiplyDefined error to VariableDefinedMultipleTimes
kantai Feb 19, 2019
29a0225
light cleaning up of test functions
kantai Feb 19, 2019
0634a09
Merge branch 'feature/smart-contract-vm' into review/smart-contract-0…
kantai Feb 19, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -20,6 +20,7 @@ serde_json = "1.0"
rust-crypto = "0.2"
sha2 = "0.8.0"
dirs = "1.0.4"
regex = "1"

[dependencies.secp256k1]
version = "0.11.5"
Expand Down
317 changes: 317 additions & 0 deletions src/address/c32.rs
Expand Up @@ -16,3 +16,320 @@
You should have received a copy of the GNU General Public License
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
*/
use super::Error;
use crypto::sha2::Sha256;
use crypto::digest::Digest;

const C32_CHARACTERS: &str = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";

fn c32_encode(input_bytes: &[u8]) -> String {
let c32_chars: &[u8] = C32_CHARACTERS.as_bytes();

let mut result = vec![];
let mut carry = 0;
let mut carry_bits = 0;

for current_value in input_bytes.iter().rev() {
let low_bits_to_take = 5 - carry_bits;
let low_bits = current_value & ((1<<low_bits_to_take) - 1);
let c32_value = (low_bits << carry_bits) + carry;
result.push(c32_chars[c32_value as usize]);
carry_bits = (8 + carry_bits) - 5;
carry = current_value >> (8 - carry_bits);

if carry_bits >= 5 {
let c32_value = carry & ((1<<5) - 1);
result.push(c32_chars[c32_value as usize]);
carry_bits = carry_bits - 5;
carry = carry >> 5;
}
}

if carry_bits > 0 {
result.push(c32_chars[carry as usize]);
}

// remove leading zeros from c32 encoding

while let Some(v) = result.pop() {
if v != c32_chars[0] {
result.push(v);
break;
}
}

// add leading zeros from input.

for current_value in input_bytes.iter() {
if *current_value == 0 {
result.push(c32_chars[0]);
} else {
break;
}
}

let result: Vec<u8> = result.drain(..).rev().collect();
String::from_utf8(result).unwrap()
}

fn c32_decode(input_str: &str) -> Result<Vec<u8>, Error> {
let mut result = vec![];
let mut carry: u16 = 0;
let mut carry_bits = 0; // can be up to 5

let iter_c32_digits = input_str.chars().rev()
.map(|x| { C32_CHARACTERS.find(x) });

for current_result in iter_c32_digits {
let current_5bit = current_result.ok_or(Error::InvalidCrockford32)?;
carry += (current_5bit as u16) << carry_bits;
carry_bits += 5;

if carry_bits >= 8 {
result.push((carry & ((1<<8) - 1)) as u8);
carry_bits -= 8;
carry = carry >> 8;
}
}

if carry_bits > 0 {
result.push(carry as u8);
}

// remove leading zeros from Vec<u8> encoding
while let Some(v) = result.pop() {
if v != 0 {
result.push(v);
break;
}
}

// add leading zeros from input.
for current_value in input_str.chars() {
if current_value == '0' {
result.push(0);
} else {
break;
}
}

result.reverse();

Ok(result)
}

fn double_sha256_checksum(data: &[u8]) -> Vec<u8> {
let mut sha2 = Sha256::new();
let mut tmp = [0u8; 32];
sha2.input(data);
sha2.result(&mut tmp);
let mut sha2 = Sha256::new();
sha2.input(&tmp);
sha2.result(&mut tmp);
tmp[0..4].to_vec()
}

fn c32_check_encode(version: u8, data: &[u8]) -> Result<String, Error> {
if version >= 32 {
return Err(Error::InvalidVersion(version))
}

let mut check_data = vec![version];
check_data.extend_from_slice(data);
let checksum = double_sha256_checksum(&check_data);

let mut encoding_data = data.to_vec();
encoding_data.extend_from_slice(&checksum);

// working with ascii strings is awful.
let mut c32_string = c32_encode(&encoding_data).into_bytes();
let version_char = C32_CHARACTERS.as_bytes()[version as usize];
c32_string.insert(0, version_char);

Ok(String::from_utf8(c32_string).unwrap())
}

fn c32_check_decode(check_data: &str) -> Result<(u8, Vec<u8>), Error> {
if check_data.len() < 2 {
return Err(Error::InvalidCrockford32)
}
let (version, data) = check_data.split_at(1);

let data_sum_bytes = c32_decode(data)?;
if data_sum_bytes.len() < 5 {
return Err(Error::InvalidCrockford32)
}

let (data_bytes, expected_sum) = data_sum_bytes.split_at(data_sum_bytes.len() - 4);

let mut check_data = c32_decode(version)?;
check_data.extend_from_slice(data_bytes);

let computed_sum = double_sha256_checksum(&check_data);
if computed_sum != expected_sum {
return Err(Error::InvalidChecksum(computed_sum, expected_sum.to_vec()))
}

let version = check_data[0];
let data = data_bytes.to_vec();
Ok((version, data))
}

pub fn c32_address_decode(c32_address_str: &str) -> Result<(u8, Vec<u8>), Error> {
if c32_address_str.len() <= 5 {
Err(Error::InvalidCrockford32)
} else {
c32_check_decode(&c32_address_str[1..])
}
}

pub fn c32_address(version: u8, data: &[u8]) -> Result<String, Error> {
let c32_string = c32_check_encode(version, data)?;
Ok(format!("S{}", c32_string))
}

mod test {
use util::hash::hex_bytes;
use super::*;

#[test]
fn test_addresses() {
let hex_strs = [
"a46ff88886c2ef9762d970b4d2c63678835bd39d",
"0000000000000000000000000000000000000000",
"0000000000000000000000000000000000000001",
"1000000000000000000000000000000000000001",
"1000000000000000000000000000000000000000"
];

let versions = [
22,
0,
31,
20,
26,
21
];

let c32_addrs = [
[
"SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7",
"SP000000000000000000002Q6VF78",
"SP00000000000000000005JA84HQ",
"SP80000000000000000000000000000004R0CMNV",
"SP800000000000000000000000000000033H8YKK"
],
[
"S02J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKPVKG2CE",
"S0000000000000000000002AA028H",
"S000000000000000000006EKBDDS",
"S080000000000000000000000000000007R1QC00",
"S080000000000000000000000000000003ENTGCQ"
],
[
"SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR",
"SZ000000000000000000002ZE1VMN",
"SZ00000000000000000005HZ3DVN",
"SZ80000000000000000000000000000004XBV6MS",
"SZ800000000000000000000000000000007VF5G0"
],
[
"SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G",
"SM0000000000000000000062QV6X",
"SM00000000000000000005VR75B2",
"SM80000000000000000000000000000004WBEWKC",
"SM80000000000000000000000000000000JGSYGV"
],
[
"ST2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQYAC0RQ",
"ST000000000000000000002AMW42H",
"ST000000000000000000042DB08Y",
"ST80000000000000000000000000000006BYJ4R4",
"ST80000000000000000000000000000002YBNPV3"
],
[
"SN2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKP6D2ZK9",
"SN000000000000000000003YDHWKJ",
"SN00000000000000000005341MC8",
"SN800000000000000000000000000000066KZWY0",
"SN800000000000000000000000000000006H75AK"
]
];

for i in 0..hex_strs.len() {
for j in 0..versions.len() {
let h = hex_strs[i];
let v = versions[j];
let b = hex_bytes(h).unwrap();
let z = c32_address(v, &b).unwrap();

assert_eq!(z, c32_addrs[j][i]);

let (decoded_version, decoded_bytes) = c32_address_decode(&z).unwrap();
assert_eq!(decoded_version, v);
assert_eq!(decoded_bytes, b);
}
}
}

#[test]
fn test_simple() {
let hex_strings = &[
"a46ff88886c2ef9762d970b4d2c63678835bd39d",
"",
"0000000000000000000000000000000000000000",
"0000000000000000000000000000000000000001",
"1000000000000000000000000000000000000001",
"1000000000000000000000000000000000000000",
"01",
"22",
"0001",
"000001",
"00000001",
"10",
"0100",
"1000",
"010000",
"100000",
"01000000",
"10000000",
"0100000000"
];
let c32_strs = [
"MHQZH246RBQSERPSE2TD5HHPF21NQMWX",
"",
"00000000000000000000",
"00000000000000000001",
"20000000000000000000000000000001",
"20000000000000000000000000000000",
"1",
"12",
"01",
"001",
"0001",
"G",
"80",
"400",
"2000",
"10000",
"G0000",
"800000",
"4000000"
];

let results: Vec<_> = hex_strings.iter().zip(c32_strs.iter())
.map(|(hex_str, expected)|
{
let bytes = hex_bytes(hex_str).unwrap();
let c32_encoded = c32_encode(&bytes);
let decoded_bytes = c32_decode(&c32_encoded).unwrap();
let result = (bytes, c32_encoded, decoded_bytes, expected);
println!("{:?}", result);
result
}).collect();
for (bytes, c32_encoded, decoded_bytes, expected_c32) in results.iter() {
assert_eq!(bytes, decoded_bytes);
assert_eq!(c32_encoded, *expected_c32);
}
}
}
23 changes: 23 additions & 0 deletions src/address/mod.rs
Expand Up @@ -17,4 +17,27 @@
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
*/

use std::error;
use std::fmt;

pub mod c32;

#[derive(Debug)]
pub enum Error {
InvalidCrockford32,
InvalidVersion(u8),
InvalidChecksum(Vec<u8>, Vec<u8>),
EmptyData
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}

impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
17 changes: 17 additions & 0 deletions src/main.rs
Expand Up @@ -30,6 +30,7 @@ extern crate curve25519_dalek;
extern crate ed25519_dalek;
extern crate sha2;
extern crate dirs;
extern crate regex;

#[macro_use] extern crate serde_derive;

Expand All @@ -38,7 +39,9 @@ mod burnchains;
mod chainstate;
mod core;
mod vm;
mod address;

use std::fs;
use std::env;
use std::process;

Expand Down Expand Up @@ -82,6 +85,20 @@ fn main() {
}
}

if argv[1] == "exec_program" {
if argv.len() < 3 {
eprintln!("Usage: {} exec_program [program-file.scm]", argv[0]);
process::exit(1);
}
let program: String = fs::read_to_string(&argv[2])
.expect(&format!("Error reading file: {}", argv[2]));
match vm::execute(&program) {
Ok(result) => println!("{}", result),
Err(error) => println!("Program Execution Error: \n {}", error)
}
return
}

if argv.len() < 4 {
eprintln!("Usage: {} blockchain network working_dir", argv[0]);
process::exit(1);
Expand Down