neo-devpack-solidity 0.22.0

Production-focused Solidity-to-NeoVM compilation system
Documentation
use super::*;

impl VMBridge {
    // Arithmetic operations on stack items

    pub(crate) fn add_stack_items(a: StackItem, b: StackItem) -> Result<StackItem, VMBridgeError> {
        match (a, b) {
            (StackItem::Integer(x), StackItem::Integer(y)) => {
                Ok(StackItem::Integer(x.wrapping_add(y)))
            }
            (StackItem::UnsignedInteger(x), StackItem::UnsignedInteger(y)) => {
                Ok(StackItem::UnsignedInteger(x.wrapping_add(y)))
            }
            (StackItem::Integer(x), StackItem::UnsignedInteger(y)) => {
                Ok(StackItem::Integer(x.wrapping_add(y as i64)))
            }
            (StackItem::UnsignedInteger(x), StackItem::Integer(y)) => {
                Ok(StackItem::UnsignedInteger(x.wrapping_add(y as u64)))
            }
            _ => Err(VMBridgeError::StackOperationFailed {
                message: "Invalid operands for ADD".to_string(),
            }),
        }
    }

    pub(crate) fn sub_stack_items(a: StackItem, b: StackItem) -> Result<StackItem, VMBridgeError> {
        match (a, b) {
            (StackItem::Integer(x), StackItem::Integer(y)) => {
                Ok(StackItem::Integer(x.wrapping_sub(y)))
            }
            (StackItem::UnsignedInteger(x), StackItem::UnsignedInteger(y)) => {
                Ok(StackItem::UnsignedInteger(x.wrapping_sub(y)))
            }
            _ => Err(VMBridgeError::StackOperationFailed {
                message: "Invalid operands for SUB".to_string(),
            }),
        }
    }

    pub(crate) fn mul_stack_items(a: StackItem, b: StackItem) -> Result<StackItem, VMBridgeError> {
        match (a, b) {
            (StackItem::Integer(x), StackItem::Integer(y)) => {
                Ok(StackItem::Integer(x.wrapping_mul(y)))
            }
            (StackItem::UnsignedInteger(x), StackItem::UnsignedInteger(y)) => {
                Ok(StackItem::UnsignedInteger(x.wrapping_mul(y)))
            }
            _ => Err(VMBridgeError::StackOperationFailed {
                message: "Invalid operands for MUL".to_string(),
            }),
        }
    }

    pub(crate) fn div_stack_items(a: StackItem, b: StackItem) -> Result<StackItem, VMBridgeError> {
        match (a, b) {
            (StackItem::Integer(x), StackItem::Integer(y)) => {
                if y == 0 {
                    return Err(VMBridgeError::StackOperationFailed {
                        message: "Division by zero".to_string(),
                    });
                }
                x.checked_div(y).map(StackItem::Integer).ok_or_else(|| {
                    VMBridgeError::StackOperationFailed {
                        message: format!("Signed integer overflow in DIV: {x} / {y}"),
                    }
                })
            }
            (StackItem::UnsignedInteger(x), StackItem::UnsignedInteger(y)) => {
                if y == 0 {
                    return Err(VMBridgeError::StackOperationFailed {
                        message: "Division by zero".to_string(),
                    });
                }
                Ok(StackItem::UnsignedInteger(x / y))
            }
            _ => Err(VMBridgeError::StackOperationFailed {
                message: "Invalid operands for DIV".to_string(),
            }),
        }
    }

    pub(crate) fn mod_stack_items(a: StackItem, b: StackItem) -> Result<StackItem, VMBridgeError> {
        match (a, b) {
            (StackItem::Integer(x), StackItem::Integer(y)) => {
                if y == 0 {
                    return Err(VMBridgeError::StackOperationFailed {
                        message: "Modulo by zero".to_string(),
                    });
                }
                x.checked_rem(y).map(StackItem::Integer).ok_or_else(|| {
                    VMBridgeError::StackOperationFailed {
                        message: format!("Signed integer overflow in MOD: {x} % {y}"),
                    }
                })
            }
            (StackItem::UnsignedInteger(x), StackItem::UnsignedInteger(y)) => {
                if y == 0 {
                    return Err(VMBridgeError::StackOperationFailed {
                        message: "Modulo by zero".to_string(),
                    });
                }
                Ok(StackItem::UnsignedInteger(x % y))
            }
            _ => Err(VMBridgeError::StackOperationFailed {
                message: "Invalid operands for MOD".to_string(),
            }),
        }
    }

    pub(crate) fn modmul_stack_items(
        a: StackItem,
        b: StackItem,
        modulus: StackItem,
    ) -> Result<StackItem, VMBridgeError> {
        let (a_int, b_int, m_int) = match (a, b, modulus) {
            (
                StackItem::UnsignedInteger(x),
                StackItem::UnsignedInteger(y),
                StackItem::UnsignedInteger(m),
            ) => (x as u128, y as u128, m as u128),
            (StackItem::Integer(x), StackItem::Integer(y), StackItem::Integer(m)) => {
                if x < 0 || y < 0 || m <= 0 {
                    return Err(VMBridgeError::StackOperationFailed {
                        message: "MODMUL expects non-negative operands".to_string(),
                    });
                }
                (x as u128, y as u128, m as u128)
            }
            _ => {
                return Err(VMBridgeError::StackOperationFailed {
                    message: "Invalid operands for MODMUL".to_string(),
                })
            }
        };

        if m_int == 0 {
            return Err(VMBridgeError::StackOperationFailed {
                message: "MODMUL: modulus is zero".to_string(),
            });
        }
        let result = ((a_int % m_int) * (b_int % m_int)) % m_int;
        Ok(StackItem::UnsignedInteger(result as u64))
    }

    pub(crate) fn modpow_stack_items(
        base: StackItem,
        exponent: StackItem,
        modulus: StackItem,
    ) -> Result<StackItem, VMBridgeError> {
        let (mut base, exp, modulus) = match (base, exponent, modulus) {
            (
                StackItem::UnsignedInteger(b),
                StackItem::UnsignedInteger(e),
                StackItem::UnsignedInteger(m),
            ) => (b as u128, e as u128, m as u128),
            (StackItem::Integer(b), StackItem::Integer(e), StackItem::Integer(m)) => {
                if b < 0 || e < 0 || m <= 0 {
                    return Err(VMBridgeError::StackOperationFailed {
                        message: "MODPOW expects non-negative operands".to_string(),
                    });
                }
                (b as u128, e as u128, m as u128)
            }
            _ => {
                return Err(VMBridgeError::StackOperationFailed {
                    message: "Invalid operands for MODPOW".to_string(),
                })
            }
        };

        if modulus == 0 {
            return Err(VMBridgeError::StackOperationFailed {
                message: "MODPOW: modulus is zero".to_string(),
            });
        }

        base %= modulus;
        let mut result: u128 = 1;
        let mut exp_mut = exp;
        while exp_mut > 0 {
            if exp_mut & 1 == 1 {
                result = (result * base) % modulus;
            }
            base = (base * base) % modulus;
            exp_mut >>= 1;
        }

        Ok(StackItem::UnsignedInteger(result as u64))
    }
}