neo_solidity/cli/bytecode/bytecode_builtins/builtin_call/
crypto.rs

1fn emit_keccak256(
2    bytecode: &mut Vec<u8>,
3    use_callt: bool,
4    token_patches: &mut Vec<MethodTokenPatch>,
5) {
6    emit_native_contract_call(
7        bytecode,
8        ir::NativeContract::CryptoLib,
9        "keccak256",
10        1,
11        use_callt,
12        token_patches,
13    )
14}
15
16fn emit_ecrecover(
17    bytecode: &mut Vec<u8>,
18    use_callt: bool,
19    token_patches: &mut Vec<MethodTokenPatch>,
20) {
21    // Neo N3 does not provide an `ecrecover` syscall. We map it to the
22    // native contract `CryptoLib.recoverSecp256K1` and then return the
23    // Neo account script hash produced by `System.Contract.CreateStandardAccount`.
24    //
25    // This makes the recovered address compatible with Neo's witness
26    // model (i.e., it can be fed into `System.Runtime.CheckWitness`).
27    //
28    // Stack input (Solidity): [hash32, v, r, s]
29    // Build signature: r || s || v (65 bytes), with v normalized to 27..30.
30    bytecode.push(0x8B); // CAT: r||s => [hash32, v, rs]
31    bytecode.push(0x50); // SWAP => [hash32, rs, v]
32
33    // If v < 27, normalize v by adding 27.
34    bytecode.push(0x4A); // DUP
35    push_integer_bigint(bytecode, &BigInt::from(27u8));
36    bytecode.push(0xB5); // LT
37    let jmp_skip_add_pos = bytecode.len();
38    bytecode.push(0x27); // JMPIFNOT_L
39    let jmp_skip_add_operand = bytecode.len();
40    bytecode.extend_from_slice(&[0, 0, 0, 0]);
41    push_integer_bigint(bytecode, &BigInt::from(27u8));
42    bytecode.push(0x9E); // ADD
43    let after_add_pos = bytecode.len();
44    let rel_after_add = (after_add_pos as i32)
45        .checked_sub(jmp_skip_add_pos as i32)
46        .unwrap_or(0);
47    bytecode[jmp_skip_add_operand..jmp_skip_add_operand + 4]
48        .copy_from_slice(&rel_after_add.to_le_bytes());
49
50    // Convert v into a single-byte buffer and append to rs.
51    // Stack: [hash32, rs, v]
52    bytecode.push(0x11); // PUSH1
53    bytecode.push(0x88); // NEWBUFFER (len=1) => [hash32, rs, v, buf]
54    bytecode.push(0x4A); // DUP => [hash32, rs, v, buf, buf]
55    bytecode.push(0x51); // ROT => [hash32, rs, buf, buf, v]
56    bytecode.push(0x10); // PUSH0 => [hash32, rs, buf, buf, v, 0]
57    bytecode.push(0x50); // SWAP => [hash32, rs, buf, buf, 0, v]
58    bytecode.push(0xD0); // SETITEM (buf[0] = v) => [hash32, rs, buf]
59    bytecode.push(0x8B); // CAT: rs||buf => [hash32, signature]
60
61    // Call CryptoLib.recoverSecp256K1(hash32, signature)
62    emit_native_contract_call(
63        bytecode,
64        ir::NativeContract::CryptoLib,
65        "recoverSecp256K1",
66        2,
67        use_callt,
68        token_patches,
69    );
70
71    // If recovery fails (null), return 0x00..00.
72    bytecode.push(0x4A); // DUP
73    bytecode.push(0xD8); // ISNULL
74    let jmp_if_not_null_pos = bytecode.len();
75    bytecode.push(0x27); // JMPIFNOT_L
76    let jmp_if_not_null_operand = bytecode.len();
77    bytecode.extend_from_slice(&[0, 0, 0, 0]);
78
79    // null path
80    bytecode.push(0x45); // DROP
81    push_data(bytecode, &[0u8; 20]);
82    let jmp_end_pos = bytecode.len();
83    bytecode.push(0x23); // JMP_L
84    let jmp_end_operand = bytecode.len();
85    bytecode.extend_from_slice(&[0, 0, 0, 0]);
86
87    // not-null path
88    let not_null_pos = bytecode.len();
89    emit_syscall(bytecode, "System.Contract.CreateStandardAccount");
90
91    let end_pos = bytecode.len();
92
93    let rel_not_null = (not_null_pos as i32)
94        .checked_sub(jmp_if_not_null_pos as i32)
95        .unwrap_or(0);
96    bytecode[jmp_if_not_null_operand..jmp_if_not_null_operand + 4]
97        .copy_from_slice(&rel_not_null.to_le_bytes());
98
99    let rel_end = (end_pos as i32)
100        .checked_sub(jmp_end_pos as i32)
101        .unwrap_or(0);
102    bytecode[jmp_end_operand..jmp_end_operand + 4].copy_from_slice(&rel_end.to_le_bytes());
103}
104
105fn emit_verify_signature(
106    bytecode: &mut Vec<u8>,
107    use_callt: bool,
108    token_patches: &mut Vec<MethodTokenPatch>,
109) {
110    push_integer_bigint(bytecode, &BigInt::from(23u8)); // secp256r1 curve
111    emit_native_contract_call(
112        bytecode,
113        ir::NativeContract::CryptoLib,
114        "verifyWithECDsa",
115        4,
116        use_callt,
117        token_patches,
118    );
119}