From b4d86fc2135a113e07d1681daf940e71c2f76d85 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Krieger Date: Mon, 22 Jan 2024 10:26:42 -0300 Subject: [PATCH] feat(cli): add validate notice command --- cmd/cartesi-rollups-cli/root/root.go | 2 + .../root/validate/validate.go | 94 +++++++++++++++++++ docs/cartesi-rollups-cli.md | 1 + docs/cartesi-rollups-cli_validate.md | 30 ++++++ pkg/ethutil/ethutil.go | 22 +++++ pkg/readerclient/proof.go | 34 +++++++ 6 files changed, 183 insertions(+) create mode 100644 cmd/cartesi-rollups-cli/root/validate/validate.go create mode 100644 docs/cartesi-rollups-cli_validate.md diff --git a/cmd/cartesi-rollups-cli/root/root.go b/cmd/cartesi-rollups-cli/root/root.go index 83333a863..2f0e99b06 100644 --- a/cmd/cartesi-rollups-cli/root/root.go +++ b/cmd/cartesi-rollups-cli/root/root.go @@ -9,6 +9,7 @@ import ( "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/read" "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/savesnapshot" "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/send" + "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/validate" "github.com/spf13/cobra" ) @@ -25,5 +26,6 @@ func init() { Cmd.AddCommand(savesnapshot.Cmd) Cmd.AddCommand(inspect.Cmd) Cmd.AddCommand(increasetime.Cmd) + Cmd.AddCommand(validate.Cmd) Cmd.DisableAutoGenTag = true } diff --git a/cmd/cartesi-rollups-cli/root/validate/validate.go b/cmd/cartesi-rollups-cli/root/validate/validate.go new file mode 100644 index 000000000..da576f90c --- /dev/null +++ b/cmd/cartesi-rollups-cli/root/validate/validate.go @@ -0,0 +1,94 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +package validate + +import ( + "fmt" + + "github.com/Khan/genqlient/graphql" + "github.com/cartesi/rollups-node/pkg/addresses" + "github.com/cartesi/rollups-node/pkg/ethutil" + "github.com/cartesi/rollups-node/pkg/readerclient" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/spf13/cobra" +) + +var Cmd = &cobra.Command{ + Use: "validate", + Short: "Validates a notice", + Example: examples, + Run: run, +} + +const examples = `# Validates notice 5 from input 6: +cartesi-rollups-cli validate --notice-index 5 --input-index 6` + +var ( + noticeIndex int + inputIndex int + graphqlEndpoint string + ethEndpoint string + addressBookFile string +) + +func init() { + Cmd.Flags().IntVar(¬iceIndex, "notice-index", 0, + "index of the notice") + + cobra.CheckErr(Cmd.MarkFlagRequired("notice-index")) + + Cmd.Flags().IntVar(&inputIndex, "input-index", 0, + "index of the input") + + cobra.CheckErr(Cmd.MarkFlagRequired("input-index")) + + Cmd.Flags().StringVar(&graphqlEndpoint, "graphql-endpoint", "http://0.0.0.0:10004/graphql", + "address used to connect to graphql") + + Cmd.Flags().StringVar(ðEndpoint, "eth-endpoint", "http://localhost:8545", + "ethereum node JSON-RPC endpoint") + + Cmd.Flags().StringVar(&addressBookFile, "address-book", "", + "if set, load the address book from the given file; else, use test addresses") +} + +func run(cmd *cobra.Command, args []string) { + ctx := cmd.Context() + graphqlClient := graphql.NewClient(graphqlEndpoint, nil) + + resp, err := readerclient.GetNotice(ctx, graphqlClient, noticeIndex, inputIndex) + cobra.CheckErr(err) + + if resp.Proof == nil { + fmt.Print("The notice has no associated proof yet.\n") + } + + client, err := ethclient.DialContext(ctx, ethEndpoint) + cobra.CheckErr(err) + fmt.Printf("connected to %v\n", ethEndpoint) + + var book *addresses.Book + if addressBookFile != "" { + book, err = addresses.GetBookFromFile(addressBookFile) + cobra.CheckErr(err) + } else { + book = addresses.GetTestBook() + } + + proof := readerclient.ConvertToContractProof(resp.Proof) + + fmt.Printf("validating notice %d from input %d with address %x\n", + noticeIndex, + inputIndex, + book.CartesiDApp, + ) + valid, err := ethutil.CallValidateNotice(client, book, resp.Payload, proof) + cobra.CheckErr(err) + + if valid { + fmt.Print("The notice is valid!\n") + } else { + fmt.Print("The notice is not valid...\n") + } +} diff --git a/docs/cartesi-rollups-cli.md b/docs/cartesi-rollups-cli.md index a437f6ec4..840241743 100644 --- a/docs/cartesi-rollups-cli.md +++ b/docs/cartesi-rollups-cli.md @@ -20,4 +20,5 @@ Cartesi Rollups node. * [cartesi-rollups-cli read](cartesi-rollups-cli_read.md) - Read the node state from the GraphQL API * [cartesi-rollups-cli save-snapshot](cartesi-rollups-cli_save-snapshot.md) - Saves the testing Cartesi machine snapshot to the designated folder * [cartesi-rollups-cli send](cartesi-rollups-cli_send.md) - Send a rollups input to the Ethereum node +* [cartesi-rollups-cli validate](cartesi-rollups-cli_validate.md) - Validates a notice diff --git a/docs/cartesi-rollups-cli_validate.md b/docs/cartesi-rollups-cli_validate.md new file mode 100644 index 000000000..c4e7430ed --- /dev/null +++ b/docs/cartesi-rollups-cli_validate.md @@ -0,0 +1,30 @@ +## cartesi-rollups-cli validate + +Validates a notice + +``` +cartesi-rollups-cli validate [flags] +``` + +### Examples + +``` +# Validates notice 5 from input 6: +cartesi-rollups-cli validate --notice-index 5 --input-index 6 +``` + +### Options + +``` + --address-book string if set, load the address book from the given file; else, use test addresses + --eth-endpoint string ethereum node JSON-RPC endpoint (default "http://localhost:8545") + --graphql-endpoint string address used to connect to graphql (default "http://0.0.0.0:10004/graphql") + -h, --help help for validate + --input-index int index of the input + --notice-index int index of the notice +``` + +### SEE ALSO + +* [cartesi-rollups-cli](cartesi-rollups-cli.md) - Command line interface for Cartesi Rollups + diff --git a/pkg/ethutil/ethutil.go b/pkg/ethutil/ethutil.go index e71593197..204f9a140 100644 --- a/pkg/ethutil/ethutil.go +++ b/pkg/ethutil/ethutil.go @@ -107,3 +107,25 @@ func GetInputFromInputBox( } return it.Event, nil } + +// Validate the given notice on the specified Dapp. +// Return whether the notice is valid or not. +func CallValidateNotice( + client *ethclient.Client, + book *addresses.Book, + notice []byte, + proof *contracts.Proof, +) (bool, error) { + + dapp, err := contracts.NewCartesiDApp(book.CartesiDApp, client) + if err != nil { + return false, fmt.Errorf("failed to connect to CartesiDapp contract: %v", err) + } + + response, err := dapp.ValidateNotice(nil, notice, *proof) + if err != nil { + return false, err + } + + return response, nil +} diff --git a/pkg/readerclient/proof.go b/pkg/readerclient/proof.go index a3e920f68..f83c92813 100644 --- a/pkg/readerclient/proof.go +++ b/pkg/readerclient/proof.go @@ -6,6 +6,7 @@ package readerclient import ( "fmt" + "github.com/cartesi/rollups-node/pkg/contracts" "github.com/ethereum/go-ethereum/common/hexutil" ) @@ -118,3 +119,36 @@ func newProof( return &proof, err } + +func ConvertToContractProof(proof *Proof) *contracts.Proof { + var ( + outputHashOutputSiblings [][32]byte + outputHashEpochSiblings [][32]byte + ) + + for _, hash := range proof.OutputHashInOutputHashesSiblings { + outputHashOutputSiblings = append(outputHashOutputSiblings, [32]byte(hash)) + } + + for _, hash := range proof.OutputHashesInEpochSiblings { + outputHashEpochSiblings = append(outputHashEpochSiblings, [32]byte(hash)) + } + + outputValidityProof := contracts.OutputValidityProof{ + InputIndexWithinEpoch: uint64(proof.InputIndexWithinEpoch), + OutputIndexWithinInput: uint64(proof.OutputIndexWithinInput), + OutputHashesRootHash: [32]byte(proof.OutputHashesRootHash), + VouchersEpochRootHash: [32]byte(proof.VouchersEpochRootHash), + NoticesEpochRootHash: [32]byte(proof.NoticesEpochRootHash), + MachineStateHash: [32]byte(proof.MachineStateHash), + OutputHashInOutputHashesSiblings: outputHashOutputSiblings, + OutputHashesInEpochSiblings: outputHashEpochSiblings, + } + + contractProof := contracts.Proof{ + Validity: outputValidityProof, + Context: proof.Context, + } + + return &contractProof +}