diff --git a/evm/Cargo.toml b/evm/Cargo.toml index 5ee3b1ff..24e5c661 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -22,6 +22,7 @@ pest = "2.1.3" pest_derive = "2.1.0" rand = "0.8.5" rand_chacha = "0.3.1" +ripemd = "0.1.3" rlp = "0.5.1" serde = { version = "1.0.144", features = ["derive"] } sha2 = "0.10.2" diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index d339fa99..0d94c86f 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -48,6 +48,13 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/mpt/storage_write.asm"), include_str!("asm/mpt/util.asm"), include_str!("asm/mpt/write.asm"), + include_str!("asm/ripemd/box.asm"), + include_str!("asm/ripemd/compression.asm"), + include_str!("asm/ripemd/constants.asm"), + include_str!("asm/ripemd/functions.asm"), + include_str!("asm/ripemd/main.asm"), + include_str!("asm/ripemd/memory.asm"), + include_str!("asm/ripemd/update.asm"), include_str!("asm/rlp/encode.asm"), include_str!("asm/rlp/decode.asm"), include_str!("asm/rlp/num_bytes.asm"), diff --git a/evm/src/cpu/kernel/asm/memory/core.asm b/evm/src/cpu/kernel/asm/memory/core.asm index 26196df5..2ec27d0e 100644 --- a/evm/src/cpu/kernel/asm/memory/core.asm +++ b/evm/src/cpu/kernel/asm/memory/core.asm @@ -55,8 +55,7 @@ // stack: (empty) %endmacro -// Load a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0), -// from the kernel. +// Load from the kernel a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0) %macro mload_kernel_u32(segment) // stack: offset DUP1 @@ -85,6 +84,32 @@ // stack: (((((c_3 << 8) | c_2) << 8) | c_1) << 8) | c_0 %endmacro +// Load from the kernel a little-endian u32, consisting of 4 bytes (c_0, c_1, c_2, c_3) +%macro mload_kernel_u32_LE(segment) + // stack: offset + DUP1 + %mload_kernel($segment) + // stack: c0 , offset + DUP2 + %add_const(1) + %mload_kernel($segment) + %shl_const(8) + OR + // stack: c0 | (c1 << 8) , offset + DUP2 + %add_const(2) + %mload_kernel($segment) + %shl_const(16) + OR + // stack: c0 | (c1 << 8) | (c2 << 16), offset + SWAP1 + %add_const(3) + %mload_kernel($segment) + %shl_const(24) + OR + // stack: c0 | (c1 << 8) | (c2 << 16) | (c3 << 24) +%endmacro + // Load a u256 (big-endian) from the kernel. %macro mload_kernel_u256(segment) // stack: offset @@ -204,11 +229,13 @@ // stack: value %endmacro -// Load a single byte from kernel general memory. -%macro mload_kernel_general - // stack: offset - %mload_kernel(@SEGMENT_KERNEL_GENERAL) - // stack: value +%macro mload_kernel_code(label) + // stack: shift + PUSH $label + ADD + // stack: label + shift + %mload_kernel_code + // stack: byte %endmacro // Load a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0), @@ -219,6 +246,24 @@ // stack: value %endmacro +%macro mload_kernel_code_u32(label) + // stack: u32_shift + %mul_const(4) + // stack: byte_shift + PUSH $label + ADD + // stack: offset + %mload_kernel_u32(@SEGMENT_CODE) + // stack: value +%endmacro + +// Load a single byte from kernel general memory. +%macro mload_kernel_general + // stack: offset + %mload_kernel(@SEGMENT_KERNEL_GENERAL) + // stack: value +%endmacro + // Load a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0), // from kernel general memory. %macro mload_kernel_general_u32 @@ -227,6 +272,14 @@ // stack: value %endmacro +// Load a little-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0), +// from kernel general memory. +%macro mload_kernel_general_u32_LE + // stack: offset + %mload_kernel_u32_LE(@SEGMENT_KERNEL_GENERAL) + // stack: value +%endmacro + // Load a u256 (big-endian) from kernel code. %macro mload_kernel_code_u256 // stack: offset @@ -248,13 +301,6 @@ // stack: (empty) %endmacro -// Store a single byte to kernel general memory. -%macro mstore_kernel_general - // stack: offset, value - %mstore_kernel(@SEGMENT_KERNEL_GENERAL) - // stack: (empty) -%endmacro - // Store a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0), // to kernel code. %macro mstore_kernel_code_u32 @@ -262,6 +308,27 @@ %mstore_kernel_u32(@SEGMENT_CODE) %endmacro +// Store a single byte to @SEGMENT_RLP_RAW. +%macro mstore_rlp + // stack: offset, value + %mstore_kernel(@SEGMENT_RLP_RAW) + // stack: (empty) +%endmacro + +%macro mstore_kernel_general + // stack: offset, value + %mstore_kernel(@SEGMENT_KERNEL_GENERAL) + // stack: +%endmacro + +%macro mstore_kernel_general(offset) + // stack: value + PUSH $offset + // stack: offset, value + %mstore_kernel_general + // stack: +%endmacro + // Store a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0), // to kernel general memory. %macro mstore_kernel_general_u32 @@ -269,9 +336,12 @@ %mstore_kernel_u32(@SEGMENT_KERNEL_GENERAL) %endmacro -// Store a single byte to @SEGMENT_RLP_RAW. -%macro mstore_rlp - // stack: offset, value - %mstore_kernel(@SEGMENT_RLP_RAW) - // stack: (empty) +// set offset i to offset j in kernel general +%macro mupdate_kernel_general + // stack: j, i + %mload_kernel_general + // stack: x, i + SWAP1 + %mstore_kernel_general + // stack: %endmacro diff --git a/evm/src/cpu/kernel/asm/ripemd/box.asm b/evm/src/cpu/kernel/asm/ripemd/box.asm new file mode 100644 index 00000000..d60d9b8c --- /dev/null +++ b/evm/src/cpu/kernel/asm/ripemd/box.asm @@ -0,0 +1,96 @@ +/// Note that we unpack STATE: 5 to a, b, c, d, e +/// All additions are u32 +/// +/// def box(a, b, c, d, e, F, K): +/// +/// box = get_box(sides, rounds, boxes) +/// a += F(b, c, d) +/// r = load(r)(box) +/// x = load_offset(r) +/// a += x + K +/// s = load(s)(box) +/// a = rol(s, a) +/// a += e +/// c = rol(10, c) +/// +/// return e, a, b, c, d, F, K + +global box: + // stack: a, b, c, d, e, F, K, boxes, rounds, sides, virt + PUSH pre_rol + DUP5 + DUP5 + DUP5 + DUP10 + // stack: F, b, c, d, pre_rol, a, b, c, d, e, F, K, boxes, rounds, sides, virt + JUMP +pre_rol: + // stack: F(b, c, d), a, b, c, d, e, F, K, boxes, rounds, sides, virt + ADD + // stack: a, b, c, d, e, F, K, boxes, rounds, sides, virt + %get_box + // stack: box, a, b, c, d, e, F, K, boxes, rounds, sides, virt + DUP12 + DUP2 + %mload_kernel_code(r_data) + ADD + // stack: virt + r, box, a, b, c, d, e, F, K, boxes, rounds, sides, virt + %mload_kernel_general_u32_LE + // stack: x, box, a, b, c, d, e, F, K, boxes, rounds, sides, virt + SWAP1 + SWAP2 + // stack: a, x, box, b, c, d, e, F, K, boxes, rounds, sides, virt + ADD + DUP8 + ADD + %as_u32 + // stack: a, box, b, c, d, e, F, K, boxes, rounds, sides, virt + PUSH mid_rol + SWAP2 + // stack: box, a, mid_rol, b, c, d, e, F, K, boxes, rounds, sides, virt + %mload_kernel_code(s_data) + // stack: s, a, mid_rol, b, c, d, e, F, K, boxes, rounds, sides, virt + %jump(rol) +mid_rol: + // stack: a, b, c, d, e, F, K, boxes, rounds, sides, virt + DUP5 + // stack: e, a, b, c, d, e, F, K, boxes, rounds, sides, virt + ADD + %as_u32 + // stack: a, b, c, d, e, F, K, boxes, rounds, sides, virt + %stack (a, b, c) -> (10, c, post_rol, a, b) + // stack: 10, c, post_rol, a, b, d, e, F, K, boxes, rounds, sides, virt + %jump(rol) +post_rol: + // stack: c, a, b, d, e, F, K, boxes , rounds, sides, virt + %stack (c, a, b, d, e, F, K, boxes) -> (boxes, 1, a, b, c, d, F, K, e) + // stack: boxes, 1, a, b, c, d, F, K, e, rounds, sides, virt + SUB + SWAP7 + // stack: e, a, b, c, d, F, K, boxes-1, rounds, sides, virt + %jump(round) + + +%macro get_round + // stack: sides, rounds + %mul_const(5) + PUSH 10 + SUB + SUB + // stack: 10 - 5*sides - rounds +%endmacro + +%macro get_box + // stack: ARGS: 7, boxes, rounds, sides + DUP10 + %mul_const(80) + DUP10 + %mul_const(16) + DUP10 + // stack: boxes , 16*rounds , 80*sides, ARGS: 7, boxes, rounds, sides + PUSH 176 + SUB + SUB + SUB + // stack: 176 - boxes - 16*rounds - 80*sides, ARGS: 7, boxes, rounds, sides +%endmacro diff --git a/evm/src/cpu/kernel/asm/ripemd/compression.asm b/evm/src/cpu/kernel/asm/ripemd/compression.asm new file mode 100644 index 00000000..a83bf832 --- /dev/null +++ b/evm/src/cpu/kernel/asm/ripemd/compression.asm @@ -0,0 +1,160 @@ +/// _block is stored in memory: its address virt stays on the stack +/// def compress(STATE: 5, _block): +/// +/// STATEL = STATE +/// STATEL = loop(STATEL) +/// +/// STATER = state +/// STATER = loop(STATER) +/// +/// return mix(STATER, STATEL, STATE) +/// +/// +/// def mix(STATER, STATEL, STATE): +/// return +/// u32(s1 + l2 + r3), +/// u32(s2 + l3 + r4), +/// u32(s3 + l4 + r0), +/// u32(s4 + l0 + r1), +/// u32(s0 + l1 + r2) +/// +/// where si, li, ri, oi, VR, RD respectively denote +/// STATE[i], STATEL[i], STATER[i], OUTPUT[i], virt, retdest + +global compress: + // stack: STATE, virt, retdest + PUSH switch + DUP7 + %stack () -> (0, 0, 16, 5, 1) + // stack: 0, 0, 16, 5, 1, virt, switch, STATE, virt, retdest + DUP12 + DUP12 + DUP12 + DUP12 + DUP12 + // stack: STATE, 0, 0, 16, 5, 1, virt, switch, STATE, virt, retdest + %jump(loop) +switch: + // stack: STATEL, STATE, virt, retdest + PUSH mix + DUP12 + %stack () -> (16, 5, 0) + // stack: 16, 5, 0, virt, mix, STATEL, STATE, virt, retdest + DUP15 + DUP15 + DUP15 + DUP15 + DUP15 + // stack: STATE, 16, 5, 0, virt, mix, STATEL, STATE, virt, retdest + %stack (STATE: 5) -> (STATE, 0, 0) + // stack: STATE, 0, 0, 16, 5, 0, virt, mix, STATEL, STATE, virt, retdest + %jump(loop) +mix: + // stack: r0, r1, r2, r3, r4, l0, l1, l2, l3, l4, s0, s1, s2, s3, s4, VR, RD + SWAP10 + // stack: s0, r1, r2, r3, r4, l0, l1, l2, l3, l4, r0, s1, s2, s3, s4, VR, RD + SWAP1 + // stack: r1, s0, r2, r3, r4, l0, l1, l2, l3, l4, r0, s1, s2, s3, s4, VR, RD + SWAP6 + // stack: l1, s0, r2, r3, r4, l0, r1, l2, l3, l4, r0, s1, s2, s3, s4, VR, RD + %add3_u32 + // stack: o4, r3, r4, l0, r1, l2, l3, l4, r0, s1, s2, s3, s4, VR, RD + SWAP14 + // stack: RD, r3, r4, l0, r1, l2, l3, l4, r0, s1, s2, s3, s4, VR, o4 + SWAP11 + // stack: s3, r3, r4, l0, r1, l2, l3, l4, r0, s1, s2, RD, s4, VR, o4 + SWAP10 + // stack: s2, r3, r4, l0, r1, l2, l3, l4, r0, s1, s3, RD, s4, VR, o4 + SWAP1 + // stack: r3, s2, r4, l0, r1, l2, l3, l4, r0, s1, s3, RD, s4, VR, o4 + SWAP6 + // stack: l3, s2, r4, l0, r1, l2, r3, l4, r0, s1, s3, RD, s4, VR, o4 + %add3_u32 + // stack: o1, l0, r1, l2, r3, l4, r0, s1, s3, RD, s4, VR, o4 + SWAP9 + // stack: RD, l0, r1, l2, r3, l4, r0, s1, s3, o1, s4, VR, o4 + SWAP10 + // stack: s4, l0, r1, l2, r3, l4, r0, s1, s3, o1, RD, VR, o4 + %add3_u32 + // stack: o3, l2, r3, l4, r0, s1, s3, o1, RD, VR, o4 + SWAP9 + // stack: VR, l2, r3, l4, r0, s1, s3, o1, RD, o3, o4 + SWAP5 + // stack: s1, l2, r3, l4, r0, VR, s3, o1, RD, o3, o4 + %add3_u32 + // stack: o0, l4, r0, VR, s3, o1, RD, o3, o4 + SWAP4 + // stack: s3, l4, r0, VR, o0, o1, RD, o3, o4 + %add3_u32 + // stack: o2, VR, o0, o1, RD, o3, o4 + SWAP4 + // stack: RD, VR, o0, o1, o2, o3, o4 + SWAP1 + // stack: VR, RD, o0, o1, o2, o3, o4 + POP + // stack: RD, o0, o1, o2, o3, o4 + JUMP + + +/// def loop(STATE: 5): +/// while rounds: +/// update_round_vars() +/// round(STATE: 5, F, K, rounds, sides) +/// +/// def update_round_vars(): +/// F = load(F)(sides, rounds) +/// K = load(K)(sides, rounds) +/// +/// def round(STATE, rounds, sides): +/// while boxes: +/// box(STATE, F, K) +/// boxes -= 1 +/// boxes = 16 +/// rounds -= 1 + +loop: + // stack: STATE, F, K, 16, rounds, sides, virt, retdest + DUP9 + // stack: round, STATE, F, K, 16, rounds, sides, virt, retdest + %jumpi(update_round_vars) + // stack: STATE, F, K, 16, 0, sides, virt, retdest + %stack (STATE: 5, F, K, boxes, rounds, sides, virt, retdest) -> (retdest, STATE) + // stack: retdest, STATE + JUMP +update_round_vars: + // stack: STATE, F , K , 16, rounds, sides, virt, retdest + DUP9 + DUP11 + %get_round + DUP1 + // stack: rnd, rnd, STATE, F , K , 16, rounds, sides, virt, retdest + SWAP7 + POP + %push_f + SWAP7 + // stack: rnd, rnd, STATE, F', K , 16, rounds, sides, virt, retdest + SWAP8 + POP + %mload_kernel_code_u32(k_data) + SWAP7 + POP + // stack: STATE, F', K', 16, rounds, sides, virt, retdest + %jump(round) +global round: + // stack: STATE, F, K, boxes, rounds , sides, virt, retdest + DUP8 + // stack: boxes, STATE, F, K, boxes, rounds , sides, virt, retdest + %jumpi(box) + // stack: STATE, F, K, 0, rounds , sides, virt, retdest + SWAP7 + POP + PUSH 16 + SWAP7 + // stack: STATE, F, K, 16, rounds , sides, virt, retdest + PUSH 1 + DUP10 + SUB + SWAP9 + POP + // stack: STATE, F, K, 16, rounds-1, sides, virt, retdest + %jump(loop) diff --git a/evm/src/cpu/kernel/asm/ripemd/constants.asm b/evm/src/cpu/kernel/asm/ripemd/constants.asm new file mode 100644 index 00000000..7a8959fe --- /dev/null +++ b/evm/src/cpu/kernel/asm/ripemd/constants.asm @@ -0,0 +1,117 @@ +global k_data: + // Left + BYTES 0x00, 0x00, 0x00, 0x00 + BYTES 0x5A, 0x82, 0x79, 0x99 + BYTES 0x6E, 0xD9, 0xEB, 0xA1 + BYTES 0x8F, 0x1B, 0xBC, 0xDC + BYTES 0xA9, 0x53, 0xFD, 0x4E + // Right + BYTES 0x50, 0xA2, 0x8B, 0xE6 + BYTES 0x5C, 0x4D, 0xD1, 0x24 + BYTES 0x6D, 0x70, 0x3E, 0xF3 + BYTES 0x7A, 0x6D, 0x76, 0xE9 + BYTES 0x00, 0x00, 0x00, 0x00 + +global s_data: + // Left Round 0 + BYTES 11, 14, 15, 12 + BYTES 05, 08, 07, 09 + BYTES 11, 13, 14, 15 + BYTES 06, 07, 09, 08 + // Left Round 1 + BYTES 07, 06, 08, 13 + BYTES 11, 09, 07, 15 + BYTES 07, 12, 15, 09 + BYTES 11, 07, 13, 12 + // Left Round 2 + BYTES 11, 13, 06, 07 + BYTES 14, 09, 13, 15 + BYTES 14, 08, 13, 06 + BYTES 05, 12, 07, 05 + // Left Round 3 + BYTES 11, 12, 14, 15 + BYTES 14, 15, 09, 08 + BYTES 09, 14, 05, 06 + BYTES 08, 06, 05, 12 + // Left Round 4 + BYTES 09, 15, 05, 11 + BYTES 06, 08, 13, 12 + BYTES 05, 12, 13, 14 + BYTES 11, 08, 05, 06 + // Right Round 0 + BYTES 08, 09, 09, 11 + BYTES 13, 15, 15, 05 + BYTES 07, 07, 08, 11 + BYTES 14, 14, 12, 06 + // Right Round 1 + BYTES 09, 13, 15, 07 + BYTES 12, 08, 09, 11 + BYTES 07, 07, 12, 07 + BYTES 06, 15, 13, 11 + // Right Round 2 + BYTES 09, 07, 15, 11 + BYTES 08, 06, 06, 14 + BYTES 12, 13, 05, 14 + BYTES 13, 13, 07, 05 + // Right Round 3 + BYTES 15, 05, 08, 11 + BYTES 14, 14, 06, 14 + BYTES 06, 09, 12, 09 + BYTES 12, 05, 15, 08 + // Right Round 4 + BYTES 08, 05, 12, 09 + BYTES 12, 05, 14, 06 + BYTES 08, 13, 06, 05 + BYTES 15, 13, 11, 11 + +global r_data: + // Left Round 0 + BYTES 00, 04, 08, 12 + BYTES 16, 20, 24, 28 + BYTES 32, 36, 40, 44 + BYTES 48, 52, 56, 60 + // Left Round 1 + BYTES 28, 16, 52, 04 + BYTES 40, 24, 60, 12 + BYTES 48, 00, 36, 20 + BYTES 08, 56, 44, 32 + // Left Round 2 + BYTES 12, 40, 56, 16 + BYTES 36, 60, 32, 04 + BYTES 08, 28, 00, 24 + BYTES 52, 44, 20, 48 + // Left Round 3 + BYTES 04, 36, 44, 40 + BYTES 00, 32, 48, 16 + BYTES 52, 12, 28, 60 + BYTES 56, 20, 24, 08 + // Left Round 4 + BYTES 16, 00, 20, 36 + BYTES 28, 48, 08, 40 + BYTES 56, 04, 12, 32 + BYTES 44, 24, 60, 52 + // Right Round 0 + BYTES 20, 56, 28, 00 + BYTES 36, 08, 44, 16 + BYTES 52, 24, 60, 32 + BYTES 04, 40, 12, 48 + // Right Round 1 + BYTES 24, 44, 12, 28 + BYTES 00, 52, 20, 40 + BYTES 56, 60, 32, 48 + BYTES 16, 36, 04, 08 + // Right Round 2 + BYTES 60, 20, 04, 12 + BYTES 28, 56, 24, 36 + BYTES 44, 32, 48, 08 + BYTES 40, 00, 16, 52 + // Right Round 3 + BYTES 32, 24, 16, 04 + BYTES 12, 44, 60, 00 + BYTES 20, 48, 08, 52 + BYTES 36, 28, 40, 56 + // Right Round 4 + BYTES 48, 60, 40, 16 + BYTES 04, 20, 32, 28 + BYTES 24, 08, 52, 56 + BYTES 00, 12, 36, 44 diff --git a/evm/src/cpu/kernel/asm/ripemd/functions.asm b/evm/src/cpu/kernel/asm/ripemd/functions.asm new file mode 100644 index 00000000..ac111215 --- /dev/null +++ b/evm/src/cpu/kernel/asm/ripemd/functions.asm @@ -0,0 +1,150 @@ +/// def rol(n, x): +/// return (u32(x << n)) | (x >> (32 - n)) + +global rol: + // stack: n, x, retdest + SWAP1 + DUP1 + DUP3 + // stack: n, x, x, n, retdest + PUSH 32 + SUB + // stack: 32-n, x, x, n, retdest + SHR + // stack: x >> (32-n), x, n, retdest + SWAP2 + // stack: n, x, x >> (32-n), retdest + SHL + // stack: x << n, x >> (32-n), retdest + %as_u32 + // stack: u32(x << n), x >> (32-n), retdest + OR + // stack: u32(x << n) | (x >> (32-n)), retdest + SWAP1 + JUMP + +// def push_f(rnd): +// Fs = [F0, F1, F2, F3, F4, F4, F3, F2, F1, F0] +// acc = 0 +// for i, F in enumerate(Fs): +// acc += (i==rnd)*F +// return acc, rnd +// +// %this_f(i,F) enacts +// acc += (i==rnd)*F + +%macro push_f + // stack: rnd + PUSH 0 + %this_f(0,F0) + %this_f(1,F1) + %this_f(2,F2) + %this_f(3,F3) + %this_f(4,F4) + %this_f(5,F4) + %this_f(6,F3) + %this_f(7,F2) + %this_f(8,F1) + %this_f(9,F0) + // stack: F, rnd +%endmacro + +%macro this_f(i, F) + // stack: acc, rnd + DUP2 + // stack: rnd , acc, rnd + %eq_const($i) + // stack: rnd==i , acc, rnd + %mul_const($F) + // stack: (rnd==i)*F , acc, rnd + ADD + // stack: (rnd==j)*F + acc, rnd +%endmacro + +/// def F0(x, y, z): +/// return x ^ y ^ z + +global F0: + // stack: x , y , z, retdest + XOR + // stack: x ^ y , z, retdest + XOR + // stack: x ^ y ^ z, retdest + SWAP1 + JUMP + +/// def F1(x, y, z): +/// return (x & y) | (u32(~x) & z) + +global F1: + // stack: x, y, z, retdest + DUP1 + // stack: x, x, y, z, retdest + SWAP2 + // stack: y, x, x, z, retdest + AND + // stack: y & x, x, z, retdest + SWAP2 + // stack: z, x, y & x , retdest + SWAP1 + // stack: x, z, y & x , retdest + %not_u32 + // stack: ~x, z, y & x , retdest + AND + // stack: ~x & z , y & x , retdest + OR + // stack: (~x & z) | (y & x), retdest + SWAP1 + JUMP + +/// def F2(x, y, z): +/// return (x | u32(~y)) ^ z + +global F2: + // stack: x , y, z, retdest + SWAP1 + // stack: y , x, z, retdest + %not_u32 + // stack: ~y , x , z, retdest + OR + // stack: ~y | x , z, retdest + XOR + // stack: (~y | x) ^ z, retdest + SWAP1 + JUMP + +/// def F3(x, y, z): +/// return (x & z) | (u32(~z) & y) + +global F3: + // stack: x, y , z , retdest + DUP3 + // stack: z , x, y , z , retdest + AND + // stack: z & x, y , z , retdest + SWAP2 + // stack: z, y, z & x , retdest + %not_u32 + // stack: ~z , y, z & x , retdest + AND + // stack: ~z & y, z & x , retdest + OR + // stack: (~z & y) | (z & x), retdest + SWAP1 + JUMP + +/// def F4(x, y, z): +/// return x ^ (y | u32(~z)) + +global F4: + // stack: x, y, z, retdest + SWAP2 + // stack: z, y, x, retdest + %not_u32 + // stack: ~z, y, x, retdest + OR + // stack: ~z | y, x, retdest + XOR + // stack: (~z | y) ^ x, retdest + SWAP1 + JUMP diff --git a/evm/src/cpu/kernel/asm/ripemd/main.asm b/evm/src/cpu/kernel/asm/ripemd/main.asm new file mode 100644 index 00000000..f9b28c94 --- /dev/null +++ b/evm/src/cpu/kernel/asm/ripemd/main.asm @@ -0,0 +1,107 @@ +/// Variables beginning with _ are in memory +/// +/// def ripemd160(_input): +/// STATE, count, _buffer = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0], 0, [0]*64 +/// STATE, count, _buffer = ripemd_update(STATE, count, _buffer, len(input) , bytes = _input ) +/// STATE, count, _buffer = ripemd_update(STATE, count, _buffer, padlength(len(input)), bytes = [0x80]+[0]*63) +/// STATE, count, _buffer = ripemd_update(STATE, count, _buffer, 8, bytes = size(len(_input))) +/// return process(STATE) +/// +/// ripemd is called on a stack with ADDR and length +/// ripemd_stack is called on a stack with length, followed by the input bytes +/// +/// ripemd_update receives and return the stack in the form: +/// stack: STATE, count, length, virt +/// where virt is the virtual address of the bytes argument + +global ripemd_stack: + // stack: length, INPUT + %stack (length) -> (64, length, 0x80, 63, length, length) + // stack: 64, length, 0x80, 63, length, length, INPUT + %jump(ripemd_storage) // stores the following into memory + // init _buffer at virt 0 [consumes 64] + // store _size at virt 64 [consumes length] + // store _padding at virt 72 [consumes 0x80, 63] + // store _input at virt 136 [consumes length] + +global ripemd: + // stack: ADDR, length + %stack (ADDR: 3, length) -> (64, length, 0x80, 63, length, ADDR, length) + // stack: 64, length, 0x80, 63, length, ADDR, length + %jump(ripemd_storage) // stores the following into memory + // init _buffer at virt 0 [consumes 64] + // store _size at virt 64 [consumes length] + // store _padding at virt 72 [consumes 0x80, 63] + // store _input at virt 136 [consumes ADDR, length] + +global ripemd_init: + // stack: length + %stack (length) -> ( 0, length, 136, ripemd_1, ripemd_2, process) + // stack: count = 0, length, virt = 136, ripemd_1, ripemd_2, process + %stack () -> (0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0) + // stack: 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0, count, length, virt, LABELS + %jump(ripemd_update) +ripemd_1: + // stack: STATE, count, length , virt , LABELS + DUP7 + // stack: length, STATE, count, length , virt , LABELS + %padlength + // stack: padlength, STATE, count, length , virt , LABELS + SWAP7 + POP + // stack: STATE, count, length = padlength, virt , LABELS + %stack (STATE: 5, count, length, virt) -> (STATE, count, length, 72) + // STATE, count, length , virt = 72, LABELS + %jump(ripemd_update) +ripemd_2: + // stack: STATE, count, length , virt , LABELS + %stack (STATE: 5, count, length, virt) -> (STATE, count, 8, 64) + // stack: STATE, count, length = 8, virt = 64, LABELS + %jump(ripemd_update) +global process: + // stack: a , b, c, d, e, count, length, virt + %reverse_bytes_u32 + %shl_const(128) + // stack: a', b, c, d, e, VARS + SWAP1 + %reverse_bytes_u32 + %shl_const(96) + OR + // stack: b' a', c, d, e, VARS + SWAP1 + %reverse_bytes_u32 + %shl_const(64) + OR + // stack: c' b' a', d, e, VARS + SWAP1 + %reverse_bytes_u32 + %shl_const(32) + OR + // stack: d' c' b' a', e, VARS + SWAP1 + %reverse_bytes_u32 + OR + // stack: e' d' c' b' a', VARS + %stack (result, VARS: 3, retdest) -> (retdest, result) + // stack: 0xdeadbeef, result + JUMP + + +/// def padlength(length): +/// t = length % 64 +/// return 56 + 64*(t > 47) - t + +%macro padlength + // stack: count + %mod_const(64) + // stack: t = count % 64 + PUSH 47 + DUP2 + // stack: t , 47 , t + GT + // stack: t > 47 , t + %mul_const(64) + %add_const(56) + // stack: 56 + 64*(t > 47), t + SUB +%endmacro diff --git a/evm/src/cpu/kernel/asm/ripemd/memory.asm b/evm/src/cpu/kernel/asm/ripemd/memory.asm new file mode 100644 index 00000000..6c684eb1 --- /dev/null +++ b/evm/src/cpu/kernel/asm/ripemd/memory.asm @@ -0,0 +1,137 @@ +global ripemd_storage: // starts by initializing buffer + // stack: i [init: 64] + %store_zeros(64, ripemd_storage) + // stack: + %jump(store_size) + +store_size: + // stack: length + %shl_const(3) + // stack: abcdefgh + %extract_and_store_byte(64) + // stack: abcdefg + %extract_and_store_byte(65) + // stack: abcdef + %extract_and_store_byte(66) + // stack: abcde + %extract_and_store_byte(67) + // stack: abcd + %extract_and_store_byte(68) + // stack: abc + %extract_and_store_byte(69) + // stack: ab + %extract_and_store_byte(70) + // stack: a + %mstore_kernel_general(71) + // stack: 0x80 // padding has 0x80 in first position and zeros elsewhere + %mstore_kernel_general(72) // store first padding term here so as to avoid extra label + %jump(store_padding) + +store_padding: + // stack: i [init 63], length + %store_zeros(136, store_padding) + // stack: length + DUP1 + %jumpi(store_input_stack) + POP + %jump(ripemd_init) + +store_input_stack: + // stack: rem, length, REM_INP + %stack (rem, length, head) -> (length, rem, 136, head, rem, length) + SUB + ADD + // stack: offset, byte, rem, length, REM_INP + %mstore_kernel_general + // stack: rem, length, REM_INP + %sub_const(1) + DUP1 + // stack: rem - 1, rem - 1, length, REM_INP + %jumpi(store_input_stack) + // stack: 0, length + POP + %jump(ripemd_init) + +store_input: + // stack: rem , ADDR , length + DUP4 + DUP4 + DUP4 + MLOAD_GENERAL + // stack: byte, rem , ADDR , length + DUP2 + DUP7 + SUB + %add_const(136) + // stack: offset, byte, rem , ADDR , length + %mstore_kernel_general + // stack: rem , ADDR , length + %sub_const(1) + // stack: rem-1, ADDR , length + SWAP3 + %add_const(1) + SWAP3 + // stack: rem-1, ADDR+1, length + DUP1 + %jumpi(store_input) + // stack: 0 , ADDR , length + %pop4 + // stack: length + %jump(ripemd_init) + +/// def buffer_update(get, set, times): +/// for i in range(times): +/// buffer[set+i] = bytestring[get+i] + +global buffer_update: + // stack: get , set , times , retdest + DUP2 + DUP2 + // stack: get, set, get , set , times , retdest + %mupdate_kernel_general + // stack: get , set , times , retdest + %add_const(1) + SWAP1 + %add_const(1) + SWAP1 + SWAP2 + %sub_const(1) + SWAP2 + // stack: get+1, set+1, times-1, retdest + DUP3 + %jumpi(buffer_update) + // stack: get , set , 0 , retdest + %pop3 + JUMP + + +%macro store_zeros(N, label) + // stack: i + %stack (i) -> ($N, i, 0, i) + SUB + // stack: offset = N-i, 0, i + %mstore_kernel_general + // stack: i + %sub_const(1) + DUP1 + // stack: i-1, i-1 + %jumpi($label) + // stack: 0 + POP +%endmacro + +%macro extract_and_store_byte(offset) + // stack: xsy + PUSH 0x100 + DUP2 + MOD + // stack: y, xsy + %stack (y, xsy) -> (xsy, y, 0x100, y) + // stack: xsy, y, 0x100, y + SUB + DIV + SWAP1 + // stack: y, xs + %mstore_kernel_general($offset) + // stack: xs +%endmacro diff --git a/evm/src/cpu/kernel/asm/ripemd/update.asm b/evm/src/cpu/kernel/asm/ripemd/update.asm new file mode 100644 index 00000000..a0c3ef68 --- /dev/null +++ b/evm/src/cpu/kernel/asm/ripemd/update.asm @@ -0,0 +1,108 @@ +/// ripemd_update will receive and return the stack in the form: +/// stack: STATE, count, length, virt +/// +/// def ripemd_update(state, count, buffer, length, bytestring): +/// have = (count // 8) % 64 +/// need = 64 - have +/// shift = 0 +/// P = length >= need and have +/// Q = length >= need +/// if P: +/// update_1() +/// if Q: +/// update_2() +/// R = length > shift +/// if R: +/// buffer_update(virt + shift, have, length - shift) +/// +/// return state, count + 8*length, buffer + +global ripemd_update: + // stack: STATE, count, length, virt, retdest + %stack (STATE: 5, count, length, virt) -> (count, 8, 64, STATE, count, length, virt) + DIV + MOD + // stack: have, STATE, count, length, virt, retdest + DUP1 + PUSH 64 + SUB + PUSH 0 + // stack: shift, need, have, STATE, count, length, virt, retdest + %stack (shift, need, have, STATE: 5, count, length) -> (length, need, STATE, shift, need, have, count, length) + // stack: length, need, STATE, shift, need, have, count, length, virt, retdest + LT + ISZERO + // stack: Q, STATE, shift, need, have, count, length, virt, retdest + %stack (Q, STATE: 5, shift, need, have) -> (have, Q, Q, STATE, shift, need, have) + %gt_const(0) + AND + // stack: P, Q, STATE, shift, need, have, count, length, virt, retdest + %jumpi(update_1) + // stack: Q, STATE, shift, need, have, count, length, virt, retdest + %jumpi(update_2) +final_update: + // stack: STATE, shift, need, have, count, length, virt, retdest + %stack (STATE: 5, shift, need, have, count, length) -> (length, shift, return_step, STATE, shift, need, have, count, length) + SUB + // stack: ARGS: 2, STATE, shift, need, have, count, length, virt, retdest + %stack (ARGS: 2, STATE: 5, shift, need, have, count, length, virt) -> (shift, virt, have, ARGS, STATE, shift, need, have, count, length, virt) + ADD + // stack: ARGS: 4, STATE, shift, need, have, count, length, virt, retdest + %stack (ARGS: 4, STATE: 5, shift, need, have, count, length) -> (length, shift, ARGS, STATE, shift, need, have, count, length) + GT + // stack: R, ARGS: 4, STATE, shift, need, have, count, length, virt, retdest + %jumpi(buffer_update) + // stack: ARGS: 4, STATE, shift, need, have, count, length, virt, retdest + %pop3 + JUMP +return_step: + // stack: STATE, shift, need, have, count, length, virt, retdest + SWAP8 + DUP10 + %mul_const(8) + ADD + SWAP8 + // stack: STATE, shift, need, have, count, length, virt, retdest + %stack (STATE: 5, shift, need, have, count, length, virt, retdest) -> (retdest, STATE, count, length, virt) + JUMP + + +/// def update_1(): +/// buffer_update(virt, have, need) +/// shift = need +/// have = 0 +/// state = compress(state, buffer) + +update_1: + // stack: Q, STATE, shift, need, have, count, length, virt, retdest + %stack (Q, STATE: 5, shift, need, have, count, length, virt) -> (virt, have, need, update_1a, STATE, shift, need, have, count, length, virt) + %jump(buffer_update) +update_1a: + // stack: STATE, shift, need, have, count, length, virt, retdest + %stack (STATE: 5, shift, need, have) -> (STATE, 0, update_2, need, need, 0) + // stack: STATE, 0, update_2, shift = need, need, have = 0, count, length, virt, retdest + %jump(compress) + +/// def update_2(): +/// while length >= shift + 64: +/// shift += 64 +/// state = compress(state, bytestring[shift-64:]) + +update_2: + // stack: STATE, shift, need, have, count, length, virt, retdest + %stack (STATE: 5, shift, need, have, count, length) -> (64, shift, length, STATE, shift, need, have, count, length) + ADD + GT + // stack: cond, STATE, shift, need, have, count, length, virt, retdest + %jumpi(final_update) + SWAP5 + %add_const(64) + SWAP5 + %stack (STATE: 5, shift) -> (shift, 64, STATE, shift) + DUP13 + ADD + SUB + // stack: offset, STATE, shift, need, have, count, length, virt, retdest + %stack (offset, STATE: 5) -> (STATE, offset, update_2) + // stack: STATE, offset, update_2, shift, need, have, count, length, virt, retdest + %jump(compress) diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index 8ac92258..bba2a2c1 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -230,6 +230,36 @@ // stack: max %endmacro +%macro as_u32 + %and_const(0xffffffff) +%endmacro + +%macro not_u32 + // stack: x + PUSH 0xffffffff + // stack: 0xffffffff, x + SUB + // stack: 0xffffffff - x +%endmacro + +// u32 addition (discarding 2^32 bit) +%macro add_u32 + // stack: x, y + ADD + // stack: x + y + %as_u32 + // stack: (x + y) & u32::MAX +%endmacro + +%macro add3_u32 + // stack: x , y , z + ADD + // stack: x + y , z + ADD + // stack: x + y + z + %as_u32 +%endmacro + %macro increment %add_const(1) %endmacro @@ -247,15 +277,30 @@ ISZERO %endmacro -%macro as_u32 - %and_const(0xFFFFFFFF) -%endmacro - -// u32 addition (discarding 2^32 bit) -%macro add_u32 - // stack: x, y - ADD - // stack: x + y - %as_u32 - // stack: (x + y) & u32::MAX +// given u32 bytestring abcd return dcba +%macro reverse_bytes_u32 + // stack: abcd + DUP1 + PUSH 28 + BYTE + // stack: a, abcd + DUP2 + PUSH 29 + BYTE + %shl_const(8) + // stack: b0, a, abcd + DUP3 + PUSH 30 + BYTE + %shl_const(16) + // stack: c00, b0, a, abcd + SWAP3 + PUSH 31 + BYTE + %shl_const(24) + // stack: d000, b0, a, c00 + OR + OR + OR + // stack: dcba %endmacro diff --git a/evm/src/cpu/kernel/tests/hash.rs b/evm/src/cpu/kernel/tests/hash.rs index 3acdce2b..67ba64b2 100644 --- a/evm/src/cpu/kernel/tests/hash.rs +++ b/evm/src/cpu/kernel/tests/hash.rs @@ -3,7 +3,8 @@ use std::str::FromStr; use anyhow::Result; use ethereum_types::U256; use rand::{thread_rng, Rng}; -use sha2::{Digest, Sha256}; +use ripemd::{Digest, Ripemd160}; +use sha2::Sha256; use crate::cpu::kernel::aggregator::combined_kernel; use crate::cpu::kernel::interpreter::run_with_kernel; @@ -15,6 +16,13 @@ fn sha2(input: Vec) -> U256 { U256::from(&hasher.finalize()[..]) } +/// Standard RipeMD implementation. +fn ripemd(input: Vec) -> U256 { + let mut hasher = Ripemd160::new(); + hasher.update(input); + U256::from(&hasher.finalize()[..]) +} + fn test_hash(hash_fn_label: &str, standard_implementation: &dyn Fn(Vec) -> U256) -> Result<()> { let kernel = combined_kernel(); let mut rng = thread_rng(); @@ -48,3 +56,8 @@ fn test_hash(hash_fn_label: &str, standard_implementation: &dyn Fn(Vec) -> U fn test_sha2() -> Result<()> { test_hash("sha2", &sha2) } + +#[test] +fn test_ripemd() -> Result<()> { + test_hash("ripemd_stack", &ripemd) +} diff --git a/evm/src/cpu/kernel/tests/mod.rs b/evm/src/cpu/kernel/tests/mod.rs index 45feb238..9148d6a4 100644 --- a/evm/src/cpu/kernel/tests/mod.rs +++ b/evm/src/cpu/kernel/tests/mod.rs @@ -5,6 +5,7 @@ mod exp; mod hash; mod mpt; mod packing; +mod ripemd; mod rlp; mod transaction_parsing; diff --git a/evm/src/cpu/kernel/tests/ripemd.rs b/evm/src/cpu/kernel/tests/ripemd.rs new file mode 100644 index 00000000..6123c336 --- /dev/null +++ b/evm/src/cpu/kernel/tests/ripemd.rs @@ -0,0 +1,57 @@ +use anyhow::Result; +use ethereum_types::U256; +use itertools::Itertools; + +use crate::cpu::kernel::aggregator::combined_kernel; +use crate::cpu::kernel::interpreter::run_with_kernel; + +fn make_input(word: &str) -> Vec { + let mut input: Vec = vec![word.len().try_into().unwrap()]; + input.append(&mut word.as_bytes().iter().map(|&x| x as u32).collect_vec()); + input.push(u32::from_str_radix("deadbeef", 16).unwrap()); + input +} + +#[test] +fn test_ripemd_reference() -> Result<()> { + let reference = vec![ + ("", "0x9c1185a5c5e9fc54612808977ee8f548b2258d31"), + ("a", "0x0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"), + ("abc", "0x8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"), + ( + "message digest", + "0x5d0689ef49d2fae572b881b123a85ffa21595f36", + ), + ( + "abcdefghijklmnopqrstuvwxyz", + "0xf71c27109c692c1b56bbdceb5b9d2865b3708dbc", + ), + ( + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "0x12a053384a9c0c88e405a06c27dcf49ada62eb2b", + ), + ( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + "0xb0e20b6e3116640286ed3a87a5713079b21f5189", + ), + ( + "12345678901234567890123456789012345678901234567890123456789012345678901234567890", + "0x9b752e45573d4b39f4dbd3323cab82bf63326bfb", + ), + ]; + + for (x, y) in reference { + let input: Vec = make_input(x); + let expected = U256::from(y); + + let kernel = combined_kernel(); + let initial_offset = kernel.global_labels["ripemd_stack"]; + let initial_stack: Vec = input.iter().map(|&x| U256::from(x as u32)).rev().collect(); + let final_stack: Vec = run_with_kernel(&kernel, initial_offset, initial_stack)? + .stack() + .to_vec(); + let actual = final_stack[0]; + assert_eq!(actual, expected); + } + Ok(()) +}