From 6132630209dacb47b8511b2d5b60859af46f2437 Mon Sep 17 00:00:00 2001 From: Paul Cadman Date: Thu, 23 Jan 2025 17:58:55 +0000 Subject: [PATCH] Make all HelloWorld resources have the same kind (#25) * Set webpack config to production * Wrap the result of fetchBinary in a Uint8Array * Make all HelloWorld resources have the same kind The HelloWorld message is now stored in the value instead of the label. Messages are retrieved using the `filterKind` endpoint of the BlockService * Add filterKind API to AnomaClient * Use the filter kind method in the HelloWorld webapp * Fix HelloWorld test script * Restore catch handler * Add comment to explain dummyResource --- HelloWorld/AppIdentity.juvix | 26 +++++++++++++ HelloWorld/GetMessage.juvix | 2 +- HelloWorld/HelloWorld.juvix | 25 ++++++++----- HelloWorld/Logic.juvix | 18 +++++++++ HelloWorld/makefile | 56 ++++++++++++++++++++++++++-- HelloWorld/tests/all-tests.sh | 4 +- HelloWorld/web-app/makefile | 20 +++++++++- HelloWorld/web-app/src/index.js | 8 +++- HelloWorld/web-app/webpack.config.js | 2 +- anoma-client/src/index.js | 16 +++++++- 10 files changed, 154 insertions(+), 23 deletions(-) create mode 100644 HelloWorld/AppIdentity.juvix create mode 100644 HelloWorld/Logic.juvix diff --git a/HelloWorld/AppIdentity.juvix b/HelloWorld/AppIdentity.juvix new file mode 100644 index 0000000..40b6ff2 --- /dev/null +++ b/HelloWorld/AppIdentity.juvix @@ -0,0 +1,26 @@ +module AppIdentity; + +import Stdlib.Prelude open; +import Applib open; + +--- The label for HelloWorld resources +label : Nat := builtinAnomaEncode "HelloWorld"; + +--- Compute the kind of the HelloWorld ;Resource; +main (encodedLogic : Encoded Logic) : Kind := + let + logic := Encode.decode encodedLogic; + -- Only the logic and label are used in the kind computation, we can use + -- default values for other fields. + dummyResource : Resource := + mkResource@{ + label; + logic; + value := 0; + quantity := 0; + nonce := 0; + ephemeral := true; + randSeed := 0; + nullifierKeyCommitment := 0; + }; + in kind dummyResource; diff --git a/HelloWorld/GetMessage.juvix b/HelloWorld/GetMessage.juvix index ced062b..e6e906a 100644 --- a/HelloWorld/GetMessage.juvix +++ b/HelloWorld/GetMessage.juvix @@ -5,4 +5,4 @@ import Applib open; --- Extract the message from a HelloWorld ;Resource; main (encodedResource : Nat) : String := - encodedResource |> builtinAnomaDecode |> Resource.label |> builtinAnomaDecode; + encodedResource |> builtinAnomaDecode |> Resource.value |> builtinAnomaDecode; diff --git a/HelloWorld/HelloWorld.juvix b/HelloWorld/HelloWorld.juvix index df84735..811bf25 100644 --- a/HelloWorld/HelloWorld.juvix +++ b/HelloWorld/HelloWorld.juvix @@ -2,19 +2,21 @@ module HelloWorld; import Stdlib.Prelude open; import Applib open; - ---- A logic function that is always valid. -logic (publicInputs : Instance) (privateInputs : Witness) : Bool := true; +import AppIdentity open using {label}; --- Creates a new ;Resource; that stores a ;String; message. --- @param nonce A number used to ensure resource uniqueness --- @param message The message to store in the ;Resource;. mkHelloWorldResource - (nonce : Nat) (message : String) {ephemeral : Bool := false} : Resource := + (logic : Logic) + (nonce : Nat) + (message : String) + {ephemeral : Bool := false} + : Resource := mkResource@{ - label := builtinAnomaEncode message; + label; logic; - value := 0; + value := builtinAnomaEncode message; quantity := 1; nonce; ephemeral; @@ -29,12 +31,13 @@ helloWorldTransaction {M : Type -> Type} {{Monad M}} {{Tx M}} + (logic : Logic) (nonce : Nat) - (label : String) + (message : String) : M Transaction := do { let - newResource := mkHelloWorldResource nonce label; + newResource := mkHelloWorldResource logic nonce message; in prepareStandardTransaction@{ -- A Transaction must be balanced, so we consume an ephemeral resource of @@ -51,5 +54,7 @@ ctx : TxContext := }; --- The function that is run to produce a Transaction to send to Anoma. -main (message : String) : TransactionRequest := - buildTransactionRequest 0 ctx (helloWorldTransaction 0 message); +main (encodedLogic : Encoded Logic) (message : String) : TransactionRequest := + let + logic : Logic := Encode.decode encodedLogic; + in buildTransactionRequest 0 ctx (helloWorldTransaction logic 0 message); diff --git a/HelloWorld/Logic.juvix b/HelloWorld/Logic.juvix new file mode 100644 index 0000000..d69535d --- /dev/null +++ b/HelloWorld/Logic.juvix @@ -0,0 +1,18 @@ +module Logic; + +import Stdlib.Prelude open; +import Applib open; + +--- A logic function that is always valid. +logic (publicInputs : Instance) (privateInputs : Witness) : Bool := true; + +type LogicWrapper := + mkLogicWrapper@{ + logic : Logic; + }; + +--- Compute the logic function for the HelloWorld ;Resource; +main : LogicWrapper := + mkLogicWrapper@{ + logic; + }; diff --git a/HelloWorld/makefile b/HelloWorld/makefile index 36b653c..3618e9e 100644 --- a/HelloWorld/makefile +++ b/HelloWorld/makefile @@ -15,6 +15,8 @@ ANOMA_PATH ?= $(error set the ANOMA_PATH variable to a path to an anoma clone) base-path = . base = HelloWorld get-message = GetMessage +kind = AppIdentity +logic = Logic message ?= "hello world\n" anoma-build-dir = anoma-build @@ -25,6 +27,12 @@ config = $(anoma-build-dir)/config.yaml temp-file := $(anoma-build-dir)/temp_line message-file = $(anoma-build-dir)/message +kind-juvix = $(base-path)/$(kind).juvix +kind-nockma = $(anoma-build-dir)/$(kind).nockma +kind-proved = $(anoma-build-dir)/$(kind).proved.nockma +kind-proved-cued = $(anoma-build-dir)/$(kind).cued.nockma +kind-proved-cued-b64 = $(anoma-build-dir)/$(kind).cued.nockma.b64 + juvix = $(base-path)/$(base).juvix nockma = $(anoma-build-dir)/$(base).nockma proved = $(anoma-build-dir)/$(base).proved.nockma @@ -33,6 +41,12 @@ get-message-juvix = $(base-path)/$(get-message).juvix get-message-nockma = $(anoma-build-dir)/$(get-message).nockma get-message-proved = $(anoma-build-dir)/$(get-message).proved.nockma +logic-juvix = $(base-path)/$(logic).juvix +logic-nockma = $(anoma-build-dir)/$(logic).nockma +logic-proved = $(anoma-build-dir)/$(logic).proved.nockma + +filter-request = $(anoma-build-dir)/filter-request.json + unspent-resources = $(anoma-build-dir)/unspent-resources last-message-txt = $(anoma-build-dir)/last-message.txt @@ -79,6 +93,12 @@ compile-add-message: $(nockma) .PHONY: compile-get-message compile-get-message: $(get-message-nockma) +.PHONY: compile-logic +compile-logic: $(logic-proved) + +.PHONY: compute-kind +compute-kind: $(kind-proved-cued) + .PHONY: add-transaction add-transaction: $(proved) $(config) @juvix dev anoma -c $(config) add-transaction $(proved) @@ -105,15 +125,43 @@ get-all-messages: $(get-message-nockma) $(config) $(unspent-resources) $(nockma): $(juvix) $(anoma-build) @juvix compile anoma $(juvix) -o $(nockma) -$(proved): $(nockma) $(config) $(message-file) - @juvix dev anoma -c $(config) prove $(nockma) -o $(proved) --arg 'bytes:$(message-file)' +$(proved): $(nockma) $(config) $(message-file) $(logic-proved) + juvix dev anoma -c $(config) prove $(nockma) -o $(proved) --arg 'bytes:$(logic-proved)' --arg 'bytes:$(message-file)' + +$(kind-nockma): $(kind-juvix) $(anoma-build) + @juvix compile anoma $(kind-juvix) -o $(kind-nockma) + +$(kind-proved): $(kind-nockma) $(config) $(logic-proved) + @juvix dev anoma -c $(config) prove $(kind-nockma) -o $(kind-proved) --arg 'bytes:$(logic-proved)' + +$(kind-proved-cued): $(kind-proved) + @juvix dev nockma encode --cue --from bytes --to bytes < $(kind-proved) > $(kind-proved-cued) + +$(kind-proved-cued-b64): $(kind-proved) + @juvix dev nockma encode --cue --from bytes --to base64 < $(kind-proved) > $(kind-proved-cued-b64) + +$(filter-request): $(kind-proved-cued-b64) + jq -n --arg KIND $$(cat $(kind-proved-cued-b64)) \ + '{node_info: {node_id: ""}, filters: [{kind: $$KIND}]}' \ + > $(filter-request) $(get-message-nockma): $(anoma-build) $(get-message-juvix) @juvix compile anoma $(get-message-juvix) -o $(get-message-nockma) +$(logic-nockma): $(anoma-build) $(logic-juvix) + juvix compile anoma $(logic-juvix) -o $(logic-nockma) + +$(logic-proved): $(logic-nockma) $(config) + juvix dev anoma -c $(config) prove $(logic-nockma) -o $(logic-proved) + .PHONY: $(unspent-resources) -$(unspent-resources): $(anoma-build) $(host) $(port) - @grpcurl -plaintext $$(cat $(host)):$$(cat $(port)) Anoma.Protobuf.IndexerService.ListUnspentResources | jq -r '.unspentResources[] // error("no messages exist")' > $(unspent-resources) +$(unspent-resources): $(anoma-build) $(host) $(port) $(filter-request) + @grpcurl -plaintext \ + -d @ \ + $$(cat $(host)):$$(cat $(port)) Anoma.Protobuf.BlockService.Filter \ + < $(filter-request) \ + | jq -r 'if has("resources") then .resources[] else empty end' \ + > $(unspent-resources) $(host): $(config) @yq -r '.url' $(config) | tr -d '\n' > $(host) diff --git a/HelloWorld/tests/all-tests.sh b/HelloWorld/tests/all-tests.sh index c4e3cdb..125bbe4 100755 --- a/HelloWorld/tests/all-tests.sh +++ b/HelloWorld/tests/all-tests.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash --e +set -e pushd message -./tests.sh +./test.sh popd echo "All HelloWorld tests passed" diff --git a/HelloWorld/web-app/makefile b/HelloWorld/web-app/makefile index 5c1646a..8a6178c 100644 --- a/HelloWorld/web-app/makefile +++ b/HelloWorld/web-app/makefile @@ -13,15 +13,21 @@ anoma-port = $(anoma-build-dir)/port add-message-program = HelloWorld.nockma get-message-program = GetMessage.nockma +logic-program = Logic.proved.nockma +kind = AppIdentity.cued.nockma add-message-program-src = $(src)/$(add-message-program) get-message-program-src = $(src)/$(get-message-program) +logic-program-src = $(src)/$(logic-program) +kind-src = $(src)/$(kind) add-message-program-dst = $(dst)/$(add-message-program) get-message-program-dst = $(dst)/$(get-message-program) +logic-program-dst = $(dst)/$(logic-program) +kind-dst = $(dst)/$(kind) .PHONY: build -build: $(add-message-program-dst) $(get-message-program-dst) +build: $(add-message-program-dst) $(get-message-program-dst) $(logic-program-dst) $(kind-dst) .PHONY: clean clean: @@ -74,8 +80,20 @@ $(add-message-program-dst): $(dst) $(add-message-program-src) $(get-message-program-dst): $(dst) $(get-message-program-src) cp $(get-message-program-src) $(get-message-program-dst) +$(logic-program-dst): $(dst) $(logic-program-src) + cp $(logic-program-src) $(logic-program-dst) + +$(kind-dst): $(dst) $(kind-src) + cp $(kind-src) $(kind-dst) + $(add-message-program-src): make -C $(app-path) compile-add-message $(get-message-program-src): make -C $(app-path) compile-get-message + +$(logic-program-src): + make -C $(app-path) compile-logic + +$(kind-src): + make -C $(app-path) compute-kind diff --git a/HelloWorld/web-app/src/index.js b/HelloWorld/web-app/src/index.js index e3b6258..25a1bbb 100644 --- a/HelloWorld/web-app/src/index.js +++ b/HelloWorld/web-app/src/index.js @@ -1,19 +1,23 @@ import { AnomaClient, fetchBinary, serialize, deserializeToString } from 'anoma-client'; import helloWorld from '../nockma/HelloWorld.nockma'; import getMessage from '../nockma/GetMessage.nockma' +import logic from '../nockma/Logic.proved.nockma' +import appIdentity from '../nockma/AppIdentity.cued.nockma' import config from '../config.json' const grpcServer = `http://${config.proxyHost}:${config.proxyPort}` const anomaClient = new AnomaClient(grpcServer); async function addMessage(message) { + const logicProgram = await fetchBinary(logic); const helloWorldProgram = await fetchBinary(helloWorld); - const tx = await anomaClient.prove(helloWorldProgram, [message]); + const tx = await anomaClient.prove(helloWorldProgram, [logicProgram, message]); return await anomaClient.addTransaction(tx); } async function getMessages() { - const unspent = await anomaClient.listUnspentResources(); + const kind = await fetchBinary(appIdentity); + const unspent = await anomaClient.filterKind(kind); if (unspent.length == 0) { throw Error("There are no stored messages"); } diff --git a/HelloWorld/web-app/webpack.config.js b/HelloWorld/web-app/webpack.config.js index 35cf291..f95fbe5 100644 --- a/HelloWorld/web-app/webpack.config.js +++ b/HelloWorld/web-app/webpack.config.js @@ -26,5 +26,5 @@ module.exports = { }, ], }, - mode: 'development' + mode: 'production' }; diff --git a/anoma-client/src/index.js b/anoma-client/src/index.js index 3890785..0319c23 100644 --- a/anoma-client/src/index.js +++ b/anoma-client/src/index.js @@ -1,7 +1,8 @@ -import { IndexerServicePromiseClient, MempoolServicePromiseClient, NockServicePromiseClient } from './grpc-client/anoma_grpc_web_pb'; +import { BlockServicePromiseClient, IndexerServicePromiseClient, MempoolServicePromiseClient, NockServicePromiseClient } from './grpc-client/anoma_grpc_web_pb'; import * as UnspentResources from './grpc-client/indexer/unspent_resources_pb.js'; import * as AddTransaction from './grpc-client/mempool/add_transaction_pb.js'; +import * as Filtered from './grpc-client/indexer/blocks/filter_pb.js' import * as Prove from './grpc-client/nock/prove_pb.js'; import { Input } from './grpc-client/nock/input_pb.js'; import serial from './nock-js/serial.js'; @@ -13,7 +14,8 @@ export async function fetchBinary(url) { if (!response.ok) { throw new Error(`Failed to fetch binary data: ${response.statusText}`); } - return await response.arrayBuffer(); + const buf = await response.arrayBuffer(); + return new Uint8Array(buf); } export function serialize(x) { @@ -42,6 +44,7 @@ export class AnomaClient { this.indexerClient = new IndexerServicePromiseClient(grpcServer); this.nockClient = new NockServicePromiseClient(grpcServer); this.mempoolClient = new MempoolServicePromiseClient(grpcServer); + this.blockServiceClient = new BlockServicePromiseClient(grpcServer); } async listUnspentResources() { @@ -50,6 +53,15 @@ export class AnomaClient { return res.getUnspentResourcesList_asU8(); } + async filterKind(kind) { + const request = new Filtered.Request(); + const filter = new Filtered.Filter(); + filter.setKind(kind); + request.setFiltersList([filter]); + const response = await this.blockServiceClient.filter(request, {}); + return response.getResourcesList_asU8(); + } + async prove(program, args) { const request = new Prove.Request(); request.setJammedProgram(program);