1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use super::*;
impl ExecutionContext {
pub(crate) fn murmur3_32(data: &[u8], seed: u32) -> u32 {
const C1: u32 = 0xcc9e2d51;
const C2: u32 = 0x1b873593;
let mut h1: u32 = seed;
let len = data.len();
// Process 4-byte chunks
let chunks = data.chunks_exact(4);
let remainder = chunks.remainder();
for chunk in chunks {
let k1 = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
let k1 = k1.wrapping_mul(C1);
let k1 = k1.rotate_left(15);
let k1 = k1.wrapping_mul(C2);
h1 ^= k1;
h1 = h1.rotate_left(13);
h1 = h1.wrapping_mul(5).wrapping_add(0xe6546b64);
}
// Process remaining bytes
if !remainder.is_empty() {
let mut k1: u32 = 0;
for (i, &byte) in remainder.iter().enumerate() {
k1 |= (byte as u32) << (i * 8);
}
let k1 = k1.wrapping_mul(C1);
let k1 = k1.rotate_left(15);
let k1 = k1.wrapping_mul(C2);
h1 ^= k1;
}
// Finalization
h1 ^= len as u32;
h1 ^= h1 >> 16;
h1 = h1.wrapping_mul(0x85ebca6b);
h1 ^= h1 >> 13;
h1 = h1.wrapping_mul(0xc2b2ae35);
h1 ^= h1 >> 16;
h1
}
/// Verify secp256k1 ECDSA signature
///
/// # Arguments
/// * `message` - The message hash (32 bytes) that was signed
/// * `pubkey` - The public key (33 or 65 bytes)
/// * `signature` - The signature (64 bytes compact or DER encoded)
///
/// # Returns
/// `true` if the signature is valid, `false` otherwise
pub(crate) fn verify_secp256k1_with_message(
message: &[u8],
pubkey: &[u8],
signature: &[u8],
) -> bool {
// Validate input lengths
if message.len() != 32 {
return false;
}
if pubkey.is_empty() || (pubkey.len() != 33 && pubkey.len() != 65) {
return false;
}
if signature.len() < 64 || signature.len() > 72 {
// 64 bytes for compact, up to 72 for DER
return false;
}
// Parse public key
let pk = match secp256k1::PublicKey::from_slice(pubkey) {
Ok(pk) => pk,
Err(_) => return false,
};
// Parse signature (try DER first, then compact)
let sig = match secp256k1::ecdsa::Signature::from_der(signature)
.or_else(|_| secp256k1::ecdsa::Signature::from_compact(signature))
{
Ok(sig) => sig,
Err(_) => return false,
};
// Parse message - use proper error handling instead of unwrap
let msg = match secp256k1::Message::from_slice(message) {
Ok(msg) => msg,
Err(_) => return false,
};
// Verify signature
let secp = secp256k1::Secp256k1::verification_only();
secp.verify_ecdsa(&msg, &sig, &pk).is_ok()
}
/// Get the current message hash for signature verification.
///
/// Returns the host-injected transaction signing hash when one was armed
/// via [`Self::override_signing_hash`] for this execution (S3 fix),
/// otherwise falls back to a deterministic synthetic hash derived from the
/// execution context (bytecode hash + storage account + invocation
/// counter). The fallback preserves the behavior of every test written
/// before the injectable-hash API existed.
pub(crate) fn get_current_message_hash(&self) -> [u8; 32] {
// S3 fix — prefer the host-injected transaction signing hash. Neo N3
// verifies signatures against the script container's verifiable
// transaction digest; the embedded runtime has no real script
// container, so hosts that need real correctness inject the digest
// explicitly. `None` here means "no override armed for this
// execution" → fall through to the synthetic hash.
if let Some(injected) = self.active_signing_hash {
return injected;
}
// Backward-compatible fallback: a deterministic message hash derived
// from the execution context. Includes bytecode hash + current account
// + invocation counter.
let mut hasher_input = Vec::new();
// Include bytecode hash
let bytecode_hash = Sha256::digest(&self.bytecode);
hasher_input.extend_from_slice(&bytecode_hash);
// Include storage account if available
if let Some(ref account) = self.storage_account {
hasher_input.extend_from_slice(account.as_bytes());
}
// Include invocation counter for uniqueness
hasher_input.extend_from_slice(&self.invocation_counter.to_le_bytes());
// Final hash
let result = Sha256::digest(&hasher_input);
let mut hash = [0u8; 32];
hash.copy_from_slice(&result);
hash
}
}