From 99314097d7b99ee33ad5eed52db6af6b51244a2e Mon Sep 17 00:00:00 2001 From: "Andrew Camilleri (Kukks)" Date: Thu, 8 Oct 2026 21:32:36 +0200 Subject: [PATCH 1/4] fix(template): push a pubkey into a covenant as its x-only key leafLookup already turns a pubkey constructor value into the 32-byte x-only key a taproot script uses, but covenantLookup passed every field through raw, so the same key reached a covenant as 33 compressed bytes. A covenant that compares a key with a taproot witness program (`scriptPubKey == key`) can then never succeed, and the derived address disagrees with the one the ts-sdk and arkadec's own e2e derive from the same artifact and params: both bind a pubkey with schnorr.SerializePubKey. Both lookups now share one conversion. DELEGATE_KEY keeps its hex spelling, since intent messages carry cosigner keys that way. No bundled artifact puts a pubkey in a covenant, so no existing template's address moves. --- pkg/template/contract.go | 27 ++++++++++++++------------- pkg/template/contract_test.go | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/pkg/template/contract.go b/pkg/template/contract.go index 43e0ac7..1a779b9 100644 --- a/pkg/template/contract.go +++ b/pkg/template/contract.go @@ -31,6 +31,18 @@ func build(def *Definition, args map[string]value, keys Keys) (*contract, error) } return v, nil } + // covenants and leaves both see a pubkey as the x-only key taproot uses + keyField := func(name string) (value, error) { + v, err := field(name) + if err != nil || v.typ != "pubkey" { + return v, err + } + pk, err := btcec.ParsePubKey(v.raw) + if err != nil { + return value{}, err + } + return value{"bytes32", schnorr.SerializePubKey(pk)}, nil + } delegate := func() (*btcec.PublicKey, error) { if keys.Delegate == nil { @@ -41,7 +53,7 @@ func build(def *Definition, args map[string]value, keys Keys) (*contract, error) // intent messages spell cosigner keys in hex covenantLookup := func(name string) (value, error) { if name != "DELEGATE_KEY" { - return field(name) + return keyField(name) } pk, err := delegate() if err != nil { @@ -94,18 +106,7 @@ func build(def *Definition, args map[string]value, keys Keys) (*contract, error) } return tweak(pk, parts[2]) } - v, err := field(name) - if err != nil { - return value{}, err - } - if v.typ == "pubkey" { - pk, err := btcec.ParsePubKey(v.raw) - if err != nil { - return value{}, err - } - return value{"bytes32", schnorr.SerializePubKey(pk)}, nil - } - return v, nil + return keyField(name) } var leaves []txscript.TapLeaf diff --git a/pkg/template/contract_test.go b/pkg/template/contract_test.go index bb69d53..a8e9400 100644 --- a/pkg/template/contract_test.go +++ b/pkg/template/contract_test.go @@ -63,6 +63,21 @@ func TestBuildTreeShapes(t *testing.T) { } } +// A covenant compares a key against a taproot witness program, so it needs the +// x-only bytes a leaf gets; arkadec's own e2e binds pubkeys the same way. +func TestBuildCovenantPushesXOnlyPubkey(t *testing.T) { + owner := testKey(t, 2) + def := parseDef(t, `{"contractName":"C","constructorInputs":[{"name":"owner","type":"pubkey"}],"functions":[ + {"name":"pay","arkade":{"inputs":[],"asm":["","OP_DROP","OP_1"]},"leaves":[{"name":"pay","asm":["","OP_CHECKSIGVERIFY","","OP_CHECKSIG"],"witness":[]}]}]}`) + c, err := build(def, map[string]value{"owner": {"pubkey", owner.SerializeCompressed()}}, Keys{Server: testKey(t, 1), Emulator: testKey(t, 3)}) + require.NoError(t, err) + + want, err := txscript.NewScriptBuilder(). + AddData(schnorr.SerializePubKey(owner)).AddOp(txscript.OP_DROP).AddOp(txscript.OP_1).Script() + require.NoError(t, err) + require.Equal(t, want, c.covenants["pay"]) +} + func TestBuildRejects(t *testing.T) { keys := Keys{Server: testKey(t, 1), Emulator: testKey(t, 3)} def := parseDef(t, `{"contractName":"C","constructorInputs":[],"functions":[ From 56fb40ba7e6c8a0f256407a40888d7f671e5cebc Mon Sep 17 00:00:00 2001 From: "Andrew Camilleri (Kukks)" Date: Sun, 11 Oct 2026 22:04:27 +0200 Subject: [PATCH 2/4] fix: update HTTP/2 dependencies --- api-spec/go.mod | 6 +++--- api-spec/go.sum | 12 ++++++------ go.mod | 12 ++++++------ go.sum | 32 ++++++++++++++++---------------- pkg/template/go.mod | 4 ++-- pkg/template/go.sum | 12 ++++++------ 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/api-spec/go.mod b/api-spec/go.mod index ed21218..642d9d8 100644 --- a/api-spec/go.mod +++ b/api-spec/go.mod @@ -10,9 +10,9 @@ require ( require ( github.com/julienschmidt/httprouter v1.3.0 // indirect - golang.org/x/net v0.57.0 // indirect - golang.org/x/sys v0.47.0 // indirect - golang.org/x/text v0.40.0 // indirect + golang.org/x/net v0.60.0 // indirect + golang.org/x/sys v0.48.0 // indirect + golang.org/x/text v0.42.0 // indirect google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect diff --git a/api-spec/go.sum b/api-spec/go.sum index 283f56e..1096f0a 100644 --- a/api-spec/go.sum +++ b/api-spec/go.sum @@ -26,12 +26,12 @@ go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRk go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= -golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= -golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= -golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= -golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= -golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= +golang.org/x/net v0.60.0 h1:79p50tfZlm0J9YfoDsSi639qSXNGVwEzOPLCxM2FsYU= +golang.org/x/net v0.60.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg= +golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= +golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= +golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= +golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= diff --git a/go.mod b/go.mod index 5d9c39a..a67e9ad 100644 --- a/go.mod +++ b/go.mod @@ -29,8 +29,8 @@ require ( github.com/prometheus/client_golang v1.23.2 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.12.1 - golang.org/x/crypto v0.56.0 - golang.org/x/sync v0.22.0 + golang.org/x/crypto v0.57.0 + golang.org/x/sync v0.23.0 golang.org/x/time v0.15.0 google.golang.org/grpc v1.83.2 google.golang.org/protobuf v1.36.11 @@ -105,10 +105,10 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 // indirect - golang.org/x/net v0.58.0 // indirect - golang.org/x/sys v0.47.0 // indirect - golang.org/x/term v0.45.0 // indirect - golang.org/x/text v0.41.0 // indirect + golang.org/x/net v0.60.0 // indirect + golang.org/x/sys v0.48.0 // indirect + golang.org/x/term v0.46.0 // indirect + golang.org/x/text v0.42.0 // indirect google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect diff --git a/go.sum b/go.sum index 4bde98e..3ff0e4e 100644 --- a/go.sum +++ b/go.sum @@ -367,27 +367,27 @@ go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= -golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y= -golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I= +golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M= +golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA= golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 h1:3yiSh9fhy5/RhCSntf4Sy0Tnx50DmMpQ4MQdKKk4yg4= golang.org/x/exp v0.0.0-20250811191247-51f88131bc50/go.mod h1:rT6SFzZ7oxADUDx58pcaKFTcZ+inxAa9fTrYx/uVYwg= -golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= -golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= -golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= -golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= -golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= -golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c= +golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o= +golang.org/x/net v0.60.0 h1:79p50tfZlm0J9YfoDsSi639qSXNGVwEzOPLCxM2FsYU= +golang.org/x/net v0.60.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg= +golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk= +golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= -golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= -golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= -golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= -golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= +golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= +golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= +golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE= +golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc= +golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= +golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= -golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= +golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= +golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= diff --git a/pkg/template/go.mod b/pkg/template/go.mod index 2b2e5f2..8665eda 100644 --- a/pkg/template/go.mod +++ b/pkg/template/go.mod @@ -34,9 +34,9 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect - golang.org/x/crypto v0.56.0 // indirect + golang.org/x/crypto v0.57.0 // indirect golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 // indirect - golang.org/x/sys v0.47.0 // indirect + golang.org/x/sys v0.48.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect google.golang.org/grpc v1.82.1 // indirect diff --git a/pkg/template/go.sum b/pkg/template/go.sum index 4efb929..81b6b9f 100644 --- a/pkg/template/go.sum +++ b/pkg/template/go.sum @@ -72,15 +72,15 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= -golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y= -golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I= +golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M= +golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA= golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 h1:3yiSh9fhy5/RhCSntf4Sy0Tnx50DmMpQ4MQdKKk4yg4= golang.org/x/exp v0.0.0-20250811191247-51f88131bc50/go.mod h1:rT6SFzZ7oxADUDx58pcaKFTcZ+inxAa9fTrYx/uVYwg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= -golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= -golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= +golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= +golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= +golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= +golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec= google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc= google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw= From 3141ef529c5c06f5ed3059b8ac667f7605aa93de Mon Sep 17 00:00:00 2001 From: louisinger Date: Wed, 21 Oct 2026 23:02:28 +0200 Subject: [PATCH 3/4] cleaning --- pkg/template/contract.go | 105 +++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 49 deletions(-) diff --git a/pkg/template/contract.go b/pkg/template/contract.go index f294cee..b638d4a 100644 --- a/pkg/template/contract.go +++ b/pkg/template/contract.go @@ -29,81 +29,88 @@ func build(def *Definition, args map[string]value, keys Keys) (*contract, error) return buildTree(def, args, keys, nil) } -// buildTree checks the declared leaves against taptree when it is given, and takes the tree's leaves and key. -func buildTree(def *Definition, args map[string]value, keys Keys, taptree []byte) (*contract, error) { - field := func(name string) (value, error) { - v, ok := args[name] - if !ok { - return value{}, fmt.Errorf("unknown constructor value %q", name) - } - return v, nil +func field(args map[string]value, name string) (value, error) { + v, ok := args[name] + if !ok { + return value{}, fmt.Errorf("unknown constructor value %q", name) } - // covenants and leaves both see a pubkey as the x-only key taproot uses - keyField := func(name string) (value, error) { - v, err := field(name) - if err != nil || v.typ != "pubkey" { - return v, err - } - pk, err := btcec.ParsePubKey(v.raw) - if err != nil { - return value{}, err - } - return value{"bytes32", schnorr.SerializePubKey(pk)}, nil + return v, nil +} + +func keyField(args map[string]value, name string) (value, error) { + v, err := field(args, name) + if err != nil || v.typ != "pubkey" { + return v, err + } + pk, err := btcec.ParsePubKey(v.raw) + if err != nil { + return value{}, err } + return value{"bytes32", schnorr.SerializePubKey(pk)}, nil +} - delegate := func() (*btcec.PublicKey, error) { - if keys.Delegate == nil { - return nil, fmt.Errorf("%w: no delegate key", ErrUnsupported) - } - return keys.Delegate, nil +func delegate(keys Keys) (*btcec.PublicKey, error) { + if keys.Delegate == nil { + return nil, fmt.Errorf("%w: no delegate key", ErrUnsupported) } - // intent messages spell cosigner keys in hex - covenantLookup := func(name string) (value, error) { - if name != "DELEGATE_KEY" { - return keyField(name) - } - pk, err := delegate() - if err != nil { - return value{}, err - } - return value{"bytes", []byte(hex.EncodeToString(pk.SerializeCompressed()))}, nil + return keys.Delegate, nil +} + + func covenantLookup(keys Keys, args map[string]value, name string) (value, error) { + if name != "DELEGATE_KEY" { + return keyField(args, name) + } + pk, err := delegate(keys) + if err != nil { + return value{}, err + } + return value{"bytes", []byte(hex.EncodeToString(pk.SerializeCompressed()))}, nil +} + +func tweak(c *contract, key *btcec.PublicKey, fn string) (value, error) { + cov, ok := c.covenants[fn] + if !ok { + return value{}, fmt.Errorf("function %q has no covenant", fn) + } + t := arkade.ComputeArkadeScriptPublicKey(key, arkade.ArkadeScriptHash(cov)) + return value{"bytes32", schnorr.SerializePubKey(t)}, nil +} + +// buildTree checks the declared leaves against taptree when it is given, and takes the tree's leaves and key. +func buildTree(def *Definition, args map[string]value, keys Keys, taptree []byte) (*contract, error) { + c := &contract{ + covenants: map[string][]byte{}, + proofs: map[[2]string]*psbt.TaprootTapLeafScript{}, } - c := &contract{covenants: map[string][]byte{}, proofs: map[[2]string]*psbt.TaprootTapLeafScript{}} for _, fn := range def.Functions { if fn.Arkade == nil { continue } - s, err := assemble(fn.Arkade.Asm, covenantLookup) + s, err := assemble(fn.Arkade.Asm, func(name string) (value, error) { + return covenantLookup(keys, args, name) + }) if err != nil { return nil, fmt.Errorf("%w: function %q: %v", ErrInvalidTemplate, fn.Name, err) } c.covenants[fn.Name] = s } - tweak := func(key *btcec.PublicKey, fn string) (value, error) { - cov, ok := c.covenants[fn] - if !ok { - return value{}, fmt.Errorf("function %q has no covenant", fn) - } - t := arkade.ComputeArkadeScriptPublicKey(key, arkade.ArkadeScriptHash(cov)) - return value{"bytes32", schnorr.SerializePubKey(t)}, nil - } leafLookup := func(name string) (value, error) { parts := strings.Split(name, ":") switch { case name == "SERVER_KEY": return value{"bytes32", schnorr.SerializePubKey(keys.Server)}, nil case name == "DELEGATE_KEY": - pk, err := delegate() + pk, err := delegate(keys) if err != nil { return value{}, err } return value{"bytes32", schnorr.SerializePubKey(pk)}, nil case len(parts) == 2 && parts[0] == "EMULATOR_KEY": - return tweak(keys.Emulator, parts[1]) + return tweak(c, keys.Emulator, parts[1]) case len(parts) == 3 && parts[0] == "TWEAK": - v, err := field(parts[1]) + v, err := field(args, parts[1]) if err != nil { return value{}, err } @@ -111,9 +118,9 @@ func buildTree(def *Definition, args map[string]value, keys Keys, taptree []byte if err != nil { return value{}, err } - return tweak(pk, parts[2]) + return tweak(c, pk, parts[2]) } - return keyField(name) + return keyField(args, name) } var leaves []txscript.TapLeaf From 94ed8c6e0e817cc30236e12a4b4736ff76ddf7be Mon Sep 17 00:00:00 2001 From: louisinger Date: Wed, 21 Oct 2026 23:05:50 +0200 Subject: [PATCH 4/4] lint --- Makefile | 6 +++++- pkg/template/contract.go | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 4d40a47..67c72da 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev) BINARY = delegateed LDFLAGS := -s -w -X 'main.Version=$(VERSION)' -.PHONY: build build-all clean test test-e2e lint fmt deps docker regtest-up regtest-run regtest-down run proto proto-lint pgsqlc pgmigrate +.PHONY: build build-all clean test test-e2e lint lint-fix fmt deps docker regtest-up regtest-run regtest-down run proto proto-lint pgsqlc pgmigrate build: go build -ldflags="$(LDFLAGS)" -o bin/$(BINARY) ./cmd/delegateed @@ -30,6 +30,10 @@ lint: golangci-lint run ./... cd pkg/template && golangci-lint run ./... +lint-fix: + golangci-lint run --fix ./... + cd pkg/template && golangci-lint run --fix ./... + ## proto: regenerates api-spec/protobuf/gen and the openapi spec with buf (docker) proto: proto-lint @docker run --rm --volume "$(shell pwd):/workspace" --workdir /workspace buf generate diff --git a/pkg/template/contract.go b/pkg/template/contract.go index b638d4a..97b8624 100644 --- a/pkg/template/contract.go +++ b/pkg/template/contract.go @@ -56,7 +56,7 @@ func delegate(keys Keys) (*btcec.PublicKey, error) { return keys.Delegate, nil } - func covenantLookup(keys Keys, args map[string]value, name string) (value, error) { +func covenantLookup(keys Keys, args map[string]value, name string) (value, error) { if name != "DELEGATE_KEY" { return keyField(args, name) } @@ -79,8 +79,8 @@ func tweak(c *contract, key *btcec.PublicKey, fn string) (value, error) { // buildTree checks the declared leaves against taptree when it is given, and takes the tree's leaves and key. func buildTree(def *Definition, args map[string]value, keys Keys, taptree []byte) (*contract, error) { c := &contract{ - covenants: map[string][]byte{}, - proofs: map[[2]string]*psbt.TaprootTapLeafScript{}, + covenants: map[string][]byte{}, + proofs: map[[2]string]*psbt.TaprootTapLeafScript{}, } for _, fn := range def.Functions {