| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693 |
- // SPDX-License-Identifier: Apache-2.0
- use crate::codegen::cfg::{ControlFlowGraph, Instr};
- use crate::codegen::encoding::AbiEncoding;
- use crate::codegen::vartable::Vartable;
- use crate::codegen::{Builtin, Expression};
- use crate::sema::ast::StructType;
- use crate::sema::ast::{Namespace, Type, Type::Uint};
- use parity_scale_codec::Encode;
- use primitive_types::U256;
- use solang_parser::pt::Loc::Codegen;
- use std::collections::HashMap;
- use super::buffer_validator::BufferValidator;
- pub(super) struct ScaleEncoding {
- storage_cache: HashMap<usize, Expression>,
- packed_encoder: bool,
- }
- impl ScaleEncoding {
- pub fn new(packed: bool) -> Self {
- Self {
- storage_cache: HashMap::new(),
- packed_encoder: packed,
- }
- }
- }
- /// Decoding the compact integer at current `offset` inside `buffer`.
- /// Returns the variable number of the decoded integer (32bit) and the width in bytes of the encoded version.
- /// More information can found in the /// [SCALE documentation](https://docs.substrate.io/reference/scale-codec/).
- fn decode_compact(
- buffer: &Expression,
- offset: &Expression,
- vartab: &mut Vartable,
- cfg: &mut ControlFlowGraph,
- ) -> (usize, Expression) {
- let decoded_var = vartab.temp_anonymous(&Uint(32));
- let size_width_var = vartab.temp_anonymous(&Uint(32));
- vartab.new_dirty_tracker();
- let read_byte = Expression::Builtin {
- loc: Codegen,
- tys: vec![Uint(8)],
- kind: Builtin::ReadFromBuffer,
- args: vec![buffer.clone(), offset.clone()],
- };
- cfg.add(
- vartab,
- Instr::Set {
- loc: Codegen,
- res: size_width_var,
- expr: Expression::ZeroExt {
- loc: Codegen,
- ty: Uint(32),
- expr: read_byte.into(),
- },
- },
- );
- let size_width = Expression::Variable {
- loc: Codegen,
- ty: Uint(32),
- var_no: size_width_var,
- };
- let two = Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: 2.into(),
- };
- let three = Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: 3.into(),
- };
- let cond = Expression::BitwiseAnd {
- loc: Codegen,
- ty: Uint(32),
- left: size_width.clone().into(),
- right: three.into(),
- };
- let cases = &[
- (
- Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: 0.into(),
- },
- cfg.new_basic_block("case_0".into()),
- ),
- (
- Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: 1.into(),
- },
- cfg.new_basic_block("case_1".into()),
- ),
- (
- Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: 2.into(),
- },
- cfg.new_basic_block("case_2".into()),
- ),
- ];
- let default = cfg.new_basic_block("case_default".into());
- cfg.add(
- vartab,
- Instr::Switch {
- cond,
- cases: cases.to_vec(),
- default,
- },
- );
- let done = cfg.new_basic_block("done".into());
- // We will land in the default block for sizes of 2**30 (1GB) or larger.
- // Such big sizes are invalid for smart contracts and should never occur anyways.
- cfg.set_basic_block(default);
- cfg.add(vartab, Instr::AssertFailure { encoded_args: None });
- cfg.set_basic_block(cases[0].1);
- let expr = Expression::ShiftRight {
- loc: Codegen,
- ty: Uint(32),
- left: size_width.clone().into(),
- right: two.clone().into(),
- signed: false,
- };
- cfg.add(
- vartab,
- Instr::Set {
- loc: Codegen,
- res: decoded_var,
- expr,
- },
- );
- cfg.add(
- vartab,
- Instr::Set {
- loc: Codegen,
- res: size_width_var,
- expr: Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: 1.into(),
- },
- },
- );
- cfg.add(vartab, Instr::Branch { block: done });
- cfg.set_basic_block(cases[1].1);
- let read_byte = Expression::Builtin {
- loc: Codegen,
- tys: vec![Uint(16)],
- kind: Builtin::ReadFromBuffer,
- args: vec![buffer.clone(), offset.clone()],
- };
- let expr = Expression::ShiftRight {
- loc: Codegen,
- ty: Uint(32),
- left: Expression::ZeroExt {
- loc: Codegen,
- ty: Uint(32),
- expr: read_byte.into(),
- }
- .into(),
- right: two.clone().into(),
- signed: false,
- };
- cfg.add(
- vartab,
- Instr::Set {
- loc: Codegen,
- res: decoded_var,
- expr,
- },
- );
- cfg.add(
- vartab,
- Instr::Set {
- loc: Codegen,
- res: size_width_var,
- expr: two.clone(),
- },
- );
- cfg.add(vartab, Instr::Branch { block: done });
- cfg.set_basic_block(cases[2].1);
- let read_byte = Expression::Builtin {
- loc: Codegen,
- tys: vec![Uint(32)],
- kind: Builtin::ReadFromBuffer,
- args: vec![buffer.clone(), offset.clone()],
- };
- let expr = Expression::ShiftRight {
- loc: Codegen,
- ty: Uint(32),
- left: read_byte.into(),
- right: two.into(),
- signed: false,
- };
- cfg.add(
- vartab,
- Instr::Set {
- loc: Codegen,
- res: decoded_var,
- expr,
- },
- );
- cfg.add(
- vartab,
- Instr::Set {
- loc: Codegen,
- res: size_width_var,
- expr: Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: 4.into(),
- },
- },
- );
- cfg.add(vartab, Instr::Branch { block: done });
- vartab.set_dirty(decoded_var);
- vartab.set_dirty(size_width_var);
- cfg.set_basic_block(done);
- cfg.set_phis(done, vartab.pop_dirty_tracker());
- (decoded_var, size_width)
- }
- /// Encode `expr` into `buffer` as a compact integer. More information can found in the
- /// [SCALE documentation](https://docs.substrate.io/reference/scale-codec/).
- fn encode_compact(
- expr: &Expression,
- buffer: Option<&Expression>,
- offset: Option<&Expression>,
- vartab: &mut Vartable,
- cfg: &mut ControlFlowGraph,
- ) -> Expression {
- let small = cfg.new_basic_block("small".into());
- let medium = cfg.new_basic_block("medium".into());
- let medium_or_big = cfg.new_basic_block("medium_or_big".into());
- let big = cfg.new_basic_block("big".into());
- let done = cfg.new_basic_block("done".into());
- let fail = cfg.new_basic_block("fail".into());
- let prepare = cfg.new_basic_block("prepare".into());
- let cmp_val = Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: (0x40000000 - 1).into(),
- };
- let compare = Expression::More {
- loc: Codegen,
- signed: false,
- left: expr.clone().into(),
- right: cmp_val.into(),
- };
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond: compare,
- true_block: fail,
- false_block: prepare,
- },
- );
- cfg.set_basic_block(fail);
- cfg.add(vartab, Instr::AssertFailure { encoded_args: None });
- cfg.set_basic_block(prepare);
- let cmp_val = Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: (0x40 - 1).into(),
- };
- let compare = Expression::More {
- loc: Codegen,
- signed: false,
- left: expr.clone().into(),
- right: cmp_val.into(),
- };
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond: compare,
- true_block: medium_or_big,
- false_block: small,
- },
- );
- cfg.set_basic_block(medium_or_big);
- let cmp_val = Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: (0x4000 - 1).into(),
- };
- let compare = Expression::More {
- loc: Codegen,
- signed: false,
- left: expr.clone().into(),
- right: cmp_val.into(),
- };
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond: compare,
- true_block: big,
- false_block: medium,
- },
- );
- let size_variable = vartab.temp_anonymous(&Uint(32));
- vartab.new_dirty_tracker();
- let four = Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: 4.into(),
- }
- .into();
- let mul = Expression::Multiply {
- loc: Codegen,
- ty: Uint(32),
- overflowing: false,
- left: expr.clone().into(),
- right: four,
- };
- cfg.set_basic_block(small);
- if let (Some(buffer), Some(offset)) = (buffer, offset) {
- cfg.add(
- vartab,
- Instr::WriteBuffer {
- buf: buffer.clone(),
- offset: offset.clone(),
- value: Expression::Cast {
- loc: Codegen,
- ty: Uint(8),
- expr: mul.clone().into(),
- },
- },
- );
- }
- let one = Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: 1.into(),
- };
- cfg.add(
- vartab,
- Instr::Set {
- loc: Codegen,
- res: size_variable,
- expr: one.clone(),
- },
- );
- cfg.add(vartab, Instr::Branch { block: done });
- cfg.set_basic_block(medium);
- if let (Some(buffer), Some(offset)) = (buffer, offset) {
- let mul = Expression::BitwiseOr {
- loc: Codegen,
- ty: Uint(32),
- left: mul.clone().into(),
- right: one.into(),
- };
- cfg.add(
- vartab,
- Instr::WriteBuffer {
- buf: buffer.clone(),
- offset: offset.clone(),
- value: Expression::Cast {
- loc: Codegen,
- ty: Uint(16),
- expr: mul.into(),
- },
- },
- );
- }
- let two = Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: 2.into(),
- };
- cfg.add(
- vartab,
- Instr::Set {
- loc: Codegen,
- res: size_variable,
- expr: two.clone(),
- },
- );
- cfg.add(vartab, Instr::Branch { block: done });
- cfg.set_basic_block(big);
- if let (Some(buffer), Some(offset)) = (buffer, offset) {
- cfg.add(
- vartab,
- Instr::WriteBuffer {
- buf: buffer.clone(),
- offset: offset.clone(),
- value: Expression::BitwiseOr {
- loc: Codegen,
- ty: Uint(32),
- left: mul.into(),
- right: two.into(),
- },
- },
- );
- }
- cfg.add(
- vartab,
- Instr::Set {
- loc: Codegen,
- res: size_variable,
- expr: Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: 4.into(),
- },
- },
- );
- cfg.add(vartab, Instr::Branch { block: done });
- cfg.set_basic_block(done);
- cfg.set_phis(done, vartab.pop_dirty_tracker());
- Expression::Variable {
- loc: Codegen,
- ty: Uint(32),
- var_no: size_variable,
- }
- }
- impl AbiEncoding for ScaleEncoding {
- fn size_width(
- &self,
- size: &Expression,
- vartab: &mut Vartable,
- cfg: &mut ControlFlowGraph,
- ) -> Expression {
- // FIXME:
- // It should be possible to optimize this to estimate always 4 bytes.
- // `codegen::abi_encode()` also returns the actual encoded size,
- // so slightly overestimating it shouldn't matter.
- // However, the actual length of the encoded data produced by `codegen::abi_encode()`
- // is ignored in some places, wich results in buggy contracts if we have not an exact estimate.
- // Once this is fixed (the encoded size return by `codegen::abi_encode()` must never be ignored),
- // this can just be always 4 bytes .
- encode_compact(size, None, None, vartab, cfg)
- }
- fn encode_external_function(
- &mut self,
- expr: &Expression,
- buffer: &Expression,
- offset: &Expression,
- ns: &Namespace,
- vartab: &mut Vartable,
- cfg: &mut ControlFlowGraph,
- ) -> Expression {
- let addr_len = ns.address_length.into();
- let address = expr.external_function_address();
- let size = self.encode_directly(&address, buffer, offset, vartab, cfg, addr_len);
- let offset = Expression::Add {
- loc: Codegen,
- ty: Uint(32),
- overflowing: false,
- left: offset.clone().into(),
- right: size.into(),
- };
- let selector = expr.external_function_selector();
- self.encode_directly(&selector, buffer, &offset, vartab, cfg, 4.into());
- Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: (ns.address_length + 4).into(),
- }
- }
- fn encode_size(
- &mut self,
- expr: &Expression,
- buffer: &Expression,
- offset: &Expression,
- _ns: &Namespace,
- vartab: &mut Vartable,
- cfg: &mut ControlFlowGraph,
- ) -> Expression {
- encode_compact(expr, Some(buffer), Some(offset), vartab, cfg)
- }
- fn decode_external_function(
- &self,
- buffer: &Expression,
- offset: &Expression,
- ty: &Type,
- validator: &mut BufferValidator,
- ns: &Namespace,
- vartab: &mut Vartable,
- cfg: &mut ControlFlowGraph,
- ) -> (Expression, Expression) {
- let size = Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: (ns.address_length + 4).into(),
- };
- validator.validate_offset_plus_size(offset, &size, ns, vartab, cfg);
- let address = Expression::Builtin {
- loc: Codegen,
- tys: vec![Type::Address(false)],
- kind: Builtin::ReadFromBuffer,
- args: vec![buffer.clone(), offset.clone()],
- };
- let new_offset = offset.clone().add_u32(Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: ns.address_length.into(),
- });
- let selector = Expression::Builtin {
- loc: Codegen,
- tys: vec![Type::FunctionSelector],
- kind: Builtin::ReadFromBuffer,
- args: vec![buffer.clone(), new_offset],
- };
- let ext_func = Expression::StructLiteral {
- loc: Codegen,
- ty: Type::Struct(StructType::ExternalFunction),
- values: vec![selector, address],
- };
- (
- Expression::Cast {
- loc: Codegen,
- ty: ty.clone(),
- expr: ext_func.into(),
- },
- size,
- )
- }
- fn retrieve_array_length(
- &self,
- buffer: &Expression,
- offset: &Expression,
- vartab: &mut Vartable,
- cfg: &mut ControlFlowGraph,
- ) -> (usize, Expression) {
- decode_compact(buffer, offset, vartab, cfg)
- }
- fn storage_cache_insert(&mut self, arg_no: usize, expr: Expression) {
- self.storage_cache.insert(arg_no, expr);
- }
- fn storage_cache_remove(&mut self, arg_no: usize) -> Option<Expression> {
- self.storage_cache.remove(&arg_no)
- }
- fn calculate_string_size(
- &self,
- expr: &Expression,
- vartab: &mut Vartable,
- cfg: &mut ControlFlowGraph,
- ) -> Expression {
- // When encoding a variable length array, the total size is "compact encoded array length + N elements"
- let length = Expression::Builtin {
- loc: Codegen,
- tys: vec![Uint(32)],
- kind: Builtin::ArrayLength,
- args: vec![expr.clone()],
- };
- if self.is_packed() {
- length
- } else {
- encode_compact(&length, None, None, vartab, cfg).add_u32(length)
- }
- }
- fn is_packed(&self) -> bool {
- self.packed_encoder
- }
- /// TODO: This is used and tested for error data (Error and Panic) only.
- fn const_encode(&self, args: &[Expression]) -> Option<Vec<u8>> {
- let mut result = vec![];
- for arg in args {
- match arg {
- Expression::AllocDynamicBytes {
- initializer: Some(data),
- ty: Type::String | Type::DynamicBytes,
- ..
- } => result.extend_from_slice(&data.encode()),
- Expression::AllocDynamicBytes {
- initializer: Some(data),
- ty: Type::Slice(inner),
- ..
- } if matches!(**inner, Type::Bytes(1)) => result.extend_from_slice(data),
- Expression::NumberLiteral {
- ty: Type::Bytes(4),
- value,
- ..
- } => {
- let bytes = value.to_bytes_be().1;
- if bytes.len() < 4 {
- let mut buf = Vec::new();
- buf.resize(4 - bytes.len(), 0);
- result.extend_from_slice(&buf);
- }
- result.extend_from_slice(&bytes[..]);
- }
- Expression::NumberLiteral {
- ty: Type::Uint(256),
- value,
- ..
- } => {
- let bytes = value.to_bytes_be().1;
- result.extend_from_slice(&U256::from_big_endian(&bytes).encode()[..]);
- }
- _ => return None,
- }
- }
- result.into()
- }
- }
- #[cfg(test)]
- mod tests {
- use num_bigint::{BigInt, Sign};
- use parity_scale_codec::Encode;
- use primitive_types::U256;
- use crate::{
- codegen::{
- encoding::{scale_encoding::ScaleEncoding, AbiEncoding},
- Expression,
- },
- sema::ast::Type,
- };
- #[test]
- fn const_encode_dynamic_bytes() {
- let data = vec![0x41, 0x41];
- let encoder = ScaleEncoding::new(false);
- let expr = Expression::AllocDynamicBytes {
- loc: Default::default(),
- ty: Type::DynamicBytes,
- size: Expression::Poison.into(),
- initializer: data.clone().into(),
- };
- let encoded = encoder.const_encode(&[expr]).unwrap();
- assert_eq!(encoded, data.encode());
- }
- #[test]
- fn const_encode_uint() {
- let encoder = ScaleEncoding::new(false);
- for value in [U256::MAX, U256::zero(), U256::one()] {
- let mut bytes = [0u8; 32].to_vec();
- value.to_big_endian(&mut bytes);
- let data = BigInt::from_bytes_be(Sign::Plus, &bytes);
- let expr = Expression::NumberLiteral {
- loc: Default::default(),
- ty: Type::Uint(256),
- value: data,
- };
- let encoded = encoder.const_encode(&[expr]).unwrap();
- assert_eq!(encoded, value.encode());
- }
- }
- #[test]
- fn const_encode_bytes4() {
- let encoder = ScaleEncoding::new(false);
- for value in [
- [0x00, 0x00, 0xff, 0xff],
- [0x00, 0xff, 0xff, 0x00],
- [0xff, 0xff, 0x00, 0x00],
- [0xff, 0xff, 0xff, 0xff],
- [0x00, 0x00, 0x00, 0x00],
- [0xde, 0xad, 0xbe, 0xef],
- [0x01, 0x00, 0x00, 0x00],
- [0x00, 0x00, 0x00, 0x01],
- ] {
- let expr = Expression::NumberLiteral {
- ty: Type::Bytes(4),
- value: BigInt::from_bytes_be(Sign::Plus, &value),
- loc: Default::default(),
- };
- assert_eq!(&encoder.const_encode(&[expr]).unwrap(), &value.encode());
- }
- }
- }
|