Skip to content

Commit 22f1fe2

Browse files
committed
feat: multi-sig script (CIP-1854)
Signed-off-by: Chris Gianelloni <[email protected]>
1 parent 72ceac7 commit 22f1fe2

30 files changed

+7027
-35
lines changed

bursa.go

Lines changed: 760 additions & 4 deletions
Large diffs are not rendered by default.

bursa_test.go

Lines changed: 495 additions & 0 deletions
Large diffs are not rendered by default.

cmd/bursa/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func main() {
3939
rootCmd.AddCommand(
4040
walletCommand(),
4141
apiCommand(),
42+
scriptCommand(),
4243
)
4344

4445
if err := rootCmd.Execute(); err != nil {

cmd/bursa/script.go

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// Copyright 2025 Blink Labs Software
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"github.com/blinklabs-io/bursa/internal/cli"
19+
"github.com/spf13/cobra"
20+
)
21+
22+
func scriptCommand() *cobra.Command {
23+
scriptCommand := cobra.Command{
24+
Use: "script",
25+
Short: "Script commands for multi-signature operations",
26+
}
27+
28+
scriptCommand.AddCommand(
29+
scriptCreateCommand(),
30+
scriptValidateCommand(),
31+
scriptAddressCommand(),
32+
)
33+
return &scriptCommand
34+
}
35+
36+
func scriptCreateCommand() *cobra.Command {
37+
var (
38+
required int
39+
keyHashes []string
40+
output string
41+
network string
42+
all bool
43+
any bool
44+
timelockBefore uint64
45+
timelockAfter uint64
46+
)
47+
48+
scriptCreateCommand := cobra.Command{
49+
Use: "create",
50+
Short: "Creates a new multi-signature script",
51+
Long: `Creates a new multi-signature script with the specified parameters.
52+
53+
Examples:
54+
# Create a 2-of-3 multi-sig script
55+
bursa script create --required 2 --key-hashes abcdef1234567890abcdef1234567890abcdef12,abcdef1234567890abcdef1234567890abcdef13,abcdef1234567890abcdef1234567890abcdef14
56+
57+
# Create an all-signers-required script
58+
bursa script create --all --key-hashes abcdef1234567890abcdef1234567890abcdef12,abcdef1234567890abcdef1234567890abcdef13
59+
60+
# Create an any-signer script
61+
bursa script create --any --key-hashes abcdef1234567890abcdef1234567890abcdef12,abcdef1234567890abcdef1234567890abcdef13,abcdef1234567890abcdef1234567890abcdef14
62+
63+
# Create a timelocked script (valid after slot 1000000)
64+
bursa script create --required 2 --key-hashes abcdef1234567890abcdef1234567890abcdef12,abcdef1234567890abcdef1234567890abcdef13 --timelock-after 1000000`,
65+
Run: func(cmd *cobra.Command, args []string) {
66+
cli.RunScriptCreate(
67+
required,
68+
keyHashes,
69+
output,
70+
network,
71+
all,
72+
any,
73+
timelockBefore,
74+
timelockAfter,
75+
)
76+
},
77+
}
78+
79+
scriptCreateCommand.Flags().
80+
IntVar(&required, "required", 0, "Number of required signatures (for N-of-M scripts)")
81+
scriptCreateCommand.Flags().
82+
StringSliceVar(&keyHashes, "key-hashes", nil, "Comma-separated list of key hashes (hex encoded)")
83+
scriptCreateCommand.Flags().
84+
StringVar(&output, "output", "", "Output file path (optional)")
85+
scriptCreateCommand.Flags().
86+
StringVar(&network, "network", "mainnet", "Network name (mainnet, testnet, etc.)")
87+
scriptCreateCommand.Flags().
88+
BoolVar(&all, "all", false, "Create all-signers-required script")
89+
scriptCreateCommand.Flags().
90+
BoolVar(&any, "any", false, "Create any-signer script")
91+
scriptCreateCommand.Flags().
92+
Uint64Var(&timelockBefore, "timelock-before", 0, "Make script valid only before this slot")
93+
scriptCreateCommand.Flags().
94+
Uint64Var(&timelockAfter, "timelock-after", 0, "Make script valid only after this slot")
95+
96+
return &scriptCreateCommand
97+
}
98+
99+
func scriptValidateCommand() *cobra.Command {
100+
var (
101+
scriptFile string
102+
signatures []string
103+
slot uint64
104+
structuralOnly bool
105+
)
106+
107+
scriptValidateCommand := cobra.Command{
108+
Use: "validate",
109+
Short: "Validates a script against signatures and slot",
110+
Long: `Validates whether a script is satisfied given a set of signatures and current slot.
111+
112+
By default, performs format validation requiring signatures for signature scripts.
113+
Use --structural-only for basic structure validation without signatures.
114+
115+
Examples:
116+
# Validate a script with signatures
117+
bursa script validate --script script.json --signatures sig1.hex,sig2.hex --slot 123456789
118+
119+
# Validate a timelocked script
120+
bursa script validate --script script.json --slot 123456789 --structural-only
121+
122+
# Perform structural validation only
123+
bursa script validate --script script.json --structural-only`,
124+
Run: func(cmd *cobra.Command, args []string) {
125+
cli.RunScriptValidate(scriptFile, signatures, slot, structuralOnly)
126+
},
127+
}
128+
129+
scriptValidateCommand.Flags().
130+
StringVar(&scriptFile, "script", "", "Path to script file (required)")
131+
scriptValidateCommand.Flags().
132+
StringSliceVar(&signatures, "signatures", nil, "Comma-separated list of signatures (hex encoded)")
133+
scriptValidateCommand.Flags().
134+
Uint64Var(&slot, "slot", 0, "Current slot number for timelock validation")
135+
scriptValidateCommand.Flags().
136+
BoolVar(&structuralOnly, "structural-only", false, "Perform structural validation only (no signature format checking)")
137+
138+
if err := scriptValidateCommand.MarkFlagRequired("script"); err != nil {
139+
panic(err)
140+
}
141+
142+
return &scriptValidateCommand
143+
}
144+
145+
func scriptAddressCommand() *cobra.Command {
146+
var (
147+
scriptFile string
148+
network string
149+
)
150+
151+
scriptAddressCommand := cobra.Command{
152+
Use: "address",
153+
Short: "Generates an address from a script",
154+
Long: `Generates a Cardano address from a multi-signature script.
155+
156+
Examples:
157+
# Generate mainnet address from script
158+
bursa script address --script script.json --network mainnet
159+
160+
# Generate testnet address from script
161+
bursa script address --script script.json --network testnet`,
162+
Run: func(cmd *cobra.Command, args []string) {
163+
cli.RunScriptAddress(scriptFile, network)
164+
},
165+
}
166+
167+
scriptAddressCommand.Flags().
168+
StringVar(&scriptFile, "script", "", "Path to script file (required)")
169+
scriptAddressCommand.Flags().
170+
StringVar(&network, "network", "mainnet", "Network name (mainnet, testnet, etc.)")
171+
172+
if err := scriptAddressCommand.MarkFlagRequired("script"); err != nil {
173+
panic(err)
174+
}
175+
176+
return &scriptAddressCommand
177+
}

0 commit comments

Comments
 (0)