diff --git a/pkg/template/artifact.go b/pkg/template/artifact.go index 39c0131..94008d8 100644 --- a/pkg/template/artifact.go +++ b/pkg/template/artifact.go @@ -220,7 +220,7 @@ func parseFunction(v any) (*Function, error) { return nil, err } for _, in := range c.Inputs { - if !slices.Contains(scalarTypes, in.Type) { + if !validType(in.Type, nil) { return nil, invalid("unsupported arkade input type %q", in.Type) } } diff --git a/pkg/template/artifact_test.go b/pkg/template/artifact_test.go index 67fd389..950cf52 100644 --- a/pkg/template/artifact_test.go +++ b/pkg/template/artifact_test.go @@ -267,3 +267,19 @@ func fnOf(m map[string]any, i int) map[string]any { func leafOf(m map[string]any, f int) map[string]any { return fnOf(m, f)["leaves"].([]any)[0].(map[string]any) } + +func TestParseArtifactArrayInput(t *testing.T) { + doc := func(typ string) string { + return `{"contractName":"C","constructorInputs":[{"name":"k","type":"pubkey"}],"functions":[{"name":"f", + "arkade":{"inputs":[{"name":"sigs","type":"` + typ + `"}],"asm":["","OP_CHECKSIG"]}, + "leaves":[{"name":"l","witness":[{"name":"s","type":"signature","encoding":"schnorr-64","injected":true}], + "asm":["","OP_CHECKSIGVERIFY","","OP_CHECKSIG"]}]}]}` + } + a, err := ParseArtifact([]byte(doc("bytes[3]"))) + require.NoError(t, err) + require.Equal(t, "bytes[3]", a.Definition.Functions[0].Arkade.Inputs[0].Type) + for _, typ := range []string{"float[3]", "bytes[0]", "bytes[]", "bytes[3][2]", "signature[3]"} { + _, err := ParseArtifact([]byte(doc(typ))) + require.ErrorIs(t, err, ErrInvalidArtifact, typ) + } +} diff --git a/pkg/template/template.go b/pkg/template/template.go index 406acbf..d45cb4f 100644 --- a/pkg/template/template.go +++ b/pkg/template/template.go @@ -536,6 +536,11 @@ func (p *parser) spend(in *input, v any) error { if f.Arkade != nil { want = f.Arkade.Inputs } + for _, w := range want { + if arrayType.MatchString(w.Type) { + return fmt.Errorf("%w: function %q takes the array input %q, which a template cannot fill", ErrUnsupported, f.Name, w.Name) + } + } if len(args) != len(want) { return bad("function %q takes %d arguments, got %d", f.Name, len(want), len(args)) } diff --git a/pkg/template/template_test.go b/pkg/template/template_test.go index 9570489..283aaae 100644 --- a/pkg/template/template_test.go +++ b/pkg/template/template_test.go @@ -697,3 +697,27 @@ const vhtlc = ` "packets": {"output_index": 1, "unmatched": "drop"} } ` + +func TestArrayInputs(t *testing.T) { + withInput := func(name string) []byte { + return edited(t, []byte(releaseDoc), func(m map[string]any) { + def := m["inputs"].([]any)[0].(map[string]any)["contract"].(map[string]any)["definition"].(map[string]any) + for _, raw := range def["functions"].([]any) { + if f := raw.(map[string]any); f["name"] == name { + if f["arkade"] == nil { + f["arkade"] = map[string]any{"asm": []any{"1"}} + } + f["arkade"].(map[string]any)["inputs"] = []any{map[string]any{"name": "sigs", "type": "bytes[3]"}} + return + } + } + t.Fatalf("releaseDoc has no function %q", name) + }) + } + _, err := Parse(t.Context(), withInput("exit"), nil) + require.NoError(t, err, "an array input on a function the template does not spend") + + _, err = Parse(t.Context(), withInput("release"), nil) + require.ErrorIs(t, err, ErrUnsupported) + require.ErrorContains(t, err, `array input "sigs"`) +}