From d6121a6d3263ebd2e7e1be6d2de2a701a9bcbb88 Mon Sep 17 00:00:00 2001 From: ccamel Date: Wed, 28 Feb 2024 16:48:32 +0100 Subject: [PATCH 1/7] build(deps): add cucumber/godog v0.14.0 dependency --- go.mod | 9 ++++++++- go.sum | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 51f67565..3065aef9 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,9 @@ require ( github.com/cosmos/gogoproto v1.4.11 github.com/cosmos/ibc-go/modules/capability v1.0.0 github.com/cosmos/ibc-go/v8 v8.0.0 + github.com/cucumber/gherkin/go/v26 v26.2.0 + github.com/cucumber/godog v0.14.0 + github.com/cucumber/messages/go/v21 v21.0.1 github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.3 @@ -51,6 +54,7 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f google.golang.org/grpc v1.60.1 google.golang.org/protobuf v1.32.0 + gopkg.in/yaml.v3 v3.0.1 gotest.tools/v3 v3.5.1 sigs.k8s.io/yaml v1.4.0 ) @@ -116,6 +120,8 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/creachadair/atomicfile v0.3.1 // indirect github.com/creachadair/tomledit v0.0.24 // indirect + github.com/cucumber/gherkin/go/v26 v26.2.0 // indirect + github.com/cucumber/messages/go/v21 v21.0.1 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -142,6 +148,7 @@ require ( github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect + github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/glog v1.2.0 // indirect @@ -166,6 +173,7 @@ require ( github.com/hashicorp/go-getter v1.7.3 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-memdb v1.3.4 // indirect github.com/hashicorp/go-plugin v1.5.2 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-version v1.6.0 // indirect @@ -284,7 +292,6 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect mvdan.cc/xurls/v2 v2.2.0 // indirect nhooyr.io/websocket v1.8.6 // indirect pgregory.net/rapid v1.1.0 // indirect diff --git a/go.sum b/go.sum index 2e4a9a96..b649a5dd 100644 --- a/go.sum +++ b/go.sum @@ -430,6 +430,7 @@ github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5n github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creachadair/atomicfile v0.3.1 h1:yQORkHjSYySh/tv5th1dkKcn02NEW5JleB84sjt+W4Q= @@ -438,6 +439,13 @@ github.com/creachadair/tomledit v0.0.24 h1:5Xjr25R2esu1rKCbQEmjZYlrhFkDspoAbAKb6 github.com/creachadair/tomledit v0.0.24/go.mod h1:9qHbShRWQzSCcn617cMzg4eab1vbLCOjOshAWSzWr8U= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= +github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= +github.com/cucumber/godog v0.14.0 h1:h/K4t7XBxsFBF+UJEahNqJ1/2VHVepRXCSq3WWWnehs= +github.com/cucumber/godog v0.14.0/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces= +github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI= +github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s= +github.com/cucumber/messages/go/v22 v22.0.0/go.mod h1:aZipXTKc0JnjCsXrJnuZpWhtay93k7Rn3Dee7iyPJjs= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= @@ -574,6 +582,10 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= @@ -741,8 +753,11 @@ github.com/hashicorp/go-getter v1.7.3/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17 github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= +github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= github.com/hashicorp/go-metrics v0.5.3 h1:M5uADWMOGCTUNU1YuC4hfknOeHNaX54LDm4oYSucoNE= github.com/hashicorp/go-metrics v0.5.3/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= @@ -765,6 +780,7 @@ github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -1180,6 +1196,7 @@ github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -1208,6 +1225,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= From 03f1a4887a3449330a77e19978b89b091f098323 Mon Sep 17 00:00:00 2001 From: ccamel Date: Wed, 28 Feb 2024 16:49:05 +0100 Subject: [PATCH 2/7] test(logic): implement features testsuite logic using cucumber --- x/logic/keeper/features_test.go | 171 ++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 x/logic/keeper/features_test.go diff --git a/x/logic/keeper/features_test.go b/x/logic/keeper/features_test.go new file mode 100644 index 00000000..2a38ba2b --- /dev/null +++ b/x/logic/keeper/features_test.go @@ -0,0 +1,171 @@ +package keeper_test + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io/fs" + "strings" + "testing" + + "github.com/cucumber/godog" + "github.com/golang/mock/gomock" + "github.com/sergi/go-diff/diffmatchpatch" + "github.com/smartystreets/goconvey/convey/reporting" + "gopkg.in/yaml.v3" + + . "github.com/smartystreets/goconvey/convey" + + storetypes "cosmossdk.io/store/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdktestutil "github.com/cosmos/cosmos-sdk/testutil" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + "github.com/okp4/okp4d/x/logic" + "github.com/okp4/okp4d/x/logic/keeper" + logictestutil "github.com/okp4/okp4d/x/logic/testutil" + "github.com/okp4/okp4d/x/logic/types" +) + +func TestFeatures(t *testing.T) { + suite := godog.TestSuite{ + ScenarioInitializer: initializeScenario(t), + Options: &godog.Options{ + Format: "pretty", + Paths: []string{"features"}, + TestingT: t, + Concurrency: 1, + }, + } + if suite.Run() != 0 { + t.Fatal("Test failed") + } +} + +type testCase struct { + ctx sdktestutil.TestContext + encCfg moduletestutil.TestEncodingConfig + queryClient types.QueryServiceClient + request types.QueryServiceAskRequest + got *types.Answer +} + +type testCaseCtxKey struct{} + +func testCaseToContext(ctx context.Context, tc testCase) context.Context { + return context.WithValue(ctx, testCaseCtxKey{}, &tc) +} + +func testCaseFromContext(ctx context.Context) *testCase { + tc, _ := ctx.Value(testCaseCtxKey{}).(*testCase) + + return tc +} + +func givenTheProgram(ctx context.Context, program *godog.DocString) error { + testCaseFromContext(ctx).request.Program = program.Content + + return nil +} + +func givenTheQuery(ctx context.Context, query *godog.DocString) error { + testCaseFromContext(ctx).request.Query = query.Content + + return nil +} + +func whenTheQueryIsRun(ctx context.Context) error { + tc := testCaseFromContext(ctx) + got, err := tc.queryClient.Ask(context.Background(), &tc.request) + if err != nil { + return err + } + tc.got = got.Answer + + return nil +} + +func theAnswerWeGetIs(ctx context.Context, want *godog.DocString) error { + got := testCaseFromContext(ctx).got + wantAnswer := &types.Answer{} + if err := yaml.Unmarshal([]byte(want.Content), &wantAnswer); err != nil { + return err + } + + return assert(got, ShouldResemble, wantAnswer) +} + +func assert(actual any, assertion Assertion, expected ...any) error { + msg := assertion(actual, expected...) + if msg == "" { + return nil + } + + failureView := reporting.FailureView{} + if err := json.Unmarshal([]byte(msg), &failureView); err != nil { + return err + } + sb := strings.Builder{} + sb.WriteString("assertion failed\n\u001B[0m") + sb.WriteString(fmt.Sprintf("Actual:\n%s\n", failureView.Actual)) + sb.WriteString(fmt.Sprintf("Expected:\n%s\n", failureView.Expected)) + sb.WriteString("Diff:\n") + + dmp := diffmatchpatch.New() + diffs := dmp.DiffMain(failureView.Actual, failureView.Expected, false) + sb.WriteString(dmp.DiffPrettyText(diffs)) + + return errors.New(sb.String()) +} + +func initializeScenario(t *testing.T) func(ctx *godog.ScenarioContext) { + return func(ctx *godog.ScenarioContext) { + ctx.Before(func(ctx context.Context, _ *godog.Scenario) (context.Context, error) { + encCfg := moduletestutil.MakeTestEncodingConfig(logic.AppModuleBasic{}) + key := storetypes.NewKVStoreKey(types.StoreKey) + testCtx := sdktestutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test")) + + ctrl := gomock.NewController(t) + accountKeeper := logictestutil.NewMockAccountKeeper(ctrl) + bankKeeper := logictestutil.NewMockBankKeeper(ctrl) + fsProvider := logictestutil.NewMockFS(ctrl) + + logicKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + key, + authtypes.NewModuleAddress(govtypes.ModuleName), + accountKeeper, + bankKeeper, + func(_ context.Context) fs.FS { + return fsProvider + }, + ) + err := logicKeeper.SetParams(testCtx.Ctx, types.DefaultParams()) + if err != nil { + return nil, err + } + + queryHelper := baseapp.NewQueryServerTestHelper(testCtx.Ctx, encCfg.InterfaceRegistry) + types.RegisterQueryServiceServer(queryHelper, logicKeeper) + queryClient := types.NewQueryServiceClient(queryHelper) + + tc := testCase{ + ctx: testCtx, + encCfg: encCfg, + queryClient: queryClient, + } + + return testCaseToContext(ctx, tc), nil + }) + + ctx.Given(`the query:`, givenTheQuery) + ctx.Given(`the program:`, givenTheProgram) + ctx.When(`the query is run`, whenTheQueryIsRun) + ctx.Then(`the answer we get is:`, theAnswerWeGetIs) + } +} From 94f9c98ba1393c44c2b8570696888cd8ca83cb9e Mon Sep 17 00:00:00 2001 From: ccamel Date: Wed, 28 Feb 2024 16:49:37 +0100 Subject: [PATCH 3/7] test(logic): describe bech32_address_2 feature --- .../keeper/features/bech32_address_2.feature | 305 ++++++++++++++++++ x/logic/predicate/address_test.go | 186 ----------- 2 files changed, 305 insertions(+), 186 deletions(-) create mode 100644 x/logic/keeper/features/bech32_address_2.feature delete mode 100644 x/logic/predicate/address_test.go diff --git a/x/logic/keeper/features/bech32_address_2.feature b/x/logic/keeper/features/bech32_address_2.feature new file mode 100644 index 00000000..8a4c7ca4 --- /dev/null +++ b/x/logic/keeper/features/bech32_address_2.feature @@ -0,0 +1,305 @@ +Feature: bech32_address/2 + This feature is to test the bech32_address/2 predicate. + + @great_for_documentation + Scenario: Decode Bech32 Address into its Address Pair representation. + This scenario demonstrates how to parse a provided bech32 address string into its `Address` pair representation. + An `Address` is a compound term `-` with two arguments, the first being the human-readable part (Hrp) and the second + being the numeric address as a list of integers ranging from 0 to 255 representing the bytes of the address in + base 64. + + Given the query: + """ prolog + bech32_address(Address, 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn'). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + variables: ["Address"] + results: + - substitutions: + - variable: Address + expression: "okp4-[163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]" + """ + @great_for_documentation + Scenario: Decode Hrp and Address from a bech32 address + This scenario illustrates how to decode a bech32 address into the human-readable part (Hrp) and the numeric address. + The process extracts these components from a given bech32 address string, showcasing the ability to parse and + separate the address into its constituent parts. + + Given the query: + """ prolog + bech32_address(-(Hrp, Address), 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn'). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + variables: ["Hrp", "Address"] + results: + - substitutions: + - variable: Hrp + expression: "okp4" + - variable: Address + expression: "[163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]" + """ + @great_for_documentation + Scenario: Extract Address only for OKP4 bech32 address + This scenario demonstrates how to extract the address from a bech32 address string, specifically for a known + protocol, in this case, the okp4 protocol. + + Given the query: + """ prolog + bech32_address(-(okp4, Address), 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn'). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + variables: ["Address"] + results: + - substitutions: + - variable: Address + expression: "[163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]" + """ + @great_for_documentation + Scenario: Encode Address Pair into Bech32 Address + This scenario demonstrates how to encode an `Address` pair representation into a bech32 address string. + + Given the query: + """ prolog + bech32_address(-('okp4', [163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), Bech32). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + variables: ["Bech32"] + results: + - substitutions: + - variable: Bech32 + expression: "okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn" + """ + @great_for_documentation + Scenario: Check if a bech32 address is part of the okp4 protocol + This scenario shows how to check if a bech32 address is part of the okp4 protocol. + + Given the program: + """ + okp4_addr(Addr) :- bech32_address(-('okp4', _), Addr). + """ + Given the query: + """ prolog + okp4_addr('okp41p8u47en82gmzfm259y6z93r9qe63l25dfwwng6'). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + results: + - substitutions: + """ + Scenario: Check if a bech32 address is part of the okp4 protocol (not success) + This scenario shows how to check if a bech32 address is part of the okp4 protocol. + + Given the program: + """ + okp4_addr(Addr) :- bech32_address(-('okp4', _), Addr). + """ + Given the query: + """ prolog + okp4_addr('cosmos15z956su069rt896dk5zrl7jmvzt9uu2gxe0l28'). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + results: + """ + Scenario: Check address equality + This scenario demonstrates how to check if two bech32 addresses representation are equal. + + Given the query: + """ prolog + bech32_address(-('okp4', [163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn'). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + results: + - substitutions: + """ + Scenario: Decode HRP from a bech32 address + This scenario demonstrates how to decode the human-readable part (Hrp) from a bech32 address string. + + Given the query: + """ prolog + bech32_address(-(Hrp, [163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn'). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + variables: ["Hrp"] + results: + - substitutions: + - variable: Hrp + expression: "okp4" + """ + @great_for_documentation + Scenario: Error on Incorrect Bech32 Address format + This scenario demonstrates the system's response to an incorrect bech32 address format. + In this case, the system generates a `domain_error`, indicating that the provided argument does not meet the + expected format for a bech32 address. + + Given the query: + """ prolog + bech32_address(Address, okp4incorrect). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + variables: ["Address"] + results: + - error: "error(domain_error(encoding(bech32),okp4incorrect),[d,e,c,o,d,i,n,g, ,b,e,c,h,3,2, ,f,a,i,l,e,d,:, ,i,n,v,a,l,i,d, ,s,e,p,a,r,a,t,o,r, ,i,n,d,e,x, ,-,1],bech32_address/2)" + """ + @great_for_documentation + Scenario: Error on Incorrect Bech32 Address type + This scenario demonstrates the system's response to an incorrect bech32 address type. + In this case, the system generates a `type_error`, indicating that the provided argument does not meet the + expected type. + + Given the query: + """ prolog + bech32_address(-('okp4', X), foo(bar)). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + variables: ["X"] + results: + - error: "error(type_error(atom,foo(bar)),bech32_address/2)" + """ + Scenario: Error on Incorrect Hrp type + This scenario demonstrates the system's response to an incorrect Hrp type. + In this case, the system generates a `type_error`, indicating that the provided argument does not meet the + expected type. + + Given the query: + """ prolog + bech32_address(foo(bar), Bech32). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + variables: ["Bech32"] + results: + - error: "error(type_error(pair,foo(bar)),bech32_address/2)" + """ + Scenario: Error on Incorrect Hrp type (2) + This scenario demonstrates the system's response to an incorrect Hrp type. + In this case, the system generates a `type_error`, indicating that the provided argument does not meet the + expected type. + + Given the query: + """ prolog + bech32_address(-(1,[]), Bech32). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + variables: ["Bech32"] + results: + - error: "error(type_error(atom,1),bech32_address/2)" + """ + Scenario: Error on Incorrect Address type + This scenario demonstrates the system's response to an incorrect Address type. + In this case, the system generates a `type_error`, indicating that the provided argument does not meet the + expected type. + + Given the query: + """ prolog + bech32_address(-('okp4', ['163',167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), Bech32). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + variables: ["Bech32"] + results: + - error: "error(type_error(byte,163),bech32_address/2)" + """ + Scenario: Error on Incorrect Address type (2) + This scenario demonstrates the system's response to an incorrect Address type. + In this case, the system generates a `type_error`, indicating that the provided argument does not meet the + expected type. + + Given the query: + """ prolog + bech32_address(-('okp4', [163,'x',23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), Bech32). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + variables: ["Bech32"] + results: + - error: "error(type_error(byte,x),bech32_address/2)" + """ + Scenario: Error on Incorrect Address type (3) + This scenario demonstrates the system's response to an incorrect Address type. + In this case, the system generates a `type_error`, indicating that the provided argument does not meet the + expected type. + + Given the query: + """ prolog + bech32_address(-('okp4', hey(2)), Bech32). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + variables: ["Bech32"] + results: + - error: "error(type_error(list,hey(2)),bech32_address/2)" + """ + Scenario: Not sufficiently instantiated + This scenario shows the system's response when the query is not sufficiently instantiated. + In this case, the system generates an `instantiation_error`, indicating that the query is not sufficiently + instantiated to generate a valid response. + + Given the query: + """ prolog + bech32_address(Address, Bech32). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + variables: ["Address", "Bech32"] + results: + - error: "error(instantiation_error,bech32_address/2)" + """ + Scenario: Not sufficiently instantiated (2) + This scenario shows the system's response when the query is not sufficiently instantiated. + In this case, the system generates an `instantiation_error`, indicating that the query is not sufficiently + instantiated to generate a valid response. + + Given the query: + """ prolog + bech32_address(-(Hrp, [163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), Bech32). + """ + When the query is run + Then the answer we get is: + """ yaml + has_more: false + variables: ["Hrp", "Bech32"] + results: + - error: "error(instantiation_error,bech32_address/2)" + """ diff --git a/x/logic/predicate/address_test.go b/x/logic/predicate/address_test.go deleted file mode 100644 index 5c624dfd..00000000 --- a/x/logic/predicate/address_test.go +++ /dev/null @@ -1,186 +0,0 @@ -//nolint:gocognit,lll -package predicate - -import ( - "fmt" - "strings" - "testing" - - dbm "github.com/cosmos/cosmos-db" - "github.com/ichiban/prolog/engine" - - . "github.com/smartystreets/goconvey/convey" - - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - - "cosmossdk.io/log" - "cosmossdk.io/store" - "cosmossdk.io/store/metrics" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/okp4/okp4d/x/logic/testutil" -) - -func TestBech32(t *testing.T) { - Convey("Given a test cases", t, func() { - cases := []struct { - program string - query string - wantResult []testutil.TermResults - wantError error - wantSuccess bool - }{ - { - query: `bech32_address(-(Hrp, Address), 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn').`, - wantResult: []testutil.TermResults{{ - "Hrp": "okp4", - "Address": "[163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]", - }}, - wantSuccess: true, - }, - { - program: `okp4_addr(Addr) :- bech32_address(-('okp4', _), Addr).`, - query: `okp4_addr('okp41p8u47en82gmzfm259y6z93r9qe63l25dfwwng6').`, - wantResult: []testutil.TermResults{{}}, - wantSuccess: true, - }, - { - query: `bech32_address(Address, 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn').`, - wantResult: []testutil.TermResults{{ - "Address": "okp4-[163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]", - }}, - wantSuccess: true, - }, - { - query: `bech32_address(-('okp4', X), foo(bar)).`, - wantError: fmt.Errorf("error(type_error(atom,foo(bar)),bech32_address/2)"), - wantSuccess: false, - }, - { - query: `bech32_address(-('okp4', Address), 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn').`, - wantResult: []testutil.TermResults{{ - "Address": "[163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]", - }}, - wantSuccess: true, - }, - { - query: `bech32_address(-('okp5', Address), 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn').`, - wantSuccess: false, - }, - { - query: `bech32_address(-('okp4', [163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), Bech32).`, - wantResult: []testutil.TermResults{{ - "Bech32": "okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn", - }}, - wantSuccess: true, - }, - { - query: `bech32_address(-('okp4', [163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn').`, - wantResult: []testutil.TermResults{{}}, - wantSuccess: true, - }, - { - query: `bech32_address(-(Hrp, [163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn').`, - wantResult: []testutil.TermResults{{"Hrp": "okp4"}}, - wantSuccess: true, - }, - { - query: `bech32_address(-(Hrp, [163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn').`, - wantResult: []testutil.TermResults{{"Hrp": "okp4"}}, - wantSuccess: true, - }, - { - query: `bech32_address(foo(bar), Bech32).`, - wantError: fmt.Errorf("error(type_error(pair,foo(bar)),bech32_address/2)"), - wantSuccess: false, - }, - { - query: `bech32_address(-('okp4', ['163',167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), Bech32).`, - wantError: fmt.Errorf("error(type_error(byte,163),bech32_address/2)"), - wantSuccess: false, - }, - { - query: `bech32_address(-('okp4', [163,'x',23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), Bech32).`, - wantError: fmt.Errorf("error(type_error(byte,x),bech32_address/2)"), - wantSuccess: false, - }, - { - query: `bech32_address(-(Hrp, [163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), Bech32).`, - wantError: fmt.Errorf("error(instantiation_error,bech32_address/2)"), - wantSuccess: false, - }, - { - query: `bech32_address(-('okp4', hey(2)), Bech32).`, - wantError: fmt.Errorf("error(type_error(list,hey(2)),bech32_address/2)"), - wantSuccess: false, - }, - { - query: `bech32_address(-('okp4', X), foo).`, - wantError: fmt.Errorf("error(domain_error(encoding(bech32),foo),[%s],bech32_address/2)", - strings.Join(strings.Split("decoding bech32 failed: invalid bech32 string length 3", ""), ",")), - wantSuccess: false, - }, - { - query: `bech32_address(Address, Bech32).`, - wantError: fmt.Errorf("error(instantiation_error,bech32_address/2)"), - wantSuccess: false, - }, - } - for nc, tc := range cases { - Convey(fmt.Sprintf("Given the query #%d: %s", nc, tc.query), func() { - Convey("and a context", func() { - db := dbm.NewMemDB() - stateStore := store.NewCommitMultiStore(db, log.NewNopLogger(), metrics.NewNoOpMetrics()) - ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) - - Convey("and a vm", func() { - interpreter := testutil.NewLightInterpreterMust(ctx) - interpreter.Register2(engine.NewAtom("bech32_address"), Bech32Address) - - err := interpreter.Compile(ctx, tc.program) - So(err, ShouldBeNil) - - Convey("When the predicate is called", func() { - sols, err := interpreter.QueryContext(ctx, tc.query) - - Convey("Then the error should be nil", func() { - So(err, ShouldBeNil) - So(sols, ShouldNotBeNil) - - Convey("and the bindings should be as expected", func() { - var got []testutil.TermResults - for sols.Next() { - m := testutil.TermResults{} - err := sols.Scan(m) - So(err, ShouldBeNil) - - got = append(got, m) - } - if tc.wantError != nil { - So(sols.Err(), ShouldNotBeNil) - So(sols.Err().Error(), ShouldEqual, tc.wantError.Error()) - } else { - So(sols.Err(), ShouldBeNil) - - if tc.wantSuccess { - So(len(got), ShouldBeGreaterThan, 0) - So(len(got), ShouldEqual, len(tc.wantResult)) - for iGot, resultGot := range got { - for varGot, termGot := range resultGot { - So(testutil.ReindexUnknownVariables(termGot), ShouldEqual, tc.wantResult[iGot][varGot]) - } - } - } else { - So(len(got), ShouldEqual, 0) - } - } - }) - }) - }) - }) - }) - }) - } - }) -} From b75e9ecfefc45518246923d6a39d12123f54caa0 Mon Sep 17 00:00:00 2001 From: ccamel Date: Wed, 28 Feb 2024 18:49:36 +0100 Subject: [PATCH 4/7] chore(project): add support for gherkin feature files --- .editorconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.editorconfig b/.editorconfig index 7dafd52e..3f2aaef3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -28,3 +28,6 @@ indent_size = 2 [*.go] indent_style = tab + +[*.feature] +indent_size = 2 From 4d16e9cde4b0e69d1cff62b3eb1e851bb6086627 Mon Sep 17 00:00:00 2001 From: ccamel Date: Wed, 28 Feb 2024 19:10:23 +0100 Subject: [PATCH 5/7] build(script): implement generation of predicate feature documentation --- scripts/generate_predicates_doc.go | 65 ++++++++++++++++--- scripts/templates/{doc.gotxt => doc.go.txt} | 0 scripts/templates/func.go.txt | 39 +++++++++++ scripts/templates/func.gotxt | 13 ---- scripts/templates/{list.gotxt => list.go.txt} | 0 scripts/templates/{text.gotxt => text.go.txt} | 0 6 files changed, 95 insertions(+), 22 deletions(-) rename scripts/templates/{doc.gotxt => doc.go.txt} (100%) create mode 100644 scripts/templates/func.go.txt delete mode 100644 scripts/templates/func.gotxt rename scripts/templates/{list.gotxt => list.go.txt} (100%) rename scripts/templates/{text.gotxt => text.go.txt} (100%) diff --git a/scripts/generate_predicates_doc.go b/scripts/generate_predicates_doc.go index 8ded281c..6e02f158 100644 --- a/scripts/generate_predicates_doc.go +++ b/scripts/generate_predicates_doc.go @@ -6,32 +6,40 @@ import ( "go/build" "os" "path" + "path/filepath" + "regexp" "slices" "strings" "github.com/Masterminds/sprig/v3" + gherkin "github.com/cucumber/gherkin/go/v26" + messages "github.com/cucumber/messages/go/v21" "github.com/huandu/xstrings" + "github.com/muesli/reflow/dedent" "github.com/princjef/gomarkdoc" "github.com/princjef/gomarkdoc/lang" "github.com/princjef/gomarkdoc/logger" "github.com/samber/lo" ) -//go:embed templates/*.gotxt +//go:embed templates/*.go.txt var f embed.FS const ( - predicatePath = "x/logic/predicate" - outputPath = "docs/predicate" + predicatesPath = "x/logic/predicate" + featuresPath = "x/logic" + outputPath = "docs/predicate" ) +var featureRegEx = regexp.MustCompile(`^.+\.feature$`) + func generatePredicateDocumentation() error { wd, err := os.Getwd() if err != nil { return err } - buildPkg, err := build.ImportDir(path.Join(wd, predicatePath), build.ImportComment) + buildPkg, err := build.ImportDir(path.Join(wd, predicatesPath), build.ImportComment) if err != nil { return err } @@ -47,20 +55,27 @@ func generatePredicateDocumentation() error { return strings.Compare(a.Name(), j.Name()) }) + features, err := loadFeatures(featuresPath) + if err != nil { + return err + } + for idx, f := range funcs { + name := strings.Replace(functorName(f), "/", "_", 1) + // globalCtx used to keep track of contexts between templates. // (yes it's a hack). globalCtx := make(map[string]interface{}) globalCtx["frontmatter"] = map[string]interface{}{ "sidebar_position": idx + 1, } + globalCtx["feature"] = features[name] out, err := createRenderer(globalCtx) if err != nil { return err } - name := strings.Replace(functorName(f), "/", "_", 1) content, err := out.Func(f) if err != nil { return err @@ -78,12 +93,12 @@ func createRenderer(ctx map[string]interface{}) (*gomarkdoc.Renderer, error) { templateFunctionOpts = append( templateFunctionOpts, - gomarkdoc.WithTemplateOverride("text", readTemplateMust("text.gotxt")), - gomarkdoc.WithTemplateOverride("doc", readTemplateMust("doc.gotxt")), - gomarkdoc.WithTemplateOverride("list", readTemplateMust("list.gotxt")), + gomarkdoc.WithTemplateOverride("text", readTemplateMust("text.go.txt")), + gomarkdoc.WithTemplateOverride("doc", readTemplateMust("doc.go.txt")), + gomarkdoc.WithTemplateOverride("list", readTemplateMust("list.go.txt")), gomarkdoc.WithTemplateOverride("import", ""), gomarkdoc.WithTemplateOverride("file", ""), - gomarkdoc.WithTemplateOverride("func", readTemplateMust("func.gotxt")), + gomarkdoc.WithTemplateOverride("func", readTemplateMust("func.go.txt")), gomarkdoc.WithTemplateOverride("index", ""), ) @@ -104,6 +119,8 @@ func createRenderer(ctx map[string]interface{}) (*gomarkdoc.Renderer, error) { }), gomarkdoc.WithTemplateFunc("functorName", functorName), gomarkdoc.WithTemplateFunc("bquote", bquote), + gomarkdoc.WithTemplateFunc("dedent", dedent.String), + gomarkdoc.WithTemplateFunc("tagged", tagged), ) return gomarkdoc.NewRenderer(templateFunctionOpts...) } @@ -141,3 +158,33 @@ func bquote(str ...interface{}) string { } return strings.Join(out, " ") } + +func tagged(tag string, feature *messages.Scenario) bool { + return lo.ContainsBy(feature.Tags, func(t *messages.Tag) bool { + return t.Name == tag + }) +} + +func loadFeatures(path string) (map[string]*messages.Feature, error) { + features := make(map[string]*messages.Feature) + err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { + if err == nil && featureRegEx.MatchString(info.Name()) { + bs, err := os.ReadFile(path) + if err != nil { + return err + } + r := strings.NewReader(string(bs)) + gherkinDocument, err := gherkin.ParseGherkinDocument(r, (&messages.Incrementing{}).NewId) + if err != nil { + return err + } + features[strings.TrimSuffix(info.Name(), filepath.Ext(info.Name()))] = gherkinDocument.Feature + } + return nil + }) + if err != nil { + return nil, err + } + + return features, nil +} diff --git a/scripts/templates/doc.gotxt b/scripts/templates/doc.go.txt similarity index 100% rename from scripts/templates/doc.gotxt rename to scripts/templates/doc.go.txt diff --git a/scripts/templates/func.go.txt b/scripts/templates/func.go.txt new file mode 100644 index 00000000..6a3d85c2 --- /dev/null +++ b/scripts/templates/func.go.txt @@ -0,0 +1,39 @@ +--- +sidebar_position: {{ index (index globalCtx "frontmatter") "sidebar_position" }} +--- +[//]: # (This file is auto-generated. Please do not modify it yourself.) +{{- $predicate := functorName . -}} +{{- $_ := set globalCtx "funcName" .Name -}} +{{- $_ := set globalCtx "predicate" $predicate }} + +# {{ $predicate }} + +## Description + +{{ template "doc" .Doc }} + +{{- $feature := index globalCtx "feature" -}} +{{- if $feature -}} +{{- spacer -}} +## Examples +{{- range $feature.Children -}} +{{- if .Scenario | tagged "@great_for_documentation" -}} +{{- spacer -}} +### {{ .Scenario.Name }} +{{- spacer -}} +{{ .Scenario.Description | dedent }} + +Here's the steps of the scenario: +{{- spacer -}} +{{- range .Scenario.Steps }} +- **{{ .Keyword | trim }}** {{ .Text }} +{{- if .DocString -}} +{{- spacer -}} +``` {{ .DocString.MediaType }} +{{ .DocString.Content }} +``` +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- end }} diff --git a/scripts/templates/func.gotxt b/scripts/templates/func.gotxt deleted file mode 100644 index 84d4c637..00000000 --- a/scripts/templates/func.gotxt +++ /dev/null @@ -1,13 +0,0 @@ ---- -sidebar_position: {{ index (index globalCtx "frontmatter") "sidebar_position" }} ---- -[//]: # (This file is auto-generated. Please do not modify it yourself.) -{{- $predicate := functorName . -}} -{{- $_ := set globalCtx "funcName" .Name -}} -{{- $_ := set globalCtx "predicate" $predicate }} - -# {{ $predicate }} - -## Description - -{{ template "doc" .Doc -}} diff --git a/scripts/templates/list.gotxt b/scripts/templates/list.go.txt similarity index 100% rename from scripts/templates/list.gotxt rename to scripts/templates/list.go.txt diff --git a/scripts/templates/text.gotxt b/scripts/templates/text.go.txt similarity index 100% rename from scripts/templates/text.gotxt rename to scripts/templates/text.go.txt From 598ac17082bd07e3d990400b18f145d63d818e63 Mon Sep 17 00:00:00 2001 From: ccamel Date: Wed, 28 Feb 2024 19:13:56 +0100 Subject: [PATCH 6/7] docs(logic): generate feature documentation for predicates --- docs/predicate/bech32_address_2.md | 189 +++++++++++++++++++++++++++-- x/logic/predicate/address.go | 16 +-- 2 files changed, 182 insertions(+), 23 deletions(-) diff --git a/docs/predicate/bech32_address_2.md b/docs/predicate/bech32_address_2.md index a73fa4db..bb9a06fe 100644 --- a/docs/predicate/bech32_address_2.md +++ b/docs/predicate/bech32_address_2.md @@ -9,12 +9,11 @@ sidebar_position: 4 `bech32_address/2` is a predicate that convert a [bech32]() encoded string into [base64]() bytes and give the address prefix, or convert a prefix \(HRP\) and [base64]() encoded bytes to [bech32]() encoded string. -The signature is as follows: +## Signature ```text -bech32_address(-Address, +Bech32) -bech32_address(+Address, -Bech32) -bech32_address(+Address, +Bech32) +bech32_address(-Address, +Bech32) is det +bech32_address(+Address, -Bech32) is det ``` where: @@ -24,11 +23,181 @@ where: ## Examples -```text -# Convert the given bech32 address into base64 encoded byte by unify the prefix of given address (Hrp) and the -base64 encoded value (Address). -- bech32_address(-(Hrp, Address), 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn'). +### Decode Bech32 Address into its Address Pair representation + +This scenario demonstrates how to parse a provided bech32 address string into its `Address` pair representation. +An `Address` is a compound term `-` with two arguments, the first being the human-readable part (Hrp) and the second +being the numeric address as a list of integers ranging from 0 to 255 representing the bytes of the address in +base 64. + +Here's the steps of the scenario: + +- **Given** the query: + +``` prolog +bech32_address(Address, 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn'). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +has_more: false +variables: ["Address"] +results: +- substitutions: + - variable: Address + expression: "okp4-[163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]" +``` + +### Decode Hrp and Address from a bech32 address + +This scenario illustrates how to decode a bech32 address into the human-readable part (Hrp) and the numeric address. +The process extracts these components from a given bech32 address string, showcasing the ability to parse and +separate the address into its constituent parts. + +Here's the steps of the scenario: + +- **Given** the query: + +``` prolog +bech32_address(-(Hrp, Address), 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn'). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +has_more: false +variables: ["Hrp", "Address"] +results: +- substitutions: + - variable: Hrp + expression: "okp4" + - variable: Address + expression: "[163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]" +``` + +### Extract Address only for OKP4 bech32 address + +This scenario demonstrates how to extract the address from a bech32 address string, specifically for a known +protocol, in this case, the okp4 protocol. + +Here's the steps of the scenario: + +- **Given** the query: + +``` prolog +bech32_address(-(okp4, Address), 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn'). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +has_more: false +variables: ["Address"] +results: +- substitutions: + - variable: Address + expression: "[163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]" +``` + +### Encode Address Pair into Bech32 Address + +This scenario demonstrates how to encode an `Address` pair representation into a bech32 address string. + +Here's the steps of the scenario: + +- **Given** the query: + +``` prolog +bech32_address(-('okp4', [163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), Bech32). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +has_more: false +variables: ["Bech32"] +results: +- substitutions: + - variable: Bech32 + expression: "okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn" +``` + +### Check if a bech32 address is part of the okp4 protocol + +This scenario shows how to check if a bech32 address is part of the okp4 protocol. + +Here's the steps of the scenario: + +- **Given** the program: + +``` +okp4_addr(Addr) :- bech32_address(-('okp4', _), Addr). +``` + +- **Given** the query: + +``` prolog +okp4_addr('okp41p8u47en82gmzfm259y6z93r9qe63l25dfwwng6'). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +has_more: false +results: +- substitutions: +``` + +### Error on Incorrect Bech32 Address format + +This scenario demonstrates the system's response to an incorrect bech32 address format. +In this case, the system generates a `domain_error`, indicating that the provided argument does not meet the +expected format for a bech32 address. + +Here's the steps of the scenario: + +- **Given** the query: + +``` prolog +bech32_address(Address, okp4incorrect). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +has_more: false +variables: ["Address"] +results: +- error: "error(domain_error(encoding(bech32),okp4incorrect),[d,e,c,o,d,i,n,g, ,b,e,c,h,3,2, ,f,a,i,l,e,d,:, ,i,n,v,a,l,i,d, ,s,e,p,a,r,a,t,o,r, ,i,n,d,e,x, ,-,1],bech32_address/2)" +``` + +### Error on Incorrect Bech32 Address type + +This scenario demonstrates the system's response to an incorrect bech32 address type. +In this case, the system generates a `type_error`, indicating that the provided argument does not meet the +expected type. + +Here's the steps of the scenario: + +- **Given** the query: + +``` prolog +bech32_address(-('okp4', X), foo(bar)). +``` + +- **When** the query is run +- **Then** the answer we get is: -# Convert the given pair of HRP and base64 encoded address byte by unify the Bech32 string encoded value. -- bech32_address(-('okp4', [163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), Bech32). +``` yaml +has_more: false +variables: ["X"] +results: +- error: "error(type_error(atom,foo(bar)),bech32_address/2)" ``` diff --git a/x/logic/predicate/address.go b/x/logic/predicate/address.go index 64f24bb3..5c9aca61 100644 --- a/x/logic/predicate/address.go +++ b/x/logic/predicate/address.go @@ -11,26 +11,16 @@ import ( // Bech32Address is a predicate that convert a [bech32] encoded string into [base64] bytes and give the address prefix, // or convert a prefix (HRP) and [base64] encoded bytes to [bech32] encoded string. // -// The signature is as follows: +// # Signature // -// bech32_address(-Address, +Bech32) -// bech32_address(+Address, -Bech32) -// bech32_address(+Address, +Bech32) +// bech32_address(-Address, +Bech32) is det +// bech32_address(+Address, -Bech32) is det // // where: // - Address is a pair of the HRP (Human-Readable Part) which holds the address prefix and a list of numbers // ranging from 0 to 255 that represent the base64 encoded bech32 address string. // - Bech32 is an Atom or string representing the bech32 encoded string address // -// # Examples: -// -// # Convert the given bech32 address into base64 encoded byte by unify the prefix of given address (Hrp) and the -// base64 encoded value (Address). -// - bech32_address(-(Hrp, Address), 'okp415wn30a9z4uc692s0kkx5fp5d4qfr3ac7sj9dqn'). -// -// # Convert the given pair of HRP and base64 encoded address byte by unify the Bech32 string encoded value. -// - bech32_address(-('okp4', [163,167,23,244,162,175,49,162,170,15,181,141,68,134,141,168,18,56,247,30]), Bech32). -// // [bech32]: https://docs.cosmos.network/main/build/spec/addresses/bech32#hrp-table // [base64]: https://fr.wikipedia.org/wiki/Base64 func Bech32Address(_ *engine.VM, address, bech32 engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise { From 939b2143e7f063c728bc03b0454c037755338f7f Mon Sep 17 00:00:00 2001 From: ccamel Date: Wed, 28 Feb 2024 20:18:56 +0100 Subject: [PATCH 7/7] build(deps): add muesli/reflow v0.3.0 dependency --- go.mod | 3 +-- go.sum | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 3065aef9..e2763f80 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( github.com/hyperledger/aries-framework-go v0.3.2 github.com/ichiban/prolog v1.2.0 github.com/ignite/cli v0.27.2 + github.com/muesli/reflow v0.3.0 github.com/nuts-foundation/go-did v0.12.0 github.com/piprate/json-gold v0.5.0 github.com/princjef/gomarkdoc v1.1.0 @@ -120,8 +121,6 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/creachadair/atomicfile v0.3.1 // indirect github.com/creachadair/tomledit v0.0.24 // indirect - github.com/cucumber/gherkin/go/v26 v26.2.0 // indirect - github.com/cucumber/messages/go/v21 v21.0.1 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect diff --git a/go.sum b/go.sum index b649a5dd..fb3ae424 100644 --- a/go.sum +++ b/go.sum @@ -989,6 +989,8 @@ github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4=