Skip to content

Commit

Permalink
Merge pull request #117 from stacks-network/feat/sha256
Browse files Browse the repository at this point in the history
Add sha256 function
  • Loading branch information
Acaccia committed Oct 17, 2023
2 parents 62bd829 + ce171f8 commit 1f1bf9b
Show file tree
Hide file tree
Showing 9 changed files with 636 additions and 4 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions clar2wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ criterion = "0.5.1"
proptest = "1.2.0"
num-integer = { version = "0.1.45", default-features = false }
clar2wasm-tests = { path = "../tests" }
hex = "0.4.3"

[lib]
path = "src/lib.rs"
Expand Down
2 changes: 1 addition & 1 deletion clar2wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use clarity::{
use walrus::Module;
use wasm_generator::{GeneratorError, WasmGenerator};

mod wasm_generator;
pub mod wasm_generator;
mod words;

// FIXME: This is copied from stacks-blockchain
Expand Down
280 changes: 280 additions & 0 deletions clar2wasm/src/standard/standard.wat
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,12 @@
(export "stack-pointer" (global $stack-pointer))
(memory (export "memory") 10)

;; (sha256) initial hash values: first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19
(data (i32.const 0) "\67\e6\09\6a\85\ae\67\bb\72\f3\6e\3c\3a\f5\4f\a5\7f\52\0e\51\8c\68\05\9b\ab\d9\83\1f\19\cd\e0\5b")

;; (sha256) K constants: first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311
(data (i32.const 32) "\98\2f\8a\42\91\44\37\71\cf\fb\c0\b5\a5\db\b5\e9\5b\c2\56\39\f1\11\f1\59\a4\82\3f\92\d5\5e\1c\ab\98\aa\07\d8\01\5b\83\12\be\85\31\24\c3\7d\0c\55\74\5d\be\72\fe\b1\de\80\a7\06\dc\9b\74\f1\9b\c1\c1\69\9b\e4\86\47\be\ef\c6\9d\c1\0f\cc\a1\0c\24\6f\2c\e9\2d\aa\84\74\4a\dc\a9\b0\5c\da\88\f9\76\52\51\3e\98\6d\c6\31\a8\c8\27\03\b0\c7\7f\59\bf\f3\0b\e0\c6\47\91\a7\d5\51\63\ca\06\67\29\29\14\85\0a\b7\27\38\21\1b\2e\fc\6d\2c\4d\13\0d\38\53\54\73\0a\65\bb\0a\6a\76\2e\c9\c2\81\85\2c\72\92\a1\e8\bf\a2\4b\66\1a\a8\70\8b\4b\c2\a3\51\6c\c7\19\e8\92\d1\24\06\99\d6\85\35\0e\f4\70\a0\6a\10\16\c1\a4\19\08\6c\37\1e\4c\77\48\27\b5\bc\b0\34\b3\0c\1c\39\4a\aa\d8\4e\4f\ca\9c\5b\f3\6f\2e\68\ee\82\8f\74\6f\63\a5\78\14\78\c8\84\08\02\c7\8c\fa\ff\be\90\eb\6c\50\a4\f7\a3\f9\be\f2\78\71\c6")

;; The error code is one of:
;; 0: overflow
;; 1: underflow
Expand Down Expand Up @@ -1190,6 +1196,278 @@
)
)

(func $sha256-buf (param $offset i32) (param $length i32) (param $offset-result i32) (result i32 i32)
(local $i i32)
;; see this for an explanation: https://sha256algorithm.com/

(call $extend-data (local.get $offset) (local.get $length))
(local.set $length)

(local.set $i (i32.const 0))
(loop
(call $block64 (local.get $i))
(call $working-vars)
(br_if 0
(i32.lt_u
(local.tee $i (i32.add (local.get $i) (i32.const 64)))
(local.get $length)
)
)
)

;; store at result position with correct endianness
(v128.store
(local.get $offset-result)
(i8x16.swizzle
(v128.load (global.get $stack-pointer))
(v128.const i8x16 3 2 1 0 7 6 5 4 11 10 9 8 15 14 13 12)
)
)
(v128.store offset=16
(local.get $offset-result)
(i8x16.swizzle
(v128.load offset=16 (global.get $stack-pointer))
(v128.const i8x16 3 2 1 0 7 6 5 4 11 10 9 8 15 14 13 12)
)
)

(local.get $offset-result) (i32.const 32)
)

(func $sha256-int (param $lo i64) (param $hi i64) (param $offset-result i32) (result i32 i32)
;; Copy data to the working stack, so that it has this relative configuration:
;; 0..32 -> Initial hash vals (will be the result hash in the end)
;; 32..288 -> Space to store W
;; 288..352 -> extended int
(memory.copy (global.get $stack-pointer) (i32.const 0) (i32.const 32))

(i64.store offset=288 (global.get $stack-pointer) (local.get $lo))
(i64.store offset=296 (global.get $stack-pointer) (local.get $hi)) ;; offset = 288 + 8
(i32.store offset=304 (global.get $stack-pointer) (i32.const 0x80)) ;; offset = 288+16
(memory.fill (i32.add (global.get $stack-pointer) (i32.const 308)) (i32.const 0) (i32.const 46)) ;; offset = 288+20
(i32.store8 offset=351 (global.get $stack-pointer) (i32.const 0x80)) ;; offset = 288+63

(call $block64 (i32.const 0))
(call $working-vars)

(v128.store
(local.get $offset-result)
(i8x16.swizzle
(v128.load (global.get $stack-pointer))
(v128.const i8x16 3 2 1 0 7 6 5 4 11 10 9 8 15 14 13 12)
)
)
(v128.store offset=16
(local.get $offset-result)
(i8x16.swizzle
(v128.load offset=16 (global.get $stack-pointer))
(v128.const i8x16 3 2 1 0 7 6 5 4 11 10 9 8 15 14 13 12)
)
)

(local.get $offset-result) (i32.const 32)
)

(func $extend-data (param $offset i32) (param $length i32) (result i32)
(local $res_len i32) (local $i i32) (local $len64 i64)
;; TODO: check if enough pages of memory and grow accordingly

;; Move data to the working stack, so that it has this relative configuration:
;; 0..32 -> Initial hash vals (will be the result hash in the end)
;; 32..288 -> Space to store W (result of $block64)
;; 288..$length+288 -> shifted data
(memory.copy (global.get $stack-pointer) (i32.const 0) (i32.const 32))
(memory.copy (i32.add (global.get $stack-pointer) (i32.const 288)) (local.get $offset) (local.get $length))

(local.set $res_len ;; total size of data with expansion
(i32.add
(i32.or
;; len + 1 byte for the added "1" + 8 bytes for the size
(i32.add (local.get $length) (i32.const 9))
(i32.const 0x3f)
)
(i32.const 1)
)
)

;; Add "1" at the end of the data
(i32.store offset=288
(i32.add (global.get $stack-pointer) (local.get $length))
(i32.const 0x80)
)
;; Fill the remaining part before the size with 0s
(memory.fill
(i32.add (i32.add (global.get $stack-pointer) (local.get $length)) (i32.const 289))
(i32.const 0)
(i32.sub (i32.sub (local.get $res_len) (local.get $length)) (i32.const 8))
)

;; Add the size, as a 64bits big-endian integer
(local.set $len64 (i64.extend_i32_u (i32.shl (local.get $length) (i32.const 3))))
(i32.sub (i32.add (global.get $stack-pointer) (local.get $res_len)) (i32.const 8))
(i64.or
(i64.or
(i64.or
(i64.shl (local.get $len64) (i64.const 0x38))
(i64.shl (i64.and (local.get $len64) (i64.const 0xff00)) (i64.const 0x28))
)
(i64.or
(i64.shl (i64.and (local.get $len64) (i64.const 0xff0000)) (i64.const 0x18))
(i64.shl (i64.and (local.get $len64) (i64.const 0xff000000)) (i64.const 0x8))
)
)
(i64.or
(i64.or
(i64.and (i64.shr_u (local.get $len64) (i64.const 0x8)) (i64.const 0xff000000))
(i64.and (i64.shr_u (local.get $len64) (i64.const 0x18)) (i64.const 0xff0000))
)
(i64.or
(i64.and (i64.shr_u (local.get $len64) (i64.const 0x28)) (i64.const 0xff00))
(i64.shr_u (local.get $len64) (i64.const 0x38))
)
)
)
i64.store offset=288

(local.get $res_len)
)

(func $block64 (param $data i32)
(local $origin i32)
(local $i i32) (local $tmp i32)

(local.set $origin (global.get $stack-pointer))
(local.set $data (i32.add (local.get $origin) (local.get $data)))

(local.set $i (i32.const 0))
;; copy first 64 bytes of data to offset as i32 with endianness adjustment
;; Using v128 to process more bytes at a time
;; TODO? : unroll this loop, since it's one instruction 4 times?
(loop
(i32.add (local.get $origin) (local.get $i))
(i8x16.swizzle
(v128.load offset=288 (i32.add (local.get $data) (local.get $i)))
(v128.const i8x16 3 2 1 0 7 6 5 4 11 10 9 8 15 14 13 12)
)
v128.store offset=32

(br_if 0
(i32.lt_u
(local.tee $i (i32.add (local.get $i) (i32.const 16)))
(i32.const 64)
)
)
)

(local.set $i (i32.const 0))
(loop
(local.set $data (i32.add (local.get $origin) (local.get $i)))
;; store address: w(current+16)
(i32.add (local.get $data) (i32.const 64))
;; w(current)
(i32.load offset=32 (local.get $data))
;; sigma 0
(local.set $tmp (i32.load offset=36 (local.get $data))) ;; offset = 32 + 4
(i32.rotr (local.get $tmp) (i32.const 7))
(i32.xor (i32.rotr (local.get $tmp) (i32.const 18)))
(i32.xor (i32.shr_u (local.get $tmp) (i32.const 3)))
i32.add
;; w(current+9)
(i32.add (i32.load offset=68 (local.get $data))) ;; offset = 32+36
;; sigma 1
(local.set $tmp (i32.load offset=88 (local.get $data))) ;; offset = 32+56
(i32.rotr (local.get $tmp) (i32.const 17))
(i32.xor (i32.rotr (local.get $tmp) (i32.const 19)))
(i32.xor (i32.shr_u (local.get $tmp) (i32.const 10)))
i32.add
;; save
i32.store offset=32

(br_if 0
(i32.lt_u
(local.tee $i (i32.add (local.get $i) (i32.const 4)))
(i32.const 192)
)
)
)
)

(func $working-vars
(local $origin i32)
(local $a i32) (local $b i32) (local $c i32) (local $d i32)
(local $e i32) (local $f i32) (local $g i32) (local $h i32)
(local $temp1 i32) (local $temp2 i32) (local $i i32)

(local.set $origin (global.get $stack-pointer))

(local.set $a (i32.load offset=0 (local.get $origin)))
(local.set $b (i32.load offset=4 (local.get $origin)))
(local.set $c (i32.load offset=8 (local.get $origin)))
(local.set $d (i32.load offset=12 (local.get $origin)))
(local.set $e (i32.load offset=16 (local.get $origin)))
(local.set $f (i32.load offset=20 (local.get $origin)))
(local.set $g (i32.load offset=24 (local.get $origin)))
(local.set $h (i32.load offset=28 (local.get $origin)))

(local.set $i (i32.const 0))
(loop
;; compute $temp1: h + sigma1 + choice + k0 + w0
(local.get $h) ;; h

(i32.rotr (local.get $e) (i32.const 6))
(i32.xor (i32.rotr (local.get $e) (i32.const 11)))
(i32.xor (i32.rotr (local.get $e) (i32.const 25)))
i32.add ;; + sigma1

(i32.and (local.get $e) (local.get $f))
(i32.xor (i32.and (i32.xor (local.get $e) (i32.const -1)) (local.get $g)))
i32.add ;; + choice

(i32.add (i32.load offset=32 (local.get $i))) ;; + k(current)

(i32.add (i32.load offset=32 (i32.add (local.get $origin) (local.get $i)))) ;; + w(current)
(local.set $temp1)

;; compute temp2: sigma0 + majority
(i32.rotr (local.get $a) (i32.const 2))
(i32.xor (i32.rotr (local.get $a) (i32.const 13)))
(i32.xor (i32.rotr (local.get $a) (i32.const 22))) ;; sigma0

(i32.and (local.get $a) (local.get $b))
(i32.xor (i32.and (local.get $a) (local.get $c)))
(i32.xor (i32.and (local.get $b) (local.get $c)))
i32.add ;; + majority
(local.set $temp2)

;; assign new variables
(local.set $h (local.get $g))
(local.set $g (local.get $f))
(local.set $f (local.get $e))
(local.set $e (i32.add (local.get $d) (local.get $temp1)))
(local.set $d (local.get $c))
(local.set $c (local.get $b))
(local.set $b (local.get $a))
(local.set $a (i32.add (local.get $temp1) (local.get $temp2)))

(br_if 0
(i32.lt_u
(local.tee $i (i32.add (local.get $i) (i32.const 4)))
(i32.const 256)
)
)
)

;; update hash
(i32.store offset=0 (local.get $origin) (i32.add (i32.load offset=0 (local.get $origin)) (local.get $a)))
(i32.store offset=4 (local.get $origin) (i32.add (i32.load offset=4 (local.get $origin)) (local.get $b)))
(i32.store offset=8 (local.get $origin) (i32.add (i32.load offset=8 (local.get $origin)) (local.get $c)))
(i32.store offset=12 (local.get $origin) (i32.add (i32.load offset=12 (local.get $origin)) (local.get $d)))
(i32.store offset=16 (local.get $origin) (i32.add (i32.load offset=16 (local.get $origin)) (local.get $e)))
(i32.store offset=20 (local.get $origin) (i32.add (i32.load offset=20 (local.get $origin)) (local.get $f)))
(i32.store offset=24 (local.get $origin) (i32.add (i32.load offset=24 (local.get $origin)) (local.get $g)))
(i32.store offset=28 (local.get $origin) (i32.add (i32.load offset=28 (local.get $origin)) (local.get $h)))
)

(export "memcpy" (func $memcpy))
(export "add-uint" (func $add-uint))
(export "add-int" (func $add-int))
Expand Down Expand Up @@ -1227,4 +1505,6 @@
(export "bit-shift-right-int" (func $bit-shift-right-int))
(export "pow-uint" (func $pow-uint))
(export "pow-int" (func $pow-int))
(export "sha256-buf" (func $sha256-buf))
(export "sha256-int" (func $sha256-int))
)
Loading

0 comments on commit 1f1bf9b

Please sign in to comment.