diff --git a/acvm-repo/acir/codegen/acir.cpp b/acvm-repo/acir/codegen/acir.cpp index 0f94e91ab10..bdee08794e6 100644 --- a/acvm-repo/acir/codegen/acir.cpp +++ b/acvm-repo/acir/codegen/acir.cpp @@ -451,16 +451,16 @@ namespace Circuit { static BinaryIntOp bincodeDeserialize(std::vector); }; - struct RegisterIndex { + struct MemoryAddress { uint64_t value; - friend bool operator==(const RegisterIndex&, const RegisterIndex&); + friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; - static RegisterIndex bincodeDeserialize(std::vector); + static MemoryAddress bincodeDeserialize(std::vector); }; struct HeapArray { - Circuit::RegisterIndex pointer; + Circuit::MemoryAddress pointer; uint64_t size; friend bool operator==(const HeapArray&, const HeapArray&); @@ -469,8 +469,8 @@ namespace Circuit { }; struct HeapVector { - Circuit::RegisterIndex pointer; - Circuit::RegisterIndex size; + Circuit::MemoryAddress pointer; + Circuit::MemoryAddress size; friend bool operator==(const HeapVector&, const HeapVector&); std::vector bincodeSerialize() const; @@ -529,7 +529,7 @@ namespace Circuit { Circuit::HeapArray public_key_x; Circuit::HeapArray public_key_y; Circuit::HeapArray signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); std::vector bincodeSerialize() const; @@ -541,7 +541,7 @@ namespace Circuit { Circuit::HeapArray public_key_x; Circuit::HeapArray public_key_y; Circuit::HeapArray signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); std::vector bincodeSerialize() const; @@ -549,11 +549,11 @@ namespace Circuit { }; struct SchnorrVerify { - Circuit::RegisterIndex public_key_x; - Circuit::RegisterIndex public_key_y; + Circuit::MemoryAddress public_key_x; + Circuit::MemoryAddress public_key_y; Circuit::HeapVector message; Circuit::HeapVector signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); std::vector bincodeSerialize() const; @@ -562,7 +562,7 @@ namespace Circuit { struct PedersenCommitment { Circuit::HeapVector inputs; - Circuit::RegisterIndex domain_separator; + Circuit::MemoryAddress domain_separator; Circuit::HeapArray output; friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); @@ -572,8 +572,8 @@ namespace Circuit { struct PedersenHash { Circuit::HeapVector inputs; - Circuit::RegisterIndex domain_separator; - Circuit::RegisterIndex output; + Circuit::MemoryAddress domain_separator; + Circuit::MemoryAddress output; friend bool operator==(const PedersenHash&, const PedersenHash&); std::vector bincodeSerialize() const; @@ -581,8 +581,8 @@ namespace Circuit { }; struct FixedBaseScalarMul { - Circuit::RegisterIndex low; - Circuit::RegisterIndex high; + Circuit::MemoryAddress low; + Circuit::MemoryAddress high; Circuit::HeapArray result; friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); @@ -591,10 +591,10 @@ namespace Circuit { }; struct EmbeddedCurveAdd { - Circuit::RegisterIndex input1_x; - Circuit::RegisterIndex input1_y; - Circuit::RegisterIndex input2_x; - Circuit::RegisterIndex input2_y; + Circuit::MemoryAddress input1_x; + Circuit::MemoryAddress input1_y; + Circuit::MemoryAddress input2_x; + Circuit::MemoryAddress input2_y; Circuit::HeapArray result; friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); @@ -603,9 +603,9 @@ namespace Circuit { }; struct BigIntAdd { - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; - Circuit::RegisterIndex output; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; friend bool operator==(const BigIntAdd&, const BigIntAdd&); std::vector bincodeSerialize() const; @@ -613,9 +613,9 @@ namespace Circuit { }; struct BigIntNeg { - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; - Circuit::RegisterIndex output; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; friend bool operator==(const BigIntNeg&, const BigIntNeg&); std::vector bincodeSerialize() const; @@ -623,9 +623,9 @@ namespace Circuit { }; struct BigIntMul { - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; - Circuit::RegisterIndex output; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; friend bool operator==(const BigIntMul&, const BigIntMul&); std::vector bincodeSerialize() const; @@ -633,9 +633,9 @@ namespace Circuit { }; struct BigIntDiv { - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; - Circuit::RegisterIndex output; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; friend bool operator==(const BigIntDiv&, const BigIntDiv&); std::vector bincodeSerialize() const; @@ -645,7 +645,7 @@ namespace Circuit { struct BigIntFromLeBytes { Circuit::HeapVector inputs; Circuit::HeapVector modulus; - Circuit::RegisterIndex output; + Circuit::MemoryAddress output; friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); std::vector bincodeSerialize() const; @@ -653,7 +653,7 @@ namespace Circuit { }; struct BigIntToLeBytes { - Circuit::RegisterIndex input; + Circuit::MemoryAddress input; Circuit::HeapVector output; friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); @@ -664,7 +664,7 @@ namespace Circuit { struct Poseidon2Permutation { Circuit::HeapVector message; Circuit::HeapArray output; - Circuit::RegisterIndex len; + Circuit::MemoryAddress len; friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); std::vector bincodeSerialize() const; @@ -688,14 +688,22 @@ namespace Circuit { static BlackBoxOp bincodeDeserialize(std::vector); }; - struct RegisterOrMemory { + struct Value { + std::string inner; - struct RegisterIndex { - Circuit::RegisterIndex value; + friend bool operator==(const Value&, const Value&); + std::vector bincodeSerialize() const; + static Value bincodeDeserialize(std::vector); + }; + + struct ValueOrArray { - friend bool operator==(const RegisterIndex&, const RegisterIndex&); + struct MemoryAddress { + Circuit::MemoryAddress value; + + friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; - static RegisterIndex bincodeDeserialize(std::vector); + static MemoryAddress bincodeDeserialize(std::vector); }; struct HeapArray { @@ -714,28 +722,20 @@ namespace Circuit { static HeapVector bincodeDeserialize(std::vector); }; - std::variant value; - - friend bool operator==(const RegisterOrMemory&, const RegisterOrMemory&); - std::vector bincodeSerialize() const; - static RegisterOrMemory bincodeDeserialize(std::vector); - }; - - struct Value { - std::string inner; + std::variant value; - friend bool operator==(const Value&, const Value&); + friend bool operator==(const ValueOrArray&, const ValueOrArray&); std::vector bincodeSerialize() const; - static Value bincodeDeserialize(std::vector); + static ValueOrArray bincodeDeserialize(std::vector); }; struct BrilligOpcode { struct BinaryFieldOp { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::BinaryFieldOp op; - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); std::vector bincodeSerialize() const; @@ -743,11 +743,11 @@ namespace Circuit { }; struct BinaryIntOp { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::BinaryIntOp op; uint32_t bit_size; - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); std::vector bincodeSerialize() const; @@ -755,7 +755,7 @@ namespace Circuit { }; struct JumpIfNot { - Circuit::RegisterIndex condition; + Circuit::MemoryAddress condition; uint64_t location; friend bool operator==(const JumpIfNot&, const JumpIfNot&); @@ -764,7 +764,7 @@ namespace Circuit { }; struct JumpIf { - Circuit::RegisterIndex condition; + Circuit::MemoryAddress condition; uint64_t location; friend bool operator==(const JumpIf&, const JumpIf&); @@ -780,6 +780,16 @@ namespace Circuit { static Jump bincodeDeserialize(std::vector); }; + struct CalldataCopy { + Circuit::MemoryAddress destination_address; + uint64_t size; + uint64_t offset; + + friend bool operator==(const CalldataCopy&, const CalldataCopy&); + std::vector bincodeSerialize() const; + static CalldataCopy bincodeDeserialize(std::vector); + }; + struct Call { uint64_t location; @@ -789,7 +799,7 @@ namespace Circuit { }; struct Const { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::Value value; friend bool operator==(const Const&, const Const&); @@ -805,8 +815,8 @@ namespace Circuit { struct ForeignCall { std::string function; - std::vector destinations; - std::vector inputs; + std::vector destinations; + std::vector inputs; friend bool operator==(const ForeignCall&, const ForeignCall&); std::vector bincodeSerialize() const; @@ -814,8 +824,8 @@ namespace Circuit { }; struct Mov { - Circuit::RegisterIndex destination; - Circuit::RegisterIndex source; + Circuit::MemoryAddress destination; + Circuit::MemoryAddress source; friend bool operator==(const Mov&, const Mov&); std::vector bincodeSerialize() const; @@ -823,8 +833,8 @@ namespace Circuit { }; struct Load { - Circuit::RegisterIndex destination; - Circuit::RegisterIndex source_pointer; + Circuit::MemoryAddress destination; + Circuit::MemoryAddress source_pointer; friend bool operator==(const Load&, const Load&); std::vector bincodeSerialize() const; @@ -832,8 +842,8 @@ namespace Circuit { }; struct Store { - Circuit::RegisterIndex destination_pointer; - Circuit::RegisterIndex source; + Circuit::MemoryAddress destination_pointer; + Circuit::MemoryAddress source; friend bool operator==(const Store&, const Store&); std::vector bincodeSerialize() const; @@ -855,12 +865,15 @@ namespace Circuit { }; struct Stop { + uint64_t return_data_offset; + uint64_t return_data_size; + friend bool operator==(const Stop&, const Stop&); std::vector bincodeSerialize() const; static Stop bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BrilligOpcode&, const BrilligOpcode&); std::vector bincodeSerialize() const; @@ -1044,6 +1057,7 @@ namespace Circuit { Circuit::PublicInputs public_parameters; Circuit::PublicInputs return_values; std::vector> assert_messages; + bool recursive; friend bool operator==(const Circuit&, const Circuit&); std::vector bincodeSerialize() const; @@ -4310,6 +4324,50 @@ Circuit::BrilligOpcode::Jump serde::Deserializable return obj; } +namespace Circuit { + + inline bool operator==(const BrilligOpcode::CalldataCopy &lhs, const BrilligOpcode::CalldataCopy &rhs) { + if (!(lhs.destination_address == rhs.destination_address)) { return false; } + if (!(lhs.size == rhs.size)) { return false; } + if (!(lhs.offset == rhs.offset)) { return false; } + return true; + } + + inline std::vector BrilligOpcode::CalldataCopy::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BrilligOpcode::CalldataCopy BrilligOpcode::CalldataCopy::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BrilligOpcode::CalldataCopy &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.destination_address, serializer); + serde::Serializable::serialize(obj.size, serializer); + serde::Serializable::serialize(obj.offset, serializer); +} + +template <> +template +Circuit::BrilligOpcode::CalldataCopy serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BrilligOpcode::CalldataCopy obj; + obj.destination_address = serde::Deserializable::deserialize(deserializer); + obj.size = serde::Deserializable::deserialize(deserializer); + obj.offset = serde::Deserializable::deserialize(deserializer); + return obj; +} + namespace Circuit { inline bool operator==(const BrilligOpcode::Call &lhs, const BrilligOpcode::Call &rhs) { @@ -4667,6 +4725,8 @@ Circuit::BrilligOpcode::Trap serde::Deserializable namespace Circuit { inline bool operator==(const BrilligOpcode::Stop &lhs, const BrilligOpcode::Stop &rhs) { + if (!(lhs.return_data_offset == rhs.return_data_offset)) { return false; } + if (!(lhs.return_data_size == rhs.return_data_size)) { return false; } return true; } @@ -4690,12 +4750,16 @@ namespace Circuit { template <> template void serde::Serializable::serialize(const Circuit::BrilligOpcode::Stop &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.return_data_offset, serializer); + serde::Serializable::serialize(obj.return_data_size, serializer); } template <> template Circuit::BrilligOpcode::Stop serde::Deserializable::deserialize(Deserializer &deserializer) { Circuit::BrilligOpcode::Stop obj; + obj.return_data_offset = serde::Deserializable::deserialize(deserializer); + obj.return_data_size = serde::Deserializable::deserialize(deserializer); return obj; } @@ -4826,6 +4890,7 @@ namespace Circuit { if (!(lhs.public_parameters == rhs.public_parameters)) { return false; } if (!(lhs.return_values == rhs.return_values)) { return false; } if (!(lhs.assert_messages == rhs.assert_messages)) { return false; } + if (!(lhs.recursive == rhs.recursive)) { return false; } return true; } @@ -4856,6 +4921,7 @@ void serde::Serializable::serialize(const Circuit::Circuit &ob serde::Serializable::serialize(obj.public_parameters, serializer); serde::Serializable::serialize(obj.return_values, serializer); serde::Serializable::serialize(obj.assert_messages, serializer); + serde::Serializable::serialize(obj.recursive, serializer); serializer.decrease_container_depth(); } @@ -4870,6 +4936,7 @@ Circuit::Circuit serde::Deserializable::deserialize(Deserializ obj.public_parameters = serde::Deserializable::deserialize(deserializer); obj.return_values = serde::Deserializable::deserialize(deserializer); obj.assert_messages = serde::Deserializable::deserialize(deserializer); + obj.recursive = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } @@ -5238,6 +5305,48 @@ Circuit::MemOp serde::Deserializable::deserialize(Deserializer & return obj; } +namespace Circuit { + + inline bool operator==(const MemoryAddress &lhs, const MemoryAddress &rhs) { + if (!(lhs.value == rhs.value)) { return false; } + return true; + } + + inline std::vector MemoryAddress::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline MemoryAddress MemoryAddress::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::MemoryAddress &obj, Serializer &serializer) { + serializer.increase_container_depth(); + serde::Serializable::serialize(obj.value, serializer); + serializer.decrease_container_depth(); +} + +template <> +template +Circuit::MemoryAddress serde::Deserializable::deserialize(Deserializer &deserializer) { + deserializer.increase_container_depth(); + Circuit::MemoryAddress obj; + obj.value = serde::Deserializable::deserialize(deserializer); + deserializer.decrease_container_depth(); + return obj; +} + namespace Circuit { inline bool operator==(const Opcode &lhs, const Opcode &rhs) { @@ -5682,20 +5791,20 @@ Circuit::PublicInputs serde::Deserializable::deserialize( namespace Circuit { - inline bool operator==(const RegisterIndex &lhs, const RegisterIndex &rhs) { - if (!(lhs.value == rhs.value)) { return false; } + inline bool operator==(const Value &lhs, const Value &rhs) { + if (!(lhs.inner == rhs.inner)) { return false; } return true; } - inline std::vector RegisterIndex::bincodeSerialize() const { + inline std::vector Value::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterIndex RegisterIndex::bincodeDeserialize(std::vector input) { + inline Value Value::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -5706,38 +5815,38 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterIndex &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::Value &obj, Serializer &serializer) { serializer.increase_container_depth(); - serde::Serializable::serialize(obj.value, serializer); + serde::Serializable::serialize(obj.inner, serializer); serializer.decrease_container_depth(); } template <> template -Circuit::RegisterIndex serde::Deserializable::deserialize(Deserializer &deserializer) { +Circuit::Value serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::RegisterIndex obj; - obj.value = serde::Deserializable::deserialize(deserializer); + Circuit::Value obj; + obj.inner = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } namespace Circuit { - inline bool operator==(const RegisterOrMemory &lhs, const RegisterOrMemory &rhs) { + inline bool operator==(const ValueOrArray &lhs, const ValueOrArray &rhs) { if (!(lhs.value == rhs.value)) { return false; } return true; } - inline std::vector RegisterOrMemory::bincodeSerialize() const { + inline std::vector ValueOrArray::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterOrMemory RegisterOrMemory::bincodeDeserialize(std::vector input) { + inline ValueOrArray ValueOrArray::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -5748,7 +5857,7 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterOrMemory &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::ValueOrArray &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -5756,9 +5865,9 @@ void serde::Serializable::serialize(const Circuit::Re template <> template -Circuit::RegisterOrMemory serde::Deserializable::deserialize(Deserializer &deserializer) { +Circuit::ValueOrArray serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::RegisterOrMemory obj; + Circuit::ValueOrArray obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; @@ -5766,20 +5875,20 @@ Circuit::RegisterOrMemory serde::Deserializable::dese namespace Circuit { - inline bool operator==(const RegisterOrMemory::RegisterIndex &lhs, const RegisterOrMemory::RegisterIndex &rhs) { + inline bool operator==(const ValueOrArray::MemoryAddress &lhs, const ValueOrArray::MemoryAddress &rhs) { if (!(lhs.value == rhs.value)) { return false; } return true; } - inline std::vector RegisterOrMemory::RegisterIndex::bincodeSerialize() const { + inline std::vector ValueOrArray::MemoryAddress::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterOrMemory::RegisterIndex RegisterOrMemory::RegisterIndex::bincodeDeserialize(std::vector input) { + inline ValueOrArray::MemoryAddress ValueOrArray::MemoryAddress::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -5790,34 +5899,34 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterOrMemory::RegisterIndex &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::ValueOrArray::MemoryAddress &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::RegisterIndex serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::RegisterOrMemory::RegisterIndex obj; +Circuit::ValueOrArray::MemoryAddress serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::ValueOrArray::MemoryAddress obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const RegisterOrMemory::HeapArray &lhs, const RegisterOrMemory::HeapArray &rhs) { + inline bool operator==(const ValueOrArray::HeapArray &lhs, const ValueOrArray::HeapArray &rhs) { if (!(lhs.value == rhs.value)) { return false; } return true; } - inline std::vector RegisterOrMemory::HeapArray::bincodeSerialize() const { + inline std::vector ValueOrArray::HeapArray::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterOrMemory::HeapArray RegisterOrMemory::HeapArray::bincodeDeserialize(std::vector input) { + inline ValueOrArray::HeapArray ValueOrArray::HeapArray::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -5828,34 +5937,34 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterOrMemory::HeapArray &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::ValueOrArray::HeapArray &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::HeapArray serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::RegisterOrMemory::HeapArray obj; +Circuit::ValueOrArray::HeapArray serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::ValueOrArray::HeapArray obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const RegisterOrMemory::HeapVector &lhs, const RegisterOrMemory::HeapVector &rhs) { + inline bool operator==(const ValueOrArray::HeapVector &lhs, const ValueOrArray::HeapVector &rhs) { if (!(lhs.value == rhs.value)) { return false; } return true; } - inline std::vector RegisterOrMemory::HeapVector::bincodeSerialize() const { + inline std::vector ValueOrArray::HeapVector::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterOrMemory::HeapVector RegisterOrMemory::HeapVector::bincodeDeserialize(std::vector input) { + inline ValueOrArray::HeapVector ValueOrArray::HeapVector::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -5866,60 +5975,18 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterOrMemory::HeapVector &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::ValueOrArray::HeapVector &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::HeapVector serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::RegisterOrMemory::HeapVector obj; +Circuit::ValueOrArray::HeapVector serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::ValueOrArray::HeapVector obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { - - inline bool operator==(const Value &lhs, const Value &rhs) { - if (!(lhs.inner == rhs.inner)) { return false; } - return true; - } - - inline std::vector Value::bincodeSerialize() const { - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); - } - - inline Value Value::bincodeDeserialize(std::vector input) { - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw serde::deserialization_error("Some input bytes were not read"); - } - return value; - } - -} // end of namespace Circuit - -template <> -template -void serde::Serializable::serialize(const Circuit::Value &obj, Serializer &serializer) { - serializer.increase_container_depth(); - serde::Serializable::serialize(obj.inner, serializer); - serializer.decrease_container_depth(); -} - -template <> -template -Circuit::Value serde::Deserializable::deserialize(Deserializer &deserializer) { - deserializer.increase_container_depth(); - Circuit::Value obj; - obj.inner = serde::Deserializable::deserialize(deserializer); - deserializer.decrease_container_depth(); - return obj; -} - namespace Circuit { inline bool operator==(const Witness &lhs, const Witness &rhs) { diff --git a/acvm-repo/acir/src/circuit/mod.rs b/acvm-repo/acir/src/circuit/mod.rs index dcb09d429eb..053cc584955 100644 --- a/acvm-repo/acir/src/circuit/mod.rs +++ b/acvm-repo/acir/src/circuit/mod.rs @@ -43,6 +43,11 @@ pub struct Circuit { // TODO: We should move towards having all the checks being evaluated in the same manner // TODO: as runtime assert messages specified by the user. This will also be a breaking change as the `Circuit` structure will change. pub assert_messages: Vec<(OpcodeLocation, String)>, + + /// States whether the backend should use a SNARK recursion friendly prover. + /// If implemented by a backend, this means that proofs generated with this circuit + /// will be friendly for recursively verifying inside of another SNARK. + pub recursive: bool, } impl Circuit { @@ -322,6 +327,7 @@ mod tests { public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2), Witness(12)])), return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(4), Witness(12)])), assert_messages: Default::default(), + recursive: false, }; fn read_write(circuit: Circuit) -> (Circuit, Circuit) { @@ -352,6 +358,7 @@ mod tests { public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])), return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])), assert_messages: Default::default(), + recursive: false, }; let json = serde_json::to_string_pretty(&circuit).unwrap(); diff --git a/acvm-repo/acir/src/lib.rs b/acvm-repo/acir/src/lib.rs index b7bcaa0c5c0..0a72cec6070 100644 --- a/acvm-repo/acir/src/lib.rs +++ b/acvm-repo/acir/src/lib.rs @@ -31,9 +31,7 @@ mod reflection { path::{Path, PathBuf}, }; - use brillig::{ - BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode, RegisterOrMemory, - }; + use brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode, ValueOrArray}; use serde_reflection::{Tracer, TracerConfig}; use crate::{ @@ -69,7 +67,7 @@ mod reflection { tracer.trace_simple_type::().unwrap(); tracer.trace_simple_type::().unwrap(); tracer.trace_simple_type::().unwrap(); - tracer.trace_simple_type::().unwrap(); + tracer.trace_simple_type::().unwrap(); let registry = tracer.registry().unwrap(); diff --git a/acvm-repo/acir/tests/test_program_serialization.rs b/acvm-repo/acir/tests/test_program_serialization.rs index 7d3b7b32d35..c94c5ae66b0 100644 --- a/acvm-repo/acir/tests/test_program_serialization.rs +++ b/acvm-repo/acir/tests/test_program_serialization.rs @@ -20,7 +20,7 @@ use acir::{ native_types::{Expression, Witness}, }; use acir_field::FieldElement; -use brillig::{HeapArray, RegisterIndex, RegisterOrMemory}; +use brillig::{HeapArray, MemoryAddress, ValueOrArray}; #[test] fn addition_circuit() { @@ -173,15 +173,23 @@ fn simple_brillig_foreign_call() { inputs: vec![ BrilligInputs::Single(w_input.into()), // Input Register 0, ], - // This tells the BrilligSolver which witnesses its output registers correspond to + // This tells the BrilligSolver which witnesses its output values correspond to outputs: vec![ BrilligOutputs::Simple(w_inverted), // Output Register 1 ], - bytecode: vec![brillig::Opcode::ForeignCall { - function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], - }], + bytecode: vec![ + brillig::Opcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 1, + offset: 0, + }, + brillig::Opcode::ForeignCall { + function: "invert".into(), + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], + }, + brillig::Opcode::Stop { return_data_offset: 0, return_data_size: 1 }, + ], predicate: None, }; @@ -196,10 +204,11 @@ fn simple_brillig_foreign_call() { let bytes = Circuit::serialize_circuit(&circuit); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 49, 10, 64, 33, 12, 67, 99, 63, 124, 60, 142, - 222, 192, 203, 56, 184, 56, 136, 120, 126, 5, 21, 226, 160, 139, 62, 40, 13, 45, 132, 68, - 3, 80, 232, 124, 164, 153, 121, 115, 99, 155, 59, 172, 122, 231, 101, 56, 175, 80, 86, 221, - 230, 31, 58, 196, 226, 83, 62, 53, 91, 16, 122, 10, 246, 84, 99, 243, 0, 30, 59, 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 177, 10, 192, 32, 12, 68, 207, 148, 150, 118, + 234, 175, 216, 63, 232, 207, 116, 232, 210, 161, 136, 223, 175, 98, 132, 27, 212, 69, 31, + 132, 28, 23, 8, 119, 59, 0, 131, 204, 66, 154, 41, 222, 173, 219, 142, 113, 153, 121, 191, + 44, 231, 21, 237, 144, 88, 43, 249, 11, 71, 156, 77, 245, 251, 249, 231, 119, 189, 214, + 204, 89, 187, 11, 25, 130, 54, 1, 36, 1, 124, 242, 107, 1, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -221,39 +230,54 @@ fn complex_brillig_foreign_call() { let brillig_data = Brillig { inputs: vec![ - // Input Register 0 + // Input 0,1,2 BrilligInputs::Array(vec![ Expression::from(a), Expression::from(b), Expression::from(c), ]), - // Input Register 1 + // Input 3 BrilligInputs::Single(Expression { mul_terms: vec![], linear_combinations: vec![(fe_1, a), (fe_1, b), (fe_1, c)], q_c: fe_0, }), ], - // This tells the BrilligSolver which witnesses its output registers correspond to + // This tells the BrilligSolver which witnesses its output values correspond to outputs: vec![ - BrilligOutputs::Array(vec![a_times_2, b_times_3, c_times_4]), // Output Register 0 - BrilligOutputs::Simple(a_plus_b_plus_c), // Output Register 1 - BrilligOutputs::Simple(a_plus_b_plus_c_times_2), // Output Register 2 + BrilligOutputs::Array(vec![a_times_2, b_times_3, c_times_4]), // Output 0,1,2 + BrilligOutputs::Simple(a_plus_b_plus_c), // Output 3 + BrilligOutputs::Simple(a_plus_b_plus_c_times_2), // Output 4 ], bytecode: vec![ + brillig::Opcode::CalldataCopy { + destination_address: MemoryAddress(32), + size: 3, + offset: 0, + }, + brillig::Opcode::Const { + destination: MemoryAddress(0), + value: brillig::Value::from(32_usize), + }, + brillig::Opcode::CalldataCopy { + destination_address: MemoryAddress(1), + size: 1, + offset: 3, + }, // Oracles are named 'foreign calls' in brillig brillig::Opcode::ForeignCall { function: "complex".into(), inputs: vec![ - RegisterOrMemory::HeapArray(HeapArray { pointer: 0.into(), size: 3 }), - RegisterOrMemory::RegisterIndex(RegisterIndex::from(1)), + ValueOrArray::HeapArray(HeapArray { pointer: 0.into(), size: 3 }), + ValueOrArray::MemoryAddress(MemoryAddress::from(1)), ], destinations: vec![ - RegisterOrMemory::HeapArray(HeapArray { pointer: 0.into(), size: 3 }), - RegisterOrMemory::RegisterIndex(RegisterIndex::from(1)), - RegisterOrMemory::RegisterIndex(RegisterIndex::from(2)), + ValueOrArray::HeapArray(HeapArray { pointer: 0.into(), size: 3 }), + ValueOrArray::MemoryAddress(MemoryAddress::from(35)), + ValueOrArray::MemoryAddress(MemoryAddress::from(36)), ], }, + brillig::Opcode::Stop { return_data_offset: 32, return_data_size: 5 }, ], predicate: None, }; @@ -269,13 +293,14 @@ fn complex_brillig_foreign_call() { let bytes = Circuit::serialize_circuit(&circuit); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 219, 10, 128, 48, 8, 117, 174, 139, 159, 179, - 254, 160, 127, 137, 222, 138, 122, 236, 243, 19, 114, 32, 22, 244, 144, 131, 118, 64, 156, - 178, 29, 14, 59, 74, 0, 16, 224, 66, 228, 64, 57, 7, 169, 53, 242, 189, 81, 114, 250, 134, - 33, 248, 113, 165, 82, 26, 177, 2, 141, 177, 128, 198, 60, 15, 63, 245, 219, 211, 23, 215, - 255, 139, 15, 251, 211, 112, 180, 28, 157, 212, 189, 100, 82, 179, 64, 170, 63, 109, 235, - 190, 204, 135, 166, 178, 150, 216, 62, 154, 252, 250, 70, 147, 35, 220, 119, 93, 227, 4, - 182, 131, 81, 25, 36, 4, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 65, 10, 132, 48, 12, 76, 218, 237, 174, 123, + 242, 11, 130, 62, 160, 250, 2, 255, 34, 222, 20, 61, 250, 124, 11, 78, 49, 4, 193, 131, 21, + 52, 16, 210, 132, 105, 50, 77, 210, 140, 136, 152, 54, 177, 65, 13, 206, 12, 95, 74, 196, + 181, 176, 254, 154, 212, 156, 46, 151, 191, 139, 163, 121, 1, 71, 123, 3, 199, 184, 15, 15, + 157, 119, 202, 185, 36, 237, 159, 61, 248, 63, 159, 160, 46, 232, 23, 254, 15, 54, 67, 156, + 96, 11, 213, 119, 82, 248, 116, 179, 104, 188, 163, 125, 15, 89, 213, 253, 139, 154, 221, + 52, 206, 67, 191, 88, 5, 213, 52, 75, 113, 174, 96, 205, 201, 157, 24, 207, 197, 211, 157, + 6, 50, 18, 233, 158, 72, 89, 1, 53, 215, 75, 175, 196, 4, 0, 0, ]; assert_eq!(bytes, expected_serialization) diff --git a/acvm-repo/acvm/src/pwg/brillig.rs b/acvm-repo/acvm/src/pwg/brillig.rs index 0db38c776e2..4b62f86f0eb 100644 --- a/acvm-repo/acvm/src/pwg/brillig.rs +++ b/acvm-repo/acvm/src/pwg/brillig.rs @@ -1,5 +1,5 @@ use acir::{ - brillig::{ForeignCallParam, ForeignCallResult, RegisterIndex, Value}, + brillig::{ForeignCallParam, ForeignCallResult, Value}, circuit::{ brillig::{Brillig, BrilligInputs, BrilligOutputs}, OpcodeLocation, @@ -8,7 +8,7 @@ use acir::{ FieldElement, }; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use brillig_vm::{Registers, VMStatus, VM}; +use brillig_vm::{VMStatus, VM}; use crate::{pwg::OpcodeNotSolvable, OpcodeResolutionError}; @@ -69,16 +69,15 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { acir_index: usize, ) -> Result { // Set input values - let mut input_register_values: Vec = Vec::new(); - let mut input_memory: Vec = Vec::new(); + let mut calldata: Vec = Vec::new(); // Each input represents an expression or array of expressions to evaluate. // Iterate over each input and evaluate the expression(s) associated with it. - // Push the results into registers and/or memory. + // Push the results into memory. // If a certain expression is not solvable, we stall the ACVM and do not proceed with Brillig VM execution. for input in &brillig.inputs { match input { BrilligInputs::Single(expr) => match get_value(expr, initial_witness) { - Ok(value) => input_register_values.push(value.into()), + Ok(value) => calldata.push(value.into()), Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), @@ -87,10 +86,9 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { }, BrilligInputs::Array(expr_arr) => { // Attempt to fetch all array input values - let memory_pointer = input_memory.len(); for expr in expr_arr.iter() { match get_value(expr, initial_witness) { - Ok(value) => input_memory.push(value.into()), + Ok(value) => calldata.push(value.into()), Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), @@ -98,28 +96,16 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { } } } - - // Push value of the array pointer as a register - input_register_values.push(Value::from(memory_pointer)); } } } - // Instantiate a Brillig VM given the solved input registers and memory + // Instantiate a Brillig VM given the solved calldata // along with the Brillig bytecode. - let input_registers = Registers::load(input_register_values); - let vm = VM::new(input_registers, input_memory, &brillig.bytecode, vec![], bb_solver); + let vm = VM::new(calldata, &brillig.bytecode, vec![], bb_solver); Ok(Self { vm, acir_index }) } - pub fn get_registers(&self) -> &Registers { - self.vm.get_registers() - } - - pub fn set_register(&mut self, register_index: usize, value: Value) { - self.vm.set_register(RegisterIndex(register_index), value); - } - pub fn get_memory(&self) -> &[Value] { self.vm.get_memory() } @@ -151,7 +137,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { // Return the "resolution" to the caller who may choose to make subsequent calls // (when it gets foreign call results for example). match vm_status { - VMStatus::Finished => Ok(BrilligSolverStatus::Finished), + VMStatus::Finished { .. } => Ok(BrilligSolverStatus::Finished), VMStatus::InProgress => Ok(BrilligSolverStatus::InProgress), VMStatus::Failure { message, call_stack } => { Err(OpcodeResolutionError::BrilligFunctionFailed { @@ -179,8 +165,8 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { // Finish the Brillig execution by writing the outputs to the witness map let vm_status = self.vm.get_status(); match vm_status { - VMStatus::Finished => { - self.write_brillig_outputs(witness, brillig)?; + VMStatus::Finished { return_data_offset, return_data_size } => { + self.write_brillig_outputs(witness, return_data_offset, return_data_size, brillig)?; Ok(()) } _ => panic!("Brillig VM has not completed execution"), @@ -190,24 +176,32 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { fn write_brillig_outputs( &self, witness_map: &mut WitnessMap, + return_data_offset: usize, + return_data_size: usize, brillig: &Brillig, ) -> Result<(), OpcodeResolutionError> { // Write VM execution results into the witness map - for (i, output) in brillig.outputs.iter().enumerate() { - let register_value = self.vm.get_registers().get(RegisterIndex::from(i)); + let memory = self.vm.get_memory(); + let mut current_ret_data_idx = return_data_offset; + for output in brillig.outputs.iter() { match output { BrilligOutputs::Simple(witness) => { - insert_value(witness, register_value.to_field(), witness_map)?; + insert_value(witness, memory[current_ret_data_idx].to_field(), witness_map)?; + current_ret_data_idx += 1; } BrilligOutputs::Array(witness_arr) => { - // Treat the register value as a pointer to memory - for (i, witness) in witness_arr.iter().enumerate() { - let value = &self.vm.get_memory()[register_value.to_usize() + i]; + for witness in witness_arr.iter() { + let value = memory[current_ret_data_idx]; insert_value(witness, value.to_field(), witness_map)?; + current_ret_data_idx += 1; } } } } + assert!( + current_ret_data_idx == return_data_offset + return_data_size, + "Brillig VM did not write the expected number of return values" + ); Ok(()) } diff --git a/acvm-repo/acvm/tests/solver.rs b/acvm-repo/acvm/tests/solver.rs index 486e04d5bf1..9ff4a4f4897 100644 --- a/acvm-repo/acvm/tests/solver.rs +++ b/acvm-repo/acvm/tests/solver.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use acir::{ - brillig::{BinaryFieldOp, Opcode as BrilligOpcode, RegisterIndex, RegisterOrMemory, Value}, + brillig::{BinaryFieldOp, MemoryAddress, Opcode as BrilligOpcode, Value, ValueOrArray}, circuit::{ brillig::{Brillig, BrilligInputs, BrilligOutputs}, opcodes::{BlockId, MemOp}, @@ -37,9 +37,9 @@ fn inversion_brillig_oracle_equivalence() { let equal_opcode = BrilligOpcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; let brillig_data = Brillig { @@ -52,20 +52,26 @@ fn inversion_brillig_oracle_equivalence() { }), BrilligInputs::Single(Expression::default()), // Input Register 1 ], - // This tells the BrilligSolver which witnesses its output registers correspond to + // This tells the BrilligSolver which witnesses its output values correspond to outputs: vec![ BrilligOutputs::Simple(w_x_plus_y), // Output Register 0 - from input BrilligOutputs::Simple(w_oracle), // Output Register 1 BrilligOutputs::Simple(w_equal_res), // Output Register 2 ], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 2, + offset: 0, + }, equal_opcode, // Oracles are named 'foreign calls' in brillig BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(1))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 3 }, ], predicate: None, }; @@ -151,9 +157,9 @@ fn double_inversion_brillig_oracle() { let equal_opcode = BrilligOpcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(4), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(4), }; let brillig_data = Brillig { @@ -180,18 +186,24 @@ fn double_inversion_brillig_oracle() { BrilligOutputs::Simple(w_equal_res), // Output Register 4 ], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 3, + offset: 0, + }, equal_opcode, // Oracles are named 'foreign calls' in brillig BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(1))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(3))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(2))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(3))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(2))], }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 5 }, ], predicate: None, }; @@ -306,17 +318,23 @@ fn oracle_dependent_execution() { BrilligOutputs::Simple(w_y_inv), // Output Register 3 ], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 3, + offset: 0, + }, // Oracles are named 'foreign calls' in brillig BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(1))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(3))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(2))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(3))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(2))], }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 4 }, ], predicate: None, }; @@ -404,9 +422,9 @@ fn brillig_oracle_predicate() { let equal_opcode = BrilligOpcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; let brillig_opcode = Opcode::Brillig(Brillig { @@ -425,12 +443,17 @@ fn brillig_oracle_predicate() { BrilligOutputs::Simple(w_lt_res), ], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 2, + offset: 0, + }, equal_opcode, // Oracles are named 'foreign calls' in brillig BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(1))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, ], predicate: Some(Expression::default()), @@ -502,21 +525,24 @@ fn unsatisfied_opcode_resolved_brillig() { let w_y = Witness(5); let w_result = Witness(6); + let calldata_copy_opcode = + BrilligOpcode::CalldataCopy { destination_address: MemoryAddress(0), size: 2, offset: 0 }; + let equal_opcode = BrilligOpcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; // Jump pass the trap if the values are equal, else // jump to the trap let location_of_stop = 3; let jmp_if_opcode = - BrilligOpcode::JumpIf { condition: RegisterIndex::from(2), location: location_of_stop }; + BrilligOpcode::JumpIf { condition: MemoryAddress::from(2), location: location_of_stop }; let trap_opcode = BrilligOpcode::Trap; - let stop_opcode = BrilligOpcode::Stop; + let stop_opcode = BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }; let brillig_opcode = Opcode::Brillig(Brillig { inputs: vec![ @@ -532,7 +558,7 @@ fn unsatisfied_opcode_resolved_brillig() { }), ], outputs: vec![BrilligOutputs::Simple(w_result)], - bytecode: vec![equal_opcode, jmp_if_opcode, trap_opcode, stop_opcode], + bytecode: vec![calldata_copy_opcode, equal_opcode, jmp_if_opcode, trap_opcode, stop_opcode], predicate: Some(Expression::one()), }); @@ -564,7 +590,7 @@ fn unsatisfied_opcode_resolved_brillig() { solver_status, ACVMStatus::Failure(OpcodeResolutionError::BrilligFunctionFailed { message: "explicit trap hit in brillig".to_string(), - call_stack: vec![OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }] + call_stack: vec![OpcodeLocation::Brillig { acir_index: 0, brillig_index: 3 }] }), "The first opcode is not satisfiable, expected an error indicating this" ); diff --git a/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts b/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts index 1b6f5e4319a..faae98bcf47 100644 --- a/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts +++ b/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts @@ -2,11 +2,12 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `complex_brillig_foreign_call` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 219, 10, 128, 48, 8, 117, 174, 139, 159, 179, 254, 160, 127, 137, 222, - 138, 122, 236, 243, 19, 114, 32, 22, 244, 144, 131, 118, 64, 156, 178, 29, 14, 59, 74, 0, 16, 224, 66, 228, 64, 57, 7, - 169, 53, 242, 189, 81, 114, 250, 134, 33, 248, 113, 165, 82, 26, 177, 2, 141, 177, 128, 198, 60, 15, 63, 245, 219, - 211, 23, 215, 255, 139, 15, 251, 211, 112, 180, 28, 157, 212, 189, 100, 82, 179, 64, 170, 63, 109, 235, 190, 204, 135, - 166, 178, 150, 216, 62, 154, 252, 250, 70, 147, 35, 220, 119, 93, 227, 4, 182, 131, 81, 25, 36, 4, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 65, 10, 132, 48, 12, 76, 218, 237, 174, 123, 242, 11, 130, 62, 160, 250, + 2, 255, 34, 222, 20, 61, 250, 124, 11, 78, 49, 4, 193, 131, 21, 52, 16, 210, 132, 105, 50, 77, 210, 140, 136, 152, 54, + 177, 65, 13, 206, 12, 95, 74, 196, 181, 176, 254, 154, 212, 156, 46, 151, 191, 139, 163, 121, 1, 71, 123, 3, 199, 184, + 15, 15, 157, 119, 202, 185, 36, 237, 159, 61, 248, 63, 159, 160, 46, 232, 23, 254, 15, 54, 67, 156, 96, 11, 213, 119, + 82, 248, 116, 179, 104, 188, 163, 125, 15, 89, 213, 253, 139, 154, 221, 52, 206, 67, 191, 88, 5, 213, 52, 75, 113, + 174, 96, 205, 201, 157, 24, 207, 197, 211, 157, 6, 50, 18, 233, 158, 72, 89, 1, 53, 215, 75, 175, 196, 4, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], diff --git a/acvm-repo/acvm_js/test/shared/foreign_call.ts b/acvm-repo/acvm_js/test/shared/foreign_call.ts index 178ec3a09d1..7a65f29117c 100644 --- a/acvm-repo/acvm_js/test/shared/foreign_call.ts +++ b/acvm-repo/acvm_js/test/shared/foreign_call.ts @@ -2,10 +2,10 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `simple_brillig_foreign_call` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 49, 10, 64, 33, 12, 67, 99, 63, 124, 60, 142, 222, 192, 203, 56, 184, 56, - 136, 120, 126, 5, 21, 226, 160, 139, 62, 40, 13, 45, 132, 68, 3, 80, 232, 124, 164, 153, 121, 115, 99, 155, 59, 172, - 122, 231, 101, 56, 175, 80, 86, 221, 230, 31, 58, 196, 226, 83, 62, 53, 91, 16, 122, 10, 246, 84, 99, 243, 0, 30, 59, - 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 177, 10, 192, 32, 12, 68, 207, 148, 150, 118, 234, 175, 216, 63, 232, + 207, 116, 232, 210, 161, 136, 223, 175, 98, 132, 27, 212, 69, 31, 132, 28, 23, 8, 119, 59, 0, 131, 204, 66, 154, 41, + 222, 173, 219, 142, 113, 153, 121, 191, 44, 231, 21, 237, 144, 88, 43, 249, 11, 71, 156, 77, 245, 251, 249, 231, 119, + 189, 214, 204, 89, 187, 11, 25, 130, 54, 1, 36, 1, 124, 242, 107, 1, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000005'], diff --git a/acvm-repo/brillig/src/black_box.rs b/acvm-repo/brillig/src/black_box.rs index 22fac6f3ba3..9195d8aa6f3 100644 --- a/acvm-repo/brillig/src/black_box.rs +++ b/acvm-repo/brillig/src/black_box.rs @@ -1,4 +1,4 @@ -use crate::{opcodes::HeapVector, HeapArray, RegisterIndex}; +use crate::{opcodes::HeapVector, HeapArray, MemoryAddress}; use serde::{Deserialize, Serialize}; /// These opcodes provide an equivalent of ACIR blackbox functions. @@ -36,7 +36,7 @@ pub enum BlackBoxOp { public_key_x: HeapArray, public_key_y: HeapArray, signature: HeapArray, - result: RegisterIndex, + result: MemoryAddress, }, /// Verifies a ECDSA signature over the secp256r1 curve. EcdsaSecp256r1 { @@ -44,75 +44,75 @@ pub enum BlackBoxOp { public_key_x: HeapArray, public_key_y: HeapArray, signature: HeapArray, - result: RegisterIndex, + result: MemoryAddress, }, /// Verifies a Schnorr signature over a curve which is "pairing friendly" with the curve on which the Brillig bytecode is defined. SchnorrVerify { - public_key_x: RegisterIndex, - public_key_y: RegisterIndex, + public_key_x: MemoryAddress, + public_key_y: MemoryAddress, message: HeapVector, signature: HeapVector, - result: RegisterIndex, + result: MemoryAddress, }, /// Calculates a Pedersen commitment to the inputs. PedersenCommitment { inputs: HeapVector, - domain_separator: RegisterIndex, + domain_separator: MemoryAddress, output: HeapArray, }, /// Calculates a Pedersen hash to the inputs. PedersenHash { inputs: HeapVector, - domain_separator: RegisterIndex, - output: RegisterIndex, + domain_separator: MemoryAddress, + output: MemoryAddress, }, /// Performs scalar multiplication over the embedded curve. FixedBaseScalarMul { - low: RegisterIndex, - high: RegisterIndex, + low: MemoryAddress, + high: MemoryAddress, result: HeapArray, }, /// Performs addition over the embedded curve. EmbeddedCurveAdd { - input1_x: RegisterIndex, - input1_y: RegisterIndex, - input2_x: RegisterIndex, - input2_y: RegisterIndex, + input1_x: MemoryAddress, + input1_y: MemoryAddress, + input2_x: MemoryAddress, + input2_y: MemoryAddress, result: HeapArray, }, BigIntAdd { - lhs: RegisterIndex, - rhs: RegisterIndex, - output: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + output: MemoryAddress, }, BigIntNeg { - lhs: RegisterIndex, - rhs: RegisterIndex, - output: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + output: MemoryAddress, }, BigIntMul { - lhs: RegisterIndex, - rhs: RegisterIndex, - output: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + output: MemoryAddress, }, BigIntDiv { - lhs: RegisterIndex, - rhs: RegisterIndex, - output: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + output: MemoryAddress, }, BigIntFromLeBytes { inputs: HeapVector, modulus: HeapVector, - output: RegisterIndex, + output: MemoryAddress, }, BigIntToLeBytes { - input: RegisterIndex, + input: MemoryAddress, output: HeapVector, }, Poseidon2Permutation { message: HeapVector, output: HeapArray, - len: RegisterIndex, + len: MemoryAddress, }, Sha256Compression { input: HeapVector, diff --git a/acvm-repo/brillig/src/lib.rs b/acvm-repo/brillig/src/lib.rs index 5e033e3c792..2e9d80e2f3b 100644 --- a/acvm-repo/brillig/src/lib.rs +++ b/acvm-repo/brillig/src/lib.rs @@ -17,9 +17,7 @@ mod value; pub use black_box::BlackBoxOp; pub use foreign_call::{ForeignCallParam, ForeignCallResult}; -pub use opcodes::{ - BinaryFieldOp, BinaryIntOp, HeapArray, HeapVector, RegisterIndex, RegisterOrMemory, -}; +pub use opcodes::{BinaryFieldOp, BinaryIntOp, HeapArray, HeapVector, MemoryAddress, ValueOrArray}; pub use opcodes::{BrilligOpcode as Opcode, Label}; pub use value::Typ; pub use value::Value; diff --git a/acvm-repo/brillig/src/opcodes.rs b/acvm-repo/brillig/src/opcodes.rs index 79295cc6e5d..9a6f11c463f 100644 --- a/acvm-repo/brillig/src/opcodes.rs +++ b/acvm-repo/brillig/src/opcodes.rs @@ -4,100 +4,106 @@ use serde::{Deserialize, Serialize}; pub type Label = usize; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct RegisterIndex(pub usize); +pub struct MemoryAddress(pub usize); -/// `RegisterIndex` refers to the index in VM register space. -impl RegisterIndex { +/// `MemoryAddress` refers to the index in VM memory. +impl MemoryAddress { pub fn to_usize(self) -> usize { self.0 } } -impl From for RegisterIndex { +impl From for MemoryAddress { fn from(value: usize) -> Self { - RegisterIndex(value) + MemoryAddress(value) } } -/// A fixed-sized array starting from a Brillig register memory location. +/// A fixed-sized array starting from a Brillig memory location. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub struct HeapArray { - pub pointer: RegisterIndex, + pub pointer: MemoryAddress, pub size: usize, } -/// A register-sized vector passed starting from a Brillig register memory location and with a register-held size +/// A memory-sized vector passed starting from a Brillig memory location and with a memory-held size #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub struct HeapVector { - pub pointer: RegisterIndex, - pub size: RegisterIndex, + pub pointer: MemoryAddress, + pub size: MemoryAddress, } /// Lays out various ways an external foreign call's input and output data may be interpreted inside Brillig. -/// This data can either be an individual register value or memory. +/// This data can either be an individual value or memory. /// /// While we are usually agnostic to how memory is passed within Brillig, /// this needs to be encoded somehow when dealing with an external system. /// For simplicity, the extra type information is given right in the ForeignCall instructions. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] -pub enum RegisterOrMemory { - /// A single register value passed to or from an external call - /// It is an 'immediate' value - used without dereferencing memory. - /// For a foreign call input, the value is read directly from the register. - /// For a foreign call output, the value is written directly to the register. - RegisterIndex(RegisterIndex), +pub enum ValueOrArray { + /// A single value passed to or from an external call + /// It is an 'immediate' value - used without dereferencing. + /// For a foreign call input, the value is read directly from memory. + /// For a foreign call output, the value is written directly to memory. + MemoryAddress(MemoryAddress), /// An array passed to or from an external call /// In the case of a foreign call input, the array is read from this Brillig memory location + usize more cells. /// In the case of a foreign call output, the array is written to this Brillig memory location with the usize being here just as a sanity check for the size write. HeapArray(HeapArray), /// A vector passed to or from an external call - /// In the case of a foreign call input, the vector is read from this Brillig memory location + as many cells as the 2nd register indicates. - /// In the case of a foreign call output, the vector is written to this Brillig memory location and as 'size' cells, with size being stored in the second register. + /// In the case of a foreign call input, the vector is read from this Brillig memory location + as many cells as the 2nd address indicates. + /// In the case of a foreign call output, the vector is written to this Brillig memory location and as 'size' cells, with size being stored in the second address. HeapVector(HeapVector), } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum BrilligOpcode { - /// Takes the fields in registers `lhs` and `rhs` + /// Takes the fields in addresses `lhs` and `rhs` /// Performs the specified binary operation - /// and stores the value in the `result` register. + /// and stores the value in the `result` address. BinaryFieldOp { - destination: RegisterIndex, + destination: MemoryAddress, op: BinaryFieldOp, - lhs: RegisterIndex, - rhs: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, }, - /// Takes the `bit_size` size integers in registers `lhs` and `rhs` + /// Takes the `bit_size` size integers in addresses `lhs` and `rhs` /// Performs the specified binary operation - /// and stores the value in the `result` register. + /// and stores the value in the `result` address. BinaryIntOp { - destination: RegisterIndex, + destination: MemoryAddress, op: BinaryIntOp, bit_size: u32, - lhs: RegisterIndex, - rhs: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, }, JumpIfNot { - condition: RegisterIndex, + condition: MemoryAddress, location: Label, }, /// Sets the program counter to the value located at `destination` /// If the value at `condition` is non-zero JumpIf { - condition: RegisterIndex, + condition: MemoryAddress, location: Label, }, /// Sets the program counter to the label. Jump { location: Label, }, + /// Copies calldata after the offset to the specified address and length + CalldataCopy { + destination_address: MemoryAddress, + size: usize, + offset: usize, + }, /// We don't support dynamic jumps or calls /// See https://github.com/ethereum/aleth/issues/3404 for reasoning Call { location: Label, }, Const { - destination: RegisterIndex, + destination: MemoryAddress, value: Value, }, Return, @@ -109,28 +115,31 @@ pub enum BrilligOpcode { /// Interpreted by caller context, ie this will have different meanings depending on /// who the caller is. function: String, - /// Destination registers (may be single values or memory pointers). - destinations: Vec, - /// Input registers (may be single values or memory pointers). - inputs: Vec, + /// Destination addresses (may be single values or memory pointers). + destinations: Vec, + /// Input addresses (may be single values or memory pointers). + inputs: Vec, }, Mov { - destination: RegisterIndex, - source: RegisterIndex, + destination: MemoryAddress, + source: MemoryAddress, }, Load { - destination: RegisterIndex, - source_pointer: RegisterIndex, + destination: MemoryAddress, + source_pointer: MemoryAddress, }, Store { - destination_pointer: RegisterIndex, - source: RegisterIndex, + destination_pointer: MemoryAddress, + source: MemoryAddress, }, BlackBox(BlackBoxOp), /// Used to denote execution failure Trap, - /// Stop execution - Stop, + /// Stop execution, returning data after the offset + Stop { + return_data_offset: usize, + return_data_size: usize, + }, } /// Binary fixed-length field expressions diff --git a/acvm-repo/brillig/src/value.rs b/acvm-repo/brillig/src/value.rs index 73a7d897eb7..5a532cbc1a7 100644 --- a/acvm-repo/brillig/src/value.rs +++ b/acvm-repo/brillig/src/value.rs @@ -37,8 +37,8 @@ impl Value { /// Panics: If `Value` cannot fit into a u64 or `Value` does //// not fit into a usize. pub fn to_usize(&self) -> usize { - usize::try_from(self.inner.try_to_u64().expect("register does not fit into u64")) - .expect("register does not fit into usize") + usize::try_from(self.inner.try_to_u64().expect("value does not fit into u64")) + .expect("value does not fit into usize") } } diff --git a/acvm-repo/brillig_vm/src/black_box.rs b/acvm-repo/brillig_vm/src/black_box.rs index e9c25200c47..de20d194dde 100644 --- a/acvm-repo/brillig_vm/src/black_box.rs +++ b/acvm-repo/brillig_vm/src/black_box.rs @@ -5,23 +5,14 @@ use acvm_blackbox_solver::{ sha256, BlackBoxFunctionSolver, BlackBoxResolutionError, }; -use crate::{Memory, Registers}; +use crate::Memory; -fn read_heap_vector<'a>( - memory: &'a Memory, - registers: &Registers, - vector: &HeapVector, -) -> &'a [Value] { - memory - .read_slice(registers.get(vector.pointer).to_usize(), registers.get(vector.size).to_usize()) +fn read_heap_vector<'a>(memory: &'a Memory, vector: &HeapVector) -> &'a [Value] { + memory.read_slice(memory.read_ref(vector.pointer), memory.read(vector.size).to_usize()) } -fn read_heap_array<'a>( - memory: &'a Memory, - registers: &Registers, - array: &HeapArray, -) -> &'a [Value] { - memory.read_slice(registers.get(array.pointer).to_usize(), array.size) +fn read_heap_array<'a>(memory: &'a Memory, array: &HeapArray) -> &'a [Value] { + memory.read_slice(memory.read_ref(array.pointer), array.size) } /// Extracts the last byte of every value @@ -42,36 +33,35 @@ fn to_value_vec(input: &[u8]) -> Vec { pub(crate) fn evaluate_black_box( op: &BlackBoxOp, solver: &Solver, - registers: &mut Registers, memory: &mut Memory, ) -> Result<(), BlackBoxResolutionError> { match op { BlackBoxOp::Sha256 { message, output } => { - let message = to_u8_vec(read_heap_vector(memory, registers, message)); + let message = to_u8_vec(read_heap_vector(memory, message)); let bytes = sha256(message.as_slice())?; - memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes)); + memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes)); Ok(()) } BlackBoxOp::Blake2s { message, output } => { - let message = to_u8_vec(read_heap_vector(memory, registers, message)); + let message = to_u8_vec(read_heap_vector(memory, message)); let bytes = blake2s(message.as_slice())?; - memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes)); + memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes)); Ok(()) } BlackBoxOp::Blake3 { message, output } => { - let message = to_u8_vec(read_heap_vector(memory, registers, message)); + let message = to_u8_vec(read_heap_vector(memory, message)); let bytes = blake3(message.as_slice())?; - memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes)); + memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes)); Ok(()) } BlackBoxOp::Keccak256 { message, output } => { - let message = to_u8_vec(read_heap_vector(memory, registers, message)); + let message = to_u8_vec(read_heap_vector(memory, message)); let bytes = keccak256(message.as_slice())?; - memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes)); + memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes)); Ok(()) } BlackBoxOp::Keccakf1600 { message, output } => { - let state_vec: Vec = read_heap_vector(memory, registers, message) + let state_vec: Vec = read_heap_vector(memory, message) .iter() .map(|value| value.to_field().try_to_u64().unwrap()) .collect(); @@ -81,7 +71,7 @@ pub(crate) fn evaluate_black_box( let new_state: Vec = new_state.into_iter().map(|x| Value::from(x as usize)).collect(); - memory.write_slice(registers.get(output.pointer).to_usize(), &new_state); + memory.write_slice(memory.read_ref(output.pointer), &new_state); Ok(()) } BlackBoxOp::EcdsaSecp256k1 { @@ -89,42 +79,37 @@ pub(crate) fn evaluate_black_box( public_key_x, public_key_y, signature, - result: result_register, + result: result_address, } | BlackBoxOp::EcdsaSecp256r1 { hashed_msg, public_key_x, public_key_y, signature, - result: result_register, + result: result_address, } => { let bb_func = black_box_function_from_op(op); - let public_key_x: [u8; 32] = to_u8_vec(read_heap_array( - memory, - registers, - public_key_x, - )) - .try_into() - .map_err(|_| { - BlackBoxResolutionError::Failed(bb_func, "Invalid public key x length".to_string()) - })?; - let public_key_y: [u8; 32] = to_u8_vec(read_heap_array( - memory, - registers, - public_key_y, - )) - .try_into() - .map_err(|_| { - BlackBoxResolutionError::Failed(bb_func, "Invalid public key y length".to_string()) - })?; - let signature: [u8; 64] = to_u8_vec(read_heap_array(memory, registers, signature)) - .try_into() - .map_err(|_| { + let public_key_x: [u8; 32] = + to_u8_vec(read_heap_array(memory, public_key_x)).try_into().map_err(|_| { + BlackBoxResolutionError::Failed( + bb_func, + "Invalid public key x length".to_string(), + ) + })?; + let public_key_y: [u8; 32] = + to_u8_vec(read_heap_array(memory, public_key_y)).try_into().map_err(|_| { + BlackBoxResolutionError::Failed( + bb_func, + "Invalid public key y length".to_string(), + ) + })?; + let signature: [u8; 64] = + to_u8_vec(read_heap_array(memory, signature)).try_into().map_err(|_| { BlackBoxResolutionError::Failed(bb_func, "Invalid signature length".to_string()) })?; - let hashed_msg = to_u8_vec(read_heap_vector(memory, registers, hashed_msg)); + let hashed_msg = to_u8_vec(read_heap_vector(memory, hashed_msg)); let result = match op { BlackBoxOp::EcdsaSecp256k1 { .. } => { @@ -136,61 +121,61 @@ pub(crate) fn evaluate_black_box( _ => unreachable!("`BlackBoxOp` is guarded against being a non-ecdsa operation"), }; - registers.set(*result_register, result.into()); + memory.write(*result_address, result.into()); Ok(()) } BlackBoxOp::SchnorrVerify { public_key_x, public_key_y, message, signature, result } => { - let public_key_x = registers.get(*public_key_x).to_field(); - let public_key_y = registers.get(*public_key_y).to_field(); - let message: Vec = to_u8_vec(read_heap_vector(memory, registers, message)); - let signature: Vec = to_u8_vec(read_heap_vector(memory, registers, signature)); + let public_key_x = memory.read(*public_key_x).to_field(); + let public_key_y = memory.read(*public_key_y).to_field(); + let message: Vec = to_u8_vec(read_heap_vector(memory, message)); + let signature: Vec = to_u8_vec(read_heap_vector(memory, signature)); let verified = solver.schnorr_verify(&public_key_x, &public_key_y, &signature, &message)?; - registers.set(*result, verified.into()); + memory.write(*result, verified.into()); Ok(()) } BlackBoxOp::FixedBaseScalarMul { low, high, result } => { - let low = registers.get(*low).to_field(); - let high = registers.get(*high).to_field(); + let low = memory.read(*low).to_field(); + let high = memory.read(*high).to_field(); let (x, y) = solver.fixed_base_scalar_mul(&low, &high)?; - memory.write_slice(registers.get(result.pointer).to_usize(), &[x.into(), y.into()]); + memory.write_slice(memory.read_ref(result.pointer), &[x.into(), y.into()]); Ok(()) } BlackBoxOp::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, result } => { - let input1_x = registers.get(*input1_x).to_field(); - let input1_y = registers.get(*input1_y).to_field(); - let input2_x = registers.get(*input2_x).to_field(); - let input2_y = registers.get(*input2_y).to_field(); + let input1_x = memory.read(*input1_x).to_field(); + let input1_y = memory.read(*input1_y).to_field(); + let input2_x = memory.read(*input2_x).to_field(); + let input2_y = memory.read(*input2_y).to_field(); let (x, y) = solver.ec_add(&input1_x, &input1_y, &input2_x, &input2_y)?; - memory.write_slice(registers.get(result.pointer).to_usize(), &[x.into(), y.into()]); + memory.write_slice(memory.read_ref(result.pointer), &[x.into(), y.into()]); Ok(()) } BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { let inputs: Vec = - read_heap_vector(memory, registers, inputs).iter().map(|x| x.to_field()).collect(); + read_heap_vector(memory, inputs).iter().map(|x| x.to_field()).collect(); let domain_separator: u32 = - registers.get(*domain_separator).to_u128().try_into().map_err(|_| { + memory.read(*domain_separator).to_u128().try_into().map_err(|_| { BlackBoxResolutionError::Failed( BlackBoxFunc::PedersenCommitment, "Invalid signature length".to_string(), ) })?; let (x, y) = solver.pedersen_commitment(&inputs, domain_separator)?; - memory.write_slice(registers.get(output.pointer).to_usize(), &[x.into(), y.into()]); + memory.write_slice(memory.read_ref(output.pointer), &[x.into(), y.into()]); Ok(()) } BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { let inputs: Vec = - read_heap_vector(memory, registers, inputs).iter().map(|x| x.to_field()).collect(); + read_heap_vector(memory, inputs).iter().map(|x| x.to_field()).collect(); let domain_separator: u32 = - registers.get(*domain_separator).to_u128().try_into().map_err(|_| { + memory.read(*domain_separator).to_u128().try_into().map_err(|_| { BlackBoxResolutionError::Failed( BlackBoxFunc::PedersenCommitment, "Invalid signature length".to_string(), ) })?; let hash = solver.pedersen_hash(&inputs, domain_separator)?; - registers.set(*output, hash.into()); + memory.write(*output, hash.into()); Ok(()) } BlackBoxOp::BigIntAdd { .. } => todo!(), @@ -231,11 +216,11 @@ fn black_box_function_from_op(op: &BlackBoxOp) -> BlackBoxFunc { #[cfg(test)] mod test { - use acir::brillig::BlackBoxOp; + use acir::brillig::{BlackBoxOp, MemoryAddress}; use crate::{ black_box::{evaluate_black_box, to_u8_vec, to_value_vec}, - DummyBlackBoxSolver, HeapArray, HeapVector, Memory, Registers, Value, + DummyBlackBoxSolver, HeapArray, HeapVector, Memory, }; #[test] @@ -243,27 +228,22 @@ mod test { let message: Vec = b"hello world".to_vec(); let message_length = message.len(); - let mut memory = Memory::from(vec![]); - let message_pointer = 0; + let mut memory = Memory::default(); + let message_pointer = 3; let result_pointer = message_pointer + message_length; - memory.write_slice(message_pointer, to_value_vec(&message).as_slice()); - - let mut registers = Registers { - inner: vec![ - Value::from(message_pointer), - Value::from(message_length), - Value::from(result_pointer), - ], - }; + memory.write(MemoryAddress(0), message_pointer.into()); + memory.write(MemoryAddress(1), message_length.into()); + memory.write(MemoryAddress(2), result_pointer.into()); + memory.write_slice(MemoryAddress(message_pointer), to_value_vec(&message).as_slice()); let op = BlackBoxOp::Sha256 { message: HeapVector { pointer: 0.into(), size: 1.into() }, output: HeapArray { pointer: 2.into(), size: 32 }, }; - evaluate_black_box(&op, &DummyBlackBoxSolver, &mut registers, &mut memory).unwrap(); + evaluate_black_box(&op, &DummyBlackBoxSolver, &mut memory).unwrap(); - let result = memory.read_slice(result_pointer, 32); + let result = memory.read_slice(MemoryAddress(result_pointer), 32); assert_eq!( to_u8_vec(result), diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index 38a562e65a1..075a0cc3e6b 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -12,8 +12,8 @@ //! [acvm]: https://crates.io/crates/acvm use acir::brillig::{ - BinaryFieldOp, BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapArray, HeapVector, Opcode, - RegisterIndex, RegisterOrMemory, Value, + BinaryFieldOp, BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapArray, HeapVector, + MemoryAddress, Opcode, Value, ValueOrArray, }; use acir::FieldElement; // Re-export `brillig`. @@ -22,7 +22,6 @@ pub use acir::brillig; mod arithmetic; mod black_box; mod memory; -mod registers; use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError}; use arithmetic::{evaluate_binary_bigint_op, evaluate_binary_field_op}; @@ -30,14 +29,16 @@ use black_box::evaluate_black_box; pub use memory::Memory; use num_bigint::BigUint; -pub use registers::Registers; /// The error call stack contains the opcode indexes of the call stack at the time of failure, plus the index of the opcode that failed. pub type ErrorCallStack = Vec; #[derive(Debug, PartialEq, Eq, Clone)] pub enum VMStatus { - Finished, + Finished { + return_data_offset: usize, + return_data_size: usize, + }, InProgress, Failure { message: String, @@ -61,8 +62,8 @@ pub enum VMStatus { #[derive(Debug, PartialEq, Eq, Clone)] /// VM encapsulates the state of the Brillig VM during execution. pub struct VM<'a, B: BlackBoxFunctionSolver> { - /// Register storage - registers: Registers, + /// Calldata to the brillig function + calldata: Vec, /// Instruction pointer program_counter: usize, /// A counter maintained throughout a Brillig process that determines @@ -86,20 +87,19 @@ pub struct VM<'a, B: BlackBoxFunctionSolver> { impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { /// Constructs a new VM instance pub fn new( - inputs: Registers, - memory: Vec, + calldata: Vec, bytecode: &'a [Opcode], foreign_call_results: Vec, black_box_solver: &'a B, ) -> Self { Self { - registers: inputs, + calldata, program_counter: 0, foreign_call_counter: 0, foreign_call_results, bytecode, status: VMStatus::InProgress, - memory: memory.into(), + memory: Memory::default(), call_stack: Vec::new(), black_box_solver, } @@ -117,8 +117,8 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } /// Sets the current status of the VM to Finished (completed execution). - fn finish(&mut self) -> VMStatus { - self.status(VMStatus::Finished) + fn finish(&mut self, return_data_offset: usize, return_data_size: usize) -> VMStatus { + self.status(VMStatus::Finished { return_data_offset, return_data_size }) } /// Sets the status of the VM to `ForeignCallWait`. @@ -154,26 +154,17 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { pub fn process_opcodes(&mut self) -> VMStatus { while !matches!( self.process_opcode(), - VMStatus::Finished | VMStatus::Failure { .. } | VMStatus::ForeignCallWait { .. } + VMStatus::Finished { .. } | VMStatus::Failure { .. } | VMStatus::ForeignCallWait { .. } ) {} self.status.clone() } - /// Returns all of the registers in the VM. - pub fn get_registers(&self) -> &Registers { - &self.registers - } - - pub fn set_register(&mut self, register_index: RegisterIndex, value: Value) { - self.registers.set(register_index, value); - } - pub fn get_memory(&self) -> &[Value] { self.memory.values() } pub fn write_memory_at(&mut self, ptr: usize, value: Value) { - self.memory.write(ptr, value); + self.memory.write(MemoryAddress(ptr), value); } /// Process a single opcode and modify the program counter. @@ -196,22 +187,27 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { Opcode::JumpIf { condition, location: destination } => { // Check if condition is true // We use 0 to mean false and any other value to mean true - let condition_value = self.registers.get(*condition); + let condition_value = self.memory.read(*condition); if !condition_value.is_zero() { return self.set_program_counter(*destination); } self.increment_program_counter() } Opcode::JumpIfNot { condition, location: destination } => { - let condition_value = self.registers.get(*condition); + let condition_value = self.memory.read(*condition); if condition_value.is_zero() { return self.set_program_counter(*destination); } self.increment_program_counter() } + Opcode::CalldataCopy { destination_address, size, offset } => { + let values = &self.calldata[*offset..(*offset + size)]; + self.memory.write_slice(*destination_address, values); + self.increment_program_counter() + } Opcode::Return => { - if let Some(register) = self.call_stack.pop() { - self.set_program_counter(register.to_usize() + 1) + if let Some(return_location) = self.call_stack.pop() { + self.set_program_counter(return_location.to_usize() + 1) } else { self.fail("return opcode hit, but callstack already empty".to_string()) } @@ -227,7 +223,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { // but has the necessary results to proceed with execution. let resolved_inputs = inputs .iter() - .map(|input| self.get_register_value_or_memory_values(*input)) + .map(|input| self.get_memory_values(*input)) .collect::>(); return self.wait_for_foreign_call(function.clone(), resolved_inputs); } @@ -237,15 +233,15 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { let mut invalid_foreign_call_result = false; for (destination, output) in destinations.iter().zip(values) { match destination { - RegisterOrMemory::RegisterIndex(value_index) => match output { + ValueOrArray::MemoryAddress(value_index) => match output { ForeignCallParam::Single(value) => { - self.registers.set(*value_index, *value); + self.memory.write(*value_index, *value); } _ => unreachable!( "Function result size does not match brillig bytecode. Expected 1 result but got {output:?}" ), }, - RegisterOrMemory::HeapArray(HeapArray { pointer: pointer_index, size }) => { + ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }) => { match output { ForeignCallParam::Array(values) => { if values.len() != *size { @@ -253,7 +249,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { break; } // Convert the destination pointer to a usize - let destination = self.registers.get(*pointer_index).to_usize(); + let destination = self.memory.read_ref(*pointer_index); // Write to our destination memory self.memory.write_slice(destination, values); } @@ -262,13 +258,13 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } } } - RegisterOrMemory::HeapVector(HeapVector { pointer: pointer_index, size: size_index }) => { + ValueOrArray::HeapVector(HeapVector { pointer: pointer_index, size: size_index }) => { match output { ForeignCallParam::Array(values) => { - // Set our size in the size register - self.registers.set(*size_index, Value::from(values.len())); + // Set our size in the size address + self.memory.write(*size_index, Value::from(values.len())); // Convert the destination pointer to a usize - let destination = self.registers.get(*pointer_index).to_usize(); + let destination = self.memory.read_ref(*pointer_index); // Write to our destination memory self.memory.write_slice(destination, values); } @@ -291,26 +287,28 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { self.foreign_call_counter += 1; self.increment_program_counter() } - Opcode::Mov { destination: destination_register, source: source_register } => { - let source_value = self.registers.get(*source_register); - self.registers.set(*destination_register, source_value); + Opcode::Mov { destination: destination_address, source: source_address } => { + let source_value = self.memory.read(*source_address); + self.memory.write(*destination_address, source_value); self.increment_program_counter() } Opcode::Trap => self.fail("explicit trap hit in brillig".to_string()), - Opcode::Stop => self.finish(), - Opcode::Load { destination: destination_register, source_pointer } => { - // Convert our source_pointer to a usize - let source = self.registers.get(*source_pointer); + Opcode::Stop { return_data_offset, return_data_size } => { + self.finish(*return_data_offset, *return_data_size) + } + Opcode::Load { destination: destination_address, source_pointer } => { + // Convert our source_pointer to an address + let source = self.memory.read_ref(*source_pointer); // Use our usize source index to lookup the value in memory - let value = &self.memory.read(source.to_usize()); - self.registers.set(*destination_register, *value); + let value = &self.memory.read(source); + self.memory.write(*destination_address, *value); self.increment_program_counter() } - Opcode::Store { destination_pointer, source: source_register } => { - // Convert our destination_pointer to a usize - let destination = self.registers.get(*destination_pointer).to_usize(); + Opcode::Store { destination_pointer, source: source_address } => { + // Convert our destination_pointer to an address + let destination = self.memory.read_ref(*destination_pointer); // Use our usize destination index to set the value in memory - self.memory.write(destination, self.registers.get(*source_register)); + self.memory.write(destination, self.memory.read(*source_address)); self.increment_program_counter() } Opcode::Call { location } => { @@ -319,16 +317,11 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { self.set_program_counter(*location) } Opcode::Const { destination, value } => { - self.registers.set(*destination, *value); + self.memory.write(*destination, *value); self.increment_program_counter() } Opcode::BlackBox(black_box_op) => { - match evaluate_black_box( - black_box_op, - self.black_box_solver, - &mut self.registers, - &mut self.memory, - ) { + match evaluate_black_box(black_box_op, self.black_box_solver, &mut self.memory) { Ok(()) => self.increment_program_counter(), Err(e) => self.fail(e.to_string()), } @@ -353,25 +346,22 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { assert!(self.program_counter < self.bytecode.len()); self.program_counter = value; if self.program_counter >= self.bytecode.len() { - self.status = VMStatus::Finished; + self.status = VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }; } self.status.clone() } - fn get_register_value_or_memory_values(&self, input: RegisterOrMemory) -> ForeignCallParam { + fn get_memory_values(&self, input: ValueOrArray) -> ForeignCallParam { match input { - RegisterOrMemory::RegisterIndex(value_index) => self.registers.get(value_index).into(), - RegisterOrMemory::HeapArray(HeapArray { pointer: pointer_index, size }) => { - let start = self.registers.get(pointer_index); - self.memory.read_slice(start.to_usize(), size).to_vec().into() + ValueOrArray::MemoryAddress(value_index) => self.memory.read(value_index).into(), + ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }) => { + let start = self.memory.read_ref(pointer_index); + self.memory.read_slice(start, size).to_vec().into() } - RegisterOrMemory::HeapVector(HeapVector { - pointer: pointer_index, - size: size_index, - }) => { - let start = self.registers.get(pointer_index); - let size = self.registers.get(size_index); - self.memory.read_slice(start.to_usize(), size.to_usize()).to_vec().into() + ValueOrArray::HeapVector(HeapVector { pointer: pointer_index, size: size_index }) => { + let start = self.memory.read_ref(pointer_index); + let size = self.memory.read(size_index); + self.memory.read_slice(start, size.to_usize()).to_vec().into() } } } @@ -381,17 +371,17 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { fn process_binary_field_op( &mut self, op: BinaryFieldOp, - lhs: RegisterIndex, - rhs: RegisterIndex, - result: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + result: MemoryAddress, ) { - let lhs_value = self.registers.get(lhs); - let rhs_value = self.registers.get(rhs); + let lhs_value = self.memory.read(lhs); + let rhs_value = self.memory.read(rhs); let result_value = evaluate_binary_field_op(&op, lhs_value.to_field(), rhs_value.to_field()); - self.registers.set(result, result_value.into()); + self.memory.write(result, result_value.into()); } /// Process a binary operation. @@ -400,20 +390,20 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { &mut self, op: BinaryIntOp, bit_size: u32, - lhs: RegisterIndex, - rhs: RegisterIndex, - result: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + result: MemoryAddress, ) -> Result<(), String> { - let lhs_value = self.registers.get(lhs); - let rhs_value = self.registers.get(rhs); + let lhs_value = self.memory.read(lhs); + let rhs_value = self.memory.read(rhs); // Convert to big integers let lhs_big = BigUint::from_bytes_be(&lhs_value.to_field().to_be_bytes()); let rhs_big = BigUint::from_bytes_be(&rhs_value.to_field().to_be_bytes()); let result_value = evaluate_binary_bigint_op(&op, lhs_big, rhs_big, bit_size)?; // Convert back to field element - self.registers - .set(result, FieldElement::from_be_bytes_reduce(&result_value.to_bytes_be()).into()); + self.memory + .write(result, FieldElement::from_be_bytes_reduce(&result_value.to_bytes_be()).into()); Ok(()) } } @@ -468,111 +458,123 @@ mod tests { #[test] fn add_single_step_smoke() { - // Load values into registers and initialize the registers that - // will be used during bytecode processing - let input_registers = - Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(0u128)]); - - // Add opcode to add the value in register `0` and `1` - // and place the output in register `2` - let opcode = Opcode::BinaryIntOp { - op: BinaryIntOp::Add, - bit_size: 2, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + let calldata = vec![Value::from(27u128)]; + + // Add opcode to add the value in address `0` and `1` + // and place the output in address `2` + let calldata_copy = Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 1, + offset: 0, }; // Start VM - let opcodes = [opcode]; - let mut vm = VM::new(input_registers, vec![], &opcodes, vec![], &DummyBlackBoxSolver); + let opcodes = [calldata_copy]; + let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); // Process a single VM opcode // // After processing a single opcode, we should have // the vm status as finished since there is only one opcode let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); - // The register at index `2` should have the value of 3 since we had an + // The address at index `2` should have the value of 3 since we had an // add opcode - let VM { registers, .. } = vm; - let output_value = registers.get(RegisterIndex::from(2)); + let VM { memory, .. } = vm; + let output_value = memory.read(MemoryAddress::from(0)); - assert_eq!(output_value, Value::from(3u128)); + assert_eq!(output_value, Value::from(27u128)); } #[test] fn jmpif_opcode() { - let mut registers = vec![]; + let mut calldata = vec![]; let mut opcodes = vec![]; let lhs = { - registers.push(Value::from(2u128)); - RegisterIndex::from(registers.len() - 1) + calldata.push(Value::from(2u128)); + MemoryAddress::from(calldata.len() - 1) }; let rhs = { - registers.push(Value::from(2u128)); - RegisterIndex::from(registers.len() - 1) + calldata.push(Value::from(2u128)); + MemoryAddress::from(calldata.len() - 1) }; - let destination = { - registers.push(Value::from(0u128)); - RegisterIndex::from(registers.len() - 1) - }; + let destination = MemoryAddress::from(calldata.len()); + opcodes.push(Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 2, + offset: 0, + }); let equal_cmp_opcode = Opcode::BinaryIntOp { op: BinaryIntOp::Equals, bit_size: 1, lhs, rhs, destination }; opcodes.push(equal_cmp_opcode); - opcodes.push(Opcode::Jump { location: 2 }); - opcodes.push(Opcode::JumpIf { condition: RegisterIndex::from(2), location: 3 }); + opcodes.push(Opcode::Jump { location: 3 }); + opcodes.push(Opcode::JumpIf { condition: MemoryAddress::from(2), location: 4 }); + + let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); - let mut vm = - VM::new(Registers::load(registers), vec![], &opcodes, vec![], &DummyBlackBoxSolver); + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.registers.get(RegisterIndex::from(2)); + let output_cmp_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(output_cmp_value, Value::from(true)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); } #[test] fn jmpifnot_opcode() { - let input_registers = - Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(0u128)]); + let calldata = vec![Value::from(1u128), Value::from(2u128)]; + + let calldata_copy = Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 2, + offset: 0, + }; + + let jump_opcode = Opcode::Jump { location: 3 }; let trap_opcode = Opcode::Trap; let not_equal_cmp_opcode = Opcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; - let jump_opcode = Opcode::Jump { location: 2 }; - let jump_if_not_opcode = - Opcode::JumpIfNot { condition: RegisterIndex::from(2), location: 1 }; + Opcode::JumpIfNot { condition: MemoryAddress::from(2), location: 2 }; let add_opcode = Opcode::BinaryFieldOp { op: BinaryFieldOp::Add, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; - let opcodes = - [jump_opcode, trap_opcode, not_equal_cmp_opcode, jump_if_not_opcode, add_opcode]; - let mut vm = VM::new(input_registers, vec![], &opcodes, vec![], &DummyBlackBoxSolver); + let opcodes = [ + calldata_copy, + jump_opcode, + trap_opcode, + not_equal_cmp_opcode, + jump_if_not_opcode, + add_opcode, + ]; + let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -580,7 +582,7 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.registers.get(RegisterIndex::from(2)); + let output_cmp_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(output_cmp_value, Value::from(false)); let status = vm.process_opcode(); @@ -591,107 +593,130 @@ mod tests { status, VMStatus::Failure { message: "explicit trap hit in brillig".to_string(), - call_stack: vec![1] + call_stack: vec![2] } ); - // The register at index `2` should have not changed as we jumped over the add opcode - let VM { registers, .. } = vm; - let output_value = registers.get(RegisterIndex::from(2)); + // The address at index `2` should have not changed as we jumped over the add opcode + let VM { memory, .. } = vm; + let output_value = memory.read(MemoryAddress::from(2)); assert_eq!(output_value, Value::from(false)); } #[test] fn mov_opcode() { - let input_registers = - Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]); + let calldata = vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]; + + let calldata_copy = Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 3, + offset: 0, + }; let mov_opcode = - Opcode::Mov { destination: RegisterIndex::from(2), source: RegisterIndex::from(0) }; + Opcode::Mov { destination: MemoryAddress::from(2), source: MemoryAddress::from(0) }; + + let opcodes = &[calldata_copy, mov_opcode]; + let mut vm = VM::new(calldata, opcodes, vec![], &DummyBlackBoxSolver); - let opcodes = &[mov_opcode]; - let mut vm = VM::new(input_registers, vec![], opcodes, vec![], &DummyBlackBoxSolver); + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); - let VM { registers, .. } = vm; + let VM { memory, .. } = vm; - let destination_value = registers.get(RegisterIndex::from(2)); + let destination_value = memory.read(MemoryAddress::from(2)); assert_eq!(destination_value, Value::from(1u128)); - let source_value = registers.get(RegisterIndex::from(0)); + let source_value = memory.read(MemoryAddress::from(0)); assert_eq!(source_value, Value::from(1u128)); } #[test] fn cmp_binary_ops() { let bit_size = 32; - let input_registers = Registers::load(vec![ + let calldata = vec![ Value::from(2u128), Value::from(2u128), Value::from(0u128), Value::from(5u128), Value::from(6u128), - ]); + ]; + + let calldata_copy = Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 5, + offset: 0, + }; let equal_opcode = Opcode::BinaryIntOp { bit_size, op: BinaryIntOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; let not_equal_opcode = Opcode::BinaryIntOp { bit_size, op: BinaryIntOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(3), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(3), + destination: MemoryAddress::from(2), }; let less_than_opcode = Opcode::BinaryIntOp { bit_size, op: BinaryIntOp::LessThan, - lhs: RegisterIndex::from(3), - rhs: RegisterIndex::from(4), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(3), + rhs: MemoryAddress::from(4), + destination: MemoryAddress::from(2), }; let less_than_equal_opcode = Opcode::BinaryIntOp { bit_size, op: BinaryIntOp::LessThanEquals, - lhs: RegisterIndex::from(3), - rhs: RegisterIndex::from(4), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(3), + rhs: MemoryAddress::from(4), + destination: MemoryAddress::from(2), }; - let opcodes = [equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode]; - let mut vm = VM::new(input_registers, vec![], &opcodes, vec![], &DummyBlackBoxSolver); + let opcodes = [ + calldata_copy, + equal_opcode, + not_equal_opcode, + less_than_opcode, + less_than_equal_opcode, + ]; + let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_eq_value = vm.registers.get(RegisterIndex::from(2)); + let output_eq_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(output_eq_value, Value::from(true)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_neq_value = vm.registers.get(RegisterIndex::from(2)); + let output_neq_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(output_neq_value, Value::from(false)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let lt_value = vm.registers.get(RegisterIndex::from(2)); + let lt_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(lt_value, Value::from(true)); let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); - let lte_value = vm.registers.get(RegisterIndex::from(2)); + let lte_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(lte_value, Value::from(true)); } #[test] @@ -703,20 +728,24 @@ mod tests { /// memory[i] = i as Value; /// i += 1; /// } - fn brillig_write_memory(memory: Vec) -> Vec { + fn brillig_write_memory(item_count: usize) -> Vec { let bit_size = 32; - let r_i = RegisterIndex::from(0); - let r_len = RegisterIndex::from(1); - let r_tmp = RegisterIndex::from(2); + let r_i = MemoryAddress::from(0); + let r_len = MemoryAddress::from(1); + let r_tmp = MemoryAddress::from(2); + let r_pointer = MemoryAddress::from(3); + let start = [ // i = 0 Opcode::Const { destination: r_i, value: 0u128.into() }, // len = memory.len() (approximation) - Opcode::Const { destination: r_len, value: Value::from(memory.len() as u128) }, + Opcode::Const { destination: r_len, value: Value::from(item_count as u128) }, + // pointer = free_memory_ptr + Opcode::Const { destination: r_pointer, value: 4u128.into() }, ]; let loop_body = [ // *i = i - Opcode::Store { destination_pointer: r_i, source: r_i }, + Opcode::Store { destination_pointer: r_pointer, source: r_i }, // tmp = 1 Opcode::Const { destination: r_tmp, value: 1u128.into() }, // i = i + 1 (tmp) @@ -727,6 +756,14 @@ mod tests { rhs: r_tmp, bit_size, }, + // pointer = pointer + 1 + Opcode::BinaryIntOp { + destination: r_pointer, + lhs: r_pointer, + op: BinaryIntOp::Add, + rhs: r_tmp, + bit_size, + }, // tmp = i < len Opcode::BinaryIntOp { destination: r_tmp, @@ -740,11 +777,11 @@ mod tests { ]; let opcodes = [&start[..], &loop_body[..]].concat(); - let vm = brillig_execute_and_get_vm(memory, &opcodes); - vm.get_memory().to_vec() + let vm = brillig_execute_and_get_vm(vec![], &opcodes); + vm.get_memory()[4..].to_vec() } - let memory = brillig_write_memory(vec![Value::from(0u128); 5]); + let memory = brillig_write_memory(5); let expected = vec![ Value::from(0u128), Value::from(1u128), @@ -754,7 +791,7 @@ mod tests { ]; assert_eq!(memory, expected); - let memory = brillig_write_memory(vec![Value::from(0u128); 1024]); + let memory = brillig_write_memory(1024); let expected: Vec = (0..1024).map(|i| Value::from(i as u128)).collect(); assert_eq!(memory, expected); } @@ -771,10 +808,12 @@ mod tests { /// } fn brillig_sum_memory(memory: Vec) -> Value { let bit_size = 32; - let r_i = RegisterIndex::from(0); - let r_len = RegisterIndex::from(1); - let r_sum = RegisterIndex::from(2); - let r_tmp = RegisterIndex::from(3); + let r_i = MemoryAddress::from(0); + let r_len = MemoryAddress::from(1); + let r_sum = MemoryAddress::from(2); + let r_tmp = MemoryAddress::from(3); + let r_pointer = MemoryAddress::from(4); + let start = [ // sum = 0 Opcode::Const { destination: r_sum, value: 0u128.into() }, @@ -782,10 +821,17 @@ mod tests { Opcode::Const { destination: r_i, value: 0u128.into() }, // len = array.len() (approximation) Opcode::Const { destination: r_len, value: Value::from(memory.len() as u128) }, + // pointer = array_ptr + Opcode::Const { destination: r_pointer, value: 5u128.into() }, + Opcode::CalldataCopy { + destination_address: MemoryAddress(5), + size: memory.len(), + offset: 0, + }, ]; let loop_body = [ // tmp = *i - Opcode::Load { destination: r_tmp, source_pointer: r_i }, + Opcode::Load { destination: r_tmp, source_pointer: r_pointer }, // sum = sum + tmp Opcode::BinaryIntOp { destination: r_sum, @@ -804,6 +850,14 @@ mod tests { rhs: r_tmp, bit_size, }, + // pointer = pointer + 1 + Opcode::BinaryIntOp { + destination: r_pointer, + lhs: r_pointer, + op: BinaryIntOp::Add, + rhs: r_tmp, + bit_size, + }, // tmp = i < len Opcode::BinaryIntOp { destination: r_tmp, @@ -818,7 +872,7 @@ mod tests { let opcodes = [&start[..], &loop_body[..]].concat(); let vm = brillig_execute_and_get_vm(memory, &opcodes); - vm.registers.get(r_sum) + vm.memory.read(r_sum) } assert_eq!( @@ -844,21 +898,24 @@ mod tests { /// memory[i as usize] = i as Value; /// recursive_write(memory, i + 1, len); /// } - /// Note we represent a 100% in-register optimized form in brillig - fn brillig_recursive_write_memory(memory: Vec) -> Vec { + /// Note we represent a 100% in-stack optimized form in brillig + fn brillig_recursive_write_memory(size: usize) -> Vec { let bit_size = 32; - let r_i = RegisterIndex::from(0); - let r_len = RegisterIndex::from(1); - let r_tmp = RegisterIndex::from(2); + let r_i = MemoryAddress::from(0); + let r_len = MemoryAddress::from(1); + let r_tmp = MemoryAddress::from(2); + let r_pointer = MemoryAddress::from(3); let start = [ // i = 0 Opcode::Const { destination: r_i, value: 0u128.into() }, - // len = memory.len() (approximation) - Opcode::Const { destination: r_len, value: Value::from(memory.len() as u128) }, + // len = size + Opcode::Const { destination: r_len, value: size.into() }, + // pointer = free_memory_ptr + Opcode::Const { destination: r_pointer, value: 4u128.into() }, // call recursive_fn Opcode::Call { - location: 4, // Call after 'start' + location: 5, // Call after 'start' }, // end program by jumping to end Opcode::Jump { location: 100 }, @@ -876,10 +933,10 @@ mod tests { // if !tmp, goto end Opcode::JumpIf { condition: r_tmp, - location: start.len() + 6, // 7 ops in recursive_fn, go to 'Return' + location: start.len() + 7, // 8 ops in recursive_fn, go to 'Return' }, // *i = i - Opcode::Store { destination_pointer: r_i, source: r_i }, + Opcode::Store { destination_pointer: r_pointer, source: r_i }, // tmp = 1 Opcode::Const { destination: r_tmp, value: 1u128.into() }, // i = i + 1 (tmp) @@ -890,17 +947,25 @@ mod tests { rhs: r_tmp, bit_size, }, + // pointer = pointer + 1 + Opcode::BinaryIntOp { + destination: r_pointer, + lhs: r_pointer, + op: BinaryIntOp::Add, + rhs: r_tmp, + bit_size, + }, // call recursive_fn Opcode::Call { location: start.len() }, Opcode::Return {}, ]; let opcodes = [&start[..], &recursive_fn[..]].concat(); - let vm = brillig_execute_and_get_vm(memory, &opcodes); - vm.get_memory().to_vec() + let vm = brillig_execute_and_get_vm(vec![], &opcodes); + vm.get_memory()[4..].to_vec() } - let memory = brillig_recursive_write_memory(vec![Value::from(0u128); 5]); + let memory = brillig_recursive_write_memory(5); let expected = vec![ Value::from(0u128), Value::from(1u128), @@ -910,20 +975,17 @@ mod tests { ]; assert_eq!(memory, expected); - let memory = brillig_recursive_write_memory(vec![Value::from(0u128); 1024]); + let memory = brillig_recursive_write_memory(1024); let expected: Vec = (0..1024).map(|i| Value::from(i as u128)).collect(); assert_eq!(memory, expected); } - fn empty_registers() -> Registers { - Registers::load(vec![Value::from(0u128); 16]) - } /// Helper to execute brillig code fn brillig_execute_and_get_vm( - memory: Vec, + calldata: Vec, opcodes: &[Opcode], ) -> VM<'_, DummyBlackBoxSolver> { - let mut vm = VM::new(empty_registers(), memory, opcodes, vec![], &DummyBlackBoxSolver); + let mut vm = VM::new(calldata, opcodes, vec![], &DummyBlackBoxSolver); brillig_execute(&mut vm); assert_eq!(vm.call_stack, vec![]); vm @@ -932,7 +994,7 @@ mod tests { fn brillig_execute(vm: &mut VM) { loop { let status = vm.process_opcode(); - if matches!(status, VMStatus::Finished | VMStatus::ForeignCallWait { .. }) { + if matches!(status, VMStatus::Finished { .. } | VMStatus::ForeignCallWait { .. }) { break; } assert_eq!(status, VMStatus::InProgress); @@ -940,18 +1002,18 @@ mod tests { } #[test] - fn foreign_call_opcode_register_result() { - let r_input = RegisterIndex::from(0); - let r_result = RegisterIndex::from(1); + fn foreign_call_opcode_simple_result() { + let r_input = MemoryAddress::from(0); + let r_result = MemoryAddress::from(1); let double_program = vec![ - // Load input register with value 5 + // Load input address with value 5 Opcode::Const { destination: r_input, value: Value::from(5u128) }, - // Call foreign function "double" with the input register + // Call foreign function "double" with the input address Opcode::ForeignCall { function: "double".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(r_result)], - inputs: vec![RegisterOrMemory::RegisterIndex(r_input)], + destinations: vec![ValueOrArray::MemoryAddress(r_result)], + inputs: vec![ValueOrArray::MemoryAddress(r_input)], }, ]; @@ -975,19 +1037,20 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); - // Check result register - let result_value = vm.registers.get(r_result); + // Check result address + let result_value = vm.memory.read(r_result); assert_eq!(result_value, Value::from(10u128)); // Ensure the foreign call counter has been incremented assert_eq!(vm.foreign_call_counter, 1); } + #[test] fn foreign_call_opcode_memory_result() { - let r_input = RegisterIndex::from(0); - let r_output = RegisterIndex::from(1); + let r_input = MemoryAddress::from(0); + let r_output = MemoryAddress::from(1); // Define a simple 2x2 matrix in memory let initial_matrix = @@ -998,18 +1061,23 @@ mod tests { vec![Value::from(1u128), Value::from(3u128), Value::from(2u128), Value::from(4u128)]; let invert_program = vec![ + Opcode::CalldataCopy { + destination_address: MemoryAddress::from(2), + size: initial_matrix.len(), + offset: 0, + }, // input = 0 - Opcode::Const { destination: r_input, value: Value::from(0u128) }, + Opcode::Const { destination: r_input, value: 2_usize.into() }, // output = 0 - Opcode::Const { destination: r_output, value: Value::from(0u128) }, + Opcode::Const { destination: r_output, value: 2_usize.into() }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), - destinations: vec![RegisterOrMemory::HeapArray(HeapArray { + destinations: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_output, size: initial_matrix.len(), })], - inputs: vec![RegisterOrMemory::HeapArray(HeapArray { + inputs: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_input, size: initial_matrix.len(), })], @@ -1034,10 +1102,10 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values = vm.memory.read_slice(0, 4).to_vec(); + let result_values = vm.memory.read_slice(MemoryAddress(2), 4).to_vec(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented @@ -1047,11 +1115,11 @@ mod tests { /// Calling a simple foreign call function that takes any string input, concatenates it with itself, and reverses the concatenation #[test] fn foreign_call_opcode_vector_input_and_output() { - let r_input_pointer = RegisterIndex::from(0); - let r_input_size = RegisterIndex::from(1); + let r_input_pointer = MemoryAddress::from(0); + let r_input_size = MemoryAddress::from(1); // We need to pass a location of appropriate size - let r_output_pointer = RegisterIndex::from(2); - let r_output_size = RegisterIndex::from(3); + let r_output_pointer = MemoryAddress::from(2); + let r_output_size = MemoryAddress::from(3); // Our first string to use the identity function with let input_string = @@ -1064,12 +1132,20 @@ mod tests { // First call: let string_double_program = vec![ - // input_pointer = 0 - Opcode::Const { destination: r_input_pointer, value: Value::from(0u128) }, + Opcode::CalldataCopy { + destination_address: MemoryAddress(4), + size: input_string.len(), + offset: 0, + }, + // input_pointer = 4 + Opcode::Const { destination: r_input_pointer, value: Value::from(4u128) }, // input_size = input_string.len() (constant here) Opcode::Const { destination: r_input_size, value: Value::from(input_string.len()) }, - // output_pointer = 0 + input_size = input_size - Opcode::Const { destination: r_output_pointer, value: Value::from(input_string.len()) }, + // output_pointer = 4 + input_size + Opcode::Const { + destination: r_output_pointer, + value: Value::from(4 + input_string.len()), + }, // output_size = input_size * 2 Opcode::Const { destination: r_output_size, @@ -1078,11 +1154,11 @@ mod tests { // output_pointer[0..output_size] = string_double(input_pointer[0...input_size]) Opcode::ForeignCall { function: "string_double".into(), - destinations: vec![RegisterOrMemory::HeapVector(HeapVector { + destinations: vec![ValueOrArray::HeapVector(HeapVector { pointer: r_output_pointer, size: r_output_size, })], - inputs: vec![RegisterOrMemory::HeapVector(HeapVector { + inputs: vec![ValueOrArray::HeapVector(HeapVector { pointer: r_input_pointer, size: r_input_size, })], @@ -1109,10 +1185,13 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values = vm.memory.read_slice(input_string.len(), output_string.len()).to_vec(); + let result_values = vm + .memory + .read_slice(MemoryAddress(4 + input_string.len()), output_string.len()) + .to_vec(); assert_eq!(result_values, output_string); // Ensure the foreign call counter has been incremented @@ -1121,8 +1200,8 @@ mod tests { #[test] fn foreign_call_opcode_memory_alloc_result() { - let r_input = RegisterIndex::from(0); - let r_output = RegisterIndex::from(1); + let r_input = MemoryAddress::from(0); + let r_output = MemoryAddress::from(1); // Define a simple 2x2 matrix in memory let initial_matrix = @@ -1133,18 +1212,23 @@ mod tests { vec![Value::from(1u128), Value::from(3u128), Value::from(2u128), Value::from(4u128)]; let invert_program = vec![ + Opcode::CalldataCopy { + destination_address: MemoryAddress::from(2), + size: initial_matrix.len(), + offset: 0, + }, // input = 0 - Opcode::Const { destination: r_input, value: Value::from(0u128) }, + Opcode::Const { destination: r_input, value: Value::from(2u128) }, // output = 0 - Opcode::Const { destination: r_output, value: Value::from(4u128) }, + Opcode::Const { destination: r_output, value: Value::from(6u128) }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), - destinations: vec![RegisterOrMemory::HeapArray(HeapArray { + destinations: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_output, size: initial_matrix.len(), })], - inputs: vec![RegisterOrMemory::HeapArray(HeapArray { + inputs: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_input, size: initial_matrix.len(), })], @@ -1169,14 +1253,14 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check initial memory still in place - let initial_values = vm.memory.read_slice(0, 4).to_vec(); + let initial_values = vm.memory.read_slice(MemoryAddress(2), 4).to_vec(); assert_eq!(initial_values, initial_matrix); // Check result in memory - let result_values = vm.memory.read_slice(4, 4).to_vec(); + let result_values = vm.memory.read_slice(MemoryAddress(6), 4).to_vec(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented @@ -1185,9 +1269,9 @@ mod tests { #[test] fn foreign_call_opcode_multiple_array_inputs_result() { - let r_input_a = RegisterIndex::from(0); - let r_input_b = RegisterIndex::from(1); - let r_output = RegisterIndex::from(2); + let r_input_a = MemoryAddress::from(0); + let r_input_b = MemoryAddress::from(1); + let r_output = MemoryAddress::from(2); // Define a simple 2x2 matrix in memory let matrix_a = @@ -1209,28 +1293,27 @@ mod tests { ]; let matrix_mul_program = vec![ - // input = 0 - Opcode::Const { destination: r_input_a, value: Value::from(0u128) }, - // input = 0 - Opcode::Const { destination: r_input_b, value: Value::from(4u128) }, + Opcode::CalldataCopy { + destination_address: MemoryAddress::from(3), + size: matrix_a.len() + matrix_b.len(), + offset: 0, + }, + // input = 3 + Opcode::Const { destination: r_input_a, value: Value::from(3u128) }, + // input = 7 + Opcode::Const { destination: r_input_b, value: Value::from(7u128) }, // output = 0 Opcode::Const { destination: r_output, value: Value::from(0u128) }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), - destinations: vec![RegisterOrMemory::HeapArray(HeapArray { + destinations: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_output, size: matrix_a.len(), })], inputs: vec![ - RegisterOrMemory::HeapArray(HeapArray { - pointer: r_input_a, - size: matrix_a.len(), - }), - RegisterOrMemory::HeapArray(HeapArray { - pointer: r_input_b, - size: matrix_b.len(), - }), + ValueOrArray::HeapArray(HeapArray { pointer: r_input_a, size: matrix_a.len() }), + ValueOrArray::HeapArray(HeapArray { pointer: r_input_b, size: matrix_b.len() }), ], }, ]; @@ -1254,10 +1337,10 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values = vm.memory.read_slice(0, 4).to_vec(); + let result_values = vm.memory.read_slice(MemoryAddress(0), 4).to_vec(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented diff --git a/acvm-repo/brillig_vm/src/memory.rs b/acvm-repo/brillig_vm/src/memory.rs index 8a6993f1353..d1c81447170 100644 --- a/acvm-repo/brillig_vm/src/memory.rs +++ b/acvm-repo/brillig_vm/src/memory.rs @@ -1,41 +1,45 @@ +use acir::{brillig::MemoryAddress, FieldElement}; + use crate::Value; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct Memory { // Memory is a vector of values. // We grow the memory when values past the end are set, extending with 0s. inner: Vec, } -impl From> for Memory { - fn from(values: Vec) -> Self { - Memory { inner: values } - } -} - impl Memory { /// Gets the value at pointer - pub fn read(&self, ptr: usize) -> Value { - self.inner[ptr] + pub fn read(&self, ptr: MemoryAddress) -> Value { + self.inner.get(ptr.to_usize()).copied().unwrap_or(0_u128.into()) } - pub fn read_slice(&self, ptr: usize, len: usize) -> &[Value] { - &self.inner[ptr..ptr + len] + pub fn read_ref(&self, ptr: MemoryAddress) -> MemoryAddress { + MemoryAddress(self.read(ptr).to_usize()) + } + + pub fn read_slice(&self, addr: MemoryAddress, len: usize) -> &[Value] { + &self.inner[addr.to_usize()..(addr.to_usize() + len)] } /// Sets the value at pointer `ptr` to `value` - pub fn write(&mut self, ptr: usize, value: Value) { - self.write_slice(ptr, &[value]); + pub fn write(&mut self, ptr: MemoryAddress, value: Value) { + self.resize_to_fit(ptr.to_usize() + 1); + self.inner[ptr.to_usize()] = value; } - /// Sets the values after pointer `ptr` to `values` - pub fn write_slice(&mut self, ptr: usize, values: &[Value]) { + fn resize_to_fit(&mut self, size: usize) { // Calculate new memory size - let new_size = std::cmp::max(self.inner.len(), ptr + values.len()); + let new_size = std::cmp::max(self.inner.len(), size); // Expand memory to new size with default values if needed - self.inner.resize(new_size, Value::from(0_usize)); + self.inner.resize(new_size, Value::from(FieldElement::zero())); + } - self.inner[ptr..ptr + values.len()].copy_from_slice(values); + /// Sets the values after pointer `ptr` to `values` + pub fn write_slice(&mut self, ptr: MemoryAddress, values: &[Value]) { + self.resize_to_fit(ptr.to_usize() + values.len()); + self.inner[ptr.to_usize()..(ptr.to_usize() + values.len())].copy_from_slice(values); } /// Returns the values of the memory diff --git a/acvm-repo/brillig_vm/src/registers.rs b/acvm-repo/brillig_vm/src/registers.rs deleted file mode 100644 index fcc596dd6c9..00000000000 --- a/acvm-repo/brillig_vm/src/registers.rs +++ /dev/null @@ -1,43 +0,0 @@ -use acir::brillig::{RegisterIndex, Value}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Registers { - // Registers are a vector of values. - // We grow the register as registers past the end are set, extending with 0s. - pub inner: Vec, -} - -/// Aims to match a reasonable max register count for a SNARK prover. -/// As well, catches obvious erroneous use of registers. -/// This can be revisited if it proves not enough. -const MAX_REGISTERS: usize = 2_usize.pow(16); - -/// Registers will store field element values during the -/// duration of the execution of the bytecode. -impl Registers { - /// Create a Registers object initialized with definite values - pub fn load(values: Vec) -> Registers { - let inner = values.into_iter().collect(); - Self { inner } - } - - /// Gets the values at register with address `index` - pub fn get(&self, register_index: RegisterIndex) -> Value { - let index = register_index.to_usize(); - assert!(index < MAX_REGISTERS, "Reading register past maximum!"); - let value = self.inner.get(index); - match value { - Some(value) => *value, - None => 0u128.into(), - } - } - - /// Sets the value at register with address `index` to `value` - pub fn set(&mut self, RegisterIndex(index): RegisterIndex, value: Value) { - assert!(index < MAX_REGISTERS, "Writing register past maximum!"); - // if size isn't at least index + 1, resize - let new_register_size = std::cmp::max(index + 1, self.inner.len()); - self.inner.resize(new_register_size, 0u128.into()); - self.inner[index] = value; - } -} diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index baabd9aa1dc..228664c83fb 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -1,5 +1,7 @@ -use iter_extended::vecmap; +use std::borrow::{Borrow, BorrowMut}; +use std::vec; +use iter_extended::vecmap; use noirc_frontend::macros_api::FieldElement; use noirc_frontend::macros_api::{ BlockExpression, CallExpression, CastExpression, Distinctness, Expression, ExpressionKind, @@ -13,6 +15,8 @@ use noirc_frontend::macros_api::{ use noirc_frontend::macros_api::{CrateId, FileId}; use noirc_frontend::macros_api::{MacroError, MacroProcessor}; use noirc_frontend::macros_api::{ModuleDefId, NodeInterner, SortedModule, StructId}; +use noirc_frontend::node_interner::{TraitId, TraitImplKind}; +use noirc_frontend::Lambda; pub struct AztecMacro; @@ -45,6 +49,8 @@ pub enum AztecMacroError { ContractHasTooManyFunctions { span: Span }, ContractConstructorMissing { span: Span }, UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, + UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, + CouldNotAssignStorageSlots { secondary_message: Option }, EventError { span: Span, message: String }, } @@ -52,12 +58,12 @@ impl From for MacroError { fn from(err: AztecMacroError) -> Self { match err { AztecMacroError::AztecDepNotFound {} => MacroError { - primary_message: "Aztec dependency not found. Please add aztec as a dependency in your Cargo.toml. For more information go to https://docs.aztec.network/dev_docs/debugging/aztecnr-errors#aztec-dependency-not-found-please-add-aztec-as-a-dependency-in-your-nargotoml".to_owned(), + primary_message: "Aztec dependency not found. Please add aztec as a dependency in your Cargo.toml. For more information go to https://docs.aztec.network/developers/debugging/aztecnr-errors#aztec-dependency-not-found-please-add-aztec-as-a-dependency-in-your-nargotoml".to_owned(), secondary_message: None, span: None, }, AztecMacroError::ComputeNoteHashAndNullifierNotFound { span } => MacroError { - primary_message: "compute_note_hash_and_nullifier function not found. Define it in your contract. For more information go to https://docs.aztec.network/dev_docs/debugging/aztecnr-errors#compute_note_hash_and_nullifier-function-not-found-define-it-in-your-contract".to_owned(), + primary_message: "compute_note_hash_and_nullifier function not found. Define it in your contract. For more information go to https://docs.aztec.network/developers/debugging/aztecnr-errors#compute_note_hash_and_nullifier-function-not-found-define-it-in-your-contract".to_owned(), secondary_message: None, span: Some(span), }, @@ -76,6 +82,16 @@ impl From for MacroError { secondary_message: None, span: Some(span), }, + AztecMacroError::UnsupportedStorageType { span, typ } => MacroError { + primary_message: format!("Provided storage type `{typ:?}` is not directly supported in Aztec. Please provide a custom storage implementation"), + secondary_message: None, + span, + }, + AztecMacroError::CouldNotAssignStorageSlots { secondary_message } => MacroError { + primary_message: "Could not assign storage slots, please provide a custom storage implementation".to_string(), + secondary_message, + span: None, + }, AztecMacroError::EventError { span, message } => MacroError { primary_message: message, secondary_message: None, @@ -166,7 +182,28 @@ fn member_access(lhs: &str, rhs: &str) -> Expression { }))) } +fn return_type(path: Path) -> FunctionReturnType { + let ty = make_type(UnresolvedTypeData::Named(path, vec![])); + FunctionReturnType::Ty(ty) +} + +fn lambda(parameters: Vec<(Pattern, UnresolvedType)>, body: Expression) -> Expression { + expression(ExpressionKind::Lambda(Box::new(Lambda { + parameters, + return_type: UnresolvedType { + typ: UnresolvedTypeData::Unspecified, + span: Some(Span::default()), + }, + body, + }))) +} + macro_rules! chained_path { + ( $base:expr ) => { + { + ident_path($base) + } + }; ( $base:expr $(, $tail:expr)* ) => { { let mut base_path = ident_path($base); @@ -196,7 +233,7 @@ fn cast(lhs: Expression, ty: UnresolvedTypeData) -> Expression { } fn make_type(typ: UnresolvedTypeData) -> UnresolvedType { - UnresolvedType { typ, span: None } + UnresolvedType { typ, span: Some(Span::default()) } } fn index_array(array: Ident, index: &str) -> Expression { @@ -251,7 +288,8 @@ fn transform_hir( crate_id: &CrateId, context: &mut HirContext, ) -> Result<(), (AztecMacroError, FileId)> { - transform_events(crate_id, context) + transform_events(crate_id, context)?; + assign_storage_slots(crate_id, context) } /// Includes an import to the aztec library if it has not been included yet @@ -288,6 +326,16 @@ fn check_for_storage_definition(module: &SortedModule) -> bool { module.types.iter().any(|r#struct| r#struct.name.0.contents == "Storage") } +// Check to see if the user has defined a storage struct +fn check_for_storage_implementation(module: &SortedModule) -> bool { + module.impls.iter().any(|r#impl| match &r#impl.object_type.typ { + UnresolvedTypeData::Named(path, _) => { + path.segments.last().is_some_and(|segment| segment.0.contents == "Storage") + } + _ => false, + }) +} + // Check if "compute_note_hash_and_nullifier(AztecAddress,Field,Field,[Field; N]) -> [Field; 4]" is defined fn check_for_compute_note_hash_and_nullifier_definition(module: &SortedModule) -> bool { module.functions.iter().any(|func| { @@ -343,9 +391,15 @@ fn transform_module( // Check for a user defined storage struct let storage_defined = check_for_storage_definition(module); + let storage_implemented = check_for_storage_implementation(module); + + let crate_graph = &context.crate_graph[crate_id]; + + if storage_defined && !storage_implemented { + generate_storage_implementation(module).map_err(|err| (err, crate_graph.root_file_id))?; + } if storage_defined && !check_for_compute_note_hash_and_nullifier_definition(module) { - let crate_graph = &context.crate_graph[crate_id]; return Err(( AztecMacroError::ComputeNoteHashAndNullifierNotFound { span: Span::default() }, crate_graph.root_file_id, @@ -370,6 +424,10 @@ fn transform_module( transform_function("Public", func, storage_defined) .map_err(|err| (err, crate_graph.root_file_id))?; has_transformed_module = true; + } else if is_custom_attribute(&secondary_attribute, "aztec(public-vm)") { + transform_vm_function(func, storage_defined) + .map_err(|err| (err, crate_graph.root_file_id))?; + has_transformed_module = true; } } // Add the storage struct to the beginning of the function if it is unconstrained in an aztec contract @@ -403,6 +461,134 @@ fn transform_module( Ok(has_transformed_module) } +/// Auxiliary function to generate the storage constructor for a given field, using +/// the Storage definition as a reference. Supports nesting. +fn generate_storage_field_constructor( + (type_ident, unresolved_type): &(Ident, UnresolvedType), + slot: Expression, +) -> Result { + let typ = &unresolved_type.typ; + match typ { + UnresolvedTypeData::Named(path, generics) => { + let mut new_path = path.clone().to_owned(); + new_path.segments.push(ident("new")); + match path.segments.last().unwrap().0.contents.as_str() { + "Map" => Ok(call( + variable_path(new_path), + vec![ + variable("context"), + slot, + lambda( + vec![ + ( + pattern("context"), + make_type(UnresolvedTypeData::Named( + chained_path!("aztec", "context", "Context"), + vec![], + )), + ), + ( + Pattern::Identifier(ident("slot")), + make_type(UnresolvedTypeData::FieldElement), + ), + ], + generate_storage_field_constructor( + &(type_ident.clone(), generics.iter().last().unwrap().clone()), + variable("slot"), + )?, + ), + ], + )), + _ => Ok(call(variable_path(new_path), vec![variable("context"), slot])), + } + } + _ => Err(AztecMacroError::UnsupportedStorageType { + typ: typ.clone(), + span: Some(type_ident.span()), + }), + } +} + +// Generates the Storage implementation block from the Storage struct definition if it does not exist +/// From: +/// +/// struct Storage { +/// a_map: Map>, +/// a_nested_map: Map>>, +/// a_field: SomeStoragePrimitive, +/// } +/// +/// To: +/// +/// impl Storage { +/// fn init(context: Context) -> Self { +/// Storage { +/// a_map: Map::new(context, 0, |context, slot| { +/// SomeStoragePrimitive::new(context, slot) +/// }), +/// a_nested_map: Map::new(context, 0, |context, slot| { +/// Map::new(context, slot, |context, slot| { +/// SomeStoragePrimitive::new(context, slot) +/// }) +/// }), +/// a_field: SomeStoragePrimitive::new(context, 0), +/// } +/// } +/// } +/// +/// Storage slots are generated as 0 and will be populated using the information from the HIR +/// at a later stage. +fn generate_storage_implementation(module: &mut SortedModule) -> Result<(), AztecMacroError> { + let definition = + module.types.iter().find(|r#struct| r#struct.name.0.contents == "Storage").unwrap(); + + let slot_zero = expression(ExpressionKind::Literal(Literal::Integer( + FieldElement::from(i128::from(0)), + false, + ))); + + let field_constructors = definition + .fields + .iter() + .flat_map(|field| { + generate_storage_field_constructor(field, slot_zero.clone()) + .map(|expression| (field.0.clone(), expression)) + }) + .collect(); + + let storage_constructor_statement = make_statement(StatementKind::Expression(expression( + ExpressionKind::constructor((chained_path!("Storage"), field_constructors)), + ))); + + let init = NoirFunction::normal(FunctionDefinition::normal( + &ident("init"), + &vec![], + &[( + ident("context"), + make_type(UnresolvedTypeData::Named( + chained_path!("aztec", "context", "Context"), + vec![], + )), + )], + &BlockExpression(vec![storage_constructor_statement]), + &[], + &return_type(chained_path!("Self")), + )); + + let storage_impl = TypeImpl { + object_type: UnresolvedType { + typ: UnresolvedTypeData::Named(chained_path!("Storage"), vec![]), + span: Some(Span::default()), + }, + type_span: Span::default(), + generics: vec![], + methods: vec![init], + }; + module.impls.push(storage_impl); + + Ok(()) +} + /// If it does, it will insert the following things: /// - A new Input that is provided for a kernel app circuit, named: {Public/Private}ContextInputs /// - Hashes all of the function input variables @@ -454,6 +640,20 @@ fn transform_function( Ok(()) } +/// Transform a function to work with AVM bytecode +fn transform_vm_function( + func: &mut NoirFunction, + _storage_defined: bool, +) -> Result<(), AztecMacroError> { + // We want the function to be seen as a public function + func.def.is_open = true; + + // NOTE: the line below is a temporary hack to trigger external transpilation tools + // It will be removed once the transpiler is integrated into the Noir compiler + func.def.name.0.contents = format!("avm_{}", func.def.name.0.contents); + Ok(()) +} + /// Transform Unconstrained /// /// Inserts the following code at the beginning of an unconstrained function @@ -484,6 +684,23 @@ fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec Vec { + let crates = context.crates(); + crates + .flat_map(|crate_id| context.def_map(&crate_id).map(|def_map| def_map.modules())) + .flatten() + .flat_map(|(_, module)| { + module.type_definitions().filter_map(|typ| { + if let ModuleDefId::TraitId(struct_id) = typ { + Some(struct_id) + } else { + None + } + }) + }) + .collect() +} + /// Substitutes the signature literal that was introduced in the selector method previously with the actual signature. fn transform_event( struct_id: StructId, @@ -576,6 +793,185 @@ fn transform_events( Ok(()) } +/// Obtains the serialized length of a type that implements the Serialize trait. +fn get_serialized_length( + traits: &[TraitId], + typ: &Type, + interner: &NodeInterner, +) -> Result { + let (struct_name, maybe_stored_in_state) = match typ { + Type::Struct(struct_type, generics) => { + Ok((struct_type.borrow().name.0.contents.clone(), generics.get(0))) + } + _ => Err(AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some("State storage variable must be a struct".to_string()), + }), + }?; + let stored_in_state = + maybe_stored_in_state.ok_or(AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some("State storage variable must be generic".to_string()), + })?; + + let is_note = traits.iter().any(|&trait_id| { + let r#trait = interner.get_trait(trait_id); + r#trait.name.0.contents == "NoteInterface" + && !interner.lookup_all_trait_implementations(stored_in_state, trait_id).is_empty() + }); + + // Maps and (private) Notes always occupy a single slot. Someone could store a Note in PublicState for whatever reason though. + if struct_name == "Map" || (is_note && struct_name != "PublicState") { + return Ok(1); + } + + let serialized_trait_impl_kind = traits + .iter() + .filter_map(|&trait_id| { + let r#trait = interner.get_trait(trait_id); + if r#trait.borrow().name.0.contents == "Serialize" + && r#trait.borrow().generics.len() == 1 + { + interner + .lookup_all_trait_implementations(stored_in_state, trait_id) + .into_iter() + .next() + } else { + None + } + }) + .next() + .ok_or(AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some("Stored data must implement Serialize trait".to_string()), + })?; + + let serialized_trait_impl_id = match serialized_trait_impl_kind { + TraitImplKind::Normal(trait_impl_id) => Ok(trait_impl_id), + _ => Err(AztecMacroError::CouldNotAssignStorageSlots { secondary_message: None }), + }?; + + let serialized_trait_impl_shared = interner.get_trait_implementation(*serialized_trait_impl_id); + let serialized_trait_impl = serialized_trait_impl_shared.borrow(); + + match serialized_trait_impl.trait_generics.get(0).unwrap() { + Type::Constant(value) => Ok(*value), + _ => Err(AztecMacroError::CouldNotAssignStorageSlots { secondary_message: None }), + } +} + +/// Assigns storage slots to the storage struct fields based on the serialized length of the types. This automatic assignment +/// will only trigger if the assigned storage slot is invalid (0 as generated by generate_storage_implementation) +fn assign_storage_slots( + crate_id: &CrateId, + context: &mut HirContext, +) -> Result<(), (AztecMacroError, FileId)> { + let traits: Vec<_> = collect_traits(context); + for struct_id in collect_crate_structs(crate_id, context) { + let interner: &mut NodeInterner = context.def_interner.borrow_mut(); + let r#struct = interner.get_struct(struct_id); + let file_id = r#struct.borrow().location.file; + if r#struct.borrow().name.0.contents == "Storage" && r#struct.borrow().id.krate().is_root() + { + let init_id = interner + .lookup_method( + &Type::Struct(interner.get_struct(struct_id), vec![]), + struct_id, + "init", + false, + ) + .ok_or(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Storage struct must have an init function".to_string(), + ), + }, + file_id, + ))?; + let init_function = interner.function(&init_id).block(interner); + let init_function_statement_id = init_function.statements().first().ok_or(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some("Init storage statement not found".to_string()), + }, + file_id, + ))?; + let storage_constructor_statement = interner.statement(init_function_statement_id); + + let storage_constructor_expression = match storage_constructor_statement { + HirStatement::Expression(expression_id) => { + match interner.expression(&expression_id) { + HirExpression::Constructor(hir_constructor_expression) => { + Ok(hir_constructor_expression) + } + _ => Err((AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Storage constructor statement must be a constructor expression" + .to_string(), + ), + }, file_id)) + } + } + _ => Err(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Storage constructor statement must be an expression".to_string(), + ), + }, + file_id, + )), + }?; + + let mut storage_slot: u64 = 1; + for (index, (_, expr_id)) in storage_constructor_expression.fields.iter().enumerate() { + let fields = r#struct.borrow().get_fields(&[]); + let (_, field_type) = fields.get(index).unwrap(); + let new_call_expression = match interner.expression(expr_id) { + HirExpression::Call(hir_call_expression) => Ok(hir_call_expression), + _ => Err(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Storage field initialization expression is not a call expression" + .to_string(), + ), + }, + file_id, + )), + }?; + + let slot_arg_expression = interner.expression(&new_call_expression.arguments[1]); + + let current_storage_slot = match slot_arg_expression { + HirExpression::Literal(HirLiteral::Integer(slot, _)) => { + Ok(slot.borrow().to_u128()) + } + _ => Err(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Storage slot argument expression must be a literal integer" + .to_string(), + ), + }, + file_id, + )), + }?; + + if current_storage_slot != 0 { + continue; + } + + let type_serialized_len = get_serialized_length(&traits, field_type, interner) + .map_err(|err| (err, file_id))?; + interner.update_expression(new_call_expression.arguments[1], |expr| { + *expr = HirExpression::Literal(HirLiteral::Integer( + FieldElement::from(u128::from(storage_slot)), + false, + )); + }); + + storage_slot += type_serialized_len; + } + } + } + Ok(()) +} + const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER"; /// Generates the impl for an event selector @@ -890,11 +1286,11 @@ fn make_return_push(push_value: Expression) -> Statement { /// Make Return push array /// /// Translates to: -/// `context.return_values.push_array({push_value})` -fn make_return_push_array(push_value: Expression) -> Statement { +/// `context.return_values.extend_from_array({push_value})` +fn make_return_extend_from_array(push_value: Expression) -> Statement { make_statement(StatementKind::Semi(method_call( context_return_values(), - "push_array", + "extend_from_array", vec![push_value], ))) } @@ -903,14 +1299,14 @@ fn make_return_push_array(push_value: Expression) -> Statement { /// /// Translates to: /// ```noir -/// `context.return_values.push_array({push_value}.serialize())` +/// `context.return_values.extend_from_array({push_value}.serialize())` fn make_struct_return_type(expression: Expression) -> Statement { let serialized_call = method_call( expression, // variable "serialize", // method name vec![], // args ); - make_return_push_array(serialized_call) + make_return_extend_from_array(serialized_call) } /// Make array return type @@ -969,9 +1365,7 @@ fn make_castable_return_type(expression: Expression) -> Statement { /// } fn create_return_type(ty: &str) -> FunctionReturnType { let return_path = chained_path!("aztec", "abi", ty); - - let ty = make_type(UnresolvedTypeData::Named(return_path, vec![])); - FunctionReturnType::Ty(ty) + return_type(return_path) } /// Create Context Finish diff --git a/compiler/integration-tests/test/browser/compile_prove_verify.test.ts b/compiler/integration-tests/test/browser/compile_prove_verify.test.ts index 0a829def09e..dba51895bb8 100644 --- a/compiler/integration-tests/test/browser/compile_prove_verify.test.ts +++ b/compiler/integration-tests/test/browser/compile_prove_verify.test.ts @@ -59,11 +59,11 @@ test_cases.forEach((testInfo) => { // JS Proving - const proofWithPublicInputs = await program.generateFinalProof(inputs); + const proofWithPublicInputs = await program.generateProof(inputs); // JS verification - const verified = await program.verifyFinalProof(proofWithPublicInputs); + const verified = await program.verifyProof(proofWithPublicInputs); expect(verified, 'Proof fails verification in JS').to.be.true; }); diff --git a/compiler/integration-tests/test/browser/recursion.test.ts b/compiler/integration-tests/test/browser/recursion.test.ts index 80199de5701..a8927aa6a75 100644 --- a/compiler/integration-tests/test/browser/recursion.test.ts +++ b/compiler/integration-tests/test/browser/recursion.test.ts @@ -19,7 +19,7 @@ await newABICoder(); await initACVM(); const base_relative_path = '../../../../..'; -const circuit_main = 'test_programs/execution_success/assert_statement'; +const circuit_main = 'test_programs/execution_success/assert_statement_recursive'; const circuit_recursion = 'compiler/integration-tests/circuits/recursion'; async function getCircuit(projectPath: string) { @@ -48,15 +48,15 @@ describe('It compiles noir program code, receiving circuit bytes and abi object. const { witness: main_witnessUint8Array } = await new Noir(main_program).execute(main_inputs); - const main_proof = await main_backend.generateIntermediateProof(main_witnessUint8Array); - const main_verification = await main_backend.verifyIntermediateProof(main_proof); + const main_proof = await main_backend.generateProof(main_witnessUint8Array); + const main_verification = await main_backend.verifyProof(main_proof); logger.debug('main_verification', main_verification); expect(main_verification).to.be.true; const numPublicInputs = 1; - const { proofAsFields, vkAsFields, vkHash } = await main_backend.generateIntermediateProofArtifacts( + const { proofAsFields, vkAsFields, vkHash } = await main_backend.generateRecursiveProofArtifacts( main_proof, numPublicInputs, ); @@ -76,20 +76,20 @@ describe('It compiles noir program code, receiving circuit bytes and abi object. const { witness: recursion_witnessUint8Array } = await new Noir(recursion_program).execute(recursion_inputs); - const recursion_proof = await recursion_backend.generateFinalProof(recursion_witnessUint8Array); + const recursion_proof = await recursion_backend.generateProof(recursion_witnessUint8Array); // Causes an "unreachable" error. // Due to the fact that it's a non-recursive proof? // // const recursion_numPublicInputs = 1; - // const { proofAsFields: recursion_proofAsFields } = await recursion_backend.generateIntermediateProofArtifacts( + // const { proofAsFields: recursion_proofAsFields } = await recursion_backend.generateRecursiveProofArtifacts( // recursion_proof, // recursion_numPublicInputs, // ); // // logger.debug('recursion_proofAsFields', recursion_proofAsFields); - const recursion_verification = await recursion_backend.verifyFinalProof(recursion_proof); + const recursion_verification = await recursion_backend.verifyProof(recursion_proof); logger.debug('recursion_verification', recursion_verification); diff --git a/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts b/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts index 9cdd80edc15..6147f770f16 100644 --- a/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts +++ b/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts @@ -16,7 +16,7 @@ it(`smart contract can verify a recursive proof`, async () => { const fm = createFileManager(basePath); const innerCompilationResult = await compile( fm, - join(basePath, './test_programs/execution_success/assert_statement'), + join(basePath, './test_programs/execution_success/assert_statement_recursive'), ); if (!('program' in innerCompilationResult)) { throw new Error('Compilation failed'); @@ -38,17 +38,17 @@ it(`smart contract can verify a recursive proof`, async () => { const inner = new Noir(innerProgram); const inner_prover_toml = readFileSync( - join(basePath, `./test_programs/execution_success/assert_statement/Prover.toml`), + join(basePath, `./test_programs/execution_success/assert_statement_recursive/Prover.toml`), ).toString(); const inner_inputs = toml.parse(inner_prover_toml); const { witness: main_witness } = await inner.execute(inner_inputs); - const intermediate_proof = await inner_backend.generateIntermediateProof(main_witness); + const intermediate_proof = await inner_backend.generateProof(main_witness); - expect(await inner_backend.verifyIntermediateProof(intermediate_proof)).to.be.true; + expect(await inner_backend.verifyProof(intermediate_proof)).to.be.true; - const { proofAsFields, vkAsFields, vkHash } = await inner_backend.generateIntermediateProofArtifacts( + const { proofAsFields, vkAsFields, vkHash } = await inner_backend.generateRecursiveProofArtifacts( intermediate_proof, 1, // 1 public input ); @@ -65,8 +65,8 @@ it(`smart contract can verify a recursive proof`, async () => { key_hash: vkHash, }; - const recursion_proof = await recursion.generateFinalProof(recursion_inputs); - expect(await recursion.verifyFinalProof(recursion_proof)).to.be.true; + const recursion_proof = await recursion.generateProof(recursion_inputs); + expect(await recursion.verifyProof(recursion_proof)).to.be.true; // Smart contract verification diff --git a/compiler/integration-tests/test/node/smart_contract_verifier.test.ts b/compiler/integration-tests/test/node/smart_contract_verifier.test.ts index d870956ea7a..79a0520da32 100644 --- a/compiler/integration-tests/test/node/smart_contract_verifier.test.ts +++ b/compiler/integration-tests/test/node/smart_contract_verifier.test.ts @@ -46,11 +46,11 @@ test_cases.forEach((testInfo) => { const prover_toml = readFileSync(resolve(`${base_relative_path}/${test_case}/Prover.toml`)).toString(); const inputs = toml.parse(prover_toml); - const proofData = await program.generateFinalProof(inputs); + const proofData = await program.generateProof(inputs); // JS verification - const verified = await program.verifyFinalProof(proofData); + const verified = await program.verifyProof(proofData); expect(verified, 'Proof fails verification in JS').to.be.true; // Smart contract verification diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 632add74e6d..85f6629ab25 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -14,7 +14,7 @@ use crate::ssa::ir::{ types::{NumericType, Type}, value::{Value, ValueId}, }; -use acvm::acir::brillig::{BinaryFieldOp, BinaryIntOp, RegisterIndex, RegisterOrMemory}; +use acvm::acir::brillig::{BinaryFieldOp, BinaryIntOp, MemoryAddress, ValueOrArray}; use acvm::brillig_vm::brillig::HeapVector; use acvm::FieldElement; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; @@ -366,16 +366,13 @@ impl<'block> BrilligBlock<'block> { ); for (i, output_register) in output_registers.iter().enumerate() { - if let RegisterOrMemory::HeapVector(HeapVector { size, .. }) = - output_register - { + if let ValueOrArray::HeapVector(HeapVector { size, .. }) = output_register { // Update the stack pointer so that we do not overwrite // dynamic memory returned from other external calls self.brillig_context.update_stack_pointer(*size); // Update the dynamic slice length maintained in SSA - if let RegisterOrMemory::RegisterIndex(len_index) = - output_registers[i - 1] + if let ValueOrArray::MemoryAddress(len_index) = output_registers[i - 1] { let element_size = dfg[result_ids[i]].get_type().element_size(); self.brillig_context.mov_instruction(len_index, *size); @@ -666,7 +663,7 @@ impl<'block> BrilligBlock<'block> { result_ids: &[ValueId], ) { // Convert the arguments to registers casting those to the types of the receiving function - let argument_registers: Vec = arguments + let argument_registers: Vec = arguments .iter() .flat_map(|argument_id| self.convert_ssa_value(*argument_id, dfg).extract_registers()) .collect(); @@ -700,7 +697,7 @@ impl<'block> BrilligBlock<'block> { }); // Collect the registers that should have been returned - let returned_registers: Vec = variables_assigned_to + let returned_registers: Vec = variables_assigned_to .iter() .flat_map(|returned_variable| returned_variable.extract_registers()) .collect(); @@ -718,7 +715,7 @@ impl<'block> BrilligBlock<'block> { fn validate_array_index( &mut self, array_variable: BrilligVariable, - index_register: RegisterIndex, + index_register: MemoryAddress, ) { let (size_as_register, should_deallocate_size) = match array_variable { BrilligVariable::BrilligArray(BrilligArray { size, .. }) => { @@ -748,8 +745,8 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn retrieve_variable_from_array( &mut self, - array_pointer: RegisterIndex, - index_register: RegisterIndex, + array_pointer: MemoryAddress, + index_register: MemoryAddress, destination_variable: BrilligVariable, ) { match destination_variable { @@ -771,7 +768,7 @@ impl<'block> BrilligBlock<'block> { &mut self, source_variable: BrilligVariable, destination_variable: BrilligVariable, - index_register: RegisterIndex, + index_register: MemoryAddress, value_variable: BrilligVariable, ) { let destination_pointer = match destination_variable { @@ -851,8 +848,8 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn store_variable_in_array_with_ctx( ctx: &mut BrilligContext, - destination_pointer: RegisterIndex, - index_register: RegisterIndex, + destination_pointer: MemoryAddress, + index_register: MemoryAddress, value_variable: BrilligVariable, ) { match value_variable { @@ -860,7 +857,7 @@ impl<'block> BrilligBlock<'block> { ctx.array_set(destination_pointer, index_register, value_register); } BrilligVariable::BrilligArray(_) => { - let reference: RegisterIndex = ctx.allocate_register(); + let reference: MemoryAddress = ctx.allocate_register(); ctx.allocate_array_reference_instruction(reference); ctx.store_variable_instruction(reference, value_variable); ctx.array_set(destination_pointer, index_register, reference); @@ -878,8 +875,8 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn store_variable_in_array( &mut self, - destination_pointer: RegisterIndex, - index_register: RegisterIndex, + destination_pointer: MemoryAddress, + index_register: MemoryAddress, value_variable: BrilligVariable, ) { Self::store_variable_in_array_with_ctx( @@ -1134,7 +1131,7 @@ impl<'block> BrilligBlock<'block> { /// of fields in the vector. fn update_slice_length( &mut self, - target_len: RegisterIndex, + target_len: MemoryAddress, source_value: ValueId, dfg: &DataFlowGraph, binary_op: BinaryIntOp, @@ -1147,7 +1144,7 @@ impl<'block> BrilligBlock<'block> { /// Converts an SSA cast to a sequence of Brillig opcodes. /// Casting is only necessary when shrinking the bit size of a numeric value. - fn convert_cast(&mut self, destination: RegisterIndex, source: RegisterIndex) { + fn convert_cast(&mut self, destination: MemoryAddress, source: MemoryAddress) { // We assume that `source` is a valid `target_type` as it's expected that a truncate instruction was emitted // to ensure this is the case. @@ -1159,7 +1156,7 @@ impl<'block> BrilligBlock<'block> { &mut self, binary: &Binary, dfg: &DataFlowGraph, - result_register: RegisterIndex, + result_register: MemoryAddress, ) { let binary_type = type_of_binary_operation(dfg[binary.lhs].get_type(), dfg[binary.rhs].get_type()); @@ -1256,12 +1253,12 @@ impl<'block> BrilligBlock<'block> { } } - /// Converts an SSA `ValueId` into a `RegisterIndex`. Initializes if necessary. + /// Converts an SSA `ValueId` into a `MemoryAddress`. Initializes if necessary. fn convert_ssa_register_value( &mut self, value_id: ValueId, dfg: &DataFlowGraph, - ) -> RegisterIndex { + ) -> MemoryAddress { let variable = self.convert_ssa_value(value_id, dfg); variable.extract_register() } @@ -1322,7 +1319,7 @@ impl<'block> BrilligBlock<'block> { fn convert_ssa_array_len( &mut self, array_id: ValueId, - result_register: RegisterIndex, + result_register: MemoryAddress, dfg: &DataFlowGraph, ) { let array_variable = self.convert_ssa_value(array_id, dfg); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs index f2e698c0aa9..49d40ca3697 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs @@ -1,4 +1,4 @@ -use acvm::brillig_vm::brillig::RegisterIndex; +use acvm::brillig_vm::brillig::MemoryAddress; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::{ @@ -75,7 +75,7 @@ impl BlockVariables { brillig_context: &mut BrilligContext, value: ValueId, dfg: &DataFlowGraph, - ) -> RegisterIndex { + ) -> MemoryAddress { let variable = self.define_variable(function_context, brillig_context, value, dfg); variable.extract_register() } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index a07865073ff..5ac2ecf06be 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -1,5 +1,5 @@ use acvm::acir::brillig::{ - BinaryFieldOp, BinaryIntOp, Opcode as BrilligOpcode, RegisterIndex, Value, + BinaryFieldOp, BinaryIntOp, MemoryAddress, Opcode as BrilligOpcode, Value, }; use crate::brillig::brillig_ir::artifact::GeneratedBrillig; @@ -13,13 +13,14 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { // The input argument, ie the value that will be inverted. // We store the result in this register too. - let input = RegisterIndex::from(0); - let one_const = RegisterIndex::from(1); + let input = MemoryAddress::from(0); + let one_const = MemoryAddress::from(1); // Location of the stop opcode let stop_location = 3; GeneratedBrillig { byte_code: vec![ + BrilligOpcode::CalldataCopy { destination_address: input, size: 1, offset: 0 }, // If the input is zero, then we jump to the stop opcode BrilligOpcode::JumpIfNot { condition: input, location: stop_location }, // Put value one in register (1) @@ -31,7 +32,7 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { rhs: input, destination: input, }, - BrilligOpcode::Stop, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 1 }, ], assert_messages: Default::default(), locations: Default::default(), @@ -52,36 +53,41 @@ pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { // `b` is (1) GeneratedBrillig { byte_code: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 2, + offset: 0, + }, //q = a/b is set into register (2) BrilligOpcode::BinaryIntOp { op: BinaryIntOp::UnsignedDiv, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), bit_size, }, //(1)= q*b BrilligOpcode::BinaryIntOp { op: BinaryIntOp::Mul, - lhs: RegisterIndex::from(2), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(1), + lhs: MemoryAddress::from(2), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(1), bit_size, }, //(1) = a-q*b BrilligOpcode::BinaryIntOp { op: BinaryIntOp::Sub, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(1), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(1), bit_size, }, //(0) = q BrilligOpcode::Mov { - destination: RegisterIndex::from(0), - source: RegisterIndex::from(2), + destination: MemoryAddress::from(0), + source: MemoryAddress::from(2), }, - BrilligOpcode::Stop, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 2 }, ], assert_messages: Default::default(), locations: Default::default(), diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 6402e6f9d97..981a16a01b2 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -1,4 +1,4 @@ -use acvm::brillig_vm::brillig::{BinaryIntOp, RegisterIndex}; +use acvm::brillig_vm::brillig::{BinaryIntOp, MemoryAddress}; use crate::brillig::brillig_ir::brillig_variable::{BrilligVariable, BrilligVector}; @@ -168,7 +168,7 @@ impl<'block> BrilligBlock<'block> { &mut self, target_vector: BrilligVector, source_vector: BrilligVector, - index: RegisterIndex, + index: MemoryAddress, items: &[BrilligVariable], ) { // First we need to allocate the target vector incrementing the size by items.len() @@ -240,7 +240,7 @@ impl<'block> BrilligBlock<'block> { &mut self, target_vector: BrilligVector, source_vector: BrilligVector, - index: RegisterIndex, + index: MemoryAddress, removed_items: &[BrilligVariable], ) { // First we need to allocate the target vector decrementing the size by removed_items.len() @@ -328,7 +328,6 @@ mod tests { use std::vec; use acvm::acir::brillig::Value; - use acvm::brillig_vm::brillig::RegisterIndex; use crate::brillig::brillig_gen::brillig_block::BrilligBlock; use crate::brillig::brillig_gen::brillig_block_variables::BlockVariables; @@ -375,17 +374,15 @@ mod tests { fn test_case_push( push_back: bool, array: Vec, - expected_mem: Vec, item_to_push: Value, + expected_return: Vec, ) { let arguments = vec![ BrilligParameter::Array(vec![BrilligParameter::Simple], array.len()), BrilligParameter::Simple, ]; - let returns = vec![ - BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() + 1), - BrilligParameter::Simple, - ]; + let returns = + vec![BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() + 1)]; let (_, mut function_context, mut context) = create_test_environment(); @@ -423,55 +420,42 @@ mod tests { ); } - context.return_instruction(&[ - target_vector.pointer, - target_vector.rc, - target_vector.size, - ]); + context.return_instruction(&[target_vector.pointer, target_vector.rc]); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm( - array.clone(), - vec![Value::from(0_usize), item_to_push], - &bytecode, + let (vm, return_data_offset, return_data_size) = + create_and_run_vm(array.into_iter().chain(vec![item_to_push]).collect(), &bytecode); + assert_eq!(return_data_size, expected_return.len()); + assert_eq!( + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + expected_return ); - - assert_eq!(vm.get_memory(), &expected_mem); - - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(array.len())); - assert_eq!(vm.get_registers().get(RegisterIndex(1)), Value::from(array.len() + 1)); } test_case_push( true, vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), Value::from(27_usize), ], - Value::from(27_usize), ); - test_case_push(true, vec![], vec![Value::from(27_usize)], Value::from(27_usize)); + test_case_push(true, vec![], Value::from(27_usize), vec![Value::from(27_usize)]); test_case_push( false, vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(27_usize), Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), ], - Value::from(27_usize), ); - test_case_push(false, vec![], vec![Value::from(27_usize)], Value::from(27_usize)); + test_case_push(false, vec![], Value::from(27_usize), vec![Value::from(27_usize)]); } #[test] @@ -479,15 +463,14 @@ mod tests { fn test_case_pop( pop_back: bool, array: Vec, - expected_mem: Vec, - expected_removed_item: Value, + expected_return_array: Vec, + expected_return_item: Value, ) { let arguments = vec![BrilligParameter::Array(vec![BrilligParameter::Simple], array.len())]; let returns = vec![ BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() - 1), BrilligParameter::Simple, - BrilligParameter::Simple, ]; let (_, mut function_context, mut context) = create_test_environment(); @@ -526,51 +509,32 @@ mod tests { ); } - context.return_instruction(&[ - target_vector.pointer, - target_vector.rc, - target_vector.size, - removed_item, - ]); + context.return_instruction(&[target_vector.pointer, target_vector.rc, removed_item]); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm(array.clone(), vec![Value::from(0_usize)], &bytecode); - - assert_eq!(vm.get_memory(), &expected_mem); - - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(array.len())); - assert_eq!(vm.get_registers().get(RegisterIndex(1)), Value::from(array.len() - 1)); - assert_eq!(vm.get_registers().get(RegisterIndex(2)), expected_removed_item); + let expected_return: Vec<_> = + expected_return_array.into_iter().chain(vec![expected_return_item]).collect(); + let (vm, return_data_offset, return_data_size) = + create_and_run_vm(array.clone(), &bytecode); + assert_eq!(return_data_size, expected_return.len()); + + assert_eq!( + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + expected_return + ); } test_case_pop( true, vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(1_usize), - Value::from(2_usize), - ], + vec![Value::from(1_usize), Value::from(2_usize)], Value::from(3_usize), ); - test_case_pop( - true, - vec![Value::from(1_usize)], - vec![Value::from(1_usize)], - Value::from(1_usize), - ); + test_case_pop(true, vec![Value::from(1_usize)], vec![], Value::from(1_usize)); test_case_pop( false, vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(2_usize), - Value::from(3_usize), - ], + vec![Value::from(2_usize), Value::from(3_usize)], Value::from(1_usize), ); } @@ -579,19 +543,17 @@ mod tests { fn test_slice_insert_operation() { fn test_case_insert( array: Vec, - expected_mem: Vec, item: Value, index: Value, + expected_return: Vec, ) { let arguments = vec![ BrilligParameter::Array(vec![BrilligParameter::Simple], array.len()), BrilligParameter::Simple, BrilligParameter::Simple, ]; - let returns = vec![ - BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() + 1), - BrilligParameter::Simple, - ]; + let returns = + vec![BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() + 1)]; let (_, mut function_context, mut context) = create_test_environment(); @@ -623,87 +585,69 @@ mod tests { &[BrilligVariable::Simple(item_to_insert)], ); - context.return_instruction(&[ - target_vector.pointer, - target_vector.rc, - target_vector.size, - ]); + context.return_instruction(&[target_vector.pointer, target_vector.rc]); + let calldata = array.into_iter().chain(vec![item]).chain(vec![index]).collect(); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm( - array.clone(), - vec![Value::from(0_usize), item, index], - &bytecode, - ); + let (vm, return_data_offset, return_data_size) = create_and_run_vm(calldata, &bytecode); + assert_eq!(return_data_size, expected_return.len()); - assert_eq!(vm.get_memory(), &expected_mem); - - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(array.len())); - assert_eq!(vm.get_registers().get(RegisterIndex(1)), Value::from(array.len() + 1)); + assert_eq!( + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + expected_return + ); } test_case_insert( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), + Value::from(1_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(1_usize), Value::from(27_usize), Value::from(2_usize), Value::from(3_usize), ], - Value::from(27_usize), - Value::from(1_usize), ); test_case_insert( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), + Value::from(0_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(27_usize), Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), ], - Value::from(27_usize), - Value::from(0_usize), ); test_case_insert( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), + Value::from(2_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(1_usize), Value::from(2_usize), Value::from(27_usize), Value::from(3_usize), ], - Value::from(27_usize), - Value::from(2_usize), ); test_case_insert( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), + Value::from(3_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), Value::from(27_usize), ], - Value::from(27_usize), - Value::from(3_usize), ); test_case_insert( vec![], - vec![Value::from(27_usize)], Value::from(27_usize), Value::from(0_usize), + vec![Value::from(27_usize)], ); } @@ -711,8 +655,8 @@ mod tests { fn test_slice_remove_operation() { fn test_case_remove( array: Vec, - expected_mem: Vec, index: Value, + expected_array: Vec, expected_removed_item: Value, ) { let arguments = vec![ @@ -722,7 +666,6 @@ mod tests { let returns = vec![ BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() - 1), BrilligParameter::Simple, - BrilligParameter::Simple, ]; let (_, mut function_context, mut context) = create_test_environment(); @@ -755,65 +698,47 @@ mod tests { &[BrilligVariable::Simple(removed_item)], ); - context.return_instruction(&[ - target_vector.pointer, - target_vector.rc, - target_vector.size, - removed_item, - ]); + context.return_instruction(&[target_vector.pointer, target_vector.size, removed_item]); + + let calldata: Vec<_> = array.into_iter().chain(vec![index]).collect(); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm(array.clone(), vec![Value::from(0_usize), index], &bytecode); + let (vm, return_data_offset, return_data_size) = create_and_run_vm(calldata, &bytecode); - assert_eq!(vm.get_memory(), &expected_mem); + let expected_return: Vec<_> = + expected_array.into_iter().chain(vec![expected_removed_item]).collect(); + assert_eq!(return_data_size, expected_return.len()); - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(array.len())); - assert_eq!(vm.get_registers().get(RegisterIndex(1)), Value::from(array.len() - 1)); - assert_eq!(vm.get_registers().get(RegisterIndex(2)), expected_removed_item); + assert_eq!( + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + expected_return + ); } test_case_remove( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(2_usize), - Value::from(3_usize), - ], Value::from(0_usize), + vec![Value::from(2_usize), Value::from(3_usize)], Value::from(1_usize), ); test_case_remove( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(1_usize), - Value::from(3_usize), - ], Value::from(1_usize), + vec![Value::from(1_usize), Value::from(3_usize)], Value::from(2_usize), ); test_case_remove( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(1_usize), - Value::from(2_usize), - ], Value::from(2_usize), + vec![Value::from(1_usize), Value::from(2_usize)], Value::from(3_usize), ); test_case_remove( - vec![Value::from(1_usize)], vec![Value::from(1_usize)], Value::from(0_usize), + vec![], Value::from(1_usize), ); } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 3e6c3d4d7de..b094ff9c4c0 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -20,8 +20,8 @@ use self::{ }; use acvm::{ acir::brillig::{ - BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode, RegisterIndex, - RegisterOrMemory, Value, + BinaryFieldOp, BinaryIntOp, BlackBoxOp, MemoryAddress, Opcode as BrilligOpcode, Value, + ValueOrArray, }, FieldElement, }; @@ -64,18 +64,18 @@ impl ReservedRegisters { } /// Returns the stack pointer register. This will get used to allocate memory in runtime. - pub(crate) fn stack_pointer() -> RegisterIndex { - RegisterIndex::from(ReservedRegisters::StackPointer as usize) + pub(crate) fn stack_pointer() -> MemoryAddress { + MemoryAddress::from(ReservedRegisters::StackPointer as usize) } /// Returns the previous stack pointer register. This will be used to restore the registers after a fn call. - pub(crate) fn previous_stack_pointer() -> RegisterIndex { - RegisterIndex::from(ReservedRegisters::PreviousStackPointer as usize) + pub(crate) fn previous_stack_pointer() -> MemoryAddress { + MemoryAddress::from(ReservedRegisters::PreviousStackPointer as usize) } /// Returns a user defined (non-reserved) register index. - fn user_register_index(index: usize) -> RegisterIndex { - RegisterIndex::from(index + ReservedRegisters::len()) + fn user_register_index(index: usize) -> MemoryAddress { + MemoryAddress::from(index + ReservedRegisters::len()) } } @@ -109,7 +109,7 @@ impl BrilligContext { } } - pub(crate) fn set_allocated_registers(&mut self, allocated_registers: Vec) { + pub(crate) fn set_allocated_registers(&mut self, allocated_registers: Vec) { self.registers = BrilligRegistersContext::from_preallocated_registers(allocated_registers); } @@ -127,27 +127,28 @@ impl BrilligContext { /// in `pointer_register` pub(crate) fn allocate_fixed_length_array( &mut self, - pointer_register: RegisterIndex, + pointer_register: MemoryAddress, size: usize, ) { // debug_show handled by allocate_array_instruction let size_register = self.make_constant(size.into()); self.allocate_array_instruction(pointer_register, size_register); + self.deallocate_register(size_register); } /// Allocates an array of size contained in size_register and stores the /// pointer to the array in `pointer_register` pub(crate) fn allocate_array_instruction( &mut self, - pointer_register: RegisterIndex, - size_register: RegisterIndex, + pointer_register: MemoryAddress, + size_register: MemoryAddress, ) { self.debug_show.allocate_array_instruction(pointer_register, size_register); self.set_array_pointer(pointer_register); self.update_stack_pointer(size_register); } - pub(crate) fn set_array_pointer(&mut self, pointer_register: RegisterIndex) { + pub(crate) fn set_array_pointer(&mut self, pointer_register: MemoryAddress) { self.debug_show.mov_instruction(pointer_register, ReservedRegisters::stack_pointer()); self.push_opcode(BrilligOpcode::Mov { destination: pointer_register, @@ -155,7 +156,7 @@ impl BrilligContext { }); } - pub(crate) fn update_stack_pointer(&mut self, size_register: RegisterIndex) { + pub(crate) fn update_stack_pointer(&mut self, size_register: MemoryAddress) { self.memory_op( ReservedRegisters::stack_pointer(), size_register, @@ -168,7 +169,7 @@ impl BrilligContext { /// pointer to the array in `pointer_register` fn allocate_variable_reference_instruction( &mut self, - pointer_register: RegisterIndex, + pointer_register: MemoryAddress, size: usize, ) { self.debug_show.allocate_instruction(pointer_register); @@ -184,16 +185,17 @@ impl BrilligContext { ReservedRegisters::stack_pointer(), BinaryIntOp::Add, ); + self.deallocate_register(size_register); } pub(crate) fn allocate_simple_reference_instruction( &mut self, - pointer_register: RegisterIndex, + pointer_register: MemoryAddress, ) { self.allocate_variable_reference_instruction(pointer_register, 1); } - pub(crate) fn allocate_array_reference_instruction(&mut self, pointer_register: RegisterIndex) { + pub(crate) fn allocate_array_reference_instruction(&mut self, pointer_register: MemoryAddress) { self.allocate_variable_reference_instruction( pointer_register, BrilligArray::registers_count(), @@ -202,7 +204,7 @@ impl BrilligContext { pub(crate) fn allocate_vector_reference_instruction( &mut self, - pointer_register: RegisterIndex, + pointer_register: MemoryAddress, ) { self.allocate_variable_reference_instruction( pointer_register, @@ -213,9 +215,9 @@ impl BrilligContext { /// Gets the value in the array at index `index` and stores it in `result` pub(crate) fn array_get( &mut self, - array_ptr: RegisterIndex, - index: RegisterIndex, - result: RegisterIndex, + array_ptr: MemoryAddress, + index: MemoryAddress, + result: MemoryAddress, ) { self.debug_show.array_get(array_ptr, index, result); // Computes array_ptr + index, ie array[index] @@ -235,9 +237,9 @@ impl BrilligContext { /// Sets the item in the array at index `index` to `value` pub(crate) fn array_set( &mut self, - array_ptr: RegisterIndex, - index: RegisterIndex, - value: RegisterIndex, + array_ptr: MemoryAddress, + index: MemoryAddress, + value: MemoryAddress, ) { self.debug_show.array_set(array_ptr, index, value); // Computes array_ptr + index, ie array[index] @@ -258,9 +260,9 @@ impl BrilligContext { /// Into the array pointed by destination pub(crate) fn copy_array_instruction( &mut self, - source_pointer: RegisterIndex, - destination_pointer: RegisterIndex, - num_elements_register: RegisterIndex, + source_pointer: MemoryAddress, + destination_pointer: MemoryAddress, + num_elements_register: MemoryAddress, ) { self.debug_show.copy_array_instruction( source_pointer, @@ -280,9 +282,9 @@ impl BrilligContext { /// This instruction will issue a loop that will iterate iteration_count times /// The body of the loop should be issued by the caller in the on_iteration closure. - pub(crate) fn loop_instruction(&mut self, iteration_count: RegisterIndex, on_iteration: F) + pub(crate) fn loop_instruction(&mut self, iteration_count: MemoryAddress, on_iteration: F) where - F: FnOnce(&mut BrilligContext, RegisterIndex), + F: FnOnce(&mut BrilligContext, MemoryAddress), { let iterator_register = self.make_constant(0_u128.into()); @@ -327,7 +329,7 @@ impl BrilligContext { /// functions to allow the given function to mutably alias its environment. pub(crate) fn branch_instruction( &mut self, - condition: RegisterIndex, + condition: MemoryAddress, mut f: impl FnMut(&mut BrilligContext, bool), ) { // Reserve 3 sections @@ -394,7 +396,7 @@ impl BrilligContext { /// Adds a unresolved `JumpIf` instruction to the bytecode. pub(crate) fn jump_if_instruction( &mut self, - condition: RegisterIndex, + condition: MemoryAddress, target_label: T, ) { self.debug_show.jump_if_instruction(condition, target_label.to_string()); @@ -414,14 +416,14 @@ impl BrilligContext { } /// Allocates an unused register. - pub(crate) fn allocate_register(&mut self) -> RegisterIndex { + pub(crate) fn allocate_register(&mut self) -> MemoryAddress { self.registers.allocate_register() } /// Push a register to the deallocation list, ready for reuse. /// TODO(AD): currently, register deallocation is only done with immediate values. /// TODO(AD): See https://github.com/noir-lang/noir/issues/1720 - pub(crate) fn deallocate_register(&mut self, register_index: RegisterIndex) { + pub(crate) fn deallocate_register(&mut self, register_index: MemoryAddress) { self.registers.deallocate_register(register_index); } } @@ -431,7 +433,7 @@ impl BrilligContext { /// is false. pub(crate) fn constrain_instruction( &mut self, - condition: RegisterIndex, + condition: MemoryAddress, assert_message: Option, ) { self.debug_show.constrain_instruction(condition); @@ -453,7 +455,7 @@ impl BrilligContext { /// Brillig does not have an explicit return instruction, so this /// method will move all register values to the first `N` values in /// the VM. - pub(crate) fn return_instruction(&mut self, return_registers: &[RegisterIndex]) { + pub(crate) fn return_instruction(&mut self, return_registers: &[MemoryAddress]) { self.debug_show.return_instruction(return_registers); let mut sources = Vec::with_capacity(return_registers.len()); let mut destinations = Vec::with_capacity(return_registers.len()); @@ -473,8 +475,8 @@ impl BrilligContext { /// It first moves all sources to new allocated registers to avoid overwriting. pub(crate) fn mov_registers_to_registers_instruction( &mut self, - sources: Vec, - destinations: Vec, + sources: Vec, + destinations: Vec, ) { let new_sources: Vec<_> = sources .iter() @@ -493,7 +495,7 @@ impl BrilligContext { /// Emits a `mov` instruction. /// /// Copies the value at `source` into `destination` - pub(crate) fn mov_instruction(&mut self, destination: RegisterIndex, source: RegisterIndex) { + pub(crate) fn mov_instruction(&mut self, destination: MemoryAddress, source: MemoryAddress) { self.debug_show.mov_instruction(destination, source); self.push_opcode(BrilligOpcode::Mov { destination, source }); } @@ -504,9 +506,9 @@ impl BrilligContext { /// and store the result in the `result` register. pub(crate) fn binary_instruction( &mut self, - lhs: RegisterIndex, - rhs: RegisterIndex, - result: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + result: MemoryAddress, operation: BrilligBinaryOp, ) { self.debug_show.binary_instruction(lhs, rhs, result, operation.clone()); @@ -527,7 +529,7 @@ impl BrilligContext { } /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction(&mut self, result: RegisterIndex, constant: Value) { + pub(crate) fn const_instruction(&mut self, result: MemoryAddress, constant: Value) { self.debug_show.const_instruction(result, constant); self.push_opcode(BrilligOpcode::Const { destination: result, value: constant }); } @@ -538,9 +540,9 @@ impl BrilligContext { /// in Brillig. pub(crate) fn not_instruction( &mut self, - input: RegisterIndex, + input: MemoryAddress, bit_size: u32, - result: RegisterIndex, + result: MemoryAddress, ) { self.debug_show.not_instruction(input, bit_size, result); // Compile !x as ((-1) - x) @@ -565,8 +567,8 @@ impl BrilligContext { pub(crate) fn foreign_call_instruction( &mut self, func_name: String, - inputs: &[RegisterOrMemory], - outputs: &[RegisterOrMemory], + inputs: &[ValueOrArray], + outputs: &[ValueOrArray], ) { self.debug_show.foreign_call_instruction(func_name.clone(), inputs, outputs); let opcode = BrilligOpcode::ForeignCall { @@ -580,8 +582,8 @@ impl BrilligContext { /// Emits a load instruction pub(crate) fn load_instruction( &mut self, - destination: RegisterIndex, - source_pointer: RegisterIndex, + destination: MemoryAddress, + source_pointer: MemoryAddress, ) { self.debug_show.load_instruction(destination, source_pointer); self.push_opcode(BrilligOpcode::Load { destination, source_pointer }); @@ -591,7 +593,7 @@ impl BrilligContext { pub(crate) fn load_variable_instruction( &mut self, destination: BrilligVariable, - variable_pointer: RegisterIndex, + variable_pointer: MemoryAddress, ) { match destination { BrilligVariable::Simple(register_index) => { @@ -630,8 +632,8 @@ impl BrilligContext { /// Emits a store instruction pub(crate) fn store_instruction( &mut self, - destination_pointer: RegisterIndex, - source: RegisterIndex, + destination_pointer: MemoryAddress, + source: MemoryAddress, ) { self.debug_show.store_instruction(destination_pointer, source); self.push_opcode(BrilligOpcode::Store { destination_pointer, source }); @@ -640,7 +642,7 @@ impl BrilligContext { /// Stores a variable by saving its registers to memory pub(crate) fn store_variable_instruction( &mut self, - variable_pointer: RegisterIndex, + variable_pointer: MemoryAddress, source: BrilligVariable, ) { match source { @@ -650,7 +652,7 @@ impl BrilligContext { BrilligVariable::BrilligArray(BrilligArray { pointer, size: _, rc }) => { self.store_instruction(variable_pointer, pointer); - let rc_pointer: RegisterIndex = self.allocate_register(); + let rc_pointer: MemoryAddress = self.allocate_register(); self.mov_instruction(rc_pointer, variable_pointer); self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 1_usize); self.store_instruction(rc_pointer, rc); @@ -664,7 +666,7 @@ impl BrilligContext { self.usize_op_in_place(size_pointer, BinaryIntOp::Add, 1_usize); self.store_instruction(size_pointer, size); - let rc_pointer: RegisterIndex = self.allocate_register(); + let rc_pointer: MemoryAddress = self.allocate_register(); self.mov_instruction(rc_pointer, variable_pointer); self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 2_usize); self.store_instruction(rc_pointer, rc); @@ -685,8 +687,8 @@ impl BrilligContext { /// For Brillig, all integer operations will overflow as its cheap. pub(crate) fn truncate_instruction( &mut self, - destination_of_truncated_value: RegisterIndex, - value_to_truncate: RegisterIndex, + destination_of_truncated_value: MemoryAddress, + value_to_truncate: MemoryAddress, bit_size: u32, ) { self.debug_show.truncate_instruction( @@ -715,11 +717,11 @@ impl BrilligContext { /// Emits a stop instruction pub(crate) fn stop_instruction(&mut self) { self.debug_show.stop_instruction(); - self.push_opcode(BrilligOpcode::Stop); + self.push_opcode(BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }); } /// Returns a register which holds the value of a constant - pub(crate) fn make_constant(&mut self, constant: Value) -> RegisterIndex { + pub(crate) fn make_constant(&mut self, constant: Value) -> MemoryAddress { let register = self.allocate_register(); self.const_instruction(register, constant); register @@ -736,9 +738,9 @@ impl BrilligContext { /// to other binary instructions. pub(crate) fn modulo_instruction( &mut self, - result_register: RegisterIndex, - left: RegisterIndex, - right: RegisterIndex, + result_register: MemoryAddress, + left: MemoryAddress, + right: MemoryAddress, bit_size: u32, signed: bool, ) { @@ -791,12 +793,12 @@ impl BrilligContext { } /// Returns the i'th register after the reserved ones - pub(crate) fn register(&self, i: usize) -> RegisterIndex { - RegisterIndex::from(ReservedRegisters::NUM_RESERVED_REGISTERS + i) + pub(crate) fn register(&self, i: usize) -> MemoryAddress { + MemoryAddress::from(ReservedRegisters::NUM_RESERVED_REGISTERS + i) } /// Saves all of the registers that have been used up until this point. - fn save_registers_of_vars(&mut self, vars: &[BrilligVariable]) -> Vec { + fn save_registers_of_vars(&mut self, vars: &[BrilligVariable]) -> Vec { // Save all of the used registers at this point in memory // because the function call will/may overwrite them. // @@ -822,7 +824,7 @@ impl BrilligContext { } /// Loads all of the registers that have been save by save_all_used_registers. - fn load_all_saved_registers(&mut self, used_registers: &[RegisterIndex]) { + fn load_all_saved_registers(&mut self, used_registers: &[MemoryAddress]) { // Load all of the used registers that we saved. // We do all the reverse operations of save_all_used_registers. // Iterate our registers in reverse @@ -839,7 +841,7 @@ impl BrilligContext { /// Utility method to perform a binary instruction with a constant value in place pub(crate) fn usize_op_in_place( &mut self, - destination: RegisterIndex, + destination: MemoryAddress, op: BinaryIntOp, constant: usize, ) { @@ -849,8 +851,8 @@ impl BrilligContext { /// Utility method to perform a binary instruction with a constant value pub(crate) fn usize_op( &mut self, - operand: RegisterIndex, - destination: RegisterIndex, + operand: MemoryAddress, + destination: MemoryAddress, op: BinaryIntOp, constant: usize, ) { @@ -863,9 +865,9 @@ impl BrilligContext { /// Utility method to perform a binary instruction with a memory address pub(crate) fn memory_op( &mut self, - lhs: RegisterIndex, - rhs: RegisterIndex, - destination: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + destination: MemoryAddress, op: BinaryIntOp, ) { self.binary_instruction( @@ -881,9 +883,9 @@ impl BrilligContext { // Move argument values to the front of the register indices. pub(crate) fn pre_call_save_registers_prep_args( &mut self, - arguments: &[RegisterIndex], + arguments: &[MemoryAddress], variables_to_save: &[BrilligVariable], - ) -> Vec { + ) -> Vec { // Save all the registers we have used to the stack. let saved_registers = self.save_registers_of_vars(variables_to_save); @@ -902,8 +904,8 @@ impl BrilligContext { // Load all the registers we have previous saved in save_registers_prep_args. pub(crate) fn post_call_prep_returns_load_registers( &mut self, - result_registers: &[RegisterIndex], - saved_registers: &[RegisterIndex], + result_registers: &[MemoryAddress], + saved_registers: &[MemoryAddress], ) { // Allocate our result registers and write into them // We assume the return values of our call are held in 0..num results register indices @@ -938,10 +940,10 @@ impl BrilligContext { /// And the radix register limb_count times to the target vector. pub(crate) fn radix_instruction( &mut self, - source: RegisterIndex, + source: MemoryAddress, target_vector: BrilligVector, - radix: RegisterIndex, - limb_count: RegisterIndex, + radix: MemoryAddress, + limb_count: MemoryAddress, big_endian: bool, ) { self.mov_instruction(target_vector.size, limb_count); @@ -951,7 +953,7 @@ impl BrilligContext { let shifted_register = self.allocate_register(); self.mov_instruction(shifted_register, source); - let modulus_register: RegisterIndex = self.allocate_register(); + let modulus_register: MemoryAddress = self.allocate_register(); self.loop_instruction(target_vector.size, |ctx, iterator_register| { // Compute the modulus @@ -1042,10 +1044,10 @@ pub(crate) mod tests { use std::vec; use acvm::acir::brillig::{ - BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapVector, RegisterIndex, - RegisterOrMemory, Value, + BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapVector, MemoryAddress, Value, + ValueOrArray, }; - use acvm::brillig_vm::{Registers, VMStatus, VM}; + use acvm::brillig_vm::{VMStatus, VM}; use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; use crate::brillig::brillig_ir::BrilligContext; @@ -1117,21 +1119,17 @@ pub(crate) mod tests { } pub(crate) fn create_and_run_vm( - memory: Vec, - param_registers: Vec, + calldata: Vec, bytecode: &[BrilligOpcode], - ) -> VM<'_, DummyBlackBoxSolver> { - let mut vm = VM::new( - Registers { inner: param_registers }, - memory, - bytecode, - vec![], - &DummyBlackBoxSolver, - ); + ) -> (VM<'_, DummyBlackBoxSolver>, usize, usize) { + let mut vm = VM::new(calldata, bytecode, vec![], &DummyBlackBoxSolver); let status = vm.process_opcodes(); - assert_eq!(status, VMStatus::Finished); - vm + if let VMStatus::Finished { return_data_offset, return_data_size } = status { + (vm, return_data_offset, return_data_size) + } else { + panic!("VM did not finish") + } } /// Test a Brillig foreign call returning a vector @@ -1150,18 +1148,18 @@ pub(crate) mod tests { let mut context = BrilligContext::new(true); let r_stack = ReservedRegisters::stack_pointer(); // Start stack pointer at 0 - context.const_instruction(r_stack, Value::from(0_usize)); - let r_input_size = RegisterIndex::from(ReservedRegisters::len()); - let r_array_ptr = RegisterIndex::from(ReservedRegisters::len() + 1); - let r_output_size = RegisterIndex::from(ReservedRegisters::len() + 2); - let r_equality = RegisterIndex::from(ReservedRegisters::len() + 3); + context.const_instruction(r_stack, Value::from(ReservedRegisters::len() + 3)); + let r_input_size = MemoryAddress::from(ReservedRegisters::len()); + let r_array_ptr = MemoryAddress::from(ReservedRegisters::len() + 1); + let r_output_size = MemoryAddress::from(ReservedRegisters::len() + 2); + let r_equality = MemoryAddress::from(ReservedRegisters::len() + 3); context.const_instruction(r_input_size, Value::from(12_usize)); // copy our stack frame to r_array_ptr context.mov_instruction(r_array_ptr, r_stack); context.foreign_call_instruction( "make_number_sequence".into(), - &[RegisterOrMemory::RegisterIndex(r_input_size)], - &[RegisterOrMemory::HeapVector(HeapVector { pointer: r_stack, size: r_output_size })], + &[ValueOrArray::MemoryAddress(r_input_size)], + &[ValueOrArray::HeapVector(HeapVector { pointer: r_stack, size: r_output_size })], ); // push stack frame by r_returned_size context.memory_op(r_stack, r_output_size, r_stack, BinaryIntOp::Add); @@ -1178,13 +1176,12 @@ pub(crate) mod tests { let bytecode = context.artifact().finish().byte_code; let number_sequence: Vec = (0_usize..12_usize).map(Value::from).collect(); let mut vm = VM::new( - Registers { inner: vec![] }, vec![], &bytecode, vec![ForeignCallResult { values: vec![ForeignCallParam::Array(number_sequence)] }], &DummyBlackBoxSolver, ); let status = vm.process_opcodes(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 9b8c3913123..437774da157 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -97,7 +97,7 @@ impl BrilligArtifact { // Replace STOP with RETURN because this is not the end of the program now. let stop_position = byte_code .iter() - .position(|opcode| matches!(opcode, BrilligOpcode::Stop)) + .position(|opcode| matches!(opcode, BrilligOpcode::Stop { .. })) .expect("Trying to link with a function that does not have a stop opcode"); byte_code[stop_position] = BrilligOpcode::Return; diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs index 46c54d55ecb..b3fe30c40b4 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs @@ -1,12 +1,12 @@ -use acvm::brillig_vm::brillig::{HeapArray, HeapVector, RegisterIndex, RegisterOrMemory}; +use acvm::brillig_vm::brillig::{HeapArray, HeapVector, MemoryAddress, ValueOrArray}; use serde::{Deserialize, Serialize}; /// The representation of a noir array in the Brillig IR #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub(crate) struct BrilligArray { - pub(crate) pointer: RegisterIndex, + pub(crate) pointer: MemoryAddress, pub(crate) size: usize, - pub(crate) rc: RegisterIndex, + pub(crate) rc: MemoryAddress, } impl BrilligArray { @@ -18,7 +18,7 @@ impl BrilligArray { 2 } - pub(crate) fn extract_registers(self) -> Vec { + pub(crate) fn extract_registers(self) -> Vec { vec![self.pointer, self.rc] } } @@ -26,9 +26,9 @@ impl BrilligArray { /// The representation of a noir slice in the Brillig IR #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub(crate) struct BrilligVector { - pub(crate) pointer: RegisterIndex, - pub(crate) size: RegisterIndex, - pub(crate) rc: RegisterIndex, + pub(crate) pointer: MemoryAddress, + pub(crate) size: MemoryAddress, + pub(crate) rc: MemoryAddress, } impl BrilligVector { @@ -40,7 +40,7 @@ impl BrilligVector { 3 } - pub(crate) fn extract_registers(self) -> Vec { + pub(crate) fn extract_registers(self) -> Vec { vec![self.pointer, self.size, self.rc] } } @@ -48,13 +48,13 @@ impl BrilligVector { /// The representation of a noir value in the Brillig IR #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub(crate) enum BrilligVariable { - Simple(RegisterIndex), + Simple(MemoryAddress), BrilligArray(BrilligArray), BrilligVector(BrilligVector), } impl BrilligVariable { - pub(crate) fn extract_register(self) -> RegisterIndex { + pub(crate) fn extract_register(self) -> MemoryAddress { match self { BrilligVariable::Simple(register_index) => register_index, _ => unreachable!("ICE: Expected register, got {self:?}"), @@ -75,7 +75,7 @@ impl BrilligVariable { } } - pub(crate) fn extract_registers(self) -> Vec { + pub(crate) fn extract_registers(self) -> Vec { match self { BrilligVariable::Simple(register_index) => vec![register_index], BrilligVariable::BrilligArray(array) => array.extract_registers(), @@ -83,16 +83,12 @@ impl BrilligVariable { } } - pub(crate) fn to_register_or_memory(self) -> RegisterOrMemory { + pub(crate) fn to_register_or_memory(self) -> ValueOrArray { match self { - BrilligVariable::Simple(register_index) => { - RegisterOrMemory::RegisterIndex(register_index) - } - BrilligVariable::BrilligArray(array) => { - RegisterOrMemory::HeapArray(array.to_heap_array()) - } + BrilligVariable::Simple(register_index) => ValueOrArray::MemoryAddress(register_index), + BrilligVariable::BrilligArray(array) => ValueOrArray::HeapArray(array.to_heap_array()), BrilligVariable::BrilligVector(vector) => { - RegisterOrMemory::HeapVector(vector.to_heap_vector()) + ValueOrArray::HeapVector(vector.to_heap_vector()) } } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index a8563dc9efe..28359f8dcea 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -3,8 +3,8 @@ use super::BrilligBinaryOp; use crate::brillig::brillig_ir::{ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE}; use acvm::acir::brillig::{ - BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapArray, HeapVector, RegisterIndex, RegisterOrMemory, - Value, + BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapArray, HeapVector, MemoryAddress, Value, + ValueOrArray, }; /// Trait for converting values into debug-friendly strings. @@ -24,7 +24,7 @@ macro_rules! default_to_string_impl { default_to_string_impl! { str usize u32 } -impl DebugToString for RegisterIndex { +impl DebugToString for MemoryAddress { fn debug_to_string(&self) -> String { if *self == ReservedRegisters::stack_pointer() { "Stack".into() @@ -112,12 +112,12 @@ impl DebugToString for Value { } } -impl DebugToString for RegisterOrMemory { +impl DebugToString for ValueOrArray { fn debug_to_string(&self) -> String { match self { - RegisterOrMemory::RegisterIndex(index) => index.debug_to_string(), - RegisterOrMemory::HeapArray(heap_array) => heap_array.debug_to_string(), - RegisterOrMemory::HeapVector(vector) => vector.debug_to_string(), + ValueOrArray::MemoryAddress(index) => index.debug_to_string(), + ValueOrArray::HeapArray(heap_array) => heap_array.debug_to_string(), + ValueOrArray::HeapVector(vector) => vector.debug_to_string(), } } } @@ -152,15 +152,15 @@ impl DebugShow { /// Emits brillig bytecode to jump to a trap condition if `condition` /// is false. - pub(crate) fn constrain_instruction(&self, condition: RegisterIndex) { + pub(crate) fn constrain_instruction(&self, condition: MemoryAddress) { debug_println!(self.enable_debug_trace, " ASSERT {} != 0", condition); } /// Processes a return instruction. - pub(crate) fn return_instruction(&self, return_registers: &[RegisterIndex]) { + pub(crate) fn return_instruction(&self, return_registers: &[MemoryAddress]) { let registers_string = return_registers .iter() - .map(RegisterIndex::debug_to_string) + .map(MemoryAddress::debug_to_string) .collect::>() .join(", "); @@ -168,32 +168,32 @@ impl DebugShow { } /// Emits a `mov` instruction. - pub(crate) fn mov_instruction(&self, destination: RegisterIndex, source: RegisterIndex) { + pub(crate) fn mov_instruction(&self, destination: MemoryAddress, source: MemoryAddress) { debug_println!(self.enable_debug_trace, " MOV {}, {}", destination, source); } /// Processes a binary instruction according `operation`. pub(crate) fn binary_instruction( &self, - lhs: RegisterIndex, - rhs: RegisterIndex, - result: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + result: MemoryAddress, operation: BrilligBinaryOp, ) { debug_println!(self.enable_debug_trace, " {} = {} {} {}", result, lhs, operation, rhs); } /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction(&self, result: RegisterIndex, constant: Value) { + pub(crate) fn const_instruction(&self, result: MemoryAddress, constant: Value) { debug_println!(self.enable_debug_trace, " CONST {} = {}", result, constant); } /// Processes a not instruction. Append with "_" as this is a high-level instruction. pub(crate) fn not_instruction( &self, - condition: RegisterIndex, + condition: MemoryAddress, bit_size: u32, - result: RegisterIndex, + result: MemoryAddress, ) { debug_println!(self.enable_debug_trace, " i{}_NOT {} = !{}", bit_size, result, condition); } @@ -202,8 +202,8 @@ impl DebugShow { pub(crate) fn foreign_call_instruction( &self, func_name: String, - inputs: &[RegisterOrMemory], - outputs: &[RegisterOrMemory], + inputs: &[ValueOrArray], + outputs: &[ValueOrArray], ) { debug_println!( self.enable_debug_trace, @@ -217,8 +217,8 @@ impl DebugShow { /// Emits a load instruction pub(crate) fn load_instruction( &self, - destination: RegisterIndex, - source_pointer: RegisterIndex, + destination: MemoryAddress, + source_pointer: MemoryAddress, ) { debug_println!(self.enable_debug_trace, " LOAD {} = *{}", destination, source_pointer); } @@ -226,8 +226,8 @@ impl DebugShow { /// Emits a store instruction pub(crate) fn store_instruction( &self, - destination_pointer: RegisterIndex, - source: RegisterIndex, + destination_pointer: MemoryAddress, + source: MemoryAddress, ) { debug_println!(self.enable_debug_trace, " STORE *{} = {}", destination_pointer, source); } @@ -240,8 +240,8 @@ impl DebugShow { /// Debug function for allocate_array_instruction pub(crate) fn allocate_array_instruction( &self, - pointer_register: RegisterIndex, - size_register: RegisterIndex, + pointer_register: MemoryAddress, + size_register: MemoryAddress, ) { debug_println!( self.enable_debug_trace, @@ -252,16 +252,16 @@ impl DebugShow { } /// Debug function for allocate_instruction - pub(crate) fn allocate_instruction(&self, pointer_register: RegisterIndex) { + pub(crate) fn allocate_instruction(&self, pointer_register: MemoryAddress) { debug_println!(self.enable_debug_trace, " ALLOCATE {} ", pointer_register); } /// Debug function for array_get pub(crate) fn array_get( &self, - array_ptr: RegisterIndex, - index: RegisterIndex, - result: RegisterIndex, + array_ptr: MemoryAddress, + index: MemoryAddress, + result: MemoryAddress, ) { debug_println!( self.enable_debug_trace, @@ -275,9 +275,9 @@ impl DebugShow { /// Debug function for array_set pub(crate) fn array_set( &self, - array_ptr: RegisterIndex, - index: RegisterIndex, - value: RegisterIndex, + array_ptr: MemoryAddress, + index: MemoryAddress, + value: MemoryAddress, ) { debug_println!(self.enable_debug_trace, " ARRAY_SET {}[{}] = {}", array_ptr, index, value); } @@ -285,9 +285,9 @@ impl DebugShow { /// Debug function for copy_array_instruction pub(crate) fn copy_array_instruction( &self, - source: RegisterIndex, - destination: RegisterIndex, - num_elements_register: RegisterIndex, + source: MemoryAddress, + destination: MemoryAddress, + num_elements_register: MemoryAddress, ) { debug_println!( self.enable_debug_trace, @@ -314,7 +314,7 @@ impl DebugShow { /// Debug function for jump_if_instruction pub(crate) fn jump_if_instruction( &self, - condition: RegisterIndex, + condition: MemoryAddress, target_label: T, ) { debug_println!( @@ -328,8 +328,8 @@ impl DebugShow { /// Debug function for cast_instruction pub(crate) fn truncate_instruction( &self, - destination: RegisterIndex, - source: RegisterIndex, + destination: MemoryAddress, + source: MemoryAddress, target_bit_size: u32, ) { debug_println!( diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index 36ca414f38e..b9d95788b80 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -1,13 +1,13 @@ -use crate::brillig::brillig_ir::ReservedRegisters; - use super::{ artifact::{BrilligArtifact, BrilligParameter}, brillig_variable::{BrilligArray, BrilligVariable}, debug_show::DebugShow, registers::BrilligRegistersContext, - BrilligContext, + BrilligContext, ReservedRegisters, }; -use acvm::acir::brillig::{Opcode as BrilligOpcode, RegisterIndex}; +use acvm::acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}; + +pub(crate) const MAX_STACK_SIZE: usize = 1024; impl BrilligContext { /// Creates an entry point artifact that will jump to the function label provided. @@ -25,87 +25,85 @@ impl BrilligContext { debug_show: DebugShow::new(false), }; - context.entry_point_instruction(arguments); + context.entry_point_instruction(&arguments, &return_parameters); context.add_external_call_instruction(target_function); - context.exit_point_instruction(return_parameters); + context.exit_point_instruction(&arguments, &return_parameters); context.artifact() } /// Adds the instructions needed to handle entry point parameters - /// The runtime will leave the parameters in the first `n` registers. - /// Arrays will be passed as pointers to the first element, with all the nested arrays flattened. - /// First, reserve the registers that contain the parameters. - /// This function also sets the starting value of the reserved registers - fn entry_point_instruction(&mut self, arguments: Vec) { - let preallocated_registers: Vec<_> = - arguments.iter().enumerate().map(|(i, _)| RegisterIndex::from(i)).collect(); - self.set_allocated_registers(preallocated_registers.clone()); - - // Then allocate and initialize the variables that will hold the parameters - let argument_variables: Vec<_> = arguments + /// The runtime will leave the parameters in calldata. + /// Arrays will be passed flattened. + fn entry_point_instruction( + &mut self, + arguments: &[BrilligParameter], + return_parameters: &[BrilligParameter], + ) { + let calldata_size = BrilligContext::flattened_tuple_size(arguments); + let return_data_size = BrilligContext::flattened_tuple_size(return_parameters); + + // Set initial value of stack pointer: MAX_STACK_SIZE + calldata_size + return_data_size + self.push_opcode(BrilligOpcode::Const { + destination: ReservedRegisters::stack_pointer(), + value: (MAX_STACK_SIZE + calldata_size + return_data_size).into(), + }); + + // Copy calldata + self.push_opcode(BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(MAX_STACK_SIZE), + size: calldata_size, + offset: 0, + }); + + // Allocate the variables for every argument: + let mut current_calldata_pointer = MAX_STACK_SIZE; + + let mut argument_variables: Vec<_> = arguments .iter() - .zip(preallocated_registers) - .map(|(argument, param_register)| match argument { + .map(|argument| match argument { BrilligParameter::Simple => { - let variable_register = self.allocate_register(); - self.mov_instruction(variable_register, param_register); - BrilligVariable::Simple(variable_register) + let simple_address = self.allocate_register(); + let var = BrilligVariable::Simple(simple_address); + self.mov_instruction(simple_address, MemoryAddress(current_calldata_pointer)); + current_calldata_pointer += 1; + var } - BrilligParameter::Array(item_types, item_count) => { - let pointer_register = self.allocate_register(); - let rc_register = self.allocate_register(); - self.mov_instruction(pointer_register, param_register); - self.const_instruction(rc_register, 1_usize.into()); - BrilligVariable::BrilligArray(BrilligArray { - pointer: pointer_register, - size: item_types.len() * item_count, + BrilligParameter::Array(_, _) => { + let pointer_to_the_array_in_calldata = + self.make_constant(current_calldata_pointer.into()); + let rc_register = self.make_constant(1_usize.into()); + let flattened_size = BrilligContext::flattened_size(argument); + let var = BrilligVariable::BrilligArray(BrilligArray { + pointer: pointer_to_the_array_in_calldata, + size: flattened_size, rc: rc_register, - }) + }); + + current_calldata_pointer += flattened_size; + var } BrilligParameter::Slice(_) => unimplemented!("Unsupported slices as parameter"), }) .collect(); - // Calculate the initial value for the stack pointer register - let size_arguments_memory: usize = arguments - .iter() - .map(|arg| match arg { - BrilligParameter::Simple => 0, - _ => BrilligContext::flattened_size(arg), - }) - .sum(); - - // Set the initial value of the stack pointer register - self.push_opcode(BrilligOpcode::Const { - destination: ReservedRegisters::stack_pointer(), - value: size_arguments_memory.into(), - }); - // Set the initial value of the previous stack pointer register - self.push_opcode(BrilligOpcode::Const { - destination: ReservedRegisters::previous_stack_pointer(), - value: 0_usize.into(), - }); - - // Deflatten the arrays - for (parameter, assigned_variable) in arguments.iter().zip(&argument_variables) { - if let BrilligParameter::Array(item_type, item_count) = parameter { - if item_type.iter().any(|param| !matches!(param, BrilligParameter::Simple)) { - let pointer_register = assigned_variable.extract_array().pointer; - let deflattened_register = - self.deflatten_array(item_type, *item_count, pointer_register); - self.mov_instruction(pointer_register, deflattened_register); + // Deflatten arrays + for (argument_variable, argument) in argument_variables.iter_mut().zip(arguments) { + if let ( + BrilligVariable::BrilligArray(array), + BrilligParameter::Array(item_type, item_count), + ) = (argument_variable, argument) + { + if BrilligContext::has_nested_arrays(item_type) { + let deflattened_address = + self.deflatten_array(item_type, array.size, array.pointer); + self.mov_instruction(array.pointer, deflattened_address); + array.size = item_type.len() * item_count; + self.deallocate_register(deflattened_address); } } } - - // Move the parameters to the first user defined registers, to follow function call convention. - for (i, register) in - argument_variables.into_iter().flat_map(|arg| arg.extract_registers()).enumerate() - { - self.mov_instruction(ReservedRegisters::user_register_index(i), register); - } } /// Computes the size of a parameter if it was flattened @@ -122,95 +120,130 @@ impl BrilligContext { } } + /// Computes the size of a parameter if it was flattened + fn flattened_tuple_size(tuple: &[BrilligParameter]) -> usize { + tuple.iter().map(BrilligContext::flattened_size).sum() + } + + /// Computes the size of a parameter if it was flattened + fn has_nested_arrays(tuple: &[BrilligParameter]) -> bool { + tuple.iter().any(|param| !matches!(param, BrilligParameter::Simple)) + } + /// Deflatten an array by recursively allocating nested arrays and copying the plain values. /// Returns the pointer to the deflattened items. fn deflatten_array( &mut self, item_type: &[BrilligParameter], item_count: usize, - flattened_array_pointer: RegisterIndex, - ) -> RegisterIndex { - let movement_register = self.allocate_register(); - let deflattened_array_pointer = self.allocate_register(); - - let target_item_size = item_type.len(); - let source_item_size: usize = item_type.iter().map(BrilligContext::flattened_size).sum(); - - self.allocate_fixed_length_array(deflattened_array_pointer, item_count * target_item_size); - - for item_index in 0..item_count { - let source_item_base_index = item_index * source_item_size; - let target_item_base_index = item_index * target_item_size; - - let mut source_offset = 0; - - for (subitem_index, subitem) in item_type.iter().enumerate() { - let source_index = - self.make_constant((source_item_base_index + source_offset).into()); - - let target_index = - self.make_constant((target_item_base_index + subitem_index).into()); - - match subitem { - BrilligParameter::Simple => { - self.array_get(flattened_array_pointer, source_index, movement_register); - self.array_set(deflattened_array_pointer, target_index, movement_register); - source_offset += 1; - } - BrilligParameter::Array(nested_array_item_type, nested_array_item_count) => { - let nested_array_pointer = self.allocate_register(); - self.mov_instruction(nested_array_pointer, flattened_array_pointer); - self.memory_op( - nested_array_pointer, - source_index, - nested_array_pointer, - acvm::brillig_vm::brillig::BinaryIntOp::Add, - ); - let deflattened_nested_array_pointer = self.deflatten_array( + flattened_array_pointer: MemoryAddress, + ) -> MemoryAddress { + if BrilligContext::has_nested_arrays(item_type) { + let movement_register = self.allocate_register(); + let deflattened_array_pointer = self.allocate_register(); + + let target_item_size = item_type.len(); + let source_item_size = BrilligContext::flattened_tuple_size(item_type); + + self.allocate_fixed_length_array( + deflattened_array_pointer, + item_count * target_item_size, + ); + + for item_index in 0..item_count { + let source_item_base_index = item_index * source_item_size; + let target_item_base_index = item_index * target_item_size; + + let mut source_offset = 0; + + for (subitem_index, subitem) in item_type.iter().enumerate() { + let source_index = + self.make_constant((source_item_base_index + source_offset).into()); + + let target_index = + self.make_constant((target_item_base_index + subitem_index).into()); + + match subitem { + BrilligParameter::Simple => { + self.array_get( + flattened_array_pointer, + source_index, + movement_register, + ); + self.array_set( + deflattened_array_pointer, + target_index, + movement_register, + ); + source_offset += 1; + } + BrilligParameter::Array( nested_array_item_type, - *nested_array_item_count, - nested_array_pointer, - ); - let reference = self.allocate_register(); - let rc = self.allocate_register(); - self.const_instruction(rc, 1_usize.into()); - - self.allocate_array_reference_instruction(reference); - self.store_variable_instruction( - reference, - BrilligVariable::BrilligArray(BrilligArray { + nested_array_item_count, + ) => { + let nested_array_pointer = self.allocate_register(); + self.mov_instruction(nested_array_pointer, flattened_array_pointer); + self.memory_op( + nested_array_pointer, + source_index, + nested_array_pointer, + acvm::brillig_vm::brillig::BinaryIntOp::Add, + ); + let deflattened_nested_array_pointer = self.deflatten_array( + nested_array_item_type, + *nested_array_item_count, + nested_array_pointer, + ); + let reference = self.allocate_register(); + let rc = self.allocate_register(); + self.const_instruction(rc, 1_usize.into()); + + self.allocate_array_reference_instruction(reference); + let array_variable = BrilligVariable::BrilligArray(BrilligArray { pointer: deflattened_nested_array_pointer, size: nested_array_item_type.len() * nested_array_item_count, rc, - }), - ); + }); + self.store_variable_instruction(reference, array_variable); - self.array_set(deflattened_array_pointer, target_index, reference); + self.array_set(deflattened_array_pointer, target_index, reference); - self.deallocate_register(nested_array_pointer); - self.deallocate_register(reference); - self.deallocate_register(rc); + self.deallocate_register(nested_array_pointer); + self.deallocate_register(reference); + array_variable + .extract_registers() + .into_iter() + .for_each(|register| self.deallocate_register(register)); - source_offset += BrilligContext::flattened_size(subitem); + source_offset += BrilligContext::flattened_size(subitem); + } + BrilligParameter::Slice(..) => unreachable!("ICE: Cannot deflatten slices"), } - BrilligParameter::Slice(..) => unreachable!("ICE: Cannot deflatten slices"), - } - self.deallocate_register(source_index); - self.deallocate_register(target_index); + self.deallocate_register(source_index); + self.deallocate_register(target_index); + } } - } - self.deallocate_register(movement_register); + self.deallocate_register(movement_register); - deflattened_array_pointer + deflattened_array_pointer + } else { + let deflattened_array_pointer = self.allocate_register(); + self.mov_instruction(deflattened_array_pointer, flattened_array_pointer); + deflattened_array_pointer + } } /// Adds the instructions needed to handle return parameters - /// The runtime expects the results in the first `n` registers. - /// Arrays are expected to be returned as pointers to the first element with all the nested arrays flattened. + /// The runtime expects the results in a contiguous memory region. + /// Arrays are expected to be returned with all the nested arrays flattened. /// However, the function called returns variables (that have extra data) and the returned arrays are deflattened. - fn exit_point_instruction(&mut self, return_parameters: Vec) { + fn exit_point_instruction( + &mut self, + arguments: &[BrilligParameter], + return_parameters: &[BrilligParameter], + ) { // First, we allocate the registers that hold the returned variables from the function call. self.set_allocated_registers(vec![]); let returned_variables: Vec<_> = return_parameters @@ -227,43 +260,45 @@ impl BrilligContext { BrilligParameter::Slice(..) => unreachable!("ICE: Cannot return slices"), }) .collect(); - // Now, we deflatten the returned arrays - for (return_param, returned_variable) in return_parameters.iter().zip(&returned_variables) { - if let BrilligParameter::Array(item_type, item_count) = return_param { - if item_type.iter().any(|item| !matches!(item, BrilligParameter::Simple)) { - let returned_pointer = returned_variable.extract_array().pointer; - let flattened_array_pointer = self.allocate_register(); - self.allocate_fixed_length_array( - flattened_array_pointer, - BrilligContext::flattened_size(return_param), + // Now, we deflatten the return data + let calldata_size = BrilligContext::flattened_tuple_size(arguments); + let return_data_size = BrilligContext::flattened_tuple_size(return_parameters); + + // Return data has a reserved space after calldata + let return_data_offset = MAX_STACK_SIZE + calldata_size; + let mut return_data_index = return_data_offset; + + for (return_param, returned_variable) in return_parameters.iter().zip(&returned_variables) { + match return_param { + BrilligParameter::Simple => { + self.mov_instruction( + MemoryAddress(return_data_index), + returned_variable.extract_register(), ); + return_data_index += 1; + } + BrilligParameter::Array(item_type, item_count) => { + let returned_pointer = returned_variable.extract_array().pointer; + let pointer_to_return_data = self.make_constant(return_data_index.into()); self.flatten_array( item_type, *item_count, - flattened_array_pointer, + pointer_to_return_data, returned_pointer, ); - self.mov_instruction(returned_pointer, flattened_array_pointer); + self.deallocate_register(pointer_to_return_data); + return_data_index += BrilligContext::flattened_size(return_param); + } + BrilligParameter::Slice(..) => { + unreachable!("ICE: Cannot return slices from brillig entrypoints") } } } - // The VM expects us to follow the calling convention of returning - // their results in the first `n` registers. So we to move the return values - // to the first `n` registers once completed. - - // Move the results to registers 0..n - for (i, returned_variable) in returned_variables.into_iter().enumerate() { - let register = match returned_variable { - BrilligVariable::Simple(register) => register, - BrilligVariable::BrilligArray(array) => array.pointer, - BrilligVariable::BrilligVector(vector) => vector.pointer, - }; - self.push_opcode(BrilligOpcode::Mov { destination: i.into(), source: register }); - } - self.push_opcode(BrilligOpcode::Stop); + + self.push_opcode(BrilligOpcode::Stop { return_data_offset, return_data_size }); } // Flattens an array by recursively copying nested arrays and regular items. @@ -271,96 +306,119 @@ impl BrilligContext { &mut self, item_type: &[BrilligParameter], item_count: usize, - flattened_array_pointer: RegisterIndex, - deflattened_array_pointer: RegisterIndex, + flattened_array_pointer: MemoryAddress, + deflattened_array_pointer: MemoryAddress, ) { - let movement_register = self.allocate_register(); - - let source_item_size = item_type.len(); - let target_item_size: usize = item_type.iter().map(BrilligContext::flattened_size).sum(); - - for item_index in 0..item_count { - let source_item_base_index = item_index * source_item_size; - let target_item_base_index = item_index * target_item_size; - - let mut target_offset = 0; - - for (subitem_index, subitem) in item_type.iter().enumerate() { - let source_index = - self.make_constant((source_item_base_index + subitem_index).into()); - let target_index = - self.make_constant((target_item_base_index + target_offset).into()); - - match subitem { - BrilligParameter::Simple => { - self.array_get(deflattened_array_pointer, source_index, movement_register); - self.array_set(flattened_array_pointer, target_index, movement_register); - target_offset += 1; - } - BrilligParameter::Array(nested_array_item_type, nested_array_item_count) => { - let nested_array_reference = self.allocate_register(); - self.array_get( - deflattened_array_pointer, - source_index, - nested_array_reference, - ); - - let nested_array_variable = BrilligVariable::BrilligArray(BrilligArray { - pointer: self.allocate_register(), - size: nested_array_item_type.len() * nested_array_item_count, - rc: self.allocate_register(), - }); - - self.load_variable_instruction( - nested_array_variable, - nested_array_reference, - ); - - let flattened_nested_array_pointer = self.allocate_register(); - - self.mov_instruction( - flattened_nested_array_pointer, - flattened_array_pointer, - ); - - self.memory_op( - flattened_nested_array_pointer, - target_index, - flattened_nested_array_pointer, - acvm::brillig_vm::brillig::BinaryIntOp::Add, - ); - - self.flatten_array( + if BrilligContext::has_nested_arrays(item_type) { + let movement_register = self.allocate_register(); + + let source_item_size = item_type.len(); + let target_item_size: usize = + item_type.iter().map(BrilligContext::flattened_size).sum(); + + for item_index in 0..item_count { + let source_item_base_index = item_index * source_item_size; + let target_item_base_index = item_index * target_item_size; + + let mut target_offset = 0; + + for (subitem_index, subitem) in item_type.iter().enumerate() { + let source_index = + self.make_constant((source_item_base_index + subitem_index).into()); + let target_index = + self.make_constant((target_item_base_index + target_offset).into()); + + match subitem { + BrilligParameter::Simple => { + self.array_get( + deflattened_array_pointer, + source_index, + movement_register, + ); + self.array_set( + flattened_array_pointer, + target_index, + movement_register, + ); + target_offset += 1; + } + BrilligParameter::Array( nested_array_item_type, - *nested_array_item_count, - flattened_nested_array_pointer, - nested_array_variable.extract_array().pointer, - ); - - self.deallocate_register(nested_array_reference); - self.deallocate_register(flattened_nested_array_pointer); - nested_array_variable - .extract_registers() - .into_iter() - .for_each(|register| self.deallocate_register(register)); - - target_offset += BrilligContext::flattened_size(subitem); + nested_array_item_count, + ) => { + let nested_array_reference = self.allocate_register(); + self.array_get( + deflattened_array_pointer, + source_index, + nested_array_reference, + ); + + let nested_array_variable = + BrilligVariable::BrilligArray(BrilligArray { + pointer: self.allocate_register(), + size: nested_array_item_type.len() * nested_array_item_count, + rc: self.allocate_register(), + }); + + self.load_variable_instruction( + nested_array_variable, + nested_array_reference, + ); + + let flattened_nested_array_pointer = self.allocate_register(); + + self.mov_instruction( + flattened_nested_array_pointer, + flattened_array_pointer, + ); + + self.memory_op( + flattened_nested_array_pointer, + target_index, + flattened_nested_array_pointer, + acvm::brillig_vm::brillig::BinaryIntOp::Add, + ); + + self.flatten_array( + nested_array_item_type, + *nested_array_item_count, + flattened_nested_array_pointer, + nested_array_variable.extract_array().pointer, + ); + + self.deallocate_register(nested_array_reference); + self.deallocate_register(flattened_nested_array_pointer); + nested_array_variable + .extract_registers() + .into_iter() + .for_each(|register| self.deallocate_register(register)); + + target_offset += BrilligContext::flattened_size(subitem); + } + BrilligParameter::Slice(..) => unreachable!("ICE: Cannot flatten slices"), } - BrilligParameter::Slice(..) => unreachable!("ICE: Cannot flatten slices"), - } - self.deallocate_register(source_index); - self.deallocate_register(target_index); + self.deallocate_register(source_index); + self.deallocate_register(target_index); + } } - } - self.deallocate_register(movement_register); + self.deallocate_register(movement_register); + } else { + let item_count = self.make_constant((item_count * item_type.len()).into()); + self.copy_array_instruction( + deflattened_array_pointer, + flattened_array_pointer, + item_count, + ); + self.deallocate_register(item_count); + } } } #[cfg(test)] mod tests { - use acvm::brillig_vm::brillig::{RegisterIndex, Value}; + use acvm::brillig_vm::brillig::Value; use crate::brillig::brillig_ir::{ artifact::BrilligParameter, @@ -370,7 +428,7 @@ mod tests { #[test] fn entry_point_with_nested_array_parameter() { - let flattened_array = vec![ + let calldata = vec![ Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), @@ -391,44 +449,19 @@ mod tests { // Allocate the parameter let array_pointer = context.allocate_register(); + let array_value = context.allocate_register(); - context.return_instruction(&[array_pointer]); + context.load_instruction(array_pointer, array_pointer); + context.load_instruction(array_pointer, array_pointer); + context.load_instruction(array_value, array_pointer); - let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm(flattened_array.clone(), vec![Value::from(0_usize)], &bytecode); - let memory = vm.get_memory(); + context.return_instruction(&[array_value]); - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(flattened_array.len())); - assert_eq!( - memory, - &vec![ - // The original flattened values - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(4_usize), - Value::from(5_usize), - Value::from(6_usize), - // The pointer to the nested reference of the first item - Value::from(12_usize), - Value::from(3_usize), - // The pointer to the nested reference of the second item - Value::from(16_usize), - Value::from(6_usize), - // The nested array of the first item - Value::from(1_usize), - Value::from(2_usize), - // The nested reference of the first item - Value::from(10_usize), - Value::from(1_usize), - // The nested array of the second item - Value::from(4_usize), - Value::from(5_usize), - // The nested reference of the second item - Value::from(14_usize), - Value::from(1_usize), - ] - ); + let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; + let (vm, return_data_offset, return_data_size) = + create_and_run_vm(calldata.clone(), &bytecode); + assert_eq!(return_data_size, 1, "Return data size is incorrect"); + assert_eq!(vm.get_memory()[return_data_offset], Value::from(1_usize)); } #[test] @@ -463,46 +496,14 @@ mod tests { context.return_instruction(&brillig_array.extract_registers()); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm(flattened_array.clone(), vec![Value::from(0_usize)], &bytecode); + let (vm, return_data_pointer, return_data_size) = + create_and_run_vm(flattened_array.clone(), &bytecode); let memory = vm.get_memory(); assert_eq!( - memory, - &vec![ - // The original flattened values - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(4_usize), - Value::from(5_usize), - Value::from(6_usize), - // The pointer to the nested reference of the first item - Value::from(12_usize), - Value::from(3_usize), - // The pointer to the nested reference of the second item - Value::from(16_usize), - Value::from(6_usize), - // The nested array of the first item - Value::from(1_usize), - Value::from(2_usize), - // The nested reference of the first item - Value::from(10_usize), - Value::from(1_usize), - // The nested array of the second item - Value::from(4_usize), - Value::from(5_usize), - // The nested reference of the second item - Value::from(14_usize), - Value::from(1_usize), - // The original flattened again - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(4_usize), - Value::from(5_usize), - Value::from(6_usize), - ] + memory[return_data_pointer..(return_data_pointer + flattened_array.len())], + flattened_array ); - assert_eq!(vm.get_registers().get(RegisterIndex(0)), 18_usize.into()); + assert_eq!(return_data_size, flattened_array.len()); } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs index e7ab1492acb..8c0e36215a9 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs @@ -1,4 +1,6 @@ -use acvm::acir::brillig::RegisterIndex; +use acvm::acir::brillig::MemoryAddress; + +use crate::brillig::brillig_ir::entry_point::MAX_STACK_SIZE; use super::ReservedRegisters; @@ -8,7 +10,7 @@ use super::ReservedRegisters; /// Each has a stack base pointer from which all stack allocations can be offset. pub(crate) struct BrilligRegistersContext { /// A free-list of registers that have been deallocated and can be used again. - deallocated_registers: Vec, + deallocated_registers: Vec, /// A usize indicating the next un-used register. next_free_register_index: usize, } @@ -23,7 +25,7 @@ impl BrilligRegistersContext { } /// Creates a new register context from a set of registers allocated previously. - pub(crate) fn from_preallocated_registers(preallocated_registers: Vec) -> Self { + pub(crate) fn from_preallocated_registers(preallocated_registers: Vec) -> Self { let next_free_register_index = preallocated_registers.iter().fold( ReservedRegisters::len(), |free_register_index, preallocated_register| { @@ -36,8 +38,8 @@ impl BrilligRegistersContext { ); let mut deallocated_registers = Vec::new(); for i in ReservedRegisters::len()..next_free_register_index { - if !preallocated_registers.contains(&RegisterIndex::from(i)) { - deallocated_registers.push(RegisterIndex::from(i)); + if !preallocated_registers.contains(&MemoryAddress::from(i)) { + deallocated_registers.push(MemoryAddress::from(i)); } } @@ -45,7 +47,7 @@ impl BrilligRegistersContext { } /// Ensures a register is allocated. - pub(crate) fn ensure_register_is_allocated(&mut self, register: RegisterIndex) { + pub(crate) fn ensure_register_is_allocated(&mut self, register: MemoryAddress) { let index = register.to_usize(); if index < self.next_free_register_index { // If it could be allocated, check if it's in the deallocated list and remove it from there @@ -53,26 +55,28 @@ impl BrilligRegistersContext { } else { // If it couldn't yet be, expand the register space. self.next_free_register_index = index + 1; + assert!(self.next_free_register_index < MAX_STACK_SIZE, "Stack too deep"); } } /// Creates a new register. - pub(crate) fn allocate_register(&mut self) -> RegisterIndex { + pub(crate) fn allocate_register(&mut self) -> MemoryAddress { // If we have a register in our free list of deallocated registers, // consume it first. This prioritizes reuse. if let Some(register) = self.deallocated_registers.pop() { return register; } // Otherwise, move to our latest register. - let register = RegisterIndex::from(self.next_free_register_index); + let register = MemoryAddress::from(self.next_free_register_index); self.next_free_register_index += 1; + assert!(self.next_free_register_index < MAX_STACK_SIZE, "Stack too deep"); register } /// Push a register to the deallocation list, ready for reuse. /// TODO(AD): currently, register deallocation is only done with immediate values. /// TODO(AD): See https://github.com/noir-lang/noir/issues/1720 - pub(crate) fn deallocate_register(&mut self, register_index: RegisterIndex) { + pub(crate) fn deallocate_register(&mut self, register_index: MemoryAddress) { assert!(!self.deallocated_registers.contains(®ister_index)); self.deallocated_registers.push(register_index); } diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 0e3076923e0..108737f1f75 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -85,6 +85,7 @@ pub fn create_circuit( enable_brillig_logging: bool, ) -> Result<(Circuit, DebugInfo, Vec, Vec, Vec), RuntimeError> { let func_sig = program.main_function_signature.clone(); + let recursive = program.recursive; let mut generated_acir = optimize_into_acir(program, enable_ssa_logging, enable_brillig_logging)?; let opcodes = generated_acir.take_opcodes(); @@ -111,6 +112,7 @@ pub fn create_circuit( public_parameters, return_values, assert_messages: assert_messages.into_iter().collect(), + recursive, }; // This converts each im::Vector in the BTreeMap to a Vec diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 13d1ad707c2..1e734941ac2 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -11,7 +11,7 @@ use acvm::acir::circuit::brillig::{BrilligInputs, BrilligOutputs}; use acvm::acir::circuit::opcodes::{BlockId, MemOp}; use acvm::acir::circuit::Opcode; use acvm::blackbox_solver; -use acvm::brillig_vm::{brillig::Value, Registers, VMStatus, VM}; +use acvm::brillig_vm::{brillig::Value, VMStatus, VM}; use acvm::{ acir::{ brillig::Opcode as BrilligOpcode, @@ -1587,23 +1587,17 @@ impl AcirContext { inputs: &[BrilligInputs], outputs_types: &[AcirType], ) -> Option> { - let (registers, memory) = execute_brillig(code, inputs)?; - - let outputs_var = vecmap(outputs_types.iter().enumerate(), |(index, output)| { - let register_value = registers.get(index.into()); - match output { - AcirType::NumericType(_) => { - let var = self.add_data(AcirVarData::Const(register_value.to_field())); - AcirValue::Var(var, output.clone()) - } - AcirType::Array(element_types, size) => { - let mem_ptr = register_value.to_usize(); - self.brillig_constant_array_output( - element_types, - *size, - &mut memory.iter().skip(mem_ptr), - ) - } + let mut memory = (execute_brillig(code, inputs)?).into_iter(); + + let outputs_var = vecmap(outputs_types.iter(), |output| match output { + AcirType::NumericType(_) => { + let var = self.add_data(AcirVarData::Const( + memory.next().expect("Missing return data").to_field(), + )); + AcirValue::Var(var, output.clone()) + } + AcirType::Array(element_types, size) => { + self.brillig_constant_array_output(element_types, *size, &mut memory) } }); @@ -1611,11 +1605,11 @@ impl AcirContext { } /// Recursively create [`AcirValue`]s for returned arrays. This is necessary because a brillig returned array can have nested arrays as elements. - fn brillig_constant_array_output<'a>( + fn brillig_constant_array_output( &mut self, element_types: &[AcirType], size: usize, - memory_iter: &mut impl Iterator, + memory_iter: &mut impl Iterator, ) -> AcirValue { let mut array_values = im::Vector::new(); for _ in 0..size { @@ -1848,42 +1842,28 @@ pub(crate) struct AcirVar(usize); /// Returns the finished state of the Brillig VM if execution can complete. /// /// Returns `None` if complete execution of the Brillig bytecode is not possible. -fn execute_brillig( - code: &[BrilligOpcode], - inputs: &[BrilligInputs], -) -> Option<(Registers, Vec)> { +fn execute_brillig(code: &[BrilligOpcode], inputs: &[BrilligInputs]) -> Option> { // Set input values - let mut input_register_values: Vec = Vec::with_capacity(inputs.len()); - let mut input_memory: Vec = Vec::new(); + let mut calldata: Vec = Vec::new(); + // Each input represents a constant or array of constants. // Iterate over each input and push it into registers and/or memory. for input in inputs { match input { BrilligInputs::Single(expr) => { - input_register_values.push(expr.to_const()?.into()); + calldata.push(expr.to_const()?.into()); } BrilligInputs::Array(expr_arr) => { // Attempt to fetch all array input values - let memory_pointer = input_memory.len(); for expr in expr_arr.iter() { - input_memory.push(expr.to_const()?.into()); + calldata.push(expr.to_const()?.into()); } - - // Push value of the array pointer as a register - input_register_values.push(Value::from(memory_pointer)); } } } // Instantiate a Brillig VM given the solved input registers and memory, along with the Brillig bytecode. - let input_registers = Registers::load(input_register_values); - let mut vm = VM::new( - input_registers, - input_memory, - code, - Vec::new(), - &blackbox_solver::StubbedBlackBoxSolver, - ); + let mut vm = VM::new(calldata, code, Vec::new(), &blackbox_solver::StubbedBlackBoxSolver); // Run the Brillig VM on these inputs, bytecode, etc! let vm_status = vm.process_opcodes(); @@ -1892,7 +1872,9 @@ fn execute_brillig( // It may be finished, in-progress, failed, or may be waiting for results of a foreign call. // If it's finished then we can omit the opcode and just write in the return values. match vm_status { - VMStatus::Finished => Some((vm.get_registers().clone(), vm.get_memory().to_vec())), + VMStatus::Finished { return_data_offset, return_data_size } => Some( + vm.get_memory()[return_data_offset..(return_data_offset + return_data_size)].to_vec(), + ), VMStatus::InProgress => unreachable!("Brillig VM has not completed execution"), VMStatus::Failure { .. } => { // TODO: Return an error stating that the brillig function failed. diff --git a/compiler/noirc_frontend/src/ast/function.rs b/compiler/noirc_frontend/src/ast/function.rs index f20fc54b101..46f0ac0fa0f 100644 --- a/compiler/noirc_frontend/src/ast/function.rs +++ b/compiler/noirc_frontend/src/ast/function.rs @@ -29,6 +29,7 @@ pub enum FunctionKind { Builtin, Normal, Oracle, + Recursive, } impl NoirFunction { @@ -106,6 +107,7 @@ impl From for NoirFunction { Some(FunctionAttribute::Foreign(_)) => FunctionKind::LowLevel, Some(FunctionAttribute::Test { .. }) => FunctionKind::Normal, Some(FunctionAttribute::Oracle(_)) => FunctionKind::Oracle, + Some(FunctionAttribute::Recursive) => FunctionKind::Recursive, None => FunctionKind::Normal, }; diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 7bd4de77e84..0752838c82e 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -84,6 +84,8 @@ pub enum ResolverError { InvalidTypeForEntryPoint { span: Span }, #[error("Nested slices are not supported")] NestedSlices { span: Span }, + #[error("#[recursive] attribute is only allowed on entry points to a program")] + MisplacedRecursiveAttribute { ident: Ident }, #[error("Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library")] LowLevelFunctionOutsideOfStdlib { ident: Ident }, } @@ -313,6 +315,18 @@ impl From for Diagnostic { "Try to use a constant sized array instead".into(), span, ), + ResolverError::MisplacedRecursiveAttribute { ident } => { + let name = &ident.0.contents; + + let mut diag = Diagnostic::simple_error( + format!("misplaced #[recursive] attribute on function {name} rather than the main function"), + "misplaced #[recursive] attribute".to_string(), + ident.0.span(), + ); + + diag.add_note("The `#[recursive]` attribute specifies to the backend whether it should use a prover which generates proofs that are friendly for recursive verification in another circuit".to_owned()); + diag + } ResolverError::LowLevelFunctionOutsideOfStdlib { ident } => Diagnostic::simple_error( "Definition of low-level function outside of standard library".into(), "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library".into(), diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 7b876244601..c273b8abc92 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -414,7 +414,7 @@ impl<'a> Resolver<'a> { FunctionKind::Builtin | FunctionKind::LowLevel | FunctionKind::Oracle => { HirFunction::empty() } - FunctionKind::Normal => { + FunctionKind::Normal | FunctionKind::Recursive => { let expr_id = self.intern_block(func.def.body); self.interner.push_expr_location(expr_id, func.def.span, self.file); HirFunction::unchecked_from_expr(expr_id) @@ -923,6 +923,12 @@ impl<'a> Resolver<'a> { { self.push_err(ResolverError::NecessaryPub { ident: func.name_ident().clone() }); } + // '#[recursive]' attribute is only allowed for entry point functions + if !self.is_entry_point_function(func) && func.kind == FunctionKind::Recursive { + self.push_err(ResolverError::MisplacedRecursiveAttribute { + ident: func.name_ident().clone(), + }); + } if !self.distinct_allowed(func) && func.def.return_distinctness != Distinctness::DuplicationAllowed diff --git a/compiler/noirc_frontend/src/hir_def/function.rs b/compiler/noirc_frontend/src/hir_def/function.rs index 9fff301f5f7..78f44696b72 100644 --- a/compiler/noirc_frontend/src/hir_def/function.rs +++ b/compiler/noirc_frontend/src/hir_def/function.rs @@ -127,7 +127,7 @@ impl FuncMeta { pub fn can_ignore_return_type(&self) -> bool { match self.kind { FunctionKind::LowLevel | FunctionKind::Builtin | FunctionKind::Oracle => true, - FunctionKind::Normal => false, + FunctionKind::Normal | FunctionKind::Recursive => false, } } diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 835a0baae3f..5d08ab03ad3 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -491,6 +491,7 @@ impl Attribute { Attribute::Function(FunctionAttribute::Oracle(name.to_string())) } ["test"] => Attribute::Function(FunctionAttribute::Test(TestScope::None)), + ["recursive"] => Attribute::Function(FunctionAttribute::Recursive), ["test", name] => { validate(name)?; let malformed_scope = @@ -541,6 +542,7 @@ pub enum FunctionAttribute { Builtin(String), Oracle(String), Test(TestScope), + Recursive, } impl FunctionAttribute { @@ -578,6 +580,7 @@ impl fmt::Display for FunctionAttribute { FunctionAttribute::Foreign(ref k) => write!(f, "#[foreign({k})]"), FunctionAttribute::Builtin(ref k) => write!(f, "#[builtin({k})]"), FunctionAttribute::Oracle(ref k) => write!(f, "#[oracle({k})]"), + FunctionAttribute::Recursive => write!(f, "#[recursive]"), } } } @@ -621,6 +624,7 @@ impl AsRef for FunctionAttribute { FunctionAttribute::Builtin(string) => string, FunctionAttribute::Oracle(string) => string, FunctionAttribute::Test { .. } => "", + FunctionAttribute::Recursive => "", } } } diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index 5172eabde46..86bfa3ea17f 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -246,6 +246,8 @@ pub struct Program { pub return_distinctness: Distinctness, pub return_location: Option, pub return_visibility: Visibility, + /// Indicates to a backend whether a SNARK-friendly prover should be used. + pub recursive: bool, } impl Program { @@ -255,6 +257,7 @@ impl Program { return_distinctness: Distinctness, return_location: Option, return_visibility: Visibility, + recursive: bool, ) -> Program { Program { functions, @@ -262,6 +265,7 @@ impl Program { return_distinctness, return_location, return_visibility, + recursive, } } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 7e06b94ca5a..e1c3f269661 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -117,6 +117,7 @@ pub fn monomorphize(main: node_interner::FuncId, interner: &NodeInterner) -> Pro meta.return_distinctness, monomorphizer.return_location, meta.return_visibility, + meta.kind == FunctionKind::Recursive, ) } @@ -195,6 +196,9 @@ impl<'interner> Monomorphizer<'interner> { _ => unreachable!("Oracle function must have an oracle attribute"), } } + FunctionKind::Recursive => { + unreachable!("Only main can be specified as recursive, which should already be checked"); + } } } } diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index b856b54f6ca..11ef12ef83e 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::ops::Deref; use arena::{Arena, Index}; use fm::FileId; @@ -1042,6 +1043,34 @@ impl NodeInterner { Ok(impl_kind) } + /// Given a `ObjectType: TraitId` pair, find all implementations without taking constraints into account or + /// applying any type bindings. Useful to look for a specific trait in a type that is used in a macro. + pub fn lookup_all_trait_implementations( + &self, + object_type: &Type, + trait_id: TraitId, + ) -> Vec<&TraitImplKind> { + let trait_impl = self.trait_implementation_map.get(&trait_id); + + trait_impl + .map(|trait_impl| { + trait_impl + .iter() + .filter_map(|(typ, impl_kind)| match &typ { + Type::Forall(_, typ) => { + if typ.deref() == object_type { + Some(impl_kind) + } else { + None + } + } + _ => None, + }) + .collect() + }) + .unwrap_or(vec![]) + } + /// Similar to `lookup_trait_implementation` but does not apply any type bindings on success. pub fn try_lookup_trait_implementation( &self, @@ -1263,7 +1292,6 @@ impl NodeInterner { force_type_check: bool, ) -> Option { let methods = self.struct_methods.get(&(id, method_name.to_owned())); - // If there is only one method, just return it immediately. // It will still be typechecked later. if !force_type_check { diff --git a/docs/docs/index.mdx b/docs/docs/index.mdx index 2cec2397051..75086ddcdde 100644 --- a/docs/docs/index.mdx +++ b/docs/docs/index.mdx @@ -34,7 +34,7 @@ Noir works differently from most ZK languages by taking a two-pronged path. Firs :::info -Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/dev_docs/contracts/main), it defaults to Aztec's Barretenberg proving backend. +Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. @@ -48,7 +48,7 @@ Noir can be used both in complex cloud-based backends and in user's smartphones, Noir Logo - Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/dev_docs/contracts/main) library. + Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. Soliditry Verifier Example diff --git a/docs/versioned_docs/version-v0.23.0/index.mdx b/docs/versioned_docs/version-v0.23.0/index.mdx index 2cec2397051..75086ddcdde 100644 --- a/docs/versioned_docs/version-v0.23.0/index.mdx +++ b/docs/versioned_docs/version-v0.23.0/index.mdx @@ -34,7 +34,7 @@ Noir works differently from most ZK languages by taking a two-pronged path. Firs :::info -Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/dev_docs/contracts/main), it defaults to Aztec's Barretenberg proving backend. +Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. @@ -48,7 +48,7 @@ Noir can be used both in complex cloud-based backends and in user's smartphones, Noir Logo - Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/dev_docs/contracts/main) library. + Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. Soliditry Verifier Example diff --git a/noir_stdlib/src/bigint.nr b/noir_stdlib/src/bigint.nr index 14790f69241..9edd59359c1 100644 --- a/noir_stdlib/src/bigint.nr +++ b/noir_stdlib/src/bigint.nr @@ -7,21 +7,21 @@ struct BigInt { impl BigInt { #[builtin(bigint_add)] - pub fn bigint_add(_self: Self, _other: BigInt) -> BigInt { + pub fn bigint_add(self, other: BigInt) -> BigInt { } #[builtin(bigint_neg)] - pub fn bigint_neg(_self: Self, _other: BigInt) -> BigInt { + pub fn bigint_neg(self, other: BigInt) -> BigInt { } #[builtin(bigint_mul)] - pub fn bigint_mul(_self: Self, _other: BigInt) -> BigInt { + pub fn bigint_mul(self, other: BigInt) -> BigInt { } #[builtin(bigint_div)] - pub fn bigint_div(_self: Self, _other: BigInt) -> BigInt { + pub fn bigint_div(self, other: BigInt) -> BigInt { } #[builtin(bigint_from_le_bytes)] - pub fn from_le_bytes(_bytes: [u8], _modulus: [u8]) -> BigInt {} + pub fn from_le_bytes(bytes: [u8], modulus: [u8]) -> BigInt {} #[builtin(bigint_to_le_bytes)] - pub fn to_le_bytes(_self: Self) -> [u8] {} + pub fn to_le_bytes(self) -> [u8] {} } impl Add for BigInt { diff --git a/test_programs/execution_success/assert_statement_recursive/Nargo.toml b/test_programs/execution_success/assert_statement_recursive/Nargo.toml new file mode 100644 index 00000000000..2a5b02cad00 --- /dev/null +++ b/test_programs/execution_success/assert_statement_recursive/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_statement_recursive" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/assert_statement_recursive/Prover.toml b/test_programs/execution_success/assert_statement_recursive/Prover.toml new file mode 100644 index 00000000000..5d1dc99124f --- /dev/null +++ b/test_programs/execution_success/assert_statement_recursive/Prover.toml @@ -0,0 +1,2 @@ +x = "3" +y = "3" diff --git a/test_programs/execution_success/assert_statement_recursive/src/main.nr b/test_programs/execution_success/assert_statement_recursive/src/main.nr new file mode 100644 index 00000000000..687a0d324ba --- /dev/null +++ b/test_programs/execution_success/assert_statement_recursive/src/main.nr @@ -0,0 +1,11 @@ +// Tests a very simple program. +// +// The features being tested is assertion +// This is the same as the `assert_statement` test except we specify +// that the backend should use a prover which will construct proofs +// friendly to recursive verification in another SNARK. +#[recursive] +fn main(x: Field, y: pub Field) { + assert(x == y, "x and y are not equal"); + assert_eq(x, y, "x and y are not equal"); +} \ No newline at end of file diff --git a/test_programs/execution_success/brillig_nested_arrays/src/main.nr b/test_programs/execution_success/brillig_nested_arrays/src/main.nr index d0a60ac0a58..5a5657246a8 100644 --- a/test_programs/execution_success/brillig_nested_arrays/src/main.nr +++ b/test_programs/execution_success/brillig_nested_arrays/src/main.nr @@ -12,11 +12,19 @@ unconstrained fn access_nested(notes: [MyNote; 2], x: Field, y: Field) -> Field notes[x].array[y] + notes[y].array[x] + notes[x].plain + notes[y].header.params[x] } -unconstrained fn create_inside_brillig(x: Field, y: Field) { +unconstrained fn create_inside_brillig() -> [MyNote; 2] { let header = Header { params: [1, 2, 3] }; let note0 = MyNote { array: [1, 2], plain: 3, header }; let note1 = MyNote { array: [4, 5], plain: 6, header }; - assert(access_nested([note0, note1], x, y) == (2 + 4 + 3 + 1)); + [note0, note1] +} + +unconstrained fn assert_inside_brillig(notes: [MyNote; 2], x: Field, y: Field) { + assert(access_nested(notes, x, y) == (2 + 4 + 3 + 1)); +} + +unconstrained fn create_and_assert_inside_brillig(x: Field, y: Field) { + assert_inside_brillig(create_inside_brillig(), x, y); } fn main(x: Field, y: Field) { @@ -24,7 +32,10 @@ fn main(x: Field, y: Field) { let note0 = MyNote { array: [1, 2], plain: 3, header }; let note1 = MyNote { array: [4, 5], plain: 6, header }; - create_inside_brillig(x, y); assert(access_nested([note0, note1], x, y) == (2 + 4 + 3 + 1)); + + let notes = create_inside_brillig(); + assert_inside_brillig(notes, x, y); + create_and_assert_inside_brillig(x, y); } diff --git a/tooling/backend_interface/src/cli/prove.rs b/tooling/backend_interface/src/cli/prove.rs index c12e516db57..c63d8afab54 100644 --- a/tooling/backend_interface/src/cli/prove.rs +++ b/tooling/backend_interface/src/cli/prove.rs @@ -13,7 +13,6 @@ use super::string_from_stderr; /// The proof will be written to the specified output file. pub(crate) struct ProveCommand { pub(crate) crs_path: PathBuf, - pub(crate) is_recursive: bool, pub(crate) bytecode_path: PathBuf, pub(crate) witness_path: PathBuf, } @@ -33,10 +32,6 @@ impl ProveCommand { .arg("-o") .arg("-"); - if self.is_recursive { - command.arg("-r"); - } - let output = command.output()?; if output.status.success() { Ok(output.stdout) @@ -61,7 +56,7 @@ fn prove_command() -> Result<(), BackendError> { std::fs::File::create(&witness_path).expect("file should be created"); let crs_path = backend.backend_directory(); - let prove_command = ProveCommand { crs_path, bytecode_path, witness_path, is_recursive: false }; + let prove_command = ProveCommand { crs_path, bytecode_path, witness_path }; let proof = prove_command.run(backend.binary_path())?; assert_eq!(proof, "proof".as_bytes()); diff --git a/tooling/backend_interface/src/cli/verify.rs b/tooling/backend_interface/src/cli/verify.rs index a31f476d84c..1a4ba50b7de 100644 --- a/tooling/backend_interface/src/cli/verify.rs +++ b/tooling/backend_interface/src/cli/verify.rs @@ -6,7 +6,6 @@ use crate::BackendError; /// to verify a proof pub(crate) struct VerifyCommand { pub(crate) crs_path: PathBuf, - pub(crate) is_recursive: bool, pub(crate) proof_path: PathBuf, pub(crate) vk_path: PathBuf, } @@ -24,10 +23,6 @@ impl VerifyCommand { .arg("-k") .arg(self.vk_path); - if self.is_recursive { - command.arg("-r"); - } - let output = command.output()?; // We currently do not distinguish between an invalid proof and an error inside the backend. @@ -64,18 +59,12 @@ fn verify_command() -> Result<(), BackendError> { write_vk_command.run(backend.binary_path())?; - let prove_command = ProveCommand { - crs_path: crs_path.clone(), - is_recursive: false, - bytecode_path, - witness_path, - }; + let prove_command = ProveCommand { crs_path: crs_path.clone(), bytecode_path, witness_path }; let proof = prove_command.run(backend.binary_path())?; write_to_file(&proof, &proof_path); - let verify_command = - VerifyCommand { crs_path, is_recursive: false, proof_path, vk_path: vk_path_output }; + let verify_command = VerifyCommand { crs_path, proof_path, vk_path: vk_path_output }; let verified = verify_command.run(backend.binary_path())?; assert!(verified); diff --git a/tooling/backend_interface/src/proof_system.rs b/tooling/backend_interface/src/proof_system.rs index 595cd7e2020..9369c91fa94 100644 --- a/tooling/backend_interface/src/proof_system.rs +++ b/tooling/backend_interface/src/proof_system.rs @@ -55,7 +55,6 @@ impl Backend { &self, circuit: &Circuit, witness_values: WitnessMap, - is_recursive: bool, ) -> Result, BackendError> { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; @@ -76,13 +75,9 @@ impl Backend { write_to_file(&serialized_circuit, &bytecode_path); // Create proof and store it in the specified path - let proof_with_public_inputs = ProveCommand { - crs_path: self.crs_directory(), - is_recursive, - bytecode_path, - witness_path, - } - .run(binary_path)?; + let proof_with_public_inputs = + ProveCommand { crs_path: self.crs_directory(), bytecode_path, witness_path } + .run(binary_path)?; let proof = bb_abstraction_leaks::remove_public_inputs( circuit.public_inputs().0.len(), @@ -97,7 +92,6 @@ impl Backend { proof: &[u8], public_inputs: WitnessMap, circuit: &Circuit, - is_recursive: bool, ) -> Result { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; @@ -127,8 +121,7 @@ impl Backend { .run(binary_path)?; // Verify the proof - VerifyCommand { crs_path: self.crs_directory(), is_recursive, proof_path, vk_path } - .run(binary_path) + VerifyCommand { crs_path: self.crs_directory(), proof_path, vk_path }.run(binary_path) } pub fn get_intermediate_proof_artifacts( diff --git a/tooling/backend_interface/src/smart_contract.rs b/tooling/backend_interface/src/smart_contract.rs index 2548079f8e3..524832c6308 100644 --- a/tooling/backend_interface/src/smart_contract.rs +++ b/tooling/backend_interface/src/smart_contract.rs @@ -56,6 +56,7 @@ mod tests { public_parameters: PublicInputs::default(), return_values: PublicInputs::default(), assert_messages: Default::default(), + recursive: false, }; let contract = get_mock_backend()?.eth_contract(&circuit)?; diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index ab807c65a46..8e5c1dacf2c 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -1,7 +1,6 @@ use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap}; -use acvm::brillig_vm::brillig::ForeignCallResult; -use acvm::brillig_vm::{brillig::Value, Registers}; +use acvm::brillig_vm::brillig::Value; use acvm::pwg::{ ACVMStatus, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, StepResult, ACVM, }; @@ -225,9 +224,6 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { let foreign_call_result = self.foreign_call_executor.execute(&foreign_call); match foreign_call_result { Ok(foreign_call_result) => { - let foreign_call_result = foreign_call_result - .get_brillig_output() - .unwrap_or(ForeignCallResult::default()); if let Some(mut solver) = self.brillig_solver.take() { solver.resolve_pending_foreign_call(foreign_call_result); self.brillig_solver = Some(solver); @@ -356,16 +352,6 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { acir_index < opcodes.len() && matches!(opcodes[acir_index], Opcode::Brillig(..)) } - pub(super) fn get_brillig_registers(&self) -> Option<&Registers> { - self.brillig_solver.as_ref().map(|solver| solver.get_registers()) - } - - pub(super) fn set_brillig_register(&mut self, register_index: usize, value: FieldElement) { - if let Some(solver) = self.brillig_solver.as_mut() { - solver.set_register(register_index, value.into()); - } - } - pub(super) fn get_brillig_memory(&self) -> Option<&[Value]> { self.brillig_solver.as_ref().map(|solver| solver.get_memory()) } @@ -446,7 +432,7 @@ mod tests { }, blackbox_solver::StubbedBlackBoxSolver, brillig_vm::brillig::{ - BinaryFieldOp, Opcode as BrilligOpcode, RegisterIndex, RegisterOrMemory, + BinaryFieldOp, MemoryAddress, Opcode as BrilligOpcode, ValueOrArray, }, }; use nargo::{artifacts::debug::DebugArtifact, ops::DefaultForeignCallExecutor}; @@ -465,16 +451,21 @@ mod tests { })], outputs: vec![], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 1, + offset: 0, + }, BrilligOpcode::Const { - destination: RegisterIndex::from(1), + destination: MemoryAddress::from(1), value: Value::from(fe_0), }, BrilligOpcode::ForeignCall { function: "clear_mock".into(), destinations: vec![], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, - BrilligOpcode::Stop, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, ], predicate: None, }; @@ -499,7 +490,7 @@ mod tests { assert_eq!(context.get_current_opcode_location(), Some(OpcodeLocation::Acir(0))); - // execute the first Brillig opcode (const) + // Execute the first Brillig opcode (calldata copy) let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Ok)); assert_eq!( @@ -507,15 +498,15 @@ mod tests { Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }) ); - // try to execute the second Brillig opcode (and resolve the foreign call) + // execute the second Brillig opcode (const) let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Ok)); assert_eq!( context.get_current_opcode_location(), - Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }) + Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }) ); - // retry the second Brillig opcode (foreign call should be finished) + // try to execute the third Brillig opcode (and resolve the foreign call) let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Ok)); assert_eq!( @@ -523,6 +514,14 @@ mod tests { Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }) ); + // retry the third Brillig opcode (foreign call should be finished) + let result = context.step_into_opcode(); + assert!(matches!(result, DebugCommandResult::Ok)); + assert_eq!( + context.get_current_opcode_location(), + Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 3 }) + ); + // last Brillig opcode let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Done)); @@ -551,13 +550,18 @@ mod tests { ], outputs: vec![BrilligOutputs::Simple(w_z)], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 2, + offset: 0, + }, BrilligOpcode::BinaryFieldOp { - destination: RegisterIndex::from(0), + destination: MemoryAddress::from(0), op: BinaryFieldOp::Add, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), }, - BrilligOpcode::Stop, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 1 }, ], predicate: None, }; @@ -615,14 +619,22 @@ mod tests { Opcode::Brillig(Brillig { inputs: vec![], outputs: vec![], - bytecode: vec![BrilligOpcode::Stop, BrilligOpcode::Stop, BrilligOpcode::Stop], + bytecode: vec![ + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + ], predicate: None, }), Opcode::MemoryInit { block_id: BlockId(0), init: vec![] }, Opcode::Brillig(Brillig { inputs: vec![], outputs: vec![], - bytecode: vec![BrilligOpcode::Stop, BrilligOpcode::Stop, BrilligOpcode::Stop], + bytecode: vec![ + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + ], predicate: None, }), Opcode::AssertZero(Expression::default()), diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 40ee6efdb86..92224ab785a 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -251,37 +251,6 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { println!("_{} = {value}", index); } - pub fn show_brillig_registers(&self) { - if !self.context.is_executing_brillig() { - println!("Not executing a Brillig block"); - return; - } - - let Some(registers) = self.context.get_brillig_registers() else { - // this can happen when just entering the Brillig block since ACVM - // would have not initialized the Brillig VM yet; in fact, the - // Brillig code may be skipped altogether - println!("Brillig VM registers not available"); - return; - }; - - for (index, value) in registers.inner.iter().enumerate() { - println!("{index} = {}", value.to_field()); - } - } - - pub fn set_brillig_register(&mut self, index: usize, value: String) { - let Some(field_value) = FieldElement::try_from_str(&value) else { - println!("Invalid value: {value}"); - return; - }; - if !self.context.is_executing_brillig() { - println!("Not executing a Brillig block"); - return; - } - self.context.set_brillig_register(index, field_value); - } - pub fn show_brillig_memory(&self) { if !self.context.is_executing_brillig() { println!("Not executing a Brillig block"); @@ -445,26 +414,6 @@ pub fn run( } }, ) - .add( - "registers", - command! { - "show Brillig registers (valid when executing a Brillig block)", - () => || { - ref_context.borrow().show_brillig_registers(); - Ok(CommandStatus::Done) - } - }, - ) - .add( - "regset", - command! { - "update a Brillig register with the given value", - (index: usize, value: String) => |index, value| { - ref_context.borrow_mut().set_brillig_register(index, value); - Ok(CommandStatus::Done) - } - }, - ) .add( "memory", command! { diff --git a/tooling/nargo_cli/src/cli/prove_cmd.rs b/tooling/nargo_cli/src/cli/prove_cmd.rs index 1d20e97af85..cc39b0535bc 100644 --- a/tooling/nargo_cli/src/cli/prove_cmd.rs +++ b/tooling/nargo_cli/src/cli/prove_cmd.rs @@ -138,12 +138,11 @@ pub(crate) fn prove_package( Format::Toml, )?; - let proof = backend.prove(&compiled_program.circuit, solved_witness, false)?; + let proof = backend.prove(&compiled_program.circuit, solved_witness)?; if check_proof { let public_inputs = public_abi.encode(&public_inputs, return_value)?; - let valid_proof = - backend.verify(&proof, public_inputs, &compiled_program.circuit, false)?; + let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.circuit)?; if !valid_proof { return Err(CliError::InvalidProof("".into())); diff --git a/tooling/nargo_cli/src/cli/verify_cmd.rs b/tooling/nargo_cli/src/cli/verify_cmd.rs index ea4aaa051bb..66b88a22f2a 100644 --- a/tooling/nargo_cli/src/cli/verify_cmd.rs +++ b/tooling/nargo_cli/src/cli/verify_cmd.rs @@ -102,7 +102,7 @@ fn verify_package( let proof = load_hex_data(&proof_path)?; - let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.circuit, false)?; + let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.circuit)?; if valid_proof { Ok(()) diff --git a/tooling/noir_js/src/program.ts b/tooling/noir_js/src/program.ts index 809943727eb..8d80ec3a247 100644 --- a/tooling/noir_js/src/program.ts +++ b/tooling/noir_js/src/program.ts @@ -67,13 +67,13 @@ export class Noir { * * @example * ```typescript - * async generateFinalProof(input) + * async generateProof(input) * ``` * */ - async generateFinalProof(inputs: InputMap, foreignCallHandler?: ForeignCallHandler): Promise { + async generateProof(inputs: InputMap, foreignCallHandler?: ForeignCallHandler): Promise { const { witness } = await this.execute(inputs, foreignCallHandler); - return this.getBackend().generateFinalProof(witness); + return this.getBackend().generateProof(witness); } /** @@ -84,11 +84,11 @@ export class Noir { * * @example * ```typescript - * async verifyFinalProof(proof) + * async verifyProof(proof) * ``` * */ - async verifyFinalProof(proofData: ProofData): Promise { - return this.getBackend().verifyFinalProof(proofData); + async verifyProof(proofData: ProofData): Promise { + return this.getBackend().verifyProof(proofData); } } diff --git a/tooling/noir_js/test/node/e2e.test.ts b/tooling/noir_js/test/node/e2e.test.ts index 33d64377b06..8921314e8ea 100644 --- a/tooling/noir_js/test/node/e2e.test.ts +++ b/tooling/noir_js/test/node/e2e.test.ts @@ -21,10 +21,10 @@ it('end-to-end proof creation and verification (outer)', async () => { // // Proof creation const prover = new Backend(assert_lt_program); - const proof = await prover.generateFinalProof(witness); + const proof = await prover.generateProof(witness); // Proof verification - const isValid = await prover.verifyFinalProof(proof); + const isValid = await prover.verifyProof(proof); expect(isValid).to.be.true; }); @@ -40,13 +40,14 @@ it('end-to-end proof creation and verification (outer) -- Program API', async () // Initialize program const program = new Noir(assert_lt_program, backend); // Generate proof - const proof = await program.generateFinalProof(inputs); + const proof = await program.generateProof(inputs); // Proof verification - const isValid = await program.verifyFinalProof(proof); + const isValid = await program.verifyProof(proof); expect(isValid).to.be.true; }); +// TODO: maybe switch to using assert_statement_recursive here to test both options it('end-to-end proof creation and verification (inner)', async () => { // Noir.Js part const inputs = { @@ -62,10 +63,10 @@ it('end-to-end proof creation and verification (inner)', async () => { // // Proof creation const prover = new Backend(assert_lt_program); - const proof = await prover.generateIntermediateProof(witness); + const proof = await prover.generateProof(witness); // Proof verification - const isValid = await prover.verifyIntermediateProof(proof); + const isValid = await prover.verifyProof(proof); expect(isValid).to.be.true; }); @@ -83,10 +84,10 @@ it('end-to-end proving and verification with different instances', async () => { // bb.js part const prover = new Backend(assert_lt_program); - const proof = await prover.generateFinalProof(witness); + const proof = await prover.generateProof(witness); const verifier = new Backend(assert_lt_program); - const proof_is_valid = await verifier.verifyFinalProof(proof); + const proof_is_valid = await verifier.verifyProof(proof); expect(proof_is_valid).to.be.true; }); @@ -115,14 +116,14 @@ it('[BUG] -- bb.js null function or function signature mismatch (outer-inner) ', const prover = new Backend(assert_lt_program); // Create a proof using both proving systems, the majority of the time // one would only use outer proofs. - const proofOuter = await prover.generateFinalProof(witness); - const _proofInner = await prover.generateIntermediateProof(witness); + const proofOuter = await prover.generateProof(witness); + const _proofInner = await prover.generateProof(witness); // Proof verification // - const isValidOuter = await prover.verifyFinalProof(proofOuter); + const isValidOuter = await prover.verifyProof(proofOuter); expect(isValidOuter).to.be.true; // We can also try verifying an inner proof and it will fail. - const isValidInner = await prover.verifyIntermediateProof(_proofInner); + const isValidInner = await prover.verifyProof(_proofInner); expect(isValidInner).to.be.true; }); diff --git a/tooling/noir_js_backend_barretenberg/src/index.ts b/tooling/noir_js_backend_barretenberg/src/index.ts index 61094a3451f..4d5b6389404 100644 --- a/tooling/noir_js_backend_barretenberg/src/index.ts +++ b/tooling/noir_js_backend_barretenberg/src/index.ts @@ -47,47 +47,15 @@ export class BarretenbergBackend implements Backend { } } - /** - * Generate a final proof. This is the proof for the circuit which will verify - * intermediate proofs and or can be seen as the proof created for regular circuits. - */ - async generateFinalProof(decompressedWitness: Uint8Array): Promise { - // The settings for this proof are the same as the settings for a "normal" proof - // i.e. one that is not in the recursive setting. - const makeEasyToVerifyInCircuit = false; - return this.generateProof(decompressedWitness, makeEasyToVerifyInCircuit); - } - - /** - * Generates an intermediate proof. This is the proof that can be verified - * in another circuit. - * - * This is sometimes referred to as a recursive proof. - * We avoid this terminology as the only property of this proof - * that matters is the fact that it is easy to verify in another circuit. - * We _could_ choose to verify this proof outside of a circuit just as easily. - * - * @example - * ```typescript - * const intermediateProof = await backend.generateIntermediateProof(witness); - * ``` - */ - async generateIntermediateProof(witness: Uint8Array): Promise { - // We set `makeEasyToVerifyInCircuit` to true, which will tell the backend to - // generate the proof using components that will make the proof - // easier to verify in a circuit. - const makeEasyToVerifyInCircuit = true; - return this.generateProof(witness, makeEasyToVerifyInCircuit); - } - - /** @ignore */ - async generateProof(compressedWitness: Uint8Array, makeEasyToVerifyInCircuit: boolean): Promise { + /** @description Generates a proof */ + async generateProof(compressedWitness: Uint8Array): Promise { await this.instantiate(); + // TODO: Change once `@aztec/bb.js` version is updated to use methods without isRecursive flag const proofWithPublicInputs = await this.api.acirCreateProof( this.acirComposer, this.acirUncompressedBytecode, gunzip(compressedWitness), - makeEasyToVerifyInCircuit, + false, ); const splitIndex = proofWithPublicInputs.length - numBytesInProofWithoutPublicInputs; @@ -105,17 +73,17 @@ export class BarretenbergBackend implements Backend { * Instead of passing the proof and verification key as a byte array, we pass them * as fields which makes it cheaper to verify in a circuit. * - * The proof that is passed here will have been created using the `generateIntermediateProof` - * method. + * The proof that is passed here will have been created using a circuit + * that has the #[recursive] attribute on its `main` method. * * The number of public inputs denotes how many public inputs are in the inner proof. * * @example * ```typescript - * const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); + * const artifacts = await backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); * ``` */ - async generateIntermediateProofArtifacts( + async generateRecursiveProofArtifacts( proofData: ProofData, numOfPublicInputs = 0, ): Promise<{ @@ -143,31 +111,13 @@ export class BarretenbergBackend implements Backend { }; } - async verifyFinalProof(proofData: ProofData): Promise { + /** @description Verifies a proof */ + async verifyProof(proofData: ProofData): Promise { const proof = reconstructProofWithPublicInputs(proofData); - const makeEasyToVerifyInCircuit = false; - const verified = await this.verifyProof(proof, makeEasyToVerifyInCircuit); - return verified; - } - - /** - * - * @example - * ```typescript - * const isValidIntermediate = await backend.verifyIntermediateProof(proof); - * ``` - */ - async verifyIntermediateProof(proofData: ProofData): Promise { - const proof = reconstructProofWithPublicInputs(proofData); - const makeEasyToVerifyInCircuit = true; - return this.verifyProof(proof, makeEasyToVerifyInCircuit); - } - - /** @ignore */ - async verifyProof(proof: Uint8Array, makeEasyToVerifyInCircuit: boolean): Promise { await this.instantiate(); await this.api.acirInitVerificationKey(this.acirComposer); - return await this.api.acirVerifyProof(this.acirComposer, proof, makeEasyToVerifyInCircuit); + // TODO: Change once `@aztec/bb.js` version is updated to use methods without isRecursive flag + return await this.api.acirVerifyProof(this.acirComposer, proof, false); } async destroy(): Promise { diff --git a/tooling/noir_js_types/src/types.ts b/tooling/noir_js_types/src/types.ts index ee4921bd606..84148c5d855 100644 --- a/tooling/noir_js_types/src/types.ts +++ b/tooling/noir_js_types/src/types.ts @@ -4,18 +4,14 @@ export { Abi, WitnessMap } from '@noir-lang/noirc_abi'; export interface Backend { /** - * @description Generates a final proof (not meant to be verified in another circuit) */ - generateFinalProof(decompressedWitness: Uint8Array): Promise; - - /** - * @description Generates an intermediate proof (meant to be verified in another circuit) */ - generateIntermediateProof(decompressedWitness: Uint8Array): Promise; + * @description Generates a proof */ + generateProof(decompressedWitness: Uint8Array): Promise; /** * * @description Retrieves the artifacts from a proof in the Field format */ - generateIntermediateProofArtifacts( + generateRecursiveProofArtifacts( proofData: ProofData, numOfPublicInputs: number, ): Promise<{ @@ -28,11 +24,8 @@ export interface Backend { }>; /** - * @description Verifies a final proof */ - verifyFinalProof(proofData: ProofData): Promise; - - /** @description Verifies an intermediate proof */ - verifyIntermediateProof(proofData: ProofData): Promise; + * @description Verifies a proof */ + verifyProof(proofData: ProofData): Promise; /** * @description Destroys the backend */