mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-05-14 22:53:46 +08:00
Implement native functions for sha256 and keccak256
This commit is contained in:
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
@@ -21,7 +21,8 @@
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"sourceLanguages": ["rust"]
|
||||
"sourceLanguages": ["rust"],
|
||||
"terminal": "integrated"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
|
||||
@@ -23,6 +23,7 @@ serde_derive = "1"
|
||||
serde_json = "1.0"
|
||||
rust-crypto = "0.2"
|
||||
sha2 = "0.8.0"
|
||||
sha3 = "0.8.2"
|
||||
dirs = "1.0.4"
|
||||
regex = "1"
|
||||
linefeed = "0.6"
|
||||
|
||||
@@ -21,8 +21,39 @@ use util::pair::*;
|
||||
|
||||
use crypto::ripemd160::Ripemd160;
|
||||
use crypto::sha2::Sha256;
|
||||
use crypto::sha3::Sha3;
|
||||
use crypto::digest::Digest;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Keccak256Hash(pub [u8; 32]);
|
||||
impl Keccak256Hash {
|
||||
pub fn from_data(data: &[u8]) -> Keccak256Hash {
|
||||
let mut ret = [0u8; 32];
|
||||
let mut sha3 = Sha3::keccak256();
|
||||
sha3.input(data);
|
||||
sha3.result(&mut ret);
|
||||
Keccak256Hash(ret)
|
||||
}
|
||||
}
|
||||
impl_array_newtype!(Keccak256Hash, u8, 32);
|
||||
impl_array_hexstring_fmt!(Keccak256Hash);
|
||||
impl_byte_array_newtype!(Keccak256Hash, u8, 32);
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Sha256Hash(pub [u8; 32]);
|
||||
impl Sha256Hash {
|
||||
pub fn from_data(data: &[u8]) -> Sha256Hash {
|
||||
let mut ret = [0u8; 32];
|
||||
let mut sha2 = Sha256::new();
|
||||
sha2.input(data);
|
||||
sha2.result(&mut ret);
|
||||
Sha256Hash(ret)
|
||||
}
|
||||
}
|
||||
impl_array_newtype!(Sha256Hash, u8, 32);
|
||||
impl_array_hexstring_fmt!(Sha256Hash);
|
||||
impl_byte_array_newtype!(Sha256Hash, u8, 32);
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Hash160(pub [u8; 20]);
|
||||
impl_array_newtype!(Hash160, u8, 20);
|
||||
|
||||
@@ -296,6 +296,12 @@ impl TypedNativeFunction {
|
||||
Hash160 =>
|
||||
Simple(SimpleNativeFunction(FunctionType::Fixed(vec![TypeSignature::new_atom( AtomTypeIdentifier::AnyType )],
|
||||
TypeSignature::new_atom( AtomTypeIdentifier::BufferType(20) )))),
|
||||
Sha256 =>
|
||||
Simple(SimpleNativeFunction(FunctionType::Fixed(vec![TypeSignature::new_atom( AtomTypeIdentifier::AnyType )],
|
||||
TypeSignature::new_atom( AtomTypeIdentifier::BufferType(32) )))),
|
||||
Keccak256 =>
|
||||
Simple(SimpleNativeFunction(FunctionType::Fixed(vec![TypeSignature::new_atom( AtomTypeIdentifier::AnyType )],
|
||||
TypeSignature::new_atom( AtomTypeIdentifier::BufferType(32) )))),
|
||||
Equals =>
|
||||
Simple(SimpleNativeFunction(FunctionType::Variadic(TypeSignature::new_atom( AtomTypeIdentifier::AnyType ),
|
||||
TypeSignature::new_atom( AtomTypeIdentifier::BoolType )))),
|
||||
|
||||
@@ -343,9 +343,9 @@ If a Void value is supplied as the inputted tuple, `get` returns Void.",
|
||||
"
|
||||
};
|
||||
|
||||
const HASH_160_API: SpecialAPI = SpecialAPI {
|
||||
const HASH160_API: SpecialAPI = SpecialAPI {
|
||||
input_type: "buff|int",
|
||||
output_type: "(buff 160)",
|
||||
output_type: "(buff 20)",
|
||||
signature: "(hash160 value)",
|
||||
description: "The `hash160` function computes RIPEMD160(SHA256(x)) of the inputted value.
|
||||
If an integer (128 bit) is supplied the hash is computed over the little endian representation of the
|
||||
@@ -353,6 +353,26 @@ integer.",
|
||||
example: "(hash160 0) => 0xe4352f72356db555721651aa612e00379167b30f"
|
||||
};
|
||||
|
||||
const SHA256_API: SpecialAPI = SpecialAPI {
|
||||
input_type: "buff|int",
|
||||
output_type: "(buff 32)",
|
||||
signature: "(sha256 value)",
|
||||
description: "The `sha256` function computes SHA256(x) of the inputted value.
|
||||
If an integer (128 bit) is supplied the hash is computer over the little endian representation of the
|
||||
integer.",
|
||||
example: "(sha256 0) => 0x374708fff7719dd5979ec875d56cd2286f6d3cf7ec317a3b25632aab28ec37bb"
|
||||
};
|
||||
|
||||
const KECCAK256_API: SpecialAPI = SpecialAPI {
|
||||
input_type: "buff|int",
|
||||
output_type: "(buff 32)",
|
||||
signature: "(keccak256 value)",
|
||||
description: "The `keccak256` function computes KECCAK256(value) of the inputted value.
|
||||
Note that this differs from the NIST SHA-3 (i.e. FIPS 202) standard. If an integer (128 bit)
|
||||
is supplied the hash is computer over the little endian representation of the integer.",
|
||||
example: "(keccak256 0) => 0xf490de2920c8a35fabeb13208852aa28c76f9be9b03a4dd2b3c075f7a26923b4"
|
||||
};
|
||||
|
||||
const CONTRACT_CALL_API: SpecialAPI = SpecialAPI {
|
||||
input_type: "ContractName, PublicFunctionName, Arg0, ...",
|
||||
output_type: "BoolType",
|
||||
@@ -417,7 +437,9 @@ fn make_api_reference(function: &NativeFunctions) -> FunctionAPI {
|
||||
TupleCons => make_for_special(&TUPLE_CONS_API),
|
||||
TupleGet => make_for_special(&TUPLE_GET_API),
|
||||
Begin => make_for_special(&BEGIN_API),
|
||||
Hash160 => make_for_special(&HASH_160_API),
|
||||
Hash160 => make_for_special(&HASH160_API),
|
||||
Sha256 => make_for_special(&SHA256_API),
|
||||
Keccak256 => make_for_special(&KECCAK256_API),
|
||||
Print => make_for_special(&PRINT_API),
|
||||
ContractCall => make_for_special(&CONTRACT_CALL_API),
|
||||
AsContract => make_for_special(&AS_CONTRACT_API)
|
||||
|
||||
@@ -43,6 +43,8 @@ pub enum NativeFunctions {
|
||||
TupleGet,
|
||||
Begin,
|
||||
Hash160,
|
||||
Sha256,
|
||||
Keccak256,
|
||||
Print,
|
||||
ContractCall,
|
||||
AsContract
|
||||
@@ -82,6 +84,8 @@ impl NativeFunctions {
|
||||
"get" => Some(TupleGet),
|
||||
"begin" => Some(Begin),
|
||||
"hash160" => Some(Hash160),
|
||||
"sha256" => Some(Sha256),
|
||||
"keccak256" => Some(Keccak256),
|
||||
"print" => Some(Print),
|
||||
"contract-call!" => Some(ContractCall),
|
||||
"as-contract" => Some(AsContract),
|
||||
@@ -124,6 +128,8 @@ pub fn lookup_reserved_functions(name: &str) -> Option<CallableType> {
|
||||
TupleGet => CallableType::SpecialFunction("native_get-tuple", &tuples::tuple_get),
|
||||
Begin => CallableType::NativeFunction("native_begin", &native_begin),
|
||||
Hash160 => CallableType::NativeFunction("native_hash160", &native_hash160),
|
||||
Sha256 => CallableType::NativeFunction("native_sha256", &native_sha256),
|
||||
Keccak256 => CallableType::NativeFunction("native_keccak256", &native_keccak256),
|
||||
Print => CallableType::NativeFunction("native_print", &native_print),
|
||||
ContractCall => CallableType::SpecialFunction("native_contract-call", &database::special_contract_call),
|
||||
AsContract => CallableType::SpecialFunction("native_as-contract", &special_as_contract),
|
||||
@@ -179,6 +185,38 @@ fn native_hash160(args: &[Value]) -> Result<Value> {
|
||||
Value::buff_from(hash160.as_bytes().to_vec())
|
||||
}
|
||||
|
||||
fn native_sha256(args: &[Value]) -> Result<Value> {
|
||||
use util::hash::Sha256Hash;
|
||||
|
||||
if !(args.len() == 1) {
|
||||
return Err(Error::new(ErrType::InvalidArguments("Wrong number of arguments to sha256 (expects 1)".to_string())))
|
||||
}
|
||||
let input = &args[0];
|
||||
let bytes = match input {
|
||||
Value::Int(value) => Ok(value.to_le_bytes().to_vec()),
|
||||
Value::Buffer(value) => Ok(value.data.clone()),
|
||||
_ => Err(Error::new(ErrType::NotImplemented))
|
||||
}?;
|
||||
let sha256 = Sha256Hash::from_data(&bytes);
|
||||
Value::buff_from(sha256.as_bytes().to_vec())
|
||||
}
|
||||
|
||||
fn native_keccak256(args: &[Value]) -> Result<Value> {
|
||||
use util::hash::Keccak256Hash;
|
||||
|
||||
if !(args.len() == 1) {
|
||||
return Err(Error::new(ErrType::InvalidArguments("Wrong number of arguments to keccak256 (expects 1)".to_string())))
|
||||
}
|
||||
let input = &args[0];
|
||||
let bytes = match input {
|
||||
Value::Int(value) => Ok(value.to_le_bytes().to_vec()),
|
||||
Value::Buffer(value) => Ok(value.data.clone()),
|
||||
_ => Err(Error::new(ErrType::NotImplemented))
|
||||
}?;
|
||||
let keccak256 = Keccak256Hash::from_data(&bytes);
|
||||
Value::buff_from(keccak256.as_bytes().to_vec())
|
||||
}
|
||||
|
||||
fn native_begin(args: &[Value]) -> Result<Value> {
|
||||
match args.last() {
|
||||
Some(v) => Ok(v.clone()),
|
||||
|
||||
@@ -4,8 +4,9 @@ use vm::errors::{ErrType};
|
||||
use vm::{Value, LocalContext, ContractContext, GlobalContext, Environment, CallStack};
|
||||
use vm::contexts::{OwnedEnvironment};
|
||||
use vm::callables::DefinedFunction;
|
||||
use vm::types::{TypeSignature, AtomTypeIdentifier};
|
||||
use vm::types::{TypeSignature, AtomTypeIdentifier, BuffData};
|
||||
use vm::parser::parse;
|
||||
use util::hash::hex_bytes;
|
||||
|
||||
#[test]
|
||||
fn test_simple_let() {
|
||||
@@ -36,6 +37,50 @@ fn test_simple_let() {
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha256() {
|
||||
let sha256_evals = [
|
||||
"(sha256 \"\")",
|
||||
"(sha256 0)",
|
||||
"(sha256 \"The quick brown fox jumps over the lazy dog\")",
|
||||
];
|
||||
|
||||
fn to_buffer(hex: &str) -> Value {
|
||||
return Value::Buffer(BuffData { data: hex_bytes(hex).unwrap() });
|
||||
}
|
||||
|
||||
let expectations = [
|
||||
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"374708fff7719dd5979ec875d56cd2286f6d3cf7ec317a3b25632aab28ec37bb",
|
||||
"d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"
|
||||
];
|
||||
|
||||
sha256_evals.iter().zip(expectations.iter())
|
||||
.for_each(|(program, expectation)| assert_eq!(Ok(to_buffer(expectation)), execute(program)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keccak256() {
|
||||
let keccak256_evals = [
|
||||
"(keccak256 \"\")",
|
||||
"(keccak256 0)",
|
||||
"(keccak256 \"The quick brown fox jumps over the lazy dog\")",
|
||||
];
|
||||
|
||||
fn to_buffer(hex: &str) -> Value {
|
||||
return Value::Buffer(BuffData { data: hex_bytes(hex).unwrap() });
|
||||
}
|
||||
|
||||
let expectations = [
|
||||
"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
|
||||
"f490de2920c8a35fabeb13208852aa28c76f9be9b03a4dd2b3c075f7a26923b4",
|
||||
"4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15"
|
||||
];
|
||||
|
||||
keccak256_evals.iter().zip(expectations.iter())
|
||||
.for_each(|(program, expectation)| assert_eq!(Ok(to_buffer(expectation)), execute(program)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_is_null() {
|
||||
let null_evals = [
|
||||
|
||||
Reference in New Issue
Block a user