1use std::io::Cursor;
2
3use base64::Engine;
4use stellar_xdr::curr as stellar_xdr;
5use stellar_xdr::{Limited, Limits, ReadXdr, ScSpecEntry};
6use wasmparser::{BinaryReaderError, Parser, Payload};
7
8#[derive(thiserror::Error, Debug)]
11pub enum ParseSpecBase64Error {
12 #[error("parsing contract spec base64")]
13 ParseBase64(base64::DecodeError),
14 #[error("parsing contract spec xdr")]
15 ParseXdr(stellar_xdr::Error),
16}
17
18pub fn parse_base64(spec: &[u8]) -> Result<Vec<ScSpecEntry>, ParseSpecBase64Error> {
19 let decoded = base64::engine::general_purpose::STANDARD
20 .decode(spec)
21 .map_err(ParseSpecBase64Error::ParseBase64)?;
22 parse_raw(&decoded).map_err(ParseSpecBase64Error::ParseXdr)
23}
24
25pub fn parse_raw(spec: &[u8]) -> Result<Vec<ScSpecEntry>, stellar_xdr::Error> {
26 let cursor = Cursor::new(spec);
27 let entries = ScSpecEntry::read_xdr_iter(&mut Limited::new(
28 cursor,
29 Limits {
30 depth: 500,
31 len: 0x1000000,
32 },
33 ))
34 .collect::<Result<Vec<_>, _>>()?;
35 Ok(entries)
36}
37
38#[derive(thiserror::Error, Debug)]
39pub enum FromWasmError {
40 #[error("reading wasm")]
41 Read(BinaryReaderError),
42 #[error("parsing contract spec")]
43 Parse(stellar_xdr::Error),
44 #[error("contract spec not found")]
45 NotFound,
46}
47
48pub fn raw_from_wasm(wasm: &[u8]) -> Result<Vec<u8>, FromWasmError> {
49 for payload in Parser::new(0).parse_all(wasm) {
50 let payload = payload.map_err(FromWasmError::Read)?;
51 if let Payload::CustomSection(section) = payload {
52 if section.name() == "contractspecv0" {
53 return Ok(section.data().to_vec());
54 }
55 };
56 }
57 Err(FromWasmError::NotFound)
58}
59
60pub fn base64_from_wasm(wasm: &[u8]) -> Result<String, FromWasmError> {
61 let raw = raw_from_wasm(wasm)?;
62 let mut res = String::new();
63 base64::engine::general_purpose::STANDARD.encode_string(raw, &mut res);
64 Ok(res)
65}
66
67pub fn from_wasm(wasm: &[u8]) -> Result<Vec<ScSpecEntry>, FromWasmError> {
68 let spec = raw_from_wasm(wasm)?;
69 parse_raw(&spec).map_err(FromWasmError::Parse)
70}