|
9 | 9 | #include "../../core/eosio/serialize.hpp" |
10 | 10 | #include "../../core/eosio/datastream.hpp" |
11 | 11 | #include "../../core/eosio/name.hpp" |
| 12 | +#include "../../core/eosio/fixed_bytes.hpp" |
12 | 13 | #include "../../core/eosio/ignore.hpp" |
13 | 14 | #include "../../core/eosio/time.hpp" |
14 | 15 |
|
@@ -48,9 +49,23 @@ namespace eosio { |
48 | 49 |
|
49 | 50 | __attribute__((eosio_wasm_import)) |
50 | 51 | uint64_t current_receiver(); |
| 52 | + |
| 53 | + __attribute__((eosio_wasm_import)) |
| 54 | + uint32_t get_code_hash( uint64_t account, uint32_t struct_version, char* result_buffer, size_t buffer_size ); |
51 | 55 | } |
52 | 56 | }; |
53 | 57 |
|
| 58 | + struct code_hash_result { |
| 59 | + unsigned_int struct_version; |
| 60 | + uint64_t code_sequence; |
| 61 | + checksum256 code_hash; |
| 62 | + uint8_t vm_type; |
| 63 | + uint8_t vm_version; |
| 64 | + |
| 65 | + CDT_REFLECT(struct_version, code_sequence, code_hash, vm_type, vm_version); |
| 66 | + EOSLIB_SERIALIZE(code_hash_result, (struct_version)(code_sequence)(code_hash)(vm_type)(vm_version)); |
| 67 | + }; |
| 68 | + |
54 | 69 | /** |
55 | 70 | * @defgroup action Action |
56 | 71 | * @ingroup contracts |
@@ -146,6 +161,50 @@ namespace eosio { |
146 | 161 | return name{internal_use_do_not_use::current_receiver()}; |
147 | 162 | } |
148 | 163 |
|
| 164 | + /** |
| 165 | + * Get the hash of the code currently published on the given account |
| 166 | + * @param account Name of the account to hash the code of |
| 167 | + * @param full_result Optional: If a full result struct is desired, a pointer to the struct to populate |
| 168 | + * @return The SHA256 hash of the specified account's code |
| 169 | + */ |
| 170 | + inline checksum256 get_code_hash( name account, code_hash_result* full_result = nullptr ) { |
| 171 | + if (full_result == nullptr) |
| 172 | + full_result = (code_hash_result*)alloca(sizeof(code_hash_result)); |
| 173 | + constexpr size_t max_stack_buffer_size = 50; |
| 174 | + |
| 175 | + // Packed size of this struct will virtually always be less than the struct size; always less after padding |
| 176 | + auto struct_buffer_size = sizeof(code_hash_result); |
| 177 | + char* struct_buffer = (char*)alloca(struct_buffer_size); |
| 178 | + |
| 179 | + using VersionType = decltype(code_hash_result::struct_version); |
| 180 | + const VersionType STRUCT_VERSION = 0; |
| 181 | + auto response_size = |
| 182 | + internal_use_do_not_use::get_code_hash(account.value, STRUCT_VERSION, struct_buffer, struct_buffer_size); |
| 183 | + // Safety check: in this case, response size should never exceed our buffer, but just in case... |
| 184 | + bool buffer_on_heap = false; |
| 185 | + if (response_size > struct_buffer_size) { |
| 186 | + // Slow path: allocate an adequate buffer and try again |
| 187 | + // No need to deallocate struct_buffer since it was alloca'd |
| 188 | + if (response_size > max_stack_buffer_size) { |
| 189 | + struct_buffer = (char*)malloc(response_size); |
| 190 | + buffer_on_heap = true; |
| 191 | + } else { |
| 192 | + struct_buffer = (char*)alloca(response_size); |
| 193 | + } |
| 194 | + internal_use_do_not_use::get_code_hash(account.value, STRUCT_VERSION, struct_buffer, struct_buffer_size); |
| 195 | + } |
| 196 | + |
| 197 | + check(unpack<VersionType>(struct_buffer, struct_buffer_size) == STRUCT_VERSION, |
| 198 | + "Hypervisor returned unexpected code hash struct version"); |
| 199 | + unpack(*full_result, struct_buffer, struct_buffer_size); |
| 200 | + |
| 201 | + // If struct_buffer is heap allocated, we must free it |
| 202 | + if (buffer_on_heap) |
| 203 | + free(struct_buffer); |
| 204 | + |
| 205 | + return full_result->code_hash; |
| 206 | + } |
| 207 | + |
149 | 208 | /** |
150 | 209 | * Copy up to length bytes of current action data to the specified location |
151 | 210 | * |
|
0 commit comments