diff --git a/.changes/20260423_cardano_rpc_search_utxos.yml b/.changes/20260423_cardano_rpc_search_utxos.yml new file mode 100644 index 0000000000..924b3d0fcc --- /dev/null +++ b/.changes/20260423_cardano_rpc_search_utxos.yml @@ -0,0 +1,6 @@ +project: cardano-rpc +pr: 1123 +kind: + - feature +description: | + Add searchUtxos gRPC method to the UTxO RPC query service, implementing predicate-based UTxO filtering with address, asset, and boolean combinators, plus cursor-based pagination. diff --git a/cardano-rpc/cardano-rpc.cabal b/cardano-rpc/cardano-rpc.cabal index a8b74026c8..9f7fc91c20 100644 --- a/cardano-rpc/cardano-rpc.cabal +++ b/cardano-rpc/cardano-rpc.cabal @@ -58,6 +58,7 @@ library Cardano.Rpc.Server.Internal.Monad Cardano.Rpc.Server.Internal.Node Cardano.Rpc.Server.Internal.Tracing + Cardano.Rpc.Server.Internal.UtxoRpc.Predicate Cardano.Rpc.Server.Internal.UtxoRpc.Query Cardano.Rpc.Server.Internal.UtxoRpc.Submit Cardano.Rpc.Server.Internal.UtxoRpc.Type @@ -121,6 +122,7 @@ test-suite cardano-rpc-test type: exitcode-stdio-1.0 build-depends: base, + bytestring, cardano-api, cardano-api:gen, cardano-ledger-api, @@ -130,9 +132,11 @@ test-suite cardano-rpc-test containers, hedgehog, hedgehog-extras, + proto-lens, rio, tasty, tasty-hedgehog, + text, time, ghc-options: @@ -142,6 +146,8 @@ test-suite cardano-rpc-test build-tool-depends: tasty-discover:tasty-discover other-modules: + Test.Cardano.Rpc.Pagination + Test.Cardano.Rpc.Predicate Test.Cardano.Rpc.ProtocolParameters Test.Cardano.Rpc.TxOutput Test.Cardano.Rpc.Type diff --git a/cardano-rpc/gen/Proto/Utxorpc/V1beta/Cardano/Cardano.hs b/cardano-rpc/gen/Proto/Utxorpc/V1beta/Cardano/Cardano.hs index 3cb3e14087..105e4e54de 100644 --- a/cardano-rpc/gen/Proto/Utxorpc/V1beta/Cardano/Cardano.hs +++ b/cardano-rpc/gen/Proto/Utxorpc/V1beta/Cardano/Cardano.hs @@ -110,12 +110,15 @@ import qualified Data.ProtoLens.Runtime.Text.Read as Text.Read {- | Fields : * 'Proto.Utxorpc.V1beta.Cardano.Cardano_Fields.exactAddress' @:: Lens' AddressPattern Data.ByteString.ByteString@ + * 'Proto.Utxorpc.V1beta.Cardano.Cardano_Fields.maybe'exactAddress' @:: Lens' AddressPattern (Prelude.Maybe Data.ByteString.ByteString)@ * 'Proto.Utxorpc.V1beta.Cardano.Cardano_Fields.paymentPart' @:: Lens' AddressPattern Data.ByteString.ByteString@ - * 'Proto.Utxorpc.V1beta.Cardano.Cardano_Fields.delegationPart' @:: Lens' AddressPattern Data.ByteString.ByteString@ -} + * 'Proto.Utxorpc.V1beta.Cardano.Cardano_Fields.maybe'paymentPart' @:: Lens' AddressPattern (Prelude.Maybe Data.ByteString.ByteString)@ + * 'Proto.Utxorpc.V1beta.Cardano.Cardano_Fields.delegationPart' @:: Lens' AddressPattern Data.ByteString.ByteString@ + * 'Proto.Utxorpc.V1beta.Cardano.Cardano_Fields.maybe'delegationPart' @:: Lens' AddressPattern (Prelude.Maybe Data.ByteString.ByteString)@ -} data AddressPattern - = AddressPattern'_constructor {_AddressPattern'exactAddress :: !Data.ByteString.ByteString, - _AddressPattern'paymentPart :: !Data.ByteString.ByteString, - _AddressPattern'delegationPart :: !Data.ByteString.ByteString, + = AddressPattern'_constructor {_AddressPattern'exactAddress :: !(Prelude.Maybe Data.ByteString.ByteString), + _AddressPattern'paymentPart :: !(Prelude.Maybe Data.ByteString.ByteString), + _AddressPattern'delegationPart :: !(Prelude.Maybe Data.ByteString.ByteString), _AddressPattern'_unknownFields :: !Data.ProtoLens.FieldSet} deriving stock (Prelude.Eq, Prelude.Ord) instance Prelude.Show AddressPattern where @@ -125,6 +128,13 @@ instance Prelude.Show AddressPattern where (Prelude.showString (Data.ProtoLens.showMessageShort __x) (Prelude.showChar '}' __s)) instance Data.ProtoLens.Field.HasField AddressPattern "exactAddress" Data.ByteString.ByteString where + fieldOf _ + = (Prelude..) + (Lens.Family2.Unchecked.lens + _AddressPattern'exactAddress + (\ x__ y__ -> x__ {_AddressPattern'exactAddress = y__})) + (Data.ProtoLens.maybeLens Data.ProtoLens.fieldDefault) +instance Data.ProtoLens.Field.HasField AddressPattern "maybe'exactAddress" (Prelude.Maybe Data.ByteString.ByteString) where fieldOf _ = (Prelude..) (Lens.Family2.Unchecked.lens @@ -132,6 +142,13 @@ instance Data.ProtoLens.Field.HasField AddressPattern "exactAddress" Data.ByteSt (\ x__ y__ -> x__ {_AddressPattern'exactAddress = y__})) Prelude.id instance Data.ProtoLens.Field.HasField AddressPattern "paymentPart" Data.ByteString.ByteString where + fieldOf _ + = (Prelude..) + (Lens.Family2.Unchecked.lens + _AddressPattern'paymentPart + (\ x__ y__ -> x__ {_AddressPattern'paymentPart = y__})) + (Data.ProtoLens.maybeLens Data.ProtoLens.fieldDefault) +instance Data.ProtoLens.Field.HasField AddressPattern "maybe'paymentPart" (Prelude.Maybe Data.ByteString.ByteString) where fieldOf _ = (Prelude..) (Lens.Family2.Unchecked.lens @@ -139,6 +156,13 @@ instance Data.ProtoLens.Field.HasField AddressPattern "paymentPart" Data.ByteStr (\ x__ y__ -> x__ {_AddressPattern'paymentPart = y__})) Prelude.id instance Data.ProtoLens.Field.HasField AddressPattern "delegationPart" Data.ByteString.ByteString where + fieldOf _ + = (Prelude..) + (Lens.Family2.Unchecked.lens + _AddressPattern'delegationPart + (\ x__ y__ -> x__ {_AddressPattern'delegationPart = y__})) + (Data.ProtoLens.maybeLens Data.ProtoLens.fieldDefault) +instance Data.ProtoLens.Field.HasField AddressPattern "maybe'delegationPart" (Prelude.Maybe Data.ByteString.ByteString) where fieldOf _ = (Prelude..) (Lens.Family2.Unchecked.lens @@ -150,10 +174,13 @@ instance Data.ProtoLens.Message AddressPattern where = Data.Text.pack "utxorpc.v1beta.cardano.AddressPattern" packedMessageDescriptor _ = "\n\ - \\SOAddressPattern\DC2#\n\ - \\rexact_address\CAN\SOH \SOH(\fR\fexactAddress\DC2!\n\ - \\fpayment_part\CAN\STX \SOH(\fR\vpaymentPart\DC2'\n\ - \\SIdelegation_part\CAN\ETX \SOH(\fR\SOdelegationPart" + \\SOAddressPattern\DC2(\n\ + \\rexact_address\CAN\SOH \SOH(\fH\NULR\fexactAddress\136\SOH\SOH\DC2&\n\ + \\fpayment_part\CAN\STX \SOH(\fH\SOHR\vpaymentPart\136\SOH\SOH\DC2,\n\ + \\SIdelegation_part\CAN\ETX \SOH(\fH\STXR\SOdelegationPart\136\SOH\SOHB\DLE\n\ + \\SO_exact_addressB\SI\n\ + \\r_payment_partB\DC2\n\ + \\DLE_delegation_part" packedFileDescriptor _ = packedFileDescriptor fieldsByTag = let @@ -162,27 +189,24 @@ instance Data.ProtoLens.Message AddressPattern where "exact_address" (Data.ProtoLens.ScalarField Data.ProtoLens.BytesField :: Data.ProtoLens.FieldTypeDescriptor Data.ByteString.ByteString) - (Data.ProtoLens.PlainField - Data.ProtoLens.Optional - (Data.ProtoLens.Field.field @"exactAddress")) :: + (Data.ProtoLens.OptionalField + (Data.ProtoLens.Field.field @"maybe'exactAddress")) :: Data.ProtoLens.FieldDescriptor AddressPattern paymentPart__field_descriptor = Data.ProtoLens.FieldDescriptor "payment_part" (Data.ProtoLens.ScalarField Data.ProtoLens.BytesField :: Data.ProtoLens.FieldTypeDescriptor Data.ByteString.ByteString) - (Data.ProtoLens.PlainField - Data.ProtoLens.Optional - (Data.ProtoLens.Field.field @"paymentPart")) :: + (Data.ProtoLens.OptionalField + (Data.ProtoLens.Field.field @"maybe'paymentPart")) :: Data.ProtoLens.FieldDescriptor AddressPattern delegationPart__field_descriptor = Data.ProtoLens.FieldDescriptor "delegation_part" (Data.ProtoLens.ScalarField Data.ProtoLens.BytesField :: Data.ProtoLens.FieldTypeDescriptor Data.ByteString.ByteString) - (Data.ProtoLens.PlainField - Data.ProtoLens.Optional - (Data.ProtoLens.Field.field @"delegationPart")) :: + (Data.ProtoLens.OptionalField + (Data.ProtoLens.Field.field @"maybe'delegationPart")) :: Data.ProtoLens.FieldDescriptor AddressPattern in Data.Map.fromList @@ -195,9 +219,9 @@ instance Data.ProtoLens.Message AddressPattern where (\ x__ y__ -> x__ {_AddressPattern'_unknownFields = y__}) defMessage = AddressPattern'_constructor - {_AddressPattern'exactAddress = Data.ProtoLens.fieldDefault, - _AddressPattern'paymentPart = Data.ProtoLens.fieldDefault, - _AddressPattern'delegationPart = Data.ProtoLens.fieldDefault, + {_AddressPattern'exactAddress = Prelude.Nothing, + _AddressPattern'paymentPart = Prelude.Nothing, + _AddressPattern'delegationPart = Prelude.Nothing, _AddressPattern'_unknownFields = []} parseMessage = let @@ -260,54 +284,50 @@ instance Data.ProtoLens.Message AddressPattern where buildMessage = \ _x -> (Data.Monoid.<>) - (let - _v - = Lens.Family2.view (Data.ProtoLens.Field.field @"exactAddress") _x - in - if (Prelude.==) _v Data.ProtoLens.fieldDefault then - Data.Monoid.mempty - else - (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt 10) - ((\ bs - -> (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt - (Prelude.fromIntegral (Data.ByteString.length bs))) - (Data.ProtoLens.Encoding.Bytes.putBytes bs)) - _v)) + (case + Lens.Family2.view + (Data.ProtoLens.Field.field @"maybe'exactAddress") _x + of + Prelude.Nothing -> Data.Monoid.mempty + (Prelude.Just _v) + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt 10) + ((\ bs + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt + (Prelude.fromIntegral (Data.ByteString.length bs))) + (Data.ProtoLens.Encoding.Bytes.putBytes bs)) + _v)) ((Data.Monoid.<>) - (let - _v - = Lens.Family2.view (Data.ProtoLens.Field.field @"paymentPart") _x - in - if (Prelude.==) _v Data.ProtoLens.fieldDefault then - Data.Monoid.mempty - else - (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt 18) - ((\ bs - -> (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt - (Prelude.fromIntegral (Data.ByteString.length bs))) - (Data.ProtoLens.Encoding.Bytes.putBytes bs)) - _v)) + (case + Lens.Family2.view + (Data.ProtoLens.Field.field @"maybe'paymentPart") _x + of + Prelude.Nothing -> Data.Monoid.mempty + (Prelude.Just _v) + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt 18) + ((\ bs + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt + (Prelude.fromIntegral (Data.ByteString.length bs))) + (Data.ProtoLens.Encoding.Bytes.putBytes bs)) + _v)) ((Data.Monoid.<>) - (let - _v - = Lens.Family2.view - (Data.ProtoLens.Field.field @"delegationPart") _x - in - if (Prelude.==) _v Data.ProtoLens.fieldDefault then - Data.Monoid.mempty - else - (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt 26) - ((\ bs - -> (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt - (Prelude.fromIntegral (Data.ByteString.length bs))) - (Data.ProtoLens.Encoding.Bytes.putBytes bs)) - _v)) + (case + Lens.Family2.view + (Data.ProtoLens.Field.field @"maybe'delegationPart") _x + of + Prelude.Nothing -> Data.Monoid.mempty + (Prelude.Just _v) + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt 26) + ((\ bs + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt + (Prelude.fromIntegral (Data.ByteString.length bs))) + (Data.ProtoLens.Encoding.Bytes.putBytes bs)) + _v)) (Data.ProtoLens.Encoding.Wire.buildFieldSet (Lens.Family2.view Data.ProtoLens.unknownFields _x)))) instance Control.DeepSeq.NFData AddressPattern where @@ -636,10 +656,12 @@ instance Control.DeepSeq.NFData Asset where {- | Fields : * 'Proto.Utxorpc.V1beta.Cardano.Cardano_Fields.policyId' @:: Lens' AssetPattern Data.ByteString.ByteString@ - * 'Proto.Utxorpc.V1beta.Cardano.Cardano_Fields.assetName' @:: Lens' AssetPattern Data.ByteString.ByteString@ -} + * 'Proto.Utxorpc.V1beta.Cardano.Cardano_Fields.maybe'policyId' @:: Lens' AssetPattern (Prelude.Maybe Data.ByteString.ByteString)@ + * 'Proto.Utxorpc.V1beta.Cardano.Cardano_Fields.assetName' @:: Lens' AssetPattern Data.ByteString.ByteString@ + * 'Proto.Utxorpc.V1beta.Cardano.Cardano_Fields.maybe'assetName' @:: Lens' AssetPattern (Prelude.Maybe Data.ByteString.ByteString)@ -} data AssetPattern - = AssetPattern'_constructor {_AssetPattern'policyId :: !Data.ByteString.ByteString, - _AssetPattern'assetName :: !Data.ByteString.ByteString, + = AssetPattern'_constructor {_AssetPattern'policyId :: !(Prelude.Maybe Data.ByteString.ByteString), + _AssetPattern'assetName :: !(Prelude.Maybe Data.ByteString.ByteString), _AssetPattern'_unknownFields :: !Data.ProtoLens.FieldSet} deriving stock (Prelude.Eq, Prelude.Ord) instance Prelude.Show AssetPattern where @@ -649,6 +671,13 @@ instance Prelude.Show AssetPattern where (Prelude.showString (Data.ProtoLens.showMessageShort __x) (Prelude.showChar '}' __s)) instance Data.ProtoLens.Field.HasField AssetPattern "policyId" Data.ByteString.ByteString where + fieldOf _ + = (Prelude..) + (Lens.Family2.Unchecked.lens + _AssetPattern'policyId + (\ x__ y__ -> x__ {_AssetPattern'policyId = y__})) + (Data.ProtoLens.maybeLens Data.ProtoLens.fieldDefault) +instance Data.ProtoLens.Field.HasField AssetPattern "maybe'policyId" (Prelude.Maybe Data.ByteString.ByteString) where fieldOf _ = (Prelude..) (Lens.Family2.Unchecked.lens @@ -656,6 +685,13 @@ instance Data.ProtoLens.Field.HasField AssetPattern "policyId" Data.ByteString.B (\ x__ y__ -> x__ {_AssetPattern'policyId = y__})) Prelude.id instance Data.ProtoLens.Field.HasField AssetPattern "assetName" Data.ByteString.ByteString where + fieldOf _ + = (Prelude..) + (Lens.Family2.Unchecked.lens + _AssetPattern'assetName + (\ x__ y__ -> x__ {_AssetPattern'assetName = y__})) + (Data.ProtoLens.maybeLens Data.ProtoLens.fieldDefault) +instance Data.ProtoLens.Field.HasField AssetPattern "maybe'assetName" (Prelude.Maybe Data.ByteString.ByteString) where fieldOf _ = (Prelude..) (Lens.Family2.Unchecked.lens @@ -667,10 +703,13 @@ instance Data.ProtoLens.Message AssetPattern where = Data.Text.pack "utxorpc.v1beta.cardano.AssetPattern" packedMessageDescriptor _ = "\n\ - \\fAssetPattern\DC2\ESC\n\ - \\tpolicy_id\CAN\SOH \SOH(\fR\bpolicyId\DC2\GS\n\ + \\fAssetPattern\DC2 \n\ + \\tpolicy_id\CAN\SOH \SOH(\fH\NULR\bpolicyId\136\SOH\SOH\DC2\"\n\ + \\n\ + \asset_name\CAN\STX \SOH(\fH\SOHR\tassetName\136\SOH\SOHB\f\n\ \\n\ - \asset_name\CAN\STX \SOH(\fR\tassetName" + \_policy_idB\r\n\ + \\v_asset_name" packedFileDescriptor _ = packedFileDescriptor fieldsByTag = let @@ -679,18 +718,16 @@ instance Data.ProtoLens.Message AssetPattern where "policy_id" (Data.ProtoLens.ScalarField Data.ProtoLens.BytesField :: Data.ProtoLens.FieldTypeDescriptor Data.ByteString.ByteString) - (Data.ProtoLens.PlainField - Data.ProtoLens.Optional - (Data.ProtoLens.Field.field @"policyId")) :: + (Data.ProtoLens.OptionalField + (Data.ProtoLens.Field.field @"maybe'policyId")) :: Data.ProtoLens.FieldDescriptor AssetPattern assetName__field_descriptor = Data.ProtoLens.FieldDescriptor "asset_name" (Data.ProtoLens.ScalarField Data.ProtoLens.BytesField :: Data.ProtoLens.FieldTypeDescriptor Data.ByteString.ByteString) - (Data.ProtoLens.PlainField - Data.ProtoLens.Optional - (Data.ProtoLens.Field.field @"assetName")) :: + (Data.ProtoLens.OptionalField + (Data.ProtoLens.Field.field @"maybe'assetName")) :: Data.ProtoLens.FieldDescriptor AssetPattern in Data.Map.fromList @@ -702,8 +739,8 @@ instance Data.ProtoLens.Message AssetPattern where (\ x__ y__ -> x__ {_AssetPattern'_unknownFields = y__}) defMessage = AssetPattern'_constructor - {_AssetPattern'policyId = Data.ProtoLens.fieldDefault, - _AssetPattern'assetName = Data.ProtoLens.fieldDefault, + {_AssetPattern'policyId = Prelude.Nothing, + _AssetPattern'assetName = Prelude.Nothing, _AssetPattern'_unknownFields = []} parseMessage = let @@ -755,35 +792,34 @@ instance Data.ProtoLens.Message AssetPattern where buildMessage = \ _x -> (Data.Monoid.<>) - (let - _v = Lens.Family2.view (Data.ProtoLens.Field.field @"policyId") _x - in - if (Prelude.==) _v Data.ProtoLens.fieldDefault then - Data.Monoid.mempty - else - (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt 10) - ((\ bs - -> (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt - (Prelude.fromIntegral (Data.ByteString.length bs))) - (Data.ProtoLens.Encoding.Bytes.putBytes bs)) - _v)) + (case + Lens.Family2.view (Data.ProtoLens.Field.field @"maybe'policyId") _x + of + Prelude.Nothing -> Data.Monoid.mempty + (Prelude.Just _v) + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt 10) + ((\ bs + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt + (Prelude.fromIntegral (Data.ByteString.length bs))) + (Data.ProtoLens.Encoding.Bytes.putBytes bs)) + _v)) ((Data.Monoid.<>) - (let - _v = Lens.Family2.view (Data.ProtoLens.Field.field @"assetName") _x - in - if (Prelude.==) _v Data.ProtoLens.fieldDefault then - Data.Monoid.mempty - else - (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt 18) - ((\ bs - -> (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt - (Prelude.fromIntegral (Data.ByteString.length bs))) - (Data.ProtoLens.Encoding.Bytes.putBytes bs)) - _v)) + (case + Lens.Family2.view + (Data.ProtoLens.Field.field @"maybe'assetName") _x + of + Prelude.Nothing -> Data.Monoid.mempty + (Prelude.Just _v) + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt 18) + ((\ bs + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt + (Prelude.fromIntegral (Data.ByteString.length bs))) + (Data.ProtoLens.Encoding.Bytes.putBytes bs)) + _v)) (Data.ProtoLens.Encoding.Wire.buildFieldSet (Lens.Family2.view Data.ProtoLens.unknownFields _x))) instance Control.DeepSeq.NFData AssetPattern where @@ -27974,9 +28010,12 @@ instance Data.ProtoLens.Message TxOutputPattern where = Data.Text.pack "utxorpc.v1beta.cardano.TxOutputPattern" packedMessageDescriptor _ = "\n\ - \\SITxOutputPattern\DC2@\n\ - \\aaddress\CAN\SOH \SOH(\v2&.utxorpc.v1beta.cardano.AddressPatternR\aaddress\DC2:\n\ - \\ENQasset\CAN\STX \SOH(\v2$.utxorpc.v1beta.cardano.AssetPatternR\ENQasset" + \\SITxOutputPattern\DC2E\n\ + \\aaddress\CAN\SOH \SOH(\v2&.utxorpc.v1beta.cardano.AddressPatternH\NULR\aaddress\136\SOH\SOH\DC2?\n\ + \\ENQasset\CAN\STX \SOH(\v2$.utxorpc.v1beta.cardano.AssetPatternH\SOHR\ENQasset\136\SOH\SOHB\n\ + \\n\ + \\b_addressB\b\n\ + \\ACK_asset" packedFileDescriptor _ = packedFileDescriptor fieldsByTag = let @@ -31565,15 +31604,21 @@ packedFileDescriptor \\EOTcoin\CAN\STX \SOH(\v2\RS.utxorpc.v1beta.cardano.BigIntR\EOTcoin\"\154\SOH\n\ \\SOUpdateDRepCert\DC2P\n\ \\SIdrep_credential\CAN\SOH \SOH(\v2'.utxorpc.v1beta.cardano.StakeCredentialR\SOdrepCredential\DC26\n\ - \\ACKanchor\CAN\STX \SOH(\v2\RS.utxorpc.v1beta.cardano.AnchorR\ACKanchor\"\129\SOH\n\ - \\SOAddressPattern\DC2#\n\ - \\rexact_address\CAN\SOH \SOH(\fR\fexactAddress\DC2!\n\ - \\fpayment_part\CAN\STX \SOH(\fR\vpaymentPart\DC2'\n\ - \\SIdelegation_part\CAN\ETX \SOH(\fR\SOdelegationPart\"J\n\ - \\fAssetPattern\DC2\ESC\n\ - \\tpolicy_id\CAN\SOH \SOH(\fR\bpolicyId\DC2\GS\n\ - \\n\ - \asset_name\CAN\STX \SOH(\fR\tassetName\"\244\EOT\n\ + \\ACKanchor\CAN\STX \SOH(\v2\RS.utxorpc.v1beta.cardano.AnchorR\ACKanchor\"\199\SOH\n\ + \\SOAddressPattern\DC2(\n\ + \\rexact_address\CAN\SOH \SOH(\fH\NULR\fexactAddress\136\SOH\SOH\DC2&\n\ + \\fpayment_part\CAN\STX \SOH(\fH\SOHR\vpaymentPart\136\SOH\SOH\DC2,\n\ + \\SIdelegation_part\CAN\ETX \SOH(\fH\STXR\SOdelegationPart\136\SOH\SOHB\DLE\n\ + \\SO_exact_addressB\SI\n\ + \\r_payment_partB\DC2\n\ + \\DLE_delegation_part\"q\n\ + \\fAssetPattern\DC2 \n\ + \\tpolicy_id\CAN\SOH \SOH(\fH\NULR\bpolicyId\136\SOH\SOH\DC2\"\n\ + \\n\ + \asset_name\CAN\STX \SOH(\fH\SOHR\tassetName\136\SOH\SOHB\f\n\ + \\n\ + \_policy_idB\r\n\ + \\v_asset_name\"\244\EOT\n\ \\DC2CertificatePattern\DC2X\n\ \\DC2stake_registration\CAN\SOH \SOH(\v2'.utxorpc.v1beta.cardano.StakeCredentialH\NULR\DC1stakeRegistration\DC2\\\n\ \\DC4stake_deregistration\CAN\STX \SOH(\v2'.utxorpc.v1beta.cardano.StakeCredentialH\NULR\DC3stakeDeregistration\DC2[\n\ @@ -31592,10 +31637,13 @@ packedFileDescriptor \\fpool_keyhash\CAN\STX \SOH(\fR\vpoolKeyhash\"P\n\ \\NAKPoolRetirementPattern\DC2!\n\ \\fpool_keyhash\CAN\SOH \SOH(\fR\vpoolKeyhash\DC2\DC4\n\ - \\ENQepoch\CAN\STX \SOH(\EOTR\ENQepoch\"\143\SOH\n\ - \\SITxOutputPattern\DC2@\n\ - \\aaddress\CAN\SOH \SOH(\v2&.utxorpc.v1beta.cardano.AddressPatternR\aaddress\DC2:\n\ - \\ENQasset\CAN\STX \SOH(\v2$.utxorpc.v1beta.cardano.AssetPatternR\ENQasset\"\193\ETX\n\ + \\ENQepoch\CAN\STX \SOH(\EOTR\ENQepoch\"\175\SOH\n\ + \\SITxOutputPattern\DC2E\n\ + \\aaddress\CAN\SOH \SOH(\v2&.utxorpc.v1beta.cardano.AddressPatternH\NULR\aaddress\136\SOH\SOH\DC2?\n\ + \\ENQasset\CAN\STX \SOH(\v2$.utxorpc.v1beta.cardano.AssetPatternH\SOHR\ENQasset\136\SOH\SOHB\n\ + \\n\ + \\b_addressB\b\n\ + \\ACK_asset\"\193\ETX\n\ \\tTxPattern\DC2C\n\ \\bconsumes\CAN\SOH \SOH(\v2'.utxorpc.v1beta.cardano.TxOutputPatternR\bconsumes\DC2C\n\ \\bproduces\CAN\STX \SOH(\v2'.utxorpc.v1beta.cardano.TxOutputPatternR\bproduces\DC2G\n\ @@ -31843,7 +31891,7 @@ packedFileDescriptor \\SYNMIR_SOURCE_UNSPECIFIED\DLE\NUL\DC2\ETB\n\ \\DC3MIR_SOURCE_RESERVES\DLE\SOH\DC2\ETB\n\ \\DC3MIR_SOURCE_TREASURY\DLE\STXB\164\SOH\n\ - \\SUBcom.utxorpc.v1beta.cardanoB\fCardanoProtoP\SOH\162\STX\ETXUVC\170\STX\SYNUtxorpc.V1beta.Cardano\202\STX\SYNUtxorpc\\V1beta\\Cardano\226\STX\"Utxorpc\\V1beta\\Cardano\\GPBMetadata\234\STX\CANUtxorpc::V1beta::CardanoJ\171\173\STX\n\ + \\SUBcom.utxorpc.v1beta.cardanoB\fCardanoProtoP\SOH\162\STX\ETXUVC\170\STX\SYNUtxorpc.V1beta.Cardano\202\STX\SYNUtxorpc\\V1beta\\Cardano\226\STX\"Utxorpc\\V1beta\\Cardano\\GPBMetadata\234\STX\CANUtxorpc::V1beta::CardanoJ\148\174\STX\n\ \\a\DC2\ENQ\NUL\NUL\185\ACK\SOH\n\ \\b\n\ \\SOH\f\DC2\ETX\NUL\NUL\DC2\n\ @@ -34394,55 +34442,70 @@ packedFileDescriptor \\v\n\ \\ETX\EOTF\SOH\DC2\EOT\133\EOT\b\SYN\n\ \B\n\ - \\EOT\EOTF\STX\NUL\DC2\EOT\134\EOT\STX\SUB\"4 The address should match this exact address value.\n\ + \\EOT\EOTF\STX\NUL\DC2\EOT\134\EOT\STX#\"4 The address should match this exact address value.\n\ \\n\ \\r\n\ - \\ENQ\EOTF\STX\NUL\ENQ\DC2\EOT\134\EOT\STX\a\n\ + \\ENQ\EOTF\STX\NUL\EOT\DC2\EOT\134\EOT\STX\n\ + \\n\ + \\r\n\ + \\ENQ\EOTF\STX\NUL\ENQ\DC2\EOT\134\EOT\v\DLE\n\ \\r\n\ - \\ENQ\EOTF\STX\NUL\SOH\DC2\EOT\134\EOT\b\NAK\n\ + \\ENQ\EOTF\STX\NUL\SOH\DC2\EOT\134\EOT\DC1\RS\n\ \\r\n\ - \\ENQ\EOTF\STX\NUL\ETX\DC2\EOT\134\EOT\CAN\EM\n\ + \\ENQ\EOTF\STX\NUL\ETX\DC2\EOT\134\EOT!\"\n\ \H\n\ - \\EOT\EOTF\STX\SOH\DC2\EOT\135\EOT\STX\EM\": The payment part of the address should match this value.\n\ + \\EOT\EOTF\STX\SOH\DC2\EOT\135\EOT\STX\"\": The payment part of the address should match this value.\n\ \\n\ \\r\n\ - \\ENQ\EOTF\STX\SOH\ENQ\DC2\EOT\135\EOT\STX\a\n\ + \\ENQ\EOTF\STX\SOH\EOT\DC2\EOT\135\EOT\STX\n\ + \\n\ \\r\n\ - \\ENQ\EOTF\STX\SOH\SOH\DC2\EOT\135\EOT\b\DC4\n\ + \\ENQ\EOTF\STX\SOH\ENQ\DC2\EOT\135\EOT\v\DLE\n\ \\r\n\ - \\ENQ\EOTF\STX\SOH\ETX\DC2\EOT\135\EOT\ETB\CAN\n\ + \\ENQ\EOTF\STX\SOH\SOH\DC2\EOT\135\EOT\DC1\GS\n\ + \\r\n\ + \\ENQ\EOTF\STX\SOH\ETX\DC2\EOT\135\EOT !\n\ \K\n\ - \\EOT\EOTF\STX\STX\DC2\EOT\136\EOT\STX\FS\"= The delegation part of the address should match this value.\n\ + \\EOT\EOTF\STX\STX\DC2\EOT\136\EOT\STX%\"= The delegation part of the address should match this value.\n\ + \\n\ + \\r\n\ + \\ENQ\EOTF\STX\STX\EOT\DC2\EOT\136\EOT\STX\n\ \\n\ \\r\n\ - \\ENQ\EOTF\STX\STX\ENQ\DC2\EOT\136\EOT\STX\a\n\ + \\ENQ\EOTF\STX\STX\ENQ\DC2\EOT\136\EOT\v\DLE\n\ \\r\n\ - \\ENQ\EOTF\STX\STX\SOH\DC2\EOT\136\EOT\b\ETB\n\ + \\ENQ\EOTF\STX\STX\SOH\DC2\EOT\136\EOT\DC1 \n\ \\r\n\ - \\ENQ\EOTF\STX\STX\ETX\DC2\EOT\136\EOT\SUB\ESC\n\ + \\ENQ\EOTF\STX\STX\ETX\DC2\EOT\136\EOT#$\n\ \[\n\ \\STX\EOTG\DC2\ACK\140\EOT\NUL\143\EOT\SOH\SUBM Pattern of a native asset that can be used to evaluate matching predicates.\n\ \\n\ \\v\n\ \\ETX\EOTG\SOH\DC2\EOT\140\EOT\b\DC4\n\ \9\n\ - \\EOT\EOTG\STX\NUL\DC2\EOT\141\EOT\STX\SYN\"+ The asset should belong to this policy id\n\ + \\EOT\EOTG\STX\NUL\DC2\EOT\141\EOT\STX\US\"+ The asset should belong to this policy id\n\ \\n\ \\r\n\ - \\ENQ\EOTG\STX\NUL\ENQ\DC2\EOT\141\EOT\STX\a\n\ + \\ENQ\EOTG\STX\NUL\EOT\DC2\EOT\141\EOT\STX\n\ + \\n\ + \\r\n\ + \\ENQ\EOTG\STX\NUL\ENQ\DC2\EOT\141\EOT\v\DLE\n\ \\r\n\ - \\ENQ\EOTG\STX\NUL\SOH\DC2\EOT\141\EOT\b\DC1\n\ + \\ENQ\EOTG\STX\NUL\SOH\DC2\EOT\141\EOT\DC1\SUB\n\ \\r\n\ - \\ENQ\EOTG\STX\NUL\ETX\DC2\EOT\141\EOT\DC4\NAK\n\ + \\ENQ\EOTG\STX\NUL\ETX\DC2\EOT\141\EOT\GS\RS\n\ \2\n\ - \\EOT\EOTG\STX\SOH\DC2\EOT\142\EOT\STX\ETB\"$ The asset should present this name\n\ + \\EOT\EOTG\STX\SOH\DC2\EOT\142\EOT\STX \"$ The asset should present this name\n\ \\n\ \\r\n\ - \\ENQ\EOTG\STX\SOH\ENQ\DC2\EOT\142\EOT\STX\a\n\ + \\ENQ\EOTG\STX\SOH\EOT\DC2\EOT\142\EOT\STX\n\ + \\n\ \\r\n\ - \\ENQ\EOTG\STX\SOH\SOH\DC2\EOT\142\EOT\b\DC2\n\ + \\ENQ\EOTG\STX\SOH\ENQ\DC2\EOT\142\EOT\v\DLE\n\ \\r\n\ - \\ENQ\EOTG\STX\SOH\ETX\DC2\EOT\142\EOT\NAK\SYN\n\ + \\ENQ\EOTG\STX\SOH\SOH\DC2\EOT\142\EOT\DC1\ESC\n\ + \\r\n\ + \\ENQ\EOTG\STX\SOH\ETX\DC2\EOT\142\EOT\RS\US\n\ \Z\n\ \\STX\EOTH\DC2\ACK\146\EOT\NUL\157\EOT\SOH\SUBL Pattern of a certificate that can be used to evaluate matching predicates.\n\ \\n\ @@ -34602,23 +34665,29 @@ packedFileDescriptor \\v\n\ \\ETX\EOTL\SOH\DC2\EOT\178\EOT\b\ETB\n\ \K\n\ - \\EOT\EOTL\STX\NUL\DC2\EOT\179\EOT\STX\GS\"= Match any address in the output that exhibits this pattern.\n\ + \\EOT\EOTL\STX\NUL\DC2\EOT\179\EOT\STX&\"= Match any address in the output that exhibits this pattern.\n\ + \\n\ + \\r\n\ + \\ENQ\EOTL\STX\NUL\EOT\DC2\EOT\179\EOT\STX\n\ \\n\ \\r\n\ - \\ENQ\EOTL\STX\NUL\ACK\DC2\EOT\179\EOT\STX\DLE\n\ + \\ENQ\EOTL\STX\NUL\ACK\DC2\EOT\179\EOT\v\EM\n\ \\r\n\ - \\ENQ\EOTL\STX\NUL\SOH\DC2\EOT\179\EOT\DC1\CAN\n\ + \\ENQ\EOTL\STX\NUL\SOH\DC2\EOT\179\EOT\SUB!\n\ \\r\n\ - \\ENQ\EOTL\STX\NUL\ETX\DC2\EOT\179\EOT\ESC\FS\n\ + \\ENQ\EOTL\STX\NUL\ETX\DC2\EOT\179\EOT$%\n\ \I\n\ - \\EOT\EOTL\STX\SOH\DC2\EOT\180\EOT\STX\EM\"; Match any asset in the output that exhibits this pattern.\n\ + \\EOT\EOTL\STX\SOH\DC2\EOT\180\EOT\STX\"\"; Match any asset in the output that exhibits this pattern.\n\ + \\n\ + \\r\n\ + \\ENQ\EOTL\STX\SOH\EOT\DC2\EOT\180\EOT\STX\n\ \\n\ \\r\n\ - \\ENQ\EOTL\STX\SOH\ACK\DC2\EOT\180\EOT\STX\SO\n\ + \\ENQ\EOTL\STX\SOH\ACK\DC2\EOT\180\EOT\v\ETB\n\ \\r\n\ - \\ENQ\EOTL\STX\SOH\SOH\DC2\EOT\180\EOT\SI\DC4\n\ + \\ENQ\EOTL\STX\SOH\SOH\DC2\EOT\180\EOT\CAN\GS\n\ \\r\n\ - \\ENQ\EOTL\STX\SOH\ETX\DC2\EOT\180\EOT\ETB\CAN\n\ + \\ENQ\EOTL\STX\SOH\ETX\DC2\EOT\180\EOT !\n\ \Q\n\ \\STX\EOTM\DC2\ACK\184\EOT\NUL\191\EOT\SOH\SUBC Pattern of a Tx that can be used to evaluate matching predicates.\n\ \\n\ diff --git a/cardano-rpc/gen/Proto/Utxorpc/V1beta/Cardano/Cardano_Fields.hs b/cardano-rpc/gen/Proto/Utxorpc/V1beta/Cardano/Cardano_Fields.hs index 350c7500ee..4c9127a046 100644 --- a/cardano-rpc/gen/Proto/Utxorpc/V1beta/Cardano/Cardano_Fields.hs +++ b/cardano-rpc/gen/Proto/Utxorpc/V1beta/Cardano/Cardano_Fields.hs @@ -856,6 +856,12 @@ maybe'asset :: Data.ProtoLens.Field.HasField s "maybe'asset" a) => Lens.Family2.LensLike' f s a maybe'asset = Data.ProtoLens.Field.field @"maybe'asset" +maybe'assetName :: + forall f s a. + (Prelude.Functor f, + Data.ProtoLens.Field.HasField s "maybe'assetName" a) => + Lens.Family2.LensLike' f s a +maybe'assetName = Data.ProtoLens.Field.field @"maybe'assetName" maybe'authCommitteeHotCert :: forall f s a. (Prelude.Functor f, @@ -1023,6 +1029,13 @@ maybe'datum :: Data.ProtoLens.Field.HasField s "maybe'datum" a) => Lens.Family2.LensLike' f s a maybe'datum = Data.ProtoLens.Field.field @"maybe'datum" +maybe'delegationPart :: + forall f s a. + (Prelude.Functor f, + Data.ProtoLens.Field.HasField s "maybe'delegationPart" a) => + Lens.Family2.LensLike' f s a +maybe'delegationPart + = Data.ProtoLens.Field.field @"maybe'delegationPart" maybe'deltaCoin :: forall f s a. (Prelude.Functor f, @@ -1073,6 +1086,13 @@ maybe'exUnits :: Data.ProtoLens.Field.HasField s "maybe'exUnits" a) => Lens.Family2.LensLike' f s a maybe'exUnits = Data.ProtoLens.Field.field @"maybe'exUnits" +maybe'exactAddress :: + forall f s a. + (Prelude.Functor f, + Data.ProtoLens.Field.HasField s "maybe'exactAddress" a) => + Lens.Family2.LensLike' f s a +maybe'exactAddress + = Data.ProtoLens.Field.field @"maybe'exactAddress" maybe'executionPrices :: forall f s a. (Prelude.Functor f, @@ -1385,6 +1405,12 @@ maybe'payload :: Data.ProtoLens.Field.HasField s "maybe'payload" a) => Lens.Family2.LensLike' f s a maybe'payload = Data.ProtoLens.Field.field @"maybe'payload" +maybe'paymentPart :: + forall f s a. + (Prelude.Functor f, + Data.ProtoLens.Field.HasField s "maybe'paymentPart" a) => + Lens.Family2.LensLike' f s a +maybe'paymentPart = Data.ProtoLens.Field.field @"maybe'paymentPart" maybe'pledge :: forall f s a. (Prelude.Functor f, @@ -1421,6 +1447,12 @@ maybe'plutusV4 :: Data.ProtoLens.Field.HasField s "maybe'plutusV4" a) => Lens.Family2.LensLike' f s a maybe'plutusV4 = Data.ProtoLens.Field.field @"maybe'plutusV4" +maybe'policyId :: + forall f s a. + (Prelude.Functor f, + Data.ProtoLens.Field.HasField s "maybe'policyId" a) => + Lens.Family2.LensLike' f s a +maybe'policyId = Data.ProtoLens.Field.field @"maybe'policyId" maybe'poolDeposit :: forall f s a. (Prelude.Functor f, diff --git a/cardano-rpc/gen/Proto/Utxorpc/V1beta/Query/Query.hs b/cardano-rpc/gen/Proto/Utxorpc/V1beta/Query/Query.hs index 80a1a10ec4..98174f8b9f 100644 --- a/cardano-rpc/gen/Proto/Utxorpc/V1beta/Query/Query.hs +++ b/cardano-rpc/gen/Proto/Utxorpc/V1beta/Query/Query.hs @@ -3631,12 +3631,14 @@ instance Control.DeepSeq.NFData ReadUtxosResponse where * 'Proto.Utxorpc.V1beta.Query.Query_Fields.fieldMask' @:: Lens' SearchUtxosRequest Proto.Google.Protobuf.FieldMask.FieldMask@ * 'Proto.Utxorpc.V1beta.Query.Query_Fields.maybe'fieldMask' @:: Lens' SearchUtxosRequest (Prelude.Maybe Proto.Google.Protobuf.FieldMask.FieldMask)@ * 'Proto.Utxorpc.V1beta.Query.Query_Fields.maxItems' @:: Lens' SearchUtxosRequest Data.Int.Int32@ - * 'Proto.Utxorpc.V1beta.Query.Query_Fields.startToken' @:: Lens' SearchUtxosRequest Data.Text.Text@ -} + * 'Proto.Utxorpc.V1beta.Query.Query_Fields.maybe'maxItems' @:: Lens' SearchUtxosRequest (Prelude.Maybe Data.Int.Int32)@ + * 'Proto.Utxorpc.V1beta.Query.Query_Fields.startToken' @:: Lens' SearchUtxosRequest Data.Text.Text@ + * 'Proto.Utxorpc.V1beta.Query.Query_Fields.maybe'startToken' @:: Lens' SearchUtxosRequest (Prelude.Maybe Data.Text.Text)@ -} data SearchUtxosRequest = SearchUtxosRequest'_constructor {_SearchUtxosRequest'predicate :: !(Prelude.Maybe UtxoPredicate), _SearchUtxosRequest'fieldMask :: !(Prelude.Maybe Proto.Google.Protobuf.FieldMask.FieldMask), - _SearchUtxosRequest'maxItems :: !Data.Int.Int32, - _SearchUtxosRequest'startToken :: !Data.Text.Text, + _SearchUtxosRequest'maxItems :: !(Prelude.Maybe Data.Int.Int32), + _SearchUtxosRequest'startToken :: !(Prelude.Maybe Data.Text.Text), _SearchUtxosRequest'_unknownFields :: !Data.ProtoLens.FieldSet} deriving stock (Prelude.Eq, Prelude.Ord) instance Prelude.Show SearchUtxosRequest where @@ -3674,6 +3676,13 @@ instance Data.ProtoLens.Field.HasField SearchUtxosRequest "maybe'fieldMask" (Pre (\ x__ y__ -> x__ {_SearchUtxosRequest'fieldMask = y__})) Prelude.id instance Data.ProtoLens.Field.HasField SearchUtxosRequest "maxItems" Data.Int.Int32 where + fieldOf _ + = (Prelude..) + (Lens.Family2.Unchecked.lens + _SearchUtxosRequest'maxItems + (\ x__ y__ -> x__ {_SearchUtxosRequest'maxItems = y__})) + (Data.ProtoLens.maybeLens Data.ProtoLens.fieldDefault) +instance Data.ProtoLens.Field.HasField SearchUtxosRequest "maybe'maxItems" (Prelude.Maybe Data.Int.Int32) where fieldOf _ = (Prelude..) (Lens.Family2.Unchecked.lens @@ -3681,6 +3690,13 @@ instance Data.ProtoLens.Field.HasField SearchUtxosRequest "maxItems" Data.Int.In (\ x__ y__ -> x__ {_SearchUtxosRequest'maxItems = y__})) Prelude.id instance Data.ProtoLens.Field.HasField SearchUtxosRequest "startToken" Data.Text.Text where + fieldOf _ + = (Prelude..) + (Lens.Family2.Unchecked.lens + _SearchUtxosRequest'startToken + (\ x__ y__ -> x__ {_SearchUtxosRequest'startToken = y__})) + (Data.ProtoLens.maybeLens Data.ProtoLens.fieldDefault) +instance Data.ProtoLens.Field.HasField SearchUtxosRequest "maybe'startToken" (Prelude.Maybe Data.Text.Text) where fieldOf _ = (Prelude..) (Lens.Family2.Unchecked.lens @@ -3692,13 +3708,18 @@ instance Data.ProtoLens.Message SearchUtxosRequest where = Data.Text.pack "utxorpc.v1beta.query.SearchUtxosRequest" packedMessageDescriptor _ = "\n\ - \\DC2SearchUtxosRequest\DC2A\n\ - \\tpredicate\CAN\SOH \SOH(\v2#.utxorpc.v1beta.query.UtxoPredicateR\tpredicate\DC29\n\ + \\DC2SearchUtxosRequest\DC2F\n\ + \\tpredicate\CAN\SOH \SOH(\v2#.utxorpc.v1beta.query.UtxoPredicateH\NULR\tpredicate\136\SOH\SOH\DC29\n\ + \\n\ + \field_mask\CAN\STX \SOH(\v2\SUB.google.protobuf.FieldMaskR\tfieldMask\DC2 \n\ + \\tmax_items\CAN\ETX \SOH(\ENQH\SOHR\bmaxItems\136\SOH\SOH\DC2$\n\ + \\vstart_token\CAN\EOT \SOH(\tH\STXR\n\ + \startToken\136\SOH\SOHB\f\n\ + \\n\ + \_predicateB\f\n\ \\n\ - \field_mask\CAN\STX \SOH(\v2\SUB.google.protobuf.FieldMaskR\tfieldMask\DC2\ESC\n\ - \\tmax_items\CAN\ETX \SOH(\ENQR\bmaxItems\DC2\US\n\ - \\vstart_token\CAN\EOT \SOH(\tR\n\ - \startToken" + \_max_itemsB\SO\n\ + \\f_start_token" packedFileDescriptor _ = packedFileDescriptor fieldsByTag = let @@ -3723,18 +3744,16 @@ instance Data.ProtoLens.Message SearchUtxosRequest where "max_items" (Data.ProtoLens.ScalarField Data.ProtoLens.Int32Field :: Data.ProtoLens.FieldTypeDescriptor Data.Int.Int32) - (Data.ProtoLens.PlainField - Data.ProtoLens.Optional - (Data.ProtoLens.Field.field @"maxItems")) :: + (Data.ProtoLens.OptionalField + (Data.ProtoLens.Field.field @"maybe'maxItems")) :: Data.ProtoLens.FieldDescriptor SearchUtxosRequest startToken__field_descriptor = Data.ProtoLens.FieldDescriptor "start_token" (Data.ProtoLens.ScalarField Data.ProtoLens.StringField :: Data.ProtoLens.FieldTypeDescriptor Data.Text.Text) - (Data.ProtoLens.PlainField - Data.ProtoLens.Optional - (Data.ProtoLens.Field.field @"startToken")) :: + (Data.ProtoLens.OptionalField + (Data.ProtoLens.Field.field @"maybe'startToken")) :: Data.ProtoLens.FieldDescriptor SearchUtxosRequest in Data.Map.fromList @@ -3750,8 +3769,8 @@ instance Data.ProtoLens.Message SearchUtxosRequest where = SearchUtxosRequest'_constructor {_SearchUtxosRequest'predicate = Prelude.Nothing, _SearchUtxosRequest'fieldMask = Prelude.Nothing, - _SearchUtxosRequest'maxItems = Data.ProtoLens.fieldDefault, - _SearchUtxosRequest'startToken = Data.ProtoLens.fieldDefault, + _SearchUtxosRequest'maxItems = Prelude.Nothing, + _SearchUtxosRequest'startToken = Prelude.Nothing, _SearchUtxosRequest'_unknownFields = []} parseMessage = let @@ -3852,33 +3871,31 @@ instance Data.ProtoLens.Message SearchUtxosRequest where (Data.ProtoLens.Encoding.Bytes.putBytes bs)) Data.ProtoLens.encodeMessage _v)) ((Data.Monoid.<>) - (let - _v = Lens.Family2.view (Data.ProtoLens.Field.field @"maxItems") _x - in - if (Prelude.==) _v Data.ProtoLens.fieldDefault then - Data.Monoid.mempty - else - (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt 24) - ((Prelude..) - Data.ProtoLens.Encoding.Bytes.putVarInt Prelude.fromIntegral _v)) + (case + Lens.Family2.view (Data.ProtoLens.Field.field @"maybe'maxItems") _x + of + Prelude.Nothing -> Data.Monoid.mempty + (Prelude.Just _v) + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt 24) + ((Prelude..) + Data.ProtoLens.Encoding.Bytes.putVarInt Prelude.fromIntegral _v)) ((Data.Monoid.<>) - (let - _v - = Lens.Family2.view (Data.ProtoLens.Field.field @"startToken") _x - in - if (Prelude.==) _v Data.ProtoLens.fieldDefault then - Data.Monoid.mempty - else - (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt 34) - ((Prelude..) - (\ bs - -> (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt - (Prelude.fromIntegral (Data.ByteString.length bs))) - (Data.ProtoLens.Encoding.Bytes.putBytes bs)) - Data.Text.Encoding.encodeUtf8 _v)) + (case + Lens.Family2.view + (Data.ProtoLens.Field.field @"maybe'startToken") _x + of + Prelude.Nothing -> Data.Monoid.mempty + (Prelude.Just _v) + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt 34) + ((Prelude..) + (\ bs + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt + (Prelude.fromIntegral (Data.ByteString.length bs))) + (Data.ProtoLens.Encoding.Bytes.putBytes bs)) + Data.Text.Encoding.encodeUtf8 _v)) (Data.ProtoLens.Encoding.Wire.buildFieldSet (Lens.Family2.view Data.ProtoLens.unknownFields _x))))) instance Control.DeepSeq.NFData SearchUtxosRequest where @@ -3900,11 +3917,12 @@ instance Control.DeepSeq.NFData SearchUtxosRequest where * 'Proto.Utxorpc.V1beta.Query.Query_Fields.vec'items' @:: Lens' SearchUtxosResponse (Data.Vector.Vector AnyUtxoData)@ * 'Proto.Utxorpc.V1beta.Query.Query_Fields.ledgerTip' @:: Lens' SearchUtxosResponse ChainPoint@ * 'Proto.Utxorpc.V1beta.Query.Query_Fields.maybe'ledgerTip' @:: Lens' SearchUtxosResponse (Prelude.Maybe ChainPoint)@ - * 'Proto.Utxorpc.V1beta.Query.Query_Fields.nextToken' @:: Lens' SearchUtxosResponse Data.Text.Text@ -} + * 'Proto.Utxorpc.V1beta.Query.Query_Fields.nextToken' @:: Lens' SearchUtxosResponse Data.Text.Text@ + * 'Proto.Utxorpc.V1beta.Query.Query_Fields.maybe'nextToken' @:: Lens' SearchUtxosResponse (Prelude.Maybe Data.Text.Text)@ -} data SearchUtxosResponse = SearchUtxosResponse'_constructor {_SearchUtxosResponse'items :: !(Data.Vector.Vector AnyUtxoData), _SearchUtxosResponse'ledgerTip :: !(Prelude.Maybe ChainPoint), - _SearchUtxosResponse'nextToken :: !Data.Text.Text, + _SearchUtxosResponse'nextToken :: !(Prelude.Maybe Data.Text.Text), _SearchUtxosResponse'_unknownFields :: !Data.ProtoLens.FieldSet} deriving stock (Prelude.Eq, Prelude.Ord) instance Prelude.Show SearchUtxosResponse where @@ -3944,6 +3962,13 @@ instance Data.ProtoLens.Field.HasField SearchUtxosResponse "maybe'ledgerTip" (Pr (\ x__ y__ -> x__ {_SearchUtxosResponse'ledgerTip = y__})) Prelude.id instance Data.ProtoLens.Field.HasField SearchUtxosResponse "nextToken" Data.Text.Text where + fieldOf _ + = (Prelude..) + (Lens.Family2.Unchecked.lens + _SearchUtxosResponse'nextToken + (\ x__ y__ -> x__ {_SearchUtxosResponse'nextToken = y__})) + (Data.ProtoLens.maybeLens Data.ProtoLens.fieldDefault) +instance Data.ProtoLens.Field.HasField SearchUtxosResponse "maybe'nextToken" (Prelude.Maybe Data.Text.Text) where fieldOf _ = (Prelude..) (Lens.Family2.Unchecked.lens @@ -3958,9 +3983,10 @@ instance Data.ProtoLens.Message SearchUtxosResponse where \\DC3SearchUtxosResponse\DC27\n\ \\ENQitems\CAN\SOH \ETX(\v2!.utxorpc.v1beta.query.AnyUtxoDataR\ENQitems\DC2?\n\ \\n\ - \ledger_tip\CAN\STX \SOH(\v2 .utxorpc.v1beta.query.ChainPointR\tledgerTip\DC2\GS\n\ + \ledger_tip\CAN\STX \SOH(\v2 .utxorpc.v1beta.query.ChainPointR\tledgerTip\DC2\"\n\ \\n\ - \next_token\CAN\ETX \SOH(\tR\tnextToken" + \next_token\CAN\ETX \SOH(\tH\NULR\tnextToken\136\SOH\SOHB\r\n\ + \\v_next_token" packedFileDescriptor _ = packedFileDescriptor fieldsByTag = let @@ -3985,9 +4011,8 @@ instance Data.ProtoLens.Message SearchUtxosResponse where "next_token" (Data.ProtoLens.ScalarField Data.ProtoLens.StringField :: Data.ProtoLens.FieldTypeDescriptor Data.Text.Text) - (Data.ProtoLens.PlainField - Data.ProtoLens.Optional - (Data.ProtoLens.Field.field @"nextToken")) :: + (Data.ProtoLens.OptionalField + (Data.ProtoLens.Field.field @"maybe'nextToken")) :: Data.ProtoLens.FieldDescriptor SearchUtxosResponse in Data.Map.fromList @@ -4002,7 +4027,7 @@ instance Data.ProtoLens.Message SearchUtxosResponse where = SearchUtxosResponse'_constructor {_SearchUtxosResponse'items = Data.Vector.Generic.empty, _SearchUtxosResponse'ledgerTip = Prelude.Nothing, - _SearchUtxosResponse'nextToken = Data.ProtoLens.fieldDefault, + _SearchUtxosResponse'nextToken = Prelude.Nothing, _SearchUtxosResponse'_unknownFields = []} parseMessage = let @@ -4105,21 +4130,21 @@ instance Data.ProtoLens.Message SearchUtxosResponse where (Data.ProtoLens.Encoding.Bytes.putBytes bs)) Data.ProtoLens.encodeMessage _v)) ((Data.Monoid.<>) - (let - _v = Lens.Family2.view (Data.ProtoLens.Field.field @"nextToken") _x - in - if (Prelude.==) _v Data.ProtoLens.fieldDefault then - Data.Monoid.mempty - else - (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt 26) - ((Prelude..) - (\ bs - -> (Data.Monoid.<>) - (Data.ProtoLens.Encoding.Bytes.putVarInt - (Prelude.fromIntegral (Data.ByteString.length bs))) - (Data.ProtoLens.Encoding.Bytes.putBytes bs)) - Data.Text.Encoding.encodeUtf8 _v)) + (case + Lens.Family2.view + (Data.ProtoLens.Field.field @"maybe'nextToken") _x + of + Prelude.Nothing -> Data.Monoid.mempty + (Prelude.Just _v) + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt 26) + ((Prelude..) + (\ bs + -> (Data.Monoid.<>) + (Data.ProtoLens.Encoding.Bytes.putVarInt + (Prelude.fromIntegral (Data.ByteString.length bs))) + (Data.ProtoLens.Encoding.Bytes.putBytes bs)) + Data.Text.Encoding.encodeUtf8 _v)) (Data.ProtoLens.Encoding.Wire.buildFieldSet (Lens.Family2.view Data.ProtoLens.unknownFields _x)))) instance Control.DeepSeq.NFData SearchUtxosResponse where @@ -4366,11 +4391,12 @@ instance Data.ProtoLens.Message UtxoPredicate where messageName _ = Data.Text.pack "utxorpc.v1beta.query.UtxoPredicate" packedMessageDescriptor _ = "\n\ - \\rUtxoPredicate\DC2:\n\ - \\ENQmatch\CAN\SOH \SOH(\v2$.utxorpc.v1beta.query.AnyUtxoPatternR\ENQmatch\DC25\n\ + \\rUtxoPredicate\DC2?\n\ + \\ENQmatch\CAN\SOH \SOH(\v2$.utxorpc.v1beta.query.AnyUtxoPatternH\NULR\ENQmatch\136\SOH\SOH\DC25\n\ \\ETXnot\CAN\STX \ETX(\v2#.utxorpc.v1beta.query.UtxoPredicateR\ETXnot\DC2:\n\ \\ACKall_of\CAN\ETX \ETX(\v2#.utxorpc.v1beta.query.UtxoPredicateR\ENQallOf\DC2:\n\ - \\ACKany_of\CAN\EOT \ETX(\v2#.utxorpc.v1beta.query.UtxoPredicateR\ENQanyOf" + \\ACKany_of\CAN\EOT \ETX(\v2#.utxorpc.v1beta.query.UtxoPredicateR\ENQanyOfB\b\n\ + \\ACK_match" packedFileDescriptor _ = packedFileDescriptor fieldsByTag = let @@ -4592,13 +4618,16 @@ data QueryService = QueryService {} instance Data.ProtoLens.Service.Types.Service QueryService where type ServiceName QueryService = "QueryService" type ServicePackage QueryService = "utxorpc.v1beta.query" - type ServiceMethods QueryService = '["readParams", "readUtxos"] + type ServiceMethods QueryService = '["readParams", + "readUtxos", + "searchUtxos"] packedServiceDescriptor _ = "\n\ \\fQueryService\DC2_\n\ \\n\ \ReadParams\DC2'.utxorpc.v1beta.query.ReadParamsRequest\SUB(.utxorpc.v1beta.query.ReadParamsResponse\DC2\\\n\ - \\tReadUtxos\DC2&.utxorpc.v1beta.query.ReadUtxosRequest\SUB'.utxorpc.v1beta.query.ReadUtxosResponse" + \\tReadUtxos\DC2&.utxorpc.v1beta.query.ReadUtxosRequest\SUB'.utxorpc.v1beta.query.ReadUtxosResponse\DC2b\n\ + \\vSearchUtxos\DC2(.utxorpc.v1beta.query.SearchUtxosRequest\SUB).utxorpc.v1beta.query.SearchUtxosResponse" instance Data.ProtoLens.Service.Types.HasMethodImpl QueryService "readParams" where type MethodName QueryService "readParams" = "ReadParams" type MethodInput QueryService "readParams" = ReadParamsRequest @@ -4609,6 +4638,11 @@ instance Data.ProtoLens.Service.Types.HasMethodImpl QueryService "readUtxos" whe type MethodInput QueryService "readUtxos" = ReadUtxosRequest type MethodOutput QueryService "readUtxos" = ReadUtxosResponse type MethodStreamingType QueryService "readUtxos" = 'Data.ProtoLens.Service.Types.NonStreaming +instance Data.ProtoLens.Service.Types.HasMethodImpl QueryService "searchUtxos" where + type MethodName QueryService "searchUtxos" = "SearchUtxos" + type MethodInput QueryService "searchUtxos" = SearchUtxosRequest + type MethodOutput QueryService "searchUtxos" = SearchUtxosResponse + type MethodStreamingType QueryService "searchUtxos" = 'Data.ProtoLens.Service.Types.NonStreaming packedFileDescriptor :: Data.ByteString.ByteString packedFileDescriptor = "\n\ @@ -4652,12 +4686,13 @@ packedFileDescriptor \ledger_tip\CAN\STX \SOH(\v2 .utxorpc.v1beta.query.ChainPointR\tledgerTip\"e\n\ \\SOAnyUtxoPattern\DC2C\n\ \\acardano\CAN\SOH \SOH(\v2'.utxorpc.v1beta.cardano.TxOutputPatternH\NULR\acardanoB\SO\n\ - \\futxo_pattern\"\250\SOH\n\ - \\rUtxoPredicate\DC2:\n\ - \\ENQmatch\CAN\SOH \SOH(\v2$.utxorpc.v1beta.query.AnyUtxoPatternR\ENQmatch\DC25\n\ + \\futxo_pattern\"\137\STX\n\ + \\rUtxoPredicate\DC2?\n\ + \\ENQmatch\CAN\SOH \SOH(\v2$.utxorpc.v1beta.query.AnyUtxoPatternH\NULR\ENQmatch\136\SOH\SOH\DC25\n\ \\ETXnot\CAN\STX \ETX(\v2#.utxorpc.v1beta.query.UtxoPredicateR\ETXnot\DC2:\n\ \\ACKall_of\CAN\ETX \ETX(\v2#.utxorpc.v1beta.query.UtxoPredicateR\ENQallOf\DC2:\n\ - \\ACKany_of\CAN\EOT \ETX(\v2#.utxorpc.v1beta.query.UtxoPredicateR\ENQanyOf\"\244\SOH\n\ + \\ACKany_of\CAN\EOT \ETX(\v2#.utxorpc.v1beta.query.UtxoPredicateR\ENQanyOfB\b\n\ + \\ACK_match\"\244\SOH\n\ \\vAnyUtxoData\DC2!\n\ \\fnative_bytes\CAN\SOH \SOH(\fR\vnativeBytes\DC25\n\ \\atxo_ref\CAN\STX \SOH(\v2\FS.utxorpc.v1beta.query.TxoRefR\ACKtxoRef\DC2<\n\ @@ -4671,20 +4706,26 @@ packedFileDescriptor \\DC1ReadUtxosResponse\DC27\n\ \\ENQitems\CAN\SOH \ETX(\v2!.utxorpc.v1beta.query.AnyUtxoDataR\ENQitems\DC2?\n\ \\n\ - \ledger_tip\CAN\STX \SOH(\v2 .utxorpc.v1beta.query.ChainPointR\tledgerTip\"\208\SOH\n\ - \\DC2SearchUtxosRequest\DC2A\n\ - \\tpredicate\CAN\SOH \SOH(\v2#.utxorpc.v1beta.query.UtxoPredicateR\tpredicate\DC29\n\ + \ledger_tip\CAN\STX \SOH(\v2 .utxorpc.v1beta.query.ChainPointR\tledgerTip\"\139\STX\n\ + \\DC2SearchUtxosRequest\DC2F\n\ + \\tpredicate\CAN\SOH \SOH(\v2#.utxorpc.v1beta.query.UtxoPredicateH\NULR\tpredicate\136\SOH\SOH\DC29\n\ \\n\ - \field_mask\CAN\STX \SOH(\v2\SUB.google.protobuf.FieldMaskR\tfieldMask\DC2\ESC\n\ - \\tmax_items\CAN\ETX \SOH(\ENQR\bmaxItems\DC2\US\n\ - \\vstart_token\CAN\EOT \SOH(\tR\n\ - \startToken\"\174\SOH\n\ + \field_mask\CAN\STX \SOH(\v2\SUB.google.protobuf.FieldMaskR\tfieldMask\DC2 \n\ + \\tmax_items\CAN\ETX \SOH(\ENQH\SOHR\bmaxItems\136\SOH\SOH\DC2$\n\ + \\vstart_token\CAN\EOT \SOH(\tH\STXR\n\ + \startToken\136\SOH\SOHB\f\n\ + \\n\ + \_predicateB\f\n\ + \\n\ + \_max_itemsB\SO\n\ + \\f_start_token\"\194\SOH\n\ \\DC3SearchUtxosResponse\DC27\n\ \\ENQitems\CAN\SOH \ETX(\v2!.utxorpc.v1beta.query.AnyUtxoDataR\ENQitems\DC2?\n\ \\n\ - \ledger_tip\CAN\STX \SOH(\v2 .utxorpc.v1beta.query.ChainPointR\tledgerTip\DC2\GS\n\ + \ledger_tip\CAN\STX \SOH(\v2 .utxorpc.v1beta.query.ChainPointR\tledgerTip\DC2\"\n\ \\n\ - \next_token\CAN\ETX \SOH(\tR\tnextToken\"`\n\ + \next_token\CAN\ETX \SOH(\tH\NULR\tnextToken\136\SOH\SOHB\r\n\ + \\v_next_token\"`\n\ \\SIReadDataRequest\DC2\DC2\n\ \\EOTkeys\CAN\SOH \ETX(\fR\EOTkeys\DC29\n\ \\n\ @@ -4711,14 +4752,15 @@ packedFileDescriptor \\SOReadTxResponse\DC20\n\ \\STXtx\CAN\SOH \SOH(\v2 .utxorpc.v1beta.query.AnyChainTxR\STXtx\DC2?\n\ \\n\ - \ledger_tip\CAN\STX \SOH(\v2 .utxorpc.v1beta.query.ChainPointR\tledgerTip2\205\SOH\n\ + \ledger_tip\CAN\STX \SOH(\v2 .utxorpc.v1beta.query.ChainPointR\tledgerTip2\177\STX\n\ \\fQueryService\DC2_\n\ \\n\ \ReadParams\DC2'.utxorpc.v1beta.query.ReadParamsRequest\SUB(.utxorpc.v1beta.query.ReadParamsResponse\DC2\\\n\ - \\tReadUtxos\DC2&.utxorpc.v1beta.query.ReadUtxosRequest\SUB'.utxorpc.v1beta.query.ReadUtxosResponseB\152\SOH\n\ + \\tReadUtxos\DC2&.utxorpc.v1beta.query.ReadUtxosRequest\SUB'.utxorpc.v1beta.query.ReadUtxosResponse\DC2b\n\ + \\vSearchUtxos\DC2(.utxorpc.v1beta.query.SearchUtxosRequest\SUB).utxorpc.v1beta.query.SearchUtxosResponseB\152\SOH\n\ \\CANcom.utxorpc.v1beta.queryB\n\ - \QueryProtoP\SOH\162\STX\ETXUVQ\170\STX\DC4Utxorpc.V1beta.Query\202\STX\DC4Utxorpc\\V1beta\\Query\226\STX Utxorpc\\V1beta\\Query\\GPBMetadata\234\STX\SYNUtxorpc::V1beta::QueryJ\234\&9\n\ - \\a\DC2\ENQ\STX\NUL\176\SOH\SOH\n\ + \QueryProtoP\SOH\162\STX\ETXUVQ\170\STX\DC4Utxorpc.V1beta.Query\202\STX\DC4Utxorpc\\V1beta\\Query\226\STX Utxorpc\\V1beta\\Query\\GPBMetadata\234\STX\SYNUtxorpc::V1beta::QueryJ\144;\n\ + \\a\DC2\ENQ\STX\NUL\177\SOH\SOH\n\ \9\n\ \\SOH\f\DC2\ETX\STX\NUL\DC22// A consistent view of the state of the ledger\n\ \\n\ @@ -5002,14 +5044,17 @@ packedFileDescriptor \\n\ \\ETX\EOT\v\SOH\DC2\ETXR\b\NAK\n\ \8\n\ - \\EOT\EOT\v\STX\NUL\DC2\ETXS\STX\ESC\"+ Predicate is true if tx exhibits pattern.\n\ + \\EOT\EOT\v\STX\NUL\DC2\ETXS\STX$\"+ Predicate is true if tx exhibits pattern.\n\ + \\n\ + \\f\n\ + \\ENQ\EOT\v\STX\NUL\EOT\DC2\ETXS\STX\n\ \\n\ \\f\n\ - \\ENQ\EOT\v\STX\NUL\ACK\DC2\ETXS\STX\DLE\n\ + \\ENQ\EOT\v\STX\NUL\ACK\DC2\ETXS\v\EM\n\ \\f\n\ - \\ENQ\EOT\v\STX\NUL\SOH\DC2\ETXS\DC1\SYN\n\ + \\ENQ\EOT\v\STX\NUL\SOH\DC2\ETXS\SUB\US\n\ \\f\n\ - \\ENQ\EOT\v\STX\NUL\ETX\DC2\ETXS\EM\SUB\n\ + \\ENQ\EOT\v\STX\NUL\ETX\DC2\ETXS\"#\n\ \?\n\ \\EOT\EOT\v\STX\SOH\DC2\ETXT\STX!\"2 Predicate is true if tx doesn't exhibit pattern.\n\ \\n\ @@ -5153,14 +5198,17 @@ packedFileDescriptor \\n\ \\ETX\EOT\SI\SOH\DC2\ETXp\b\SUB\n\ \)\n\ - \\EOT\EOT\SI\STX\NUL\DC2\ETXq\STX\RS\"\FS Pattern to match UTxOs by.\n\ + \\EOT\EOT\SI\STX\NUL\DC2\ETXq\STX'\"\FS Pattern to match UTxOs by.\n\ + \\n\ + \\f\n\ + \\ENQ\EOT\SI\STX\NUL\EOT\DC2\ETXq\STX\n\ \\n\ \\f\n\ - \\ENQ\EOT\SI\STX\NUL\ACK\DC2\ETXq\STX\SI\n\ + \\ENQ\EOT\SI\STX\NUL\ACK\DC2\ETXq\v\CAN\n\ \\f\n\ - \\ENQ\EOT\SI\STX\NUL\SOH\DC2\ETXq\DLE\EM\n\ + \\ENQ\EOT\SI\STX\NUL\SOH\DC2\ETXq\EM\"\n\ \\f\n\ - \\ENQ\EOT\SI\STX\NUL\ETX\DC2\ETXq\FS\GS\n\ + \\ENQ\EOT\SI\STX\NUL\ETX\DC2\ETXq%&\n\ \7\n\ \\EOT\EOT\SI\STX\SOH\DC2\ETXr\STX+\"* Field mask to selectively return fields.\n\ \\n\ @@ -5171,23 +5219,29 @@ packedFileDescriptor \\f\n\ \\ENQ\EOT\SI\STX\SOH\ETX\DC2\ETXr)*\n\ \5\n\ - \\EOT\EOT\SI\STX\STX\DC2\ETXs\STX\SYN\"( The maximum number of items to return.\n\ + \\EOT\EOT\SI\STX\STX\DC2\ETXs\STX\US\"( The maximum number of items to return.\n\ \\n\ \\f\n\ - \\ENQ\EOT\SI\STX\STX\ENQ\DC2\ETXs\STX\a\n\ + \\ENQ\EOT\SI\STX\STX\EOT\DC2\ETXs\STX\n\ + \\n\ + \\f\n\ + \\ENQ\EOT\SI\STX\STX\ENQ\DC2\ETXs\v\DLE\n\ \\f\n\ - \\ENQ\EOT\SI\STX\STX\SOH\DC2\ETXs\b\DC1\n\ + \\ENQ\EOT\SI\STX\STX\SOH\DC2\ETXs\DC1\SUB\n\ \\f\n\ - \\ENQ\EOT\SI\STX\STX\ETX\DC2\ETXs\DC4\NAK\n\ + \\ENQ\EOT\SI\STX\STX\ETX\DC2\ETXs\GS\RS\n\ \R\n\ - \\EOT\EOT\SI\STX\ETX\DC2\ETXt\STX\EM\"E The next_page_token value returned from a previous request, if any.\n\ + \\EOT\EOT\SI\STX\ETX\DC2\ETXt\STX\"\"E The next_page_token value returned from a previous request, if any.\n\ + \\n\ + \\f\n\ + \\ENQ\EOT\SI\STX\ETX\EOT\DC2\ETXt\STX\n\ \\n\ \\f\n\ - \\ENQ\EOT\SI\STX\ETX\ENQ\DC2\ETXt\STX\b\n\ + \\ENQ\EOT\SI\STX\ETX\ENQ\DC2\ETXt\v\DC1\n\ \\f\n\ - \\ENQ\EOT\SI\STX\ETX\SOH\DC2\ETXt\t\DC4\n\ + \\ENQ\EOT\SI\STX\ETX\SOH\DC2\ETXt\DC2\GS\n\ \\f\n\ - \\ENQ\EOT\SI\STX\ETX\ETX\DC2\ETXt\ETB\CAN\n\ + \\ENQ\EOT\SI\STX\ETX\ETX\DC2\ETXt !\n\ \O\n\ \\STX\EOT\DLE\DC2\EOTx\NUL|\SOH\SUBC Response containing the UTxOs that match the requested addresses.\n\ \\n\ @@ -5215,15 +5269,18 @@ packedFileDescriptor \\ENQ\EOT\DLE\STX\SOH\SOH\DC2\ETXz\r\ETB\n\ \\f\n\ \\ENQ\EOT\DLE\STX\SOH\ETX\DC2\ETXz\SUB\ESC\n\ - \a\n\ - \\EOT\EOT\DLE\STX\STX\DC2\ETX{\STX\CAN\"T Token to retrieve the next page of results, or empty if there are no more results.\n\ + \_\n\ + \\EOT\EOT\DLE\STX\STX\DC2\ETX{\STX!\"R Token to retrieve the next page of results, absent if there are no more results.\n\ + \\n\ + \\f\n\ + \\ENQ\EOT\DLE\STX\STX\EOT\DC2\ETX{\STX\n\ \\n\ \\f\n\ - \\ENQ\EOT\DLE\STX\STX\ENQ\DC2\ETX{\STX\b\n\ + \\ENQ\EOT\DLE\STX\STX\ENQ\DC2\ETX{\v\DC1\n\ \\f\n\ - \\ENQ\EOT\DLE\STX\STX\SOH\DC2\ETX{\t\DC3\n\ + \\ENQ\EOT\DLE\STX\STX\SOH\DC2\ETX{\DC2\FS\n\ \\f\n\ - \\ENQ\EOT\DLE\STX\STX\ETX\DC2\ETX{\SYN\ETB\n\ + \\ENQ\EOT\DLE\STX\STX\ETX\DC2\ETX{\US \n\ \:\n\ \\STX\EOT\DC1\DC2\ENQ\DEL\NUL\130\SOH\SOH\SUB- Request to get data (as in plural of datum)\n\ \\n\ @@ -5394,7 +5451,7 @@ packedFileDescriptor \\r\n\ \\ENQ\EOT\SYN\STX\SOH\ETX\DC2\EOT\165\SOH\SUB\ESC\n\ \G\n\ - \\STX\ACK\NUL\DC2\ACK\169\SOH\NUL\176\SOH\SOH\SUB9 Service definition for querying the state of the chain.\n\ + \\STX\ACK\NUL\DC2\ACK\169\SOH\NUL\177\SOH\SOH\SUB9 Service definition for querying the state of the chain.\n\ \\n\ \\v\n\ \\ETX\ACK\NUL\SOH\DC2\EOT\169\SOH\b\DC4\n\ @@ -5415,4 +5472,13 @@ packedFileDescriptor \\r\n\ \\ENQ\ACK\NUL\STX\SOH\STX\DC2\EOT\171\SOH\DLE \n\ \\r\n\ - \\ENQ\ACK\NUL\STX\SOH\ETX\DC2\EOT\171\SOH+ Lens.Family2.LensLike' f s a maybe'match = Data.ProtoLens.Field.field @"maybe'match" +maybe'maxItems :: + forall f s a. + (Prelude.Functor f, + Data.ProtoLens.Field.HasField s "maybe'maxItems" a) => + Lens.Family2.LensLike' f s a +maybe'maxItems = Data.ProtoLens.Field.field @"maybe'maxItems" +maybe'nextToken :: + forall f s a. + (Prelude.Functor f, + Data.ProtoLens.Field.HasField s "maybe'nextToken" a) => + Lens.Family2.LensLike' f s a +maybe'nextToken = Data.ProtoLens.Field.field @"maybe'nextToken" maybe'params :: forall f s a. (Prelude.Functor f, @@ -173,6 +185,12 @@ maybe'predicate :: Data.ProtoLens.Field.HasField s "maybe'predicate" a) => Lens.Family2.LensLike' f s a maybe'predicate = Data.ProtoLens.Field.field @"maybe'predicate" +maybe'startToken :: + forall f s a. + (Prelude.Functor f, + Data.ProtoLens.Field.HasField s "maybe'startToken" a) => + Lens.Family2.LensLike' f s a +maybe'startToken = Data.ProtoLens.Field.field @"maybe'startToken" maybe'summary :: forall f s a. (Prelude.Functor f, diff --git a/cardano-rpc/proto/utxorpc/v1beta/cardano/cardano.proto b/cardano-rpc/proto/utxorpc/v1beta/cardano/cardano.proto index 05c6a803b8..a0109f0519 100644 --- a/cardano-rpc/proto/utxorpc/v1beta/cardano/cardano.proto +++ b/cardano-rpc/proto/utxorpc/v1beta/cardano/cardano.proto @@ -516,15 +516,15 @@ message UpdateDRepCert { // Pattern of an address that can be used to evaluate matching predicates. message AddressPattern { - bytes exact_address = 1; // The address should match this exact address value. - bytes payment_part = 2; // The payment part of the address should match this value. - bytes delegation_part = 3; // The delegation part of the address should match this value. + optional bytes exact_address = 1; // The address should match this exact address value. + optional bytes payment_part = 2; // The payment part of the address should match this value. + optional bytes delegation_part = 3; // The delegation part of the address should match this value. } // Pattern of a native asset that can be used to evaluate matching predicates. message AssetPattern { - bytes policy_id = 1; // The asset should belong to this policy id - bytes asset_name = 2; // The asset should present this name + optional bytes policy_id = 1; // The asset should belong to this policy id + optional bytes asset_name = 2; // The asset should present this name } // Pattern of a certificate that can be used to evaluate matching predicates. @@ -561,8 +561,8 @@ message PoolRetirementPattern { // Pattern of a tx output that can be used to evaluate matching predicates. message TxOutputPattern { - AddressPattern address = 1; // Match any address in the output that exhibits this pattern. - AssetPattern asset = 2; // Match any asset in the output that exhibits this pattern. + optional AddressPattern address = 1; // Match any address in the output that exhibits this pattern. + optional AssetPattern asset = 2; // Match any asset in the output that exhibits this pattern. } // Pattern of a Tx that can be used to evaluate matching predicates. diff --git a/cardano-rpc/proto/utxorpc/v1beta/query/query.proto b/cardano-rpc/proto/utxorpc/v1beta/query/query.proto index 302941e36a..8040718ad8 100644 --- a/cardano-rpc/proto/utxorpc/v1beta/query/query.proto +++ b/cardano-rpc/proto/utxorpc/v1beta/query/query.proto @@ -81,7 +81,7 @@ message AnyUtxoPattern { // Represents a simple utxo predicate that can composed to create more complex ones message UtxoPredicate { - AnyUtxoPattern match = 1; // Predicate is true if tx exhibits pattern. + optional AnyUtxoPattern match = 1; // Predicate is true if tx exhibits pattern. repeated UtxoPredicate not = 2; // Predicate is true if tx doesn't exhibit pattern. repeated UtxoPredicate all_of = 3; // Predicate is true if utxo exhibits all of the patterns. repeated UtxoPredicate any_of = 4; // Predicate is true if utxo exhibits any of the patterns. @@ -111,17 +111,17 @@ message ReadUtxosResponse { // Request to search for UTxO based on a pattern. message SearchUtxosRequest { - UtxoPredicate predicate = 1; // Pattern to match UTxOs by. + optional UtxoPredicate predicate = 1; // Pattern to match UTxOs by. google.protobuf.FieldMask field_mask = 2; // Field mask to selectively return fields. - int32 max_items = 3; // The maximum number of items to return. - string start_token = 4; // The next_page_token value returned from a previous request, if any. + optional int32 max_items = 3; // The maximum number of items to return. + optional string start_token = 4; // The next_page_token value returned from a previous request, if any. } // Response containing the UTxOs that match the requested addresses. message SearchUtxosResponse { repeated AnyUtxoData items = 1; // List of UTxOs. ChainPoint ledger_tip = 2; // The chain point that represent the ledger current position. - string next_token = 3; // Token to retrieve the next page of results, or empty if there are no more results. + optional string next_token = 3; // Token to retrieve the next page of results, absent if there are no more results. } // Request to get data (as in plural of datum) @@ -170,6 +170,7 @@ message ReadTxResponse { service QueryService { rpc ReadParams(ReadParamsRequest) returns (ReadParamsResponse); // Get overall chain state. rpc ReadUtxos(ReadUtxosRequest) returns (ReadUtxosResponse); // Read specific UTxOs by reference. + rpc SearchUtxos(SearchUtxosRequest) returns (SearchUtxosResponse); // Search for UTxO based on a pattern. // TODO: decide if we want to expand the scope // rpc DumpUtxos(ReadUtxosRequest) returns (stream ReadUtxosResponse); // Dump all available utxos diff --git a/cardano-rpc/src/Cardano/Rpc/Proto/Api/UtxoRpc/Query.hs b/cardano-rpc/src/Cardano/Rpc/Proto/Api/UtxoRpc/Query.hs index 02b7433fce..f9167bf17e 100644 --- a/cardano-rpc/src/Cardano/Rpc/Proto/Api/UtxoRpc/Query.hs +++ b/cardano-rpc/src/Cardano/Rpc/Proto/Api/UtxoRpc/Query.hs @@ -27,7 +27,10 @@ import Proto.Utxorpc.V1beta.Cardano.Cardano_Fields hiding , vec'values ) import Proto.Utxorpc.V1beta.Query.Query -import Proto.Utxorpc.V1beta.Query.Query_Fields +import Proto.Utxorpc.V1beta.Query.Query_Fields hiding + ( maybe'tx + , tx + ) type instance RequestMetadata (Protobuf QueryService meth) = NoMetadata diff --git a/cardano-rpc/src/Cardano/Rpc/Proto/Api/UtxoRpc/Submit.hs b/cardano-rpc/src/Cardano/Rpc/Proto/Api/UtxoRpc/Submit.hs index 6179d960c1..a0ac14466c 100644 --- a/cardano-rpc/src/Cardano/Rpc/Proto/Api/UtxoRpc/Submit.hs +++ b/cardano-rpc/src/Cardano/Rpc/Proto/Api/UtxoRpc/Submit.hs @@ -11,7 +11,27 @@ import Network.GRPC.Common import Network.GRPC.Common.Protobuf import Proto.Utxorpc.V1beta.Submit.Submit -import Proto.Utxorpc.V1beta.Submit.Submit_Fields +import Proto.Utxorpc.V1beta.Submit.Submit_Fields hiding + ( allOf + , anyOf + , cardano + , fieldMask + , items + , match + , maybe'cardano + , maybe'chain + , maybe'fieldMask + , maybe'match + , maybe'parsedState + , maybe'predicate + , nativeBytes + , not + , predicate + , vec'allOf + , vec'anyOf + , vec'items + , vec'not + ) type instance RequestMetadata (Protobuf SubmitService meth) = NoMetadata diff --git a/cardano-rpc/src/Cardano/Rpc/Server.hs b/cardano-rpc/src/Cardano/Rpc/Server.hs index a7469b7103..c165ff0815 100644 --- a/cardano-rpc/src/Cardano/Rpc/Server.hs +++ b/cardano-rpc/src/Cardano/Rpc/Server.hs @@ -56,6 +56,7 @@ methodsUtxoRpc methodsUtxoRpc = Method (mkNonStreaming $ wrapInSpan TraceRpcQueryParamsSpan . readParamsMethod) . Method (mkNonStreaming $ wrapInSpan TraceRpcQueryReadUtxosSpan . readUtxosMethod) + . Method (mkNonStreaming $ wrapInSpan TraceRpcQuerySearchUtxosSpan . searchUtxosMethod) $ NoMoreMethods methodsUtxoRpcSubmit diff --git a/cardano-rpc/src/Cardano/Rpc/Server/Internal/Tracing.hs b/cardano-rpc/src/Cardano/Rpc/Server/Internal/Tracing.hs index 1e421c1276..79dbb31343 100644 --- a/cardano-rpc/src/Cardano/Rpc/Server/Internal/Tracing.hs +++ b/cardano-rpc/src/Cardano/Rpc/Server/Internal/Tracing.hs @@ -27,6 +27,8 @@ data TraceRpcQuery TraceRpcQueryParamsSpan TraceSpanEvent | -- | Span trace marking ReadUtxos query TraceRpcQueryReadUtxosSpan TraceSpanEvent + | -- | Span trace marking SearchUtxos query + TraceRpcQuerySearchUtxosSpan TraceSpanEvent deriving Show instance Pretty TraceRpc where @@ -53,6 +55,8 @@ instance Pretty TraceRpcQuery where TraceRpcQueryParamsSpan (SpanEnd _) -> "Finished query params method" TraceRpcQueryReadUtxosSpan (SpanBegin _) -> "Started query read UTXO method" TraceRpcQueryReadUtxosSpan (SpanEnd _) -> "Finished query read UTXO method" + TraceRpcQuerySearchUtxosSpan (SpanBegin _) -> "Started query search UTXO method" + TraceRpcQuerySearchUtxosSpan (SpanEnd _) -> "Finished query search UTXO method" instance Error TraceRpcQuery where prettyError = pretty diff --git a/cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/Predicate.hs b/cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/Predicate.hs new file mode 100644 index 0000000000..18b8238bc6 --- /dev/null +++ b/cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/Predicate.hs @@ -0,0 +1,136 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Cardano.Rpc.Server.Internal.UtxoRpc.Predicate + ( matchesUtxoPredicate + , extractAddressesFromPredicate + , matchesAddressPattern + , matchesAssetPattern + , matchesTxOutputPattern + , matchesAnyUtxoPattern + , serialisePaymentCredential + , serialiseStakeCredential + ) +where + +import Cardano.Api.Address +import Cardano.Api.Era +import Cardano.Api.Serialise.Raw +import Cardano.Api.Tx +import Cardano.Api.Value +import Cardano.Rpc.Proto.Api.UtxoRpc.Query qualified as UtxoRpc + +import RIO hiding (toList) + +import Data.ByteString qualified as BS +import Data.Set qualified as Set +import GHC.IsList + +-- | Check if a UTxO entry matches a 'UtxoPredicate'. +-- All present fields are combined with AND logic. +matchesUtxoPredicate + :: IsCardanoEra era + => UtxoRpc.UtxoPredicate + -> TxOut CtxUTxO era + -> Bool +matchesUtxoPredicate p txOut = + all (`matchesAnyUtxoPattern` txOut) (p ^. UtxoRpc.maybe'match) + && not (any (`matchesUtxoPredicate` txOut) (p ^. UtxoRpc.not)) + && all (`matchesUtxoPredicate` txOut) (p ^. UtxoRpc.allOf) + && (null (p ^. UtxoRpc.anyOf) || any (`matchesUtxoPredicate` txOut) (p ^. UtxoRpc.anyOf)) + +-- | Check if a UTxO entry matches an 'AnyUtxoPattern'. +-- Delegates to the Cardano-specific 'TxOutputPattern' if present. +matchesAnyUtxoPattern + :: IsCardanoEra era + => UtxoRpc.AnyUtxoPattern + -> TxOut CtxUTxO era + -> Bool +matchesAnyUtxoPattern pat txOut = + all (`matchesTxOutputPattern` txOut) (pat ^. UtxoRpc.maybe'cardano) + +-- | Check if a tx output matches a 'TxOutputPattern'. +-- Address and asset filters are combined with AND; absent fields are vacuously true. +matchesTxOutputPattern + :: IsCardanoEra era + => UtxoRpc.TxOutputPattern + -> TxOut CtxUTxO era + -> Bool +matchesTxOutputPattern pat (TxOut addrInEra txOutValue _datum _script) = + all (`matchesAddressPattern` addrInEra) (pat ^. UtxoRpc.maybe'address) + && all (`matchesAssetPattern` txOutValueToValue txOutValue) (pat ^. UtxoRpc.maybe'asset) + +-- | Check if an address matches an 'AddressPattern'. +-- All present fields (exact, payment, delegation) must match (AND logic). +-- Byron addresses only support exact matching; payment\/delegation filters reject them. +matchesAddressPattern + :: IsCardanoEra era + => UtxoRpc.AddressPattern + -> AddressInEra era + -> Bool +matchesAddressPattern pat addr = + exactMatch && paymentMatch && delegationMatch + where + -- proto3 optional bytes defaults to empty when absent + matchesRawField field actual = BS.null field || field == actual + + exactMatch = matchesRawField (pat ^. UtxoRpc.exactAddress) $ serialiseToRawBytes addr + paymentMatch = case addr of + AddressInEra ShelleyAddressInEra{} (ShelleyAddress _ payCred _) -> + matchesRawField (pat ^. UtxoRpc.paymentPart) . serialisePaymentCredential $ + fromShelleyPaymentCredential payCred + _ -> BS.null $ pat ^. UtxoRpc.paymentPart + delegationMatch = case addr of + AddressInEra ShelleyAddressInEra{} (ShelleyAddress _ _ stakeRef) -> + case fromShelleyStakeReference stakeRef of + StakeAddressByValue cred -> + matchesRawField (pat ^. UtxoRpc.delegationPart) $ serialiseStakeCredential cred + _ -> BS.null $ pat ^. UtxoRpc.delegationPart + _ -> BS.null $ pat ^. UtxoRpc.delegationPart + +-- | Serialise a 'PaymentCredential' to raw bytes (the key or script hash). +serialisePaymentCredential :: PaymentCredential -> ByteString +serialisePaymentCredential (PaymentCredentialByKey h) = serialiseToRawBytes h +serialisePaymentCredential (PaymentCredentialByScript h) = serialiseToRawBytes h + +-- | Serialise a 'StakeCredential' to raw bytes (the key or script hash). +serialiseStakeCredential :: StakeCredential -> ByteString +serialiseStakeCredential (StakeCredentialByKey h) = serialiseToRawBytes h +serialiseStakeCredential (StakeCredentialByScript h) = serialiseToRawBytes h + +-- | Check if a 'Value' contains a native asset matching an 'AssetPattern'. +-- Ada entries are always skipped; zero-quantity entries do not match. +matchesAssetPattern + :: UtxoRpc.AssetPattern + -> Value + -> Bool +matchesAssetPattern pat value = + any matchesEntry (toList value) + where + patternPolicy = pat ^. UtxoRpc.policyId + patternTokenName = pat ^. UtxoRpc.assetName + matchesEntry (AssetId policy tokenName, Quantity qty) = + (BS.null patternPolicy || serialiseToRawBytes policy == patternPolicy) + && (BS.null patternTokenName || serialiseToRawBytes tokenName == patternTokenName) + && qty > 0 + matchesEntry (AdaAssetId, _) = False + +-- | Try to extract a set of exact addresses from the predicate for use with 'QueryUTxOByAddress'. +-- Returns 'Just' if the optimization is applicable, 'Nothing' otherwise. +extractAddressesFromPredicate :: UtxoRpc.UtxoPredicate -> Maybe (Set AddressAny) +extractAddressesFromPredicate p = + case (p ^. UtxoRpc.maybe'match, p ^. UtxoRpc.not, p ^. UtxoRpc.allOf, p ^. UtxoRpc.anyOf) of + (Just pat, [], [], []) -> extractAddressFromPattern pat + (Nothing, [], [], anyPreds@(_ : _)) -> + Set.unions <$> traverse extractAddressesFromPredicate anyPreds + _ -> Nothing + where + extractAddressFromPattern :: UtxoRpc.AnyUtxoPattern -> Maybe (Set AddressAny) + extractAddressFromPattern pat = do + txoPat <- pat ^. UtxoRpc.maybe'cardano + addrPat <- txoPat ^. UtxoRpc.maybe'address + let exact = addrPat ^. UtxoRpc.exactAddress + guard $ not (BS.null exact) + addrAny <- either (const Nothing) Just $ deserialiseFromRawBytes AsAddressAny exact + pure $ Set.singleton addrAny diff --git a/cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/Query.hs b/cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/Query.hs index de245c691c..3d9364c043 100644 --- a/cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/Query.hs +++ b/cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/Query.hs @@ -4,6 +4,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE QuantifiedConstraints #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -12,26 +13,35 @@ module Cardano.Rpc.Server.Internal.UtxoRpc.Query ( readParamsMethod , readUtxosMethod + , searchUtxosMethod + , paginateByTxIn ) where import Cardano.Api import Cardano.Api.Experimental.Era +import Cardano.Api.Parser.Text qualified as P import Cardano.Rpc.Proto.Api.UtxoRpc.Query qualified as U5c import Cardano.Rpc.Proto.Api.UtxoRpc.Query qualified as UtxoRpc import Cardano.Rpc.Server.Internal.Error import Cardano.Rpc.Server.Internal.Monad import Cardano.Rpc.Server.Internal.Orphans () +import Cardano.Rpc.Server.Internal.UtxoRpc.Predicate import Cardano.Rpc.Server.Internal.UtxoRpc.Type import RIO hiding (toList) +import Control.Error.Util (hush) import Data.Default +import Data.List (sortBy) import Data.ProtoLens (defMessage) import Data.Time.Clock (UTCTime) import GHC.IsList import Network.GRPC.Spec +-- | Handle the @ReadParams@ RPC method. +-- Queries the node for current protocol parameters and returns them +-- along with the ledger tip. readParamsMethod :: MonadRpc e m => Proto UtxoRpc.ReadParamsRequest @@ -61,6 +71,9 @@ readParamsMethod _req = do & U5c.ledgerTip .~ mkChainPointMsg chainPoint blockNo timestamp & U5c.values . U5c.cardano .~ obtainCommonConstraints eon (protocolParamsToUtxoRpcPParams eon pparams) +-- | Handle the @ReadUtxos@ RPC method. +-- Looks up specific UTxO entries by their 'TxIn' keys and returns them +-- along with the ledger tip. readUtxosMethod :: MonadRpc e m => Proto UtxoRpc.ReadUtxosRequest @@ -69,9 +82,6 @@ readUtxosMethod req = do utxoFilter <- if not (null $ req ^. U5c.keys) then QueryUTxOByTxIn . fromList <$> mapM txoRefToTxIn (req ^. U5c.keys) - -- TODO: reimplement this part as SearchUtxosRequest - -- \| Just addressesProto <- req ^. U5c.maybe'cardanoAddresses -> - -- QueryUTxOByAddress . fromList <$> mapM readAddress (addressesProto ^. U5c.items) else pure QueryUTxOWhole nodeConnInfo <- grab @@ -99,10 +109,82 @@ readUtxosMethod req = do txId' <- throwEither $ deserialiseFromRawBytes AsTxId $ r ^. U5c.hash pure $ TxIn txId' (TxIx . fromIntegral $ r ^. U5c.index) --- TODO: reimplement this part as SearchUtxosRequest --- readAddress :: MonadRpc e m => ByteString -> m AddressAny --- readAddress = --- throwEither . first stringException . P.runParser parseAddressAny <=< throwEither . T.decodeUtf8' +-- | Handle the @SearchUtxos@ RPC method. +-- Filters the UTxO set by a predicate and returns a paginated result. +-- When the predicate contains exact address matches, the query is narrowed +-- to those addresses; otherwise the entire UTxO set is fetched. +searchUtxosMethod + :: MonadRpc e m + => Proto UtxoRpc.SearchUtxosRequest + -> m (Proto UtxoRpc.SearchUtxosResponse) +searchUtxosMethod req = do + -- TODO: field masks are ignored for now (same as readParamsMethod) + let mPredicate = fmap getProto $ req ^. U5c.maybe'predicate + maxItems = req ^. U5c.maxItems + startToken = req ^. U5c.maybe'startToken + + -- Determine query strategy: use address-based query if possible, otherwise fetch whole UTxO + let utxoFilter = case mPredicate >>= extractAddressesFromPredicate of + Just addrs -> QueryUTxOByAddress addrs + _ -> QueryUTxOWhole + + nodeConnInfo <- grab + AnyCardanoEra era <- liftIO . throwExceptT $ determineEra nodeConnInfo + eon <- forEraInEon @Era era (error "Minimum Conway era required") pure + + let target = VolatileTip + (utxo, chainPoint, blockNo, systemStart, eraHistory) <- liftIO . (throwEither =<<) $ executeLocalStateQueryExpr nodeConnInfo target $ do + utxo <- throwEither =<< throwEither =<< queryUtxo (convert eon) utxoFilter + chainPoint <- throwEither =<< queryChainPoint + blockNo <- throwEither =<< queryChainBlockNo + systemStart <- throwEither =<< querySystemStart + eraHistory <- throwEither =<< queryEraHistory + pure (utxo, chainPoint, blockNo, systemStart, eraHistory) + + timestamp <- slotToTimestamp systemStart eraHistory chainPoint + + obtainCommonConstraints eon $ do + let filtered = + maybe id (\p -> filter $ matchesUtxoPredicate p . snd) mPredicate $ + toList utxo + + let (page, nextTok) = paginateByTxIn filtered startToken maxItems + + pure $ + defMessage + & U5c.ledgerTip .~ mkChainPointMsg chainPoint blockNo timestamp + & U5c.items .~ map (uncurry txInTxOutToAnyUtxoData) page + & U5c.maybe'nextToken .~ nextTok + +-- | Paginate a list of UTxO entries using cursor-based pagination. +-- Items are sorted by 'TxIn'\'s 'Ord' instance (lexicographic on 'TxId', then numeric on 'TxIx'). +-- The start token is the 'renderTxIn' of the last item on the previous page; +-- all items up to and including it are skipped, so the next page begins +-- immediately after that cursor. +paginateByTxIn + :: [(TxIn, a)] + -- ^ UTxO entries to paginate + -> Maybe Text + -- ^ start token: the 'renderTxIn' of the last 'TxIn' from the previous page, + -- or 'Nothing' for the first page + -> Int32 + -- ^ maximum number of items per page (0 defaults to 'defaultPageSize', + -- capped at 'maxPageSize') + -> ([(TxIn, a)], Maybe Text) + -- ^ page of results and the next start token ('Nothing' when there are no more pages) +paginateByTxIn items startToken maxItems = (page, nextToken) + where + sorted = sortBy (compare `on` fst) items + afterToken = maybe sorted dropAfterCursor $ hush . P.runParser parseTxIn =<< startToken + dropAfterCursor cursor = dropWhile (\(txIn, _) -> txIn <= cursor) sorted + limit = min (if maxItems > 0 then fromIntegral maxItems else defaultPageSize) maxPageSize + page = take limit afterToken + hasMore = not . null $ drop limit afterToken + nextToken = do + guard hasMore + pure . renderTxIn . fst $ last page + defaultPageSize = 100 + maxPageSize = 10_000 slotToTimestamp :: HasCallStack diff --git a/cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/Type.hs b/cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/Type.hs index 93ba6e75d2..211c095592 100644 --- a/cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/Type.hs +++ b/cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/Type.hs @@ -12,6 +12,7 @@ module Cardano.Rpc.Server.Internal.UtxoRpc.Type ( utxoRpcPParamsToProtocolParams , utxoToUtxoRpcAnyUtxoData + , txInTxOutToAnyUtxoData , anyUtxoDataUtxoRpcToUtxo , txOutToUtxoRpcTxOutput , utxoRpcTxOutputToTxOut @@ -485,17 +486,20 @@ scriptDataToUtxoRpcPlutusData = \case defMessage & U5c.constr .~ constr utxoToUtxoRpcAnyUtxoData :: forall era. IsEra era => UTxO era -> [Proto UtxoRpc.AnyUtxoData] -utxoToUtxoRpcAnyUtxoData utxo = - toList utxo <&> \(txIn, txOut) -> do - let era = useEra @era - txOutCbor = - obtainCommonConstraints era $ - CBOR.serialize' $ - toShelleyTxOut (convert era) txOut - defMessage - & U5c.nativeBytes .~ txOutCbor - & U5c.txoRef .~ inject txIn - & U5c.cardano .~ txOutToUtxoRpcTxOutput txOut +utxoToUtxoRpcAnyUtxoData = map (uncurry txInTxOutToAnyUtxoData) . toList + +txInTxOutToAnyUtxoData + :: forall era. IsEra era => TxIn -> TxOut CtxUTxO era -> Proto UtxoRpc.AnyUtxoData +txInTxOutToAnyUtxoData txIn txOut = do + let era = useEra @era + txOutCbor = + obtainCommonConstraints era $ + CBOR.serialize' $ + toShelleyTxOut (convert era) txOut + defMessage + & U5c.nativeBytes .~ txOutCbor + & U5c.txoRef .~ inject txIn + & U5c.cardano .~ txOutToUtxoRpcTxOutput txOut anyUtxoDataUtxoRpcToUtxo :: forall era m diff --git a/cardano-rpc/test/cardano-rpc-test/Test/Cardano/Rpc/Pagination.hs b/cardano-rpc/test/cardano-rpc-test/Test/Cardano/Rpc/Pagination.hs new file mode 100644 index 0000000000..ab90b9727f --- /dev/null +++ b/cardano-rpc/test/cardano-rpc-test/Test/Cardano/Rpc/Pagination.hs @@ -0,0 +1,89 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Test.Cardano.Rpc.Pagination where + +import Cardano.Api (TxIn) +import Cardano.Api.Parser.Text qualified as P +import Cardano.Api.Tx (parseTxIn, renderTxIn) +import Cardano.Rpc.Server.Internal.UtxoRpc.Query (paginateByTxIn) + +import Data.Maybe (isJust, isNothing) +import Data.Text (Text) +import Data.Text qualified as T +import GHC.Stack (HasCallStack) + +import Hedgehog as H +import Hedgehog.Extras qualified as H + +-- --------------------------------------------------------------------------- +-- Pagination +-- --------------------------------------------------------------------------- + +hprop_paginate_empty_input :: Property +hprop_paginate_empty_input = H.propertyOnce $ do + let (page, nextToken) = paginateByTxIn ([] :: [(TxIn, ())]) Nothing 10 + page === [] + H.assertWith nextToken isNothing + +hprop_paginate_all_items_fit :: Property +hprop_paginate_all_items_fit = H.propertyOnce $ do + let items = [mkItem 0, mkItem 1, mkItem 2] + (page, nextToken) = paginateByTxIn items Nothing 10 + length page === 3 + H.assertWith nextToken isNothing + +hprop_paginate_respects_limit :: Property +hprop_paginate_respects_limit = H.propertyOnce $ do + let items = [mkItem i | i <- [0 .. 9]] + (page, nextToken) = paginateByTxIn items Nothing 3 + length page === 3 + H.assertWith nextToken isJust + +hprop_paginate_multi_digit_indices :: Property +hprop_paginate_multi_digit_indices = H.propertyOnce $ do + -- Regression: indices 5 and 10 must not produce duplicates across pages + let items = [mkItem i | i <- [0, 1, 2, 5, 10, 11, 20]] + (page1, token1) = paginateByTxIn items Nothing 3 + length page1 === 3 + H.assertWith token1 isJust + let (page2, token2) = paginateByTxIn items token1 3 + length page2 === 3 + H.assertWith token2 isJust + let (page3, token3) = paginateByTxIn items token2 3 + length page3 === 1 + H.assertWith token3 isNothing + -- All items returned exactly once + let allRendered = map (renderTxIn . fst) $ page1 ++ page2 ++ page3 + length allRendered === 7 + +hprop_paginate_default_limit :: Property +hprop_paginate_default_limit = H.propertyOnce $ do + let items = [mkItem i | i <- [0 .. 49]] + (page, nextToken) = paginateByTxIn items Nothing 0 + -- Default page size is 100; all 50 items fit + length page === 50 + H.assertWith nextToken isNothing + +hprop_paginate_sequential_pages_cover_all :: Property +hprop_paginate_sequential_pages_cover_all = H.propertyOnce $ do + let items = [mkItem i | i <- [0 .. 4]] + (page1, token1) = paginateByTxIn items Nothing 2 + length page1 === 2 + let (page2, token2) = paginateByTxIn items token1 2 + length page2 === 2 + let (page3, token3) = paginateByTxIn items token2 2 + length page3 === 1 + H.assertWith token3 isNothing + +-- --------------------------------------------------------------------------- +-- Helpers +-- --------------------------------------------------------------------------- + +hash01 :: Text +hash01 = "0101010101010101010101010101010101010101010101010101010101010101" + +mkTxIn :: HasCallStack => Text -> TxIn +mkTxIn = either error id . P.runParser parseTxIn + +mkItem :: Int -> (TxIn, ()) +mkItem i = (mkTxIn $ hash01 <> "#" <> T.show i, ()) diff --git a/cardano-rpc/test/cardano-rpc-test/Test/Cardano/Rpc/Predicate.hs b/cardano-rpc/test/cardano-rpc-test/Test/Cardano/Rpc/Predicate.hs new file mode 100644 index 0000000000..f8ee08d493 --- /dev/null +++ b/cardano-rpc/test/cardano-rpc-test/Test/Cardano/Rpc/Predicate.hs @@ -0,0 +1,542 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedLists #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Test.Cardano.Rpc.Predicate where + +import Cardano.Api.Address +import Cardano.Api.Era (MaryEraOnwards, ShelleyBasedEra) +import Cardano.Api.Experimental.Era +import Cardano.Api.Plutus (ReferenceScript (..)) +import Cardano.Api.Serialise.Raw +import Cardano.Api.Tx +import Cardano.Api.Value +import Cardano.Rpc.Proto.Api.UtxoRpc.Query qualified as U5c +import Cardano.Rpc.Server.Internal.UtxoRpc.Predicate + +import RIO + +import Data.ByteString qualified as BS +import Data.ProtoLens (defMessage) +import Data.Set qualified as Set + +import Test.Gen.Cardano.Api.Typed + ( genAddressByron + , genAddressInEra + , genAddressShelley + , genAssetName + , genNetworkId + , genPaymentCredential + , genPolicyId + , genPositiveQuantity + , genStakeAddressReference + , genStakeCredential + , genTxOutUTxOContext + ) + +import Hedgehog as H +import Hedgehog.Extras qualified as H + +-- --------------------------------------------------------------------------- +-- A. Default/empty patterns match everything +-- --------------------------------------------------------------------------- + +hprop_default_predicate_matches_everything :: Property +hprop_default_predicate_matches_everything = H.property $ do + txOut <- forAll genTxOut + H.assertWith txOut $ matchesUtxoPredicate defMessage + +hprop_default_address_pattern_matches_any_address :: Property +hprop_default_address_pattern_matches_any_address = H.property $ do + address <- forAll $ genAddressInEra sbe + H.assertWith address $ matchesAddressPattern defMessage + +hprop_default_asset_pattern_matches_value_with_native_asset :: Property +hprop_default_asset_pattern_matches_value_with_native_asset = H.property $ do + (value, _) <- forAll genValueWithNativeAsset + H.assertWith value $ matchesAssetPattern defMessage + +-- --------------------------------------------------------------------------- +-- B. Address matching — exact +-- --------------------------------------------------------------------------- + +hprop_exact_address_matches_same :: Property +hprop_exact_address_matches_same = H.property $ do + address <- forAll $ genAddressInEra sbe + let addressPattern = defMessage & U5c.exactAddress .~ serialiseToRawBytes address + H.assertWith address $ matchesAddressPattern addressPattern + +hprop_exact_byron_address_matches_same :: Property +hprop_exact_byron_address_matches_same = H.property $ do + byronAddress <- forAll genAddressByron + let address :: AddressInEra TestEra + address = AddressInEra ByronAddressInAnyEra byronAddress + addressPattern = defMessage & U5c.exactAddress .~ serialiseToRawBytes address + H.assertWith address $ matchesAddressPattern addressPattern + +hprop_exact_address_rejects_different :: Property +hprop_exact_address_rejects_different = H.property $ do + address1 <- forAll $ genAddressInEra sbe + address2 <- forAll $ genAddressInEra sbe + let addressPattern = defMessage & U5c.exactAddress .~ serialiseToRawBytes address1 + when (serialiseToRawBytes address1 /= serialiseToRawBytes address2) $ + H.assertWith address2 $ + not . matchesAddressPattern addressPattern + +hprop_exact_byron_address_rejects_shelley :: Property +hprop_exact_byron_address_rejects_shelley = H.property $ do + byronAddress <- forAll genAddressByron + shelleyAddress <- forAll $ genAddressInEra sbe + let address :: AddressInEra TestEra + address = AddressInEra ByronAddressInAnyEra byronAddress + addressPattern = defMessage & U5c.exactAddress .~ serialiseToRawBytes address + -- Byron and Shelley addresses always differ (different header byte) + H.assertWith shelleyAddress $ not . matchesAddressPattern addressPattern + +hprop_payment_part_ignored_for_byron :: Property +hprop_payment_part_ignored_for_byron = H.property $ do + byronAddress <- forAll genAddressByron + credential <- forAll genPaymentCredential + let address :: AddressInEra TestEra + address = AddressInEra ByronAddressInAnyEra byronAddress + addressPattern = defMessage & U5c.paymentPart .~ serialisePaymentCredential credential + -- Byron addresses have no payment credential, so a paymentPart filter must reject + H.assertWith address $ not . matchesAddressPattern addressPattern + +hprop_delegation_part_ignored_for_byron :: Property +hprop_delegation_part_ignored_for_byron = H.property $ do + byronAddress <- forAll genAddressByron + credential <- forAll genStakeCredential + let address :: AddressInEra TestEra + address = AddressInEra ByronAddressInAnyEra byronAddress + addressPattern = defMessage & U5c.delegationPart .~ serialiseStakeCredential credential + -- Byron addresses have no delegation credential, so a delegationPart filter must reject + H.assertWith address $ not . matchesAddressPattern addressPattern + +-- --------------------------------------------------------------------------- +-- C. Address matching — payment & delegation parts +-- --------------------------------------------------------------------------- + +hprop_payment_part_matches_same_credential :: Property +hprop_payment_part_matches_same_credential = H.property $ do + credential <- forAll genPaymentCredential + network <- forAll genNetworkId + stakeReference <- forAll genStakeAddressReference + let shelleyAddress = makeShelleyAddress network credential stakeReference + address = shelleyAddressInEra sbe shelleyAddress + addressPattern = defMessage & U5c.paymentPart .~ serialisePaymentCredential credential + H.assertWith address $ matchesAddressPattern addressPattern + +hprop_payment_part_rejects_different_credential :: Property +hprop_payment_part_rejects_different_credential = H.property $ do + credential1 <- forAll genPaymentCredential + credential2 <- forAll genPaymentCredential + network <- forAll genNetworkId + stakeReference <- forAll genStakeAddressReference + let shelleyAddress = makeShelleyAddress network credential1 stakeReference + address = shelleyAddressInEra sbe shelleyAddress + addressPattern = defMessage & U5c.paymentPart .~ serialisePaymentCredential credential2 + when (serialisePaymentCredential credential1 /= serialisePaymentCredential credential2) $ + H.assertWith address $ + not . matchesAddressPattern addressPattern + +hprop_delegation_part_matches_same_credential :: Property +hprop_delegation_part_matches_same_credential = H.property $ do + stakeCredential <- forAll genStakeCredential + paymentCredential <- forAll genPaymentCredential + network <- forAll genNetworkId + let stakeReference = StakeAddressByValue stakeCredential + shelleyAddress = makeShelleyAddress network paymentCredential stakeReference + address = shelleyAddressInEra sbe shelleyAddress + addressPattern = defMessage & U5c.delegationPart .~ serialiseStakeCredential stakeCredential + H.assertWith address $ matchesAddressPattern addressPattern + +hprop_delegation_part_rejects_when_no_stake_address :: Property +hprop_delegation_part_rejects_when_no_stake_address = H.property $ do + stakeCredential <- forAll genStakeCredential + paymentCredential <- forAll genPaymentCredential + network <- forAll genNetworkId + let shelleyAddress = makeShelleyAddress network paymentCredential NoStakeAddress + address = shelleyAddressInEra sbe shelleyAddress + addressPattern = defMessage & U5c.delegationPart .~ serialiseStakeCredential stakeCredential + H.assertWith address $ not . matchesAddressPattern addressPattern + +hprop_address_pattern_all_fields_must_match :: Property +hprop_address_pattern_all_fields_must_match = H.property $ do + paymentCredential <- forAll genPaymentCredential + stakeCredential <- forAll genStakeCredential + network <- forAll genNetworkId + let stakeReference = StakeAddressByValue stakeCredential + shelleyAddress = makeShelleyAddress network paymentCredential stakeReference + address = shelleyAddressInEra sbe shelleyAddress + -- Pattern with all three fields matching + addressPattern = + defMessage + & U5c.exactAddress .~ serialiseToRawBytes shelleyAddress + & U5c.paymentPart .~ serialisePaymentCredential paymentCredential + & U5c.delegationPart .~ serialiseStakeCredential stakeCredential + -- Should match when all fields agree + H.assertWith address $ matchesAddressPattern addressPattern + -- Break the payment part → fail + otherPaymentCredential <- forAll genPaymentCredential + when + (serialisePaymentCredential paymentCredential /= serialisePaymentCredential otherPaymentCredential) + $ do + let brokenPattern = addressPattern & U5c.paymentPart .~ serialisePaymentCredential otherPaymentCredential + H.assertWith address $ not . matchesAddressPattern brokenPattern + -- Break the exact address → fail + otherAddress <- forAll genAddressShelley + when (serialiseToRawBytes shelleyAddress /= serialiseToRawBytes otherAddress) $ do + let brokenPattern = addressPattern & U5c.exactAddress .~ serialiseToRawBytes otherAddress + H.assertWith address $ not . matchesAddressPattern brokenPattern + -- Break the delegation part → fail + otherStakeCredential <- forAll genStakeCredential + when + (serialiseStakeCredential stakeCredential /= serialiseStakeCredential otherStakeCredential) + $ do + let brokenPattern = addressPattern & U5c.delegationPart .~ serialiseStakeCredential otherStakeCredential + H.assertWith address $ not . matchesAddressPattern brokenPattern + +-- --------------------------------------------------------------------------- +-- D. Asset matching +-- --------------------------------------------------------------------------- + +hprop_asset_pattern_matches_by_policy :: Property +hprop_asset_pattern_matches_by_policy = H.property $ do + (value, AssetId policy _tokenName) <- forAll genValueWithNativeAsset + let assetPattern = defMessage & U5c.policyId .~ serialiseToRawBytes policy + H.assertWith value $ matchesAssetPattern assetPattern + +hprop_asset_pattern_matches_by_policy_and_name :: Property +hprop_asset_pattern_matches_by_policy_and_name = H.property $ do + (value, AssetId policy tokenName) <- forAll genValueWithNativeAsset + let assetPattern = + defMessage + & U5c.policyId .~ serialiseToRawBytes policy + & U5c.assetName .~ serialiseToRawBytes tokenName + H.assertWith value $ matchesAssetPattern assetPattern + +hprop_asset_pattern_rejects_wrong_policy :: Property +hprop_asset_pattern_rejects_wrong_policy = H.property $ do + (value, AssetId policy _tokenName) <- forAll genValueWithNativeAsset + otherPolicy <- forAll genPolicyId + when (serialiseToRawBytes policy /= serialiseToRawBytes otherPolicy) $ do + let assetPattern = defMessage & U5c.policyId .~ serialiseToRawBytes otherPolicy + H.assertWith value $ not . matchesAssetPattern assetPattern + +hprop_asset_pattern_skips_ada :: Property +hprop_asset_pattern_skips_ada = H.property $ do + policy <- forAll genPolicyId + let value = [(AdaAssetId, Quantity 1_000_000)] + assetPattern = defMessage & U5c.policyId .~ serialiseToRawBytes policy + H.assertWith value $ not . matchesAssetPattern assetPattern + +hprop_asset_pattern_rejects_zero_quantity :: Property +hprop_asset_pattern_rejects_zero_quantity = H.property $ do + policy <- forAll genPolicyId + tokenName <- forAll genAssetName + let value = [(AdaAssetId, Quantity 1_000_000), (AssetId policy tokenName, Quantity 0)] + assetPattern = defMessage & U5c.policyId .~ serialiseToRawBytes policy + H.assertWith value $ not . matchesAssetPattern assetPattern + +hprop_asset_pattern_matches_by_name_only :: Property +hprop_asset_pattern_matches_by_name_only = H.property $ do + (value, AssetId _policy tokenName) <- forAll genValueWithNativeAsset + let assetPattern = defMessage & U5c.assetName .~ serialiseToRawBytes tokenName + H.assertWith value $ matchesAssetPattern assetPattern + +hprop_asset_pattern_matches_one_of_multiple_assets :: Property +hprop_asset_pattern_matches_one_of_multiple_assets = H.property $ do + policy1 <- forAll genPolicyId + tokenName1 <- forAll genAssetName + quantity1 <- forAll genPositiveQuantity + policy2 <- forAll genPolicyId + tokenName2 <- forAll genAssetName + quantity2 <- forAll genPositiveQuantity + let value = + [ (AdaAssetId, Quantity 2_000_000) + , (AssetId policy1 tokenName1, quantity1) + , (AssetId policy2 tokenName2, quantity2) + ] + -- Match only the second asset by policy + assetPattern = defMessage & U5c.policyId .~ serialiseToRawBytes policy2 + H.assertWith value $ matchesAssetPattern assetPattern + +hprop_default_asset_pattern_rejects_ada_only :: Property +hprop_default_asset_pattern_rejects_ada_only = H.propertyOnce $ do + -- An empty AssetPattern requires at least one native asset to exist; + -- Ada alone is never considered a native asset. + let value = [(AdaAssetId, Quantity 2_000_000)] + H.assertWith value $ not . matchesAssetPattern defMessage + +-- --------------------------------------------------------------------------- +-- E. TxOutputPattern (AND of address + asset) +-- --------------------------------------------------------------------------- + +hprop_tx_output_pattern_requires_both :: Property +hprop_tx_output_pattern_requires_both = H.property $ do + paymentCredential <- forAll genPaymentCredential + network <- forAll genNetworkId + stakeReference <- forAll genStakeAddressReference + (value, AssetId policy tokenName) <- forAll genValueWithNativeAsset + + let shelleyAddress = makeShelleyAddress network paymentCredential stakeReference + address = shelleyAddressInEra sbe shelleyAddress + ledgerValue = toLedgerValue meo value + txOutValue = TxOutValueShelleyBased sbe ledgerValue + txOut = TxOut address txOutValue TxOutDatumNone ReferenceScriptNone + addressPattern = defMessage & U5c.exactAddress .~ serialiseToRawBytes address + assetPattern = + defMessage + & U5c.policyId .~ serialiseToRawBytes policy + & U5c.assetName .~ serialiseToRawBytes tokenName + -- Matching address + matching asset → match + outputPattern = + defMessage + & U5c.address .~ addressPattern + & U5c.asset .~ assetPattern + H.assertWith txOut $ matchesTxOutputPattern outputPattern + + -- Matching address + wrong asset → fail + otherPolicy <- forAll genPolicyId + when (serialiseToRawBytes policy /= serialiseToRawBytes otherPolicy) $ do + let wrongOutputPattern = + defMessage + & U5c.address .~ addressPattern + & U5c.asset .~ (defMessage & U5c.policyId .~ serialiseToRawBytes otherPolicy) + H.assertWith txOut $ not . matchesTxOutputPattern wrongOutputPattern + +hprop_tx_output_pattern_address_only :: Property +hprop_tx_output_pattern_address_only = H.property $ do + paymentCredential <- forAll genPaymentCredential + network <- forAll genNetworkId + stakeReference <- forAll genStakeAddressReference + (value, _) <- forAll genValueWithNativeAsset + + let shelleyAddress = makeShelleyAddress network paymentCredential stakeReference + address = shelleyAddressInEra sbe shelleyAddress + ledgerValue = toLedgerValue meo value + txOutValue = TxOutValueShelleyBased sbe ledgerValue + txOut = TxOut address txOutValue TxOutDatumNone ReferenceScriptNone + -- Address-only pattern; absent asset field is vacuously true + outputPattern = defMessage & U5c.address .~ (defMessage & U5c.exactAddress .~ serialiseToRawBytes address) + H.assertWith txOut $ matchesTxOutputPattern outputPattern + +hprop_tx_output_pattern_asset_only :: Property +hprop_tx_output_pattern_asset_only = H.property $ do + paymentCredential <- forAll genPaymentCredential + network <- forAll genNetworkId + stakeReference <- forAll genStakeAddressReference + (value, AssetId policy tokenName) <- forAll genValueWithNativeAsset + + let shelleyAddress = makeShelleyAddress network paymentCredential stakeReference + address = shelleyAddressInEra sbe shelleyAddress + ledgerValue = toLedgerValue meo value + txOutValue = TxOutValueShelleyBased sbe ledgerValue + txOut = TxOut address txOutValue TxOutDatumNone ReferenceScriptNone + -- Asset-only pattern; absent address field is vacuously true + outputPattern = + defMessage + & U5c.asset + .~ ( defMessage + & U5c.policyId .~ serialiseToRawBytes policy + & U5c.assetName .~ serialiseToRawBytes tokenName + ) + H.assertWith txOut $ matchesTxOutputPattern outputPattern + +-- --------------------------------------------------------------------------- +-- F. Boolean combinators (via matchesUtxoPredicate) +-- --------------------------------------------------------------------------- + +hprop_not_inverts_match :: Property +hprop_not_inverts_match = H.property $ do + txOut <- forAll genTxOut + let inner = wrapInPredicate defMessage -- defMessage matches everything + predicate = defMessage & U5c.not .~ [inner] + -- not [match-everything] should reject everything + H.assertWith txOut $ not . matchesUtxoPredicate predicate + +hprop_allOf_conjunction :: Property +hprop_allOf_conjunction = H.property $ do + txOut <- forAll genTxOut + let predicate1 = wrapInPredicate defMessage -- matches everything + predicate2 = wrapInPredicate defMessage -- matches everything + predicate = defMessage & U5c.allOf .~ [predicate1, predicate2] + H.assertWith txOut $ matchesUtxoPredicate predicate + +hprop_anyOf_disjunction :: Property +hprop_anyOf_disjunction = H.property $ do + txOut <- forAll genTxOut + -- one that matches everything, one with impossible asset + let matchAll = wrapInPredicate defMessage + impossibleAsset = defMessage & U5c.asset .~ (defMessage & U5c.policyId .~ BS.replicate 28 0xff) + matchNone = wrapInPredicate impossibleAsset + predicate = defMessage & U5c.anyOf .~ [matchAll, matchNone] + H.assertWith txOut $ matchesUtxoPredicate predicate + +hprop_anyOf_empty_is_vacuously_true :: Property +hprop_anyOf_empty_is_vacuously_true = H.property $ do + txOut <- forAll genTxOut + let predicate = defMessage & U5c.anyOf .~ [] + H.assertWith txOut $ matchesUtxoPredicate predicate + +hprop_match_and_not_combined :: Property +hprop_match_and_not_combined = H.property $ do + txOut <- forAll genTxOut + -- match=defMessage matches everything, not=[defMessage] negates everything → always fails + let inner = wrapInPredicate defMessage + predicate = + defMessage + & U5c.match .~ (defMessage & U5c.cardano .~ defMessage) + & U5c.not .~ [inner] + H.assertWith txOut $ not . matchesUtxoPredicate predicate + +hprop_nested_allOf_anyOf :: Property +hprop_nested_allOf_anyOf = H.property $ do + txOut <- forAll genTxOut + -- allOf [match-everything, anyOf [match-everything, impossible]] + let matchAll = wrapInPredicate defMessage + impossibleAsset = defMessage & U5c.asset .~ (defMessage & U5c.policyId .~ BS.replicate 28 0xff) + matchNone = wrapInPredicate impossibleAsset + anyOfPredicate = defMessage & U5c.anyOf .~ [matchAll, matchNone] + predicate = defMessage & U5c.allOf .~ [matchAll, anyOfPredicate] + H.assertWith txOut $ matchesUtxoPredicate predicate + +hprop_allOf_rejects_when_one_fails :: Property +hprop_allOf_rejects_when_one_fails = H.property $ do + txOut <- forAll genTxOut + let matchAll = wrapInPredicate defMessage + impossibleAsset = defMessage & U5c.asset .~ (defMessage & U5c.policyId .~ BS.replicate 28 0xff) + matchNone = wrapInPredicate impossibleAsset + -- allOf requires all to match; one impossible → always rejects + predicate = defMessage & U5c.allOf .~ [matchAll, matchNone] + H.assertWith txOut $ not . matchesUtxoPredicate predicate + +hprop_not_rejects_when_any_element_matches :: Property +hprop_not_rejects_when_any_element_matches = H.property $ do + txOut <- forAll genTxOut + let impossibleAsset = defMessage & U5c.asset .~ (defMessage & U5c.policyId .~ BS.replicate 28 0xff) + matchNone = wrapInPredicate impossibleAsset + matchAll = wrapInPredicate defMessage + -- not [match-none, match-all]: match-all triggers, so not-clause rejects + predicate = defMessage & U5c.not .~ [matchNone, matchAll] + H.assertWith txOut $ not . matchesUtxoPredicate predicate + +-- --------------------------------------------------------------------------- +-- G. extractAddressesFromPredicate +-- --------------------------------------------------------------------------- + +hprop_extract_simple_exact_address :: Property +hprop_extract_simple_exact_address = H.property $ do + address <- forAll genAddressShelley + let addressBytes = serialiseToRawBytes address + outputPattern :: U5c.TxOutputPattern + outputPattern = defMessage & U5c.address .~ (defMessage & U5c.exactAddress .~ addressBytes) + predicate = wrapInPredicate outputPattern + addresses <- H.nothingFail $ extractAddressesFromPredicate predicate + H.annotate $ "Extracted: " <> show addresses + Set.size addresses === 1 + +hprop_extract_nothing_for_complex_predicates :: Property +hprop_extract_nothing_for_complex_predicates = H.property $ do + address <- forAll genAddressShelley + let addressBytes = serialiseToRawBytes address + outputPattern = defMessage & U5c.address .~ (defMessage & U5c.exactAddress .~ addressBytes) + inner = wrapInPredicate outputPattern + -- Predicate with not → should be Nothing + predicate = defMessage & U5c.not .~ [inner] + extractAddressesFromPredicate predicate === Nothing + +hprop_extract_nothing_for_non_exact_pattern :: Property +hprop_extract_nothing_for_non_exact_pattern = H.property $ do + credential <- forAll genPaymentCredential + let outputPattern = + defMessage & U5c.address .~ (defMessage & U5c.paymentPart .~ serialisePaymentCredential credential) + predicate = wrapInPredicate outputPattern + extractAddressesFromPredicate predicate === Nothing + +hprop_extract_anyOf_unions_addresses :: Property +hprop_extract_anyOf_unions_addresses = H.property $ do + address1 <- forAll genAddressShelley + address2 <- forAll genAddressShelley + let makePredicate address = + let addressBytes = serialiseToRawBytes address + outputPattern = defMessage & U5c.address .~ (defMessage & U5c.exactAddress .~ addressBytes) + in wrapInPredicate outputPattern + predicate = defMessage & U5c.anyOf .~ [makePredicate address1, makePredicate address2] + addresses <- H.nothingFail $ extractAddressesFromPredicate predicate + H.annotate $ "Extracted: " <> show addresses + -- Should contain at least 1, at most 2 (might be same address) + H.assertWith addresses $ \a -> Set.size a >= 1 && Set.size a <= 2 + +hprop_extract_consistent_with_matches :: Property +hprop_extract_consistent_with_matches = H.property $ do + -- If we can extract addresses, then each should match the predicate + address <- forAll genAddressShelley + let addressBytes = serialiseToRawBytes address + outputPattern = defMessage & U5c.address .~ (defMessage & U5c.exactAddress .~ addressBytes) + predicate = wrapInPredicate outputPattern + addressInEra = shelleyAddressInEra sbe address + _addresses <- H.nothingFail $ extractAddressesFromPredicate predicate + -- The extracted address should match via matchesAddressPattern + let addressPattern :: U5c.AddressPattern + addressPattern = defMessage & U5c.exactAddress .~ addressBytes + H.assertWith addressInEra $ matchesAddressPattern addressPattern + +hprop_extract_nothing_for_invalid_address_bytes :: Property +hprop_extract_nothing_for_invalid_address_bytes = H.propertyOnce $ do + let invalidBytes = BS.pack [0xff, 0xfe, 0xfd] + outputPattern = defMessage & U5c.address .~ (defMessage & U5c.exactAddress .~ invalidBytes) + predicate = wrapInPredicate outputPattern + extractAddressesFromPredicate predicate === Nothing + +hprop_extract_nothing_for_allOf_with_exact_address :: Property +hprop_extract_nothing_for_allOf_with_exact_address = H.property $ do + address <- forAll genAddressShelley + let addressBytes = serialiseToRawBytes address + outputPattern = defMessage & U5c.address .~ (defMessage & U5c.exactAddress .~ addressBytes) + inner = wrapInPredicate outputPattern + -- allOf path is not handled by the extract optimization + predicate = defMessage & U5c.allOf .~ [inner] + extractAddressesFromPredicate predicate === Nothing + +hprop_extract_nothing_for_default_predicate :: Property +hprop_extract_nothing_for_default_predicate = H.propertyOnce $ do + -- Empty predicate: no match field, all lists empty → Nothing + extractAddressesFromPredicate defMessage === Nothing + +-- --------------------------------------------------------------------------- +-- Helpers +-- --------------------------------------------------------------------------- + +type TestEra = ConwayEra + +sbe :: ShelleyBasedEra TestEra +sbe = convert useEra + +meo :: MaryEraOnwards TestEra +meo = convert useEra + +genTxOut :: Gen (TxOut CtxUTxO TestEra) +genTxOut = genTxOutUTxOContext sbe + +-- | Generate a Value that contains at least one non-Ada native asset with positive quantity. +genValueWithNativeAsset :: Gen (Value, AssetId) +genValueWithNativeAsset = do + policy <- genPolicyId + tokenName <- genAssetName + quantity <- genPositiveQuantity + let asset = AssetId policy tokenName + value = [(AdaAssetId, Quantity 2_000_000), (asset, quantity)] + pure (value, asset) + +-- | Wrap a TxOutputPattern in a UtxoPredicate via match.cardano. +wrapInPredicate :: U5c.TxOutputPattern -> U5c.UtxoPredicate +wrapInPredicate outputPattern = + defMessage + & U5c.match + .~ ( defMessage + & U5c.cardano .~ outputPattern + )