1use {
2 crate::{config::ExecutionCost, runtime::LogCollector},
3 sbpf_vm::{compute::ComputeMeter, errors::SbpfVmResult, memory::Memory},
4};
5
6pub fn sol_log(
7 registers: [u64; 5],
8 memory: &Memory,
9 compute: &ComputeMeter,
10 costs: &ExecutionCost,
11 log_collector: &LogCollector,
12) -> SbpfVmResult<u64> {
13 let msg_ptr = registers[0];
14 let msg_len = registers[1];
15
16 compute.consume(costs.syscall_base_cost.max(msg_len))?;
17
18 let msg_bytes = memory.read_bytes(msg_ptr, msg_len as usize)?;
19 let msg = String::from_utf8_lossy(msg_bytes);
20 log_collector
21 .borrow_mut()
22 .push(format!("Program log: {}", msg));
23 Ok(0)
24}
25
26pub fn sol_log_64(
27 registers: [u64; 5],
28 compute: &ComputeMeter,
29 costs: &ExecutionCost,
30 log_collector: &LogCollector,
31) -> SbpfVmResult<u64> {
32 compute.consume(costs.log_64_units)?;
33 log_collector.borrow_mut().push(format!(
34 "Program log: {:#x}, {:#x}, {:#x}, {:#x}, {:#x}",
35 registers[0], registers[1], registers[2], registers[3], registers[4]
36 ));
37 Ok(0)
38}
39
40pub fn sol_log_pubkey(
41 registers: [u64; 5],
42 memory: &Memory,
43 compute: &ComputeMeter,
44 costs: &ExecutionCost,
45 log_collector: &LogCollector,
46) -> SbpfVmResult<u64> {
47 compute.consume(costs.log_pubkey_units)?;
48
49 let pubkey_bytes = memory.read_bytes(registers[0], 32)?;
50 let pubkey_base58 = bs58::encode(pubkey_bytes).into_string();
51 log_collector
52 .borrow_mut()
53 .push(format!("Program log: {}", pubkey_base58));
54 Ok(0)
55}
56
57pub fn sol_log_compute_units(
58 compute: &ComputeMeter,
59 costs: &ExecutionCost,
60 log_collector: &LogCollector,
61) -> SbpfVmResult<u64> {
62 compute.consume(costs.syscall_base_cost)?;
63 log_collector.borrow_mut().push(format!(
64 "Program consumption: {} units remaining",
65 compute.get_remaining()
66 ));
67 Ok(0)
68}
69
70pub fn sol_remaining_compute_units(
71 compute: &ComputeMeter,
72 costs: &ExecutionCost,
73) -> SbpfVmResult<u64> {
74 compute.consume(costs.syscall_base_cost)?;
75 Ok(compute.get_remaining())
76}
77
78#[cfg(test)]
79mod tests {
80 use {
81 super::*,
82 crate::{
83 runtime::LogCollector,
84 syscalls::tests::test_helpers::{costs, make_memory, meter},
85 },
86 sbpf_vm::{errors::SbpfVmError, memory::Memory},
87 std::{cell::RefCell, rc::Rc},
88 };
89
90 fn new_log() -> LogCollector {
91 Rc::new(RefCell::new(Vec::new()))
92 }
93
94 #[test]
95 fn test_sol_log_basic() {
96 let mut memory = make_memory();
97 let msg = b"hello world";
98 memory.write_bytes(Memory::HEAP_START, msg).unwrap();
99
100 let log = new_log();
101 let registers = [Memory::HEAP_START, msg.len() as u64, 0, 0, 0];
102 sol_log(registers, &memory, &meter(1_000_000), &costs(), &log).unwrap();
103
104 assert_eq!(log.borrow()[0], "Program log: hello world");
105 }
106
107 #[test]
108 fn test_sol_log_empty_message() {
109 let memory = make_memory();
110 let log = new_log();
111 let registers = [Memory::HEAP_START, 0, 0, 0, 0];
112 sol_log(registers, &memory, &meter(1_000_000), &costs(), &log).unwrap();
113
114 assert_eq!(log.borrow()[0], "Program log: ");
115 }
116
117 #[test]
118 fn test_sol_log_invalid_utf8_is_lossy() {
119 let mut memory = make_memory();
120 memory.write_bytes(Memory::HEAP_START, &[0xFF]).unwrap();
121 let log = new_log();
122 let registers = [Memory::HEAP_START, 1, 0, 0, 0];
123 sol_log(registers, &memory, &meter(1_000_000), &costs(), &log).unwrap();
124
125 assert!(log.borrow()[0].starts_with("Program log: "));
126 }
127
128 #[test]
129 fn test_sol_log_compute_exhausted() {
130 let memory = make_memory();
131 let log = new_log();
132 let registers = [Memory::HEAP_START, 0, 0, 0, 0];
133 assert!(matches!(
134 sol_log(registers, &memory, &meter(99), &costs(), &log),
135 Err(SbpfVmError::ComputeBudgetExceeded { .. })
136 ));
137 }
138
139 #[test]
140 fn test_sol_log_64_format() {
141 let log = new_log();
142 let registers = [1, 2, 3, 4, 5];
143 sol_log_64(registers, &meter(1_000_000), &costs(), &log).unwrap();
144
145 assert_eq!(log.borrow()[0], "Program log: 0x1, 0x2, 0x3, 0x4, 0x5");
146 }
147
148 #[test]
149 fn test_sol_log_64_zeros() {
150 let log = new_log();
151 sol_log_64([0, 0, 0, 0, 0], &meter(1_000_000), &costs(), &log).unwrap();
152 assert_eq!(log.borrow()[0], "Program log: 0x0, 0x0, 0x0, 0x0, 0x0");
153 }
154
155 #[test]
156 fn test_sol_log_64_compute_exhausted() {
157 let log = new_log();
158 assert!(matches!(
159 sol_log_64([0; 5], &meter(99), &costs(), &log),
160 Err(SbpfVmError::ComputeBudgetExceeded { .. })
161 ));
162 }
163
164 #[test]
165 fn test_sol_log_pubkey_formats_base58() {
166 let mut memory = make_memory();
167 let pubkey = [0u8; 32];
168 memory.write_bytes(Memory::HEAP_START, &pubkey).unwrap();
169
170 let log = new_log();
171 let registers = [Memory::HEAP_START, 0, 0, 0, 0];
172 sol_log_pubkey(registers, &memory, &meter(1_000_000), &costs(), &log).unwrap();
173
174 let expected = bs58::encode(&pubkey).into_string();
175 assert_eq!(log.borrow()[0], format!("Program log: {}", expected));
176 }
177
178 #[test]
179 fn test_sol_log_pubkey_nonzero() {
180 let mut memory = make_memory();
181 let mut pubkey = [0u8; 32];
182 pubkey[0] = 1;
183 memory.write_bytes(Memory::HEAP_START, &pubkey).unwrap();
184
185 let log = new_log();
186 let registers = [Memory::HEAP_START, 0, 0, 0, 0];
187 sol_log_pubkey(registers, &memory, &meter(1_000_000), &costs(), &log).unwrap();
188
189 let expected = bs58::encode(&pubkey).into_string();
190 assert_eq!(log.borrow()[0], format!("Program log: {}", expected));
191 }
192
193 #[test]
194 fn test_sol_log_compute_units_logs_remaining() {
195 let compute = meter(1_000_000);
196 let log = new_log();
197 sol_log_compute_units(&compute, &costs(), &log).unwrap();
198
199 let msg = &log.borrow()[0];
201 assert!(msg.starts_with("Program consumption:"));
202 assert!(msg.contains("999900 units remaining"));
203 }
204
205 #[test]
206 fn test_sol_remaining_compute_units_returns_remaining() {
207 let compute = meter(1_000);
208 let result = sol_remaining_compute_units(&compute, &costs()).unwrap();
209 assert_eq!(result, 900);
210 }
211
212 #[test]
213 fn test_sol_remaining_compute_units_exhausted() {
214 let compute = meter(50);
215 assert!(matches!(
216 sol_remaining_compute_units(&compute, &costs()),
217 Err(SbpfVmError::ComputeBudgetExceeded { .. })
218 ));
219 }
220}