1#![allow(
10 clippy::indexing_slicing,
11 clippy::arithmetic_side_effects,
12 clippy::cast_possible_truncation
13)]
14
15use std::cell::Cell;
16
17use scale_info::{form::PortableForm, Field, TypeDef, TypeDefPrimitive};
18
19use crate::codec::value::Value;
20use crate::error::CoreError;
21use crate::keys::public_key_from_ss58;
22use crate::runtime::type_string::{Primitive, TypeSpec};
23use crate::runtime::Runtime;
24
25const MAX_ENCODE_DEPTH: usize = 256;
32
33thread_local! {
34 static ENCODE_DEPTH: Cell<usize> = const { Cell::new(0) };
35}
36
37struct DepthGuard;
38
39impl DepthGuard {
40 fn enter() -> Result<Self, CoreError> {
41 let depth = ENCODE_DEPTH.with(|d| {
42 let next = d.get() + 1;
43 d.set(next);
44 next
45 });
46 let guard = DepthGuard;
49 if depth > MAX_ENCODE_DEPTH {
50 return Err(CoreError::Codec(format!(
51 "encode recursion exceeds {MAX_ENCODE_DEPTH} levels (malformed or malicious type definition)"
52 )));
53 }
54 Ok(guard)
55 }
56}
57
58impl Drop for DepthGuard {
59 fn drop(&mut self) {
60 ENCODE_DEPTH.with(|d| d.set(d.get().saturating_sub(1)));
61 }
62}
63
64pub fn compact(value: u128, out: &mut Vec<u8>) -> Result<(), CoreError> {
66 if value < 1 << 6 {
67 out.push((value as u8) << 2);
68 } else if value < 1 << 14 {
69 out.extend_from_slice(&(((value as u16) << 2) | 0b01).to_le_bytes());
70 } else if value < 1 << 30 {
71 out.extend_from_slice(&(((value as u32) << 2) | 0b10).to_le_bytes());
72 } else {
73 let bytes = value.to_le_bytes();
74 let significant = 16 - bytes.iter().rev().take_while(|&&b| b == 0).count();
75 let len = significant.max(4);
76 out.push((((len - 4) as u8) << 2) | 0b11);
77 out.extend_from_slice(&bytes[..len]);
78 }
79 Ok(())
80}
81
82impl Runtime {
83 pub fn encode_spec(&self, spec: &TypeSpec, value: &Value) -> Result<Vec<u8>, CoreError> {
84 let mut out = Vec::new();
85 self.encode_value(spec, value, &mut out)?;
86 Ok(out)
87 }
88
89 pub fn encode_value(
90 &self,
91 spec: &TypeSpec,
92 value: &Value,
93 out: &mut Vec<u8>,
94 ) -> Result<(), CoreError> {
95 let _depth = DepthGuard::enter()?;
96 match spec {
97 TypeSpec::Id(id) => self.encode_id(*id, value, out),
98 TypeSpec::Primitive(p) => encode_primitive_spec(*p, value, out),
99 TypeSpec::Sequence(inner) => {
100 if matches!(**inner, TypeSpec::Primitive(Primitive::U8)) {
101 let bytes = coerce_bytes(value, None)?;
102 compact(bytes.len() as u128, out)?;
103 out.extend_from_slice(&bytes);
104 return Ok(());
105 }
106 let items = coerce_list(value)?;
107 compact(items.len() as u128, out)?;
108 for item in items {
109 self.encode_value(inner, item, out)?;
110 }
111 Ok(())
112 }
113 TypeSpec::Option(inner) => {
114 if matches!(value, Value::Null) {
115 out.push(0);
116 return Ok(());
117 }
118 out.push(1);
119 self.encode_value(inner, value, out)
120 }
121 TypeSpec::Array(inner, len) => {
122 if matches!(**inner, TypeSpec::Primitive(Primitive::U8)) {
123 let bytes = coerce_bytes(value, Some(*len as usize))?;
124 out.extend_from_slice(&bytes);
125 return Ok(());
126 }
127 let items = coerce_list(value)?;
128 if items.len() != *len as usize {
129 return Err(CoreError::Codec(format!(
130 "array expects {len} elements, got {}",
131 items.len()
132 )));
133 }
134 for item in items {
135 self.encode_value(inner, item, out)?;
136 }
137 Ok(())
138 }
139 TypeSpec::Tuple(parts) => {
140 let items = coerce_list(value)?;
141 if items.len() != parts.len() {
142 return Err(CoreError::Codec(format!(
143 "tuple expects {} elements, got {}",
144 parts.len(),
145 items.len()
146 )));
147 }
148 for (part, item) in parts.iter().zip(items) {
149 self.encode_value(part, item, out)?;
150 }
151 Ok(())
152 }
153 TypeSpec::Compact(_) => compact(coerce_uint(value)?, out),
154 TypeSpec::Bytes => {
155 let bytes = coerce_bytes(value, None)?;
156 compact(bytes.len() as u128, out)?;
157 out.extend_from_slice(&bytes);
158 Ok(())
159 }
160 TypeSpec::AccountId => {
161 let raw = self.coerce_account_id(value)?;
162 out.extend_from_slice(&raw);
163 Ok(())
164 }
165 TypeSpec::Era => self.encode_era_value(value, out),
166 TypeSpec::Call => {
167 self.encode_call_input(value, out)
169 }
170 TypeSpec::Extrinsic => Err(CoreError::Codec(
171 "extrinsics are assembled by the transport layer".into(),
172 )),
173 }
174 }
175
176 pub fn encode_id(&self, id: u32, value: &Value, out: &mut Vec<u8>) -> Result<(), CoreError> {
177 let _depth = DepthGuard::enter()?;
178 let ty = self.resolve(id)?;
179 let segments = &ty.path.segments;
180 let last = segments.last().map(String::as_str);
181
182 match last {
183 Some("AccountId32") => {
184 let raw = self.coerce_account_id(value)?;
185 out.extend_from_slice(&raw);
186 return Ok(());
187 }
188 Some("Era") if segments.first().map(String::as_str) == Some("sp_runtime") => {
189 return self.encode_era_value(value, out);
190 }
191 Some("MultiAddress") => {
192 return self.encode_multiaddress(ty, value, out);
193 }
194 Some("BTreeMap") => {
195 return self.encode_btreemap(ty, value, out);
196 }
197 _ => {}
198 }
199
200 if let Value::Bytes(raw) = value {
203 if self.extrinsic.call_type == Some(id) {
204 out.extend_from_slice(raw);
205 return Ok(());
206 }
207 }
208
209 match &ty.type_def {
210 TypeDef::Primitive(p) => encode_primitive(p, value, out),
211 TypeDef::Compact(_) => compact(coerce_uint(value)?, out),
212 TypeDef::Composite(composite) => self.encode_fields(&composite.fields, value, out),
213 TypeDef::Variant(variant) => {
214 let is_option = last == Some("Option") && segments.len() == 1;
215 if is_option {
216 if matches!(value, Value::Null) {
217 out.push(0);
218 return Ok(());
219 }
220 let some = variant
221 .variants
222 .iter()
223 .find(|v| v.name == "Some")
224 .ok_or_else(|| CoreError::Codec("Option without Some".into()))?;
225 out.push(some.index);
226 return self.encode_fields(&some.fields, value, out);
227 }
228 let (name, payload) = coerce_variant(value)?;
229 let chosen = variant
230 .variants
231 .iter()
232 .find(|v| v.name == name)
233 .ok_or_else(|| {
234 CoreError::Codec(format!("no variant named {name:?} in type {id}"))
235 })?;
236 out.push(chosen.index);
237 if chosen.fields.is_empty() {
238 return Ok(());
239 }
240 self.encode_fields(&chosen.fields, &payload, out)
241 }
242 TypeDef::Sequence(s) => {
243 if self.is_u8_encode(s.type_param.id) {
244 let bytes = coerce_bytes(value, None)?;
245 compact(bytes.len() as u128, out)?;
246 out.extend_from_slice(&bytes);
247 return Ok(());
248 }
249 let items = coerce_list(value)?;
250 compact(items.len() as u128, out)?;
251 for item in items {
252 self.encode_id(s.type_param.id, item, out)?;
253 }
254 Ok(())
255 }
256 TypeDef::Array(a) => {
257 if self.is_u8_encode(a.type_param.id) {
258 let bytes = coerce_bytes(value, Some(a.len as usize))?;
259 out.extend_from_slice(&bytes);
260 return Ok(());
261 }
262 let items = coerce_list(value)?;
263 if items.len() != a.len as usize {
264 return Err(CoreError::Codec(format!(
265 "array expects {} elements, got {}",
266 a.len,
267 items.len()
268 )));
269 }
270 for item in items {
271 self.encode_id(a.type_param.id, item, out)?;
272 }
273 Ok(())
274 }
275 TypeDef::Tuple(t) => {
276 if t.fields.is_empty() {
277 return Ok(());
278 }
279 let items = coerce_list(value)?;
280 if items.len() != t.fields.len() {
281 return Err(CoreError::Codec(format!(
282 "tuple expects {} elements, got {}",
283 t.fields.len(),
284 items.len()
285 )));
286 }
287 for (field, item) in t.fields.iter().zip(items) {
288 self.encode_id(field.id, item, out)?;
289 }
290 Ok(())
291 }
292 TypeDef::BitSequence(_) => Err(CoreError::Codec(
293 "bit sequences are not used by this chain".into(),
294 )),
295 }
296 }
297
298 fn is_u8_encode(&self, id: u32) -> bool {
299 matches!(
300 self.types.resolve(id).map(|t| &t.type_def),
301 Some(TypeDef::Primitive(TypeDefPrimitive::U8))
302 )
303 }
304
305 fn encode_fields(
308 &self,
309 fields: &[Field<PortableForm>],
310 value: &Value,
311 out: &mut Vec<u8>,
312 ) -> Result<(), CoreError> {
313 if fields.is_empty() {
314 return Ok(());
315 }
316 let named = fields.iter().all(|f| f.name.is_some());
317 if named {
318 let Value::Dict(entries) = value else {
319 return Err(CoreError::Codec(format!(
320 "struct expects a dict, got {value}"
321 )));
322 };
323 for field in fields {
324 let name = field.name.as_deref().unwrap_or_default();
325 let item = entries
326 .iter()
327 .find(|(k, _)| matches!(k, Value::Str(s) if s == name))
328 .map(|(_, v)| v)
329 .ok_or_else(|| CoreError::Codec(format!("missing struct field {name:?}")))?;
330 self.encode_id(field.ty.id, item, out)?;
331 }
332 return Ok(());
333 }
334 if fields.len() == 1 {
335 if let Value::List(items) | Value::Tuple(items) = value {
343 if items.len() == 1
344 && matches!(items[0], Value::List(_))
345 && matches!(
346 self.resolve(fields[0].ty.id).map(|t| &t.type_def),
347 Ok(TypeDef::Sequence(_))
348 )
349 {
350 let start = out.len();
351 if self.encode_id(fields[0].ty.id, value, out).is_ok() {
352 return Ok(());
353 }
354 out.truncate(start);
355 return self.encode_id(fields[0].ty.id, &items[0], out);
356 }
357 }
358 return self.encode_id(fields[0].ty.id, value, out);
359 }
360 let items = coerce_list(value)?;
361 if items.len() != fields.len() {
362 return Err(CoreError::Codec(format!(
363 "expects {} unnamed fields, got {}",
364 fields.len(),
365 items.len()
366 )));
367 }
368 for (field, item) in fields.iter().zip(items) {
369 self.encode_id(field.ty.id, item, out)?;
370 }
371 Ok(())
372 }
373
374 fn encode_multiaddress(
375 &self,
376 ty: &scale_info::Type<PortableForm>,
377 value: &Value,
378 out: &mut Vec<u8>,
379 ) -> Result<(), CoreError> {
380 let TypeDef::Variant(variant) = &ty.type_def else {
381 return Err(CoreError::Codec("MultiAddress is not an enum".into()));
382 };
383 match value {
385 Value::Str(_) | Value::Bytes(_) => {
386 let id_variant = variant
387 .variants
388 .iter()
389 .find(|v| v.name == "Id")
390 .ok_or_else(|| CoreError::Codec("MultiAddress without Id".into()))?;
391 out.push(id_variant.index);
392 let raw = self.coerce_account_id(value)?;
393 out.extend_from_slice(&raw);
394 Ok(())
395 }
396 Value::Dict(_) => {
397 let (name, payload) = coerce_variant(value)?;
398 let chosen = variant
399 .variants
400 .iter()
401 .find(|v| v.name == name)
402 .ok_or_else(|| {
403 CoreError::Codec(format!("no MultiAddress variant named {name:?}"))
404 })?;
405 out.push(chosen.index);
406 self.encode_fields(&chosen.fields, &payload, out)
407 }
408 other => Err(CoreError::Codec(format!(
409 "cannot encode {other} as MultiAddress"
410 ))),
411 }
412 }
413
414 fn encode_btreemap(
415 &self,
416 ty: &scale_info::Type<PortableForm>,
417 value: &Value,
418 out: &mut Vec<u8>,
419 ) -> Result<(), CoreError> {
420 let TypeDef::Composite(composite) = &ty.type_def else {
421 return Err(CoreError::Codec("BTreeMap is not a composite".into()));
422 };
423 let inner = composite
424 .fields
425 .first()
426 .map(|f| f.ty.id)
427 .ok_or_else(|| CoreError::Codec("BTreeMap has no inner field".into()))?;
428 let TypeDef::Sequence(seq) = &self.resolve(inner)?.type_def else {
429 return Err(CoreError::Codec("BTreeMap inner is not a sequence".into()));
430 };
431 let TypeDef::Tuple(pair) = &self.resolve(seq.type_param.id)?.type_def else {
432 return Err(CoreError::Codec("BTreeMap pair is not a tuple".into()));
433 };
434 let (key_ty, value_ty) = match pair.fields.as_slice() {
435 [k, v] => (k.id, v.id),
436 _ => return Err(CoreError::Codec("BTreeMap pair is not 2-ary".into())),
437 };
438 let Value::Dict(entries) = value else {
439 return Err(CoreError::Codec("BTreeMap expects a dict".into()));
440 };
441 compact(entries.len() as u128, out)?;
442 for (k, v) in entries {
443 self.encode_id(key_ty, k, out)?;
444 self.encode_id(value_ty, v, out)?;
445 }
446 Ok(())
447 }
448
449 pub fn encode_era_value(&self, value: &Value, out: &mut Vec<u8>) -> Result<(), CoreError> {
452 match value {
453 Value::Str(s) if s == "00" => {
454 out.push(0);
455 Ok(())
456 }
457 Value::Dict(entries) => {
458 let get = |name: &str| -> Option<u64> {
459 entries.iter().find_map(|(k, v)| match (k, v) {
460 (Value::Str(s), Value::Int(i)) if s == name => u64::try_from(*i).ok(),
461 (Value::Str(s), Value::Uint(u)) if s == name => u64::try_from(*u).ok(),
462 _ => None,
463 })
464 };
465 let period = get("period")
466 .ok_or_else(|| CoreError::Codec("mortal era needs a period".into()))?;
467 let phase = match (get("phase"), get("current")) {
468 (Some(phase), _) => phase,
469 (None, Some(current)) => {
470 let calculated = calculated_period(period);
471 let quantize_factor = (calculated >> 12).max(1);
472 (current % calculated) / quantize_factor * quantize_factor
473 }
474 (None, None) => {
475 return Err(CoreError::Codec(
476 "mortal era needs a phase or current block".into(),
477 ))
478 }
479 };
480 let calculated = calculated_period(period);
481 let quantize_factor = (calculated >> 12).max(1);
482 let low = u64::from(calculated.trailing_zeros())
485 .saturating_sub(1)
486 .clamp(1, 15);
487 let encoded = low | ((phase / quantize_factor) << 4);
488 out.extend_from_slice(&(encoded as u16).to_le_bytes());
489 Ok(())
490 }
491 other => Err(CoreError::Codec(format!("cannot encode {other} as Era"))),
492 }
493 }
494
495 pub fn coerce_account_id(&self, value: &Value) -> Result<[u8; 32], CoreError> {
498 match value {
499 Value::Str(s) if s.starts_with("0x") => {
500 let raw = hex::decode(s.trim_start_matches("0x"))
501 .map_err(|e| CoreError::Codec(format!("bad hex account id: {e}")))?;
502 raw.as_slice()
503 .try_into()
504 .map_err(|_| CoreError::Codec("account id must be 32 bytes".into()))
505 }
506 Value::Str(s) => public_key_from_ss58(s),
507 Value::Bytes(b) => b
508 .as_slice()
509 .try_into()
510 .map_err(|_| CoreError::Codec("account id must be 32 bytes".into())),
511 other => Err(CoreError::Codec(format!(
512 "cannot interpret {other} as an account id"
513 ))),
514 }
515 }
516
517 fn encode_call_input(&self, value: &Value, out: &mut Vec<u8>) -> Result<(), CoreError> {
518 match value {
519 Value::Bytes(raw) => {
520 out.extend_from_slice(raw);
521 Ok(())
522 }
523 Value::Dict(_) => {
524 let call_type = self
525 .extrinsic
526 .call_type
527 .ok_or_else(|| CoreError::Codec("runtime has no call type".into()))?;
528 self.encode_id(call_type, value, out)
529 }
530 other => Err(CoreError::Codec(format!("cannot encode {other} as Call"))),
531 }
532 }
533
534 pub fn compose_call(
539 &self,
540 pallet: &str,
541 function: &str,
542 params: &Value,
543 ) -> Result<Vec<u8>, CoreError> {
544 let pallet_info = self
545 .pallet(pallet)
546 .ok_or_else(|| CoreError::Codec(format!("pallet {pallet:?} not found")))?;
547 let calls_type = pallet_info
548 .calls_type
549 .ok_or_else(|| CoreError::Codec(format!("pallet {pallet:?} has no calls")))?;
550 let TypeDef::Variant(variant) = &self.resolve(calls_type)?.type_def else {
551 return Err(CoreError::Codec("call type is not an enum".into()));
552 };
553 let chosen = variant
554 .variants
555 .iter()
556 .find(|v| v.name == function)
557 .ok_or_else(|| CoreError::Codec(format!("call {pallet}.{function} not found")))?;
558 let mut out = vec![pallet_info.index, chosen.index];
559 match params {
560 Value::Dict(entries) => {
561 for field in &chosen.fields {
562 let name = field.name.as_deref().unwrap_or_default();
563 let item = entries
564 .iter()
565 .find(|(k, _)| matches!(k, Value::Str(s) if s == name))
566 .map(|(_, v)| v)
567 .ok_or_else(|| {
568 CoreError::Codec(format!(
569 "missing call param {name:?} for {pallet}.{function}"
570 ))
571 })?;
572 self.encode_id(field.ty.id, item, &mut out)?;
573 }
574 }
575 Value::List(items) | Value::Tuple(items) => {
576 if items.len() != chosen.fields.len() {
577 return Err(CoreError::Codec(format!(
578 "call {pallet}.{function} expects {} positional params, got {}",
579 chosen.fields.len(),
580 items.len()
581 )));
582 }
583 for (field, item) in chosen.fields.iter().zip(items.iter()) {
584 self.encode_id(field.ty.id, item, &mut out)?;
585 }
586 }
587 other => {
588 return Err(CoreError::Codec(format!(
589 "call params must be a dict or positional list, got {other}"
590 )));
591 }
592 }
593 Ok(out)
594 }
595}
596
597fn calculated_period(period: u64) -> u64 {
598 period.next_power_of_two().clamp(4, 1 << 16)
599}
600
601fn coerce_list(value: &Value) -> Result<&[Value], CoreError> {
602 match value {
603 Value::List(items) | Value::Tuple(items) => Ok(items),
604 other => Err(CoreError::Codec(format!("expected a list, got {other}"))),
605 }
606}
607
608fn coerce_uint(value: &Value) -> Result<u128, CoreError> {
609 match value {
610 Value::Int(i) => u128::try_from(*i)
611 .map_err(|_| CoreError::Codec("negative value for unsigned type".into())),
612 Value::Uint(u) => Ok(*u),
613 Value::Bool(b) => Ok(u128::from(*b)),
614 other => Err(CoreError::Codec(format!(
615 "expected an integer, got {other}"
616 ))),
617 }
618}
619
620fn coerce_int(value: &Value) -> Result<i128, CoreError> {
621 match value {
622 Value::Int(i) => Ok(*i),
623 Value::Uint(u) => i128::try_from(*u)
624 .map_err(|_| CoreError::Codec("value too large for signed type".into())),
625 other => Err(CoreError::Codec(format!(
626 "expected an integer, got {other}"
627 ))),
628 }
629}
630
631fn coerce_bytes(value: &Value, expected_len: Option<usize>) -> Result<Vec<u8>, CoreError> {
635 let bytes = match value {
636 Value::Bytes(b) => b.clone(),
637 Value::Str(s) if s.starts_with("0x") => hex::decode(s.trim_start_matches("0x"))
638 .map_err(|e| CoreError::Codec(format!("bad hex: {e}")))?,
639 Value::Str(s) => s.as_bytes().to_vec(),
640 Value::List(items) | Value::Tuple(items) => {
641 let mut out = Vec::with_capacity(items.len());
642 for item in items {
643 let v = coerce_uint(item)?;
644 out.push(
645 u8::try_from(v)
646 .map_err(|_| CoreError::Codec("byte value out of range".into()))?,
647 );
648 }
649 out
650 }
651 other => return Err(CoreError::Codec(format!("expected bytes, got {other}"))),
652 };
653 if let Some(len) = expected_len {
654 if bytes.len() != len {
655 return Err(CoreError::Codec(format!(
656 "expected {len} bytes, got {}",
657 bytes.len()
658 )));
659 }
660 }
661 Ok(bytes)
662}
663
664fn coerce_variant(value: &Value) -> Result<(String, Value), CoreError> {
667 match value {
668 Value::Str(name) => Ok((name.clone(), Value::Tuple(Vec::new()))),
669 Value::Dict(entries) if entries.len() == 1 => match &entries[0] {
670 (Value::Str(name), payload) => Ok((name.clone(), payload.clone())),
671 _ => Err(CoreError::Codec("enum dict key must be a string".into())),
672 },
673 other => Err(CoreError::Codec(format!(
674 "expected an enum variant, got {other}"
675 ))),
676 }
677}
678
679fn encode_primitive_spec(p: Primitive, value: &Value, out: &mut Vec<u8>) -> Result<(), CoreError> {
680 use Primitive as P;
681 let def = match p {
682 P::Bool => TypeDefPrimitive::Bool,
683 P::Char => TypeDefPrimitive::Char,
684 P::Str => TypeDefPrimitive::Str,
685 P::U8 => TypeDefPrimitive::U8,
686 P::U16 => TypeDefPrimitive::U16,
687 P::U32 => TypeDefPrimitive::U32,
688 P::U64 => TypeDefPrimitive::U64,
689 P::U128 => TypeDefPrimitive::U128,
690 P::U256 => TypeDefPrimitive::U256,
691 P::I8 => TypeDefPrimitive::I8,
692 P::I16 => TypeDefPrimitive::I16,
693 P::I32 => TypeDefPrimitive::I32,
694 P::I64 => TypeDefPrimitive::I64,
695 P::I128 => TypeDefPrimitive::I128,
696 P::I256 => TypeDefPrimitive::I256,
697 };
698 encode_primitive(&def, value, out)
699}
700
701fn encode_primitive(
702 p: &TypeDefPrimitive,
703 value: &Value,
704 out: &mut Vec<u8>,
705) -> Result<(), CoreError> {
706 use TypeDefPrimitive as P;
707 match p {
708 P::Bool => {
709 let b = match value {
710 Value::Bool(b) => *b,
711 Value::Int(i) => *i != 0,
712 other => return Err(CoreError::Codec(format!("expected bool, got {other}"))),
713 };
714 out.push(u8::from(b));
715 }
716 P::Char => {
717 let Value::Str(s) = value else {
718 return Err(CoreError::Codec("expected a 1-char string".into()));
719 };
720 let c = s
721 .chars()
722 .next()
723 .ok_or_else(|| CoreError::Codec("expected a 1-char string".into()))?;
724 out.extend_from_slice(&(c as u32).to_le_bytes());
725 }
726 P::Str => {
727 let Value::Str(s) = value else {
728 return Err(CoreError::Codec(format!("expected a string, got {value}")));
729 };
730 compact(s.len() as u128, out)?;
731 out.extend_from_slice(s.as_bytes());
732 }
733 P::U8 => out.push(int_as::<u8>(value)?),
734 P::U16 => out.extend_from_slice(&int_as::<u16>(value)?.to_le_bytes()),
735 P::U32 => out.extend_from_slice(&int_as::<u32>(value)?.to_le_bytes()),
736 P::U64 => out.extend_from_slice(&int_as::<u64>(value)?.to_le_bytes()),
737 P::U128 => out.extend_from_slice(&coerce_uint(value)?.to_le_bytes()),
738 P::U256 => match value {
739 Value::U256(le) => out.extend_from_slice(le),
740 _ => {
741 let v = coerce_uint(value)?;
742 let mut buf = [0u8; 32];
743 buf[..16].copy_from_slice(&v.to_le_bytes());
744 out.extend_from_slice(&buf);
745 }
746 },
747 P::I8 => out.extend_from_slice(&sint_as::<i8>(value)?.to_le_bytes()),
748 P::I16 => out.extend_from_slice(&sint_as::<i16>(value)?.to_le_bytes()),
749 P::I32 => out.extend_from_slice(&sint_as::<i32>(value)?.to_le_bytes()),
750 P::I64 => out.extend_from_slice(&sint_as::<i64>(value)?.to_le_bytes()),
751 P::I128 => out.extend_from_slice(&coerce_int(value)?.to_le_bytes()),
752 P::I256 => match value {
753 Value::U256(le) => out.extend_from_slice(le),
754 _ => {
755 let v = coerce_int(value)?;
756 let mut buf = if v < 0 { [0xffu8; 32] } else { [0u8; 32] };
757 buf[..16].copy_from_slice(&v.to_le_bytes());
758 out.extend_from_slice(&buf);
759 }
760 },
761 }
762 Ok(())
763}
764
765fn int_as<T: TryFrom<u128>>(value: &Value) -> Result<T, CoreError> {
766 T::try_from(coerce_uint(value)?).map_err(|_| CoreError::Codec("integer out of range".into()))
767}
768
769fn sint_as<T: TryFrom<i128>>(value: &Value) -> Result<T, CoreError> {
770 T::try_from(coerce_int(value)?).map_err(|_| CoreError::Codec("integer out of range".into()))
771}