Skip to content
Open
139 changes: 138 additions & 1 deletion src/codegen/encoding/soroban_encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ pub fn soroban_decode_arg(
},
Type::Uint(64) => decode_u64(wrapper_cfg, vartab, arg),

Type::Address(_) | Type::String => arg.clone(),
Type::Address(_) | Type::String | Type::DynamicBytes => arg.clone(),

Type::Enum(enum_no) => {
let decoded = soroban_decode_arg(arg, wrapper_cfg, vartab, ns, Some(Type::Uint(32)));
Expand Down Expand Up @@ -209,6 +209,75 @@ pub fn soroban_decode_arg(
}
}

Type::Bytes(n) => {
let n_expr = Expression::NumberLiteral {
loc: Loc::Codegen,
ty: Type::Uint(32),
value: BigInt::from(n as u64),
};

let buf_var = vartab.temp_name("bytes_buf", &Type::DynamicBytes);
wrapper_cfg.add(
vartab,
Instr::Set {
loc: Loc::Codegen,
res: buf_var,
expr: Expression::AllocDynamicBytes {
loc: Loc::Codegen,
ty: Type::DynamicBytes,
size: Box::new(n_expr.clone()),
initializer: None,
},
},
);
let buf = Expression::Variable {
loc: Loc::Codegen,
ty: Type::DynamicBytes,
var_no: buf_var,
};

let dest_ptr = Expression::VectorData {
pointer: Box::new(buf.clone()),
};
let dest_encoded = zext_shift_add(Loc::Codegen, dest_ptr, 32, 4);
let src_off_encoded = zext_shift_add(
Loc::Codegen,
Expression::NumberLiteral {
loc: Loc::Codegen,
ty: Type::Uint(32),
value: BigInt::from(0u64),
},
32,
4,
);
let len_encoded = zext_shift_add(Loc::Codegen, n_expr, 32, 4);

let unused = vartab.temp_name("bytes_copy_ret", &Type::Uint(64));
wrapper_cfg.add(
vartab,
Instr::Call {
res: vec![unused],
return_tys: vec![Type::Uint(64)],
call: InternalCallTy::HostFunction {
name: HostFunctions::BytesCopyToLinearMemory.name().to_string(),
},
args: vec![arg, src_off_encoded, dest_encoded, len_encoded],
},
);

Expression::Load {
loc: Loc::Codegen,
ty: Type::Bytes(n),
expr: Box::new(Expression::Cast {
loc: Loc::Codegen,
ty: Type::Ref(Box::new(Type::DynamicBytes)),
expr: Box::new(Expression::VectorData {
pointer: Box::new(buf),
}),
}),
}
}

_ => unimplemented!("unimplemented ty {:#?} in soroban decoder", ty),
}
}
Expand Down Expand Up @@ -721,6 +790,74 @@ pub fn soroban_encode_arg(
res: obj,
expr: encode_vector(item.clone(), cfg, vartab),
},
Type::DynamicBytes => Instr::Set {
loc: Loc::Codegen,
res: obj,
expr: item.clone(),
},

Type::Bytes(n) => {
let n_val = BigInt::from(n as u64);

let n_expr = Expression::NumberLiteral {
loc: item.loc(),
ty: Type::Uint(32),
value: n_val.clone(),
};

let buf_var = vartab.temp_name("bytes_spill", &Type::DynamicBytes);
cfg.add(
vartab,
Instr::Set {
loc: item.loc(),
res: buf_var,
expr: Expression::AllocDynamicBytes {
loc: item.loc(),
ty: Type::DynamicBytes,
size: Box::new(n_expr.clone()),
initializer: None,
},
},
);
let buf = Expression::Variable {
loc: item.loc(),
ty: Type::DynamicBytes,
var_no: buf_var,
};

cfg.add(
vartab,
Instr::Store {
dest: Expression::Cast {
loc: item.loc(),
ty: Type::Ref(Box::new(Type::DynamicBytes)),
expr: Box::new(Expression::VectorData {
pointer: Box::new(buf.clone()),
}),
},
data: item.clone(),
},
);

let encoded_ptr = zext_shift_add(
item.loc(),
Expression::VectorData {
pointer: Box::new(buf),
},
32,
4,
);
let encoded_len = zext_shift_add(item.loc(), n_expr, 32, 4);

Instr::Call {
res: vec![obj],
return_tys: vec![Type::Uint(64)],
call: InternalCallTy::HostFunction {
name: HostFunctions::BytesNewFromLinearMemory.name().to_string(),
},
args: vec![encoded_ptr, encoded_len],
}
}

_ => todo!("Type not yet supported in soroban encoder: {:?}", item.ty()),
};
Expand Down
7 changes: 2 additions & 5 deletions src/emit/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -888,11 +888,8 @@ impl<'a> Binary<'a> {

fn var_ty_uses_pointer_storage(&self, ty: &Type) -> bool {
match ty.deref_memory() {
Type::Struct(_)
| Type::Array(..)
| Type::DynamicBytes
| Type::ExternalFunction { .. } => true,
Type::String => self.ns.target != Target::Soroban,
Type::Struct(_) | Type::Array(..) | Type::ExternalFunction { .. } => true,
Type::String | Type::DynamicBytes => self.ns.target != Target::Soroban,
_ => false,
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/emit/soroban/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,9 @@ impl SorobanTarget {
ast::Type::Uint(256) => ScSpecTypeDef::U256,
ast::Type::Bool => ScSpecTypeDef::Bool,
ast::Type::Address(_) => ScSpecTypeDef::Address,
ast::Type::Bytes(_) => ScSpecTypeDef::Bytes,
ast::Type::Bytes(_) | ast::Type::DynamicBytes => {
ScSpecTypeDef::Bytes
}
ast::Type::String => ScSpecTypeDef::String,
ast::Type::Array(ty, _) => {
let element = Self::vec_spec_type(ty.as_ref());
Expand Down Expand Up @@ -377,7 +379,7 @@ impl SorobanTarget {
ast::Type::Int(_) => ScSpecTypeDef::I32,
ast::Type::Bool => ScSpecTypeDef::Bool,
ast::Type::Address(_) => ScSpecTypeDef::Address,
ast::Type::Bytes(_) => ScSpecTypeDef::Bytes,
ast::Type::Bytes(_) | ast::Type::DynamicBytes => ScSpecTypeDef::Bytes,
ast::Type::String => ScSpecTypeDef::String,
ast::Type::Void => ScSpecTypeDef::Void,
ast::Type::Struct(_) => ScSpecTypeDef::Void, // TODO: Map struct types.
Expand Down
74 changes: 74 additions & 0 deletions tests/soroban_testcases/bytes_n_codec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: Apache-2.0

use crate::build_solidity;
use soroban_sdk::{BytesN, FromVal, IntoVal};

#[test]
fn echo_bytes1() {
let src = build_solidity(
r#"contract BytesNCodec {
function echo_bytes1(bytes1 x) public pure returns (bytes1) {
return x;
}
}"#,
|_| {},
);
let addr = src.contracts.last().unwrap();

let payload = BytesN::<1>::from_array(&src.env, &[0xAB]);
let res = src.invoke_contract(
addr,
"echo_bytes1",
vec![payload.clone().into_val(&src.env)],
);
assert_eq!(BytesN::<1>::from_val(&src.env, &res), payload);
}

#[test]
fn echo_bytes5() {
let src = build_solidity(
r#"contract BytesNCodec {
function echo_bytes5(bytes5 x) public pure returns (bytes5) {
return x;
}
}"#,
|_| {},
);
let addr = src.contracts.last().unwrap();

let payload = BytesN::<5>::from_array(&src.env, &[0x01, 0x02, 0x03, 0x04, 0x05]);
let res = src.invoke_contract(
addr,
"echo_bytes5",
vec![payload.clone().into_val(&src.env)],
);
assert_eq!(BytesN::<5>::from_val(&src.env, &res), payload);
}

#[test]
fn echo_bytes32() {
let src = build_solidity(
r#"contract BytesNCodec {
function echo_bytes32(bytes32 x) public pure returns (bytes32) {
return x;
}
}"#,
|_| {},
);
let addr = src.contracts.last().unwrap();

let payload = BytesN::<32>::from_array(
&src.env,
&[
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD,
0xEE, 0xFF, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44,
0x33, 0x22, 0x11, 0x00,
],
);
let res = src.invoke_contract(
addr,
"echo_bytes32",
vec![payload.clone().into_val(&src.env)],
);
assert_eq!(BytesN::<32>::from_val(&src.env, &res), payload);
}
32 changes: 32 additions & 0 deletions tests/soroban_testcases/dynamic_bytes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: Apache-2.0

use crate::build_solidity;
use soroban_sdk::{Bytes, FromVal, IntoVal};

#[test]
fn set_and_get_bytes() {
let src = build_solidity(
r#"contract BytesWriteStub {
bytes public data;

function set_data(bytes memory d) public {
data = d;
}
}"#,
|_| {},
);

let addr = src.contracts.last().unwrap();

// empty bytes round-trip
let empty = Bytes::from_slice(&src.env, b"");
src.invoke_contract(addr, "set_data", vec![empty.clone().into_val(&src.env)]);
let res = src.invoke_contract(addr, "data", vec![]);
assert_eq!(Bytes::from_val(&src.env, &res), empty);

// non-empty bytes round-trip
let payload = Bytes::from_slice(&src.env, b"hello");
src.invoke_contract(addr, "set_data", vec![payload.clone().into_val(&src.env)]);
let res = src.invoke_contract(addr, "data", vec![]);
assert_eq!(Bytes::from_val(&src.env, &res), payload);
}
2 changes: 2 additions & 0 deletions tests/soroban_testcases/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
mod alloc;
mod array_args;
mod bytes_n_codec;
mod dynamic_bytes;
mod atomic_swap;
mod auth;
mod constructor;
Expand Down