From 877b7d38cdd99a6d59fb41e3b6c37d8f296c9f2e Mon Sep 17 00:00:00 2001 From: Louis Singer <41042567+louisinger@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:20:57 +0200 Subject: [PATCH] Support parsing multiple levels descriptors (#325) * [descriptor] fix parser to support multiple levels descriptors * add andTokenSize constant --- common/descriptor/expression.go | 21 ++--- common/descriptor/parser.go | 5 + common/descriptor/parser_test.go | 155 +++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 12 deletions(-) diff --git a/common/descriptor/expression.go b/common/descriptor/expression.go index 2af20a121..b2ca9aedf 100644 --- a/common/descriptor/expression.go +++ b/common/descriptor/expression.go @@ -19,6 +19,10 @@ var ( ErrNotExpectedPolicy = errors.New("not the expected policy") ) +const ( + andTokenSize = len("and(") +) + type Expression interface { Parse(policy string) error Script(verify bool) (string, error) @@ -156,28 +160,21 @@ func (e *And) Parse(policy string) error { return ErrNotExpectedPolicy } - index := strings.LastIndexByte(policy, ')') - if index == -1 { - return ErrInvalidAndPolicy - } - - childrenPolicy := policy[4:index] - if len(childrenPolicy) == 0 { + parts, err := splitScriptTree(policy[andTokenSize : len(policy)-1]) + if err != nil { return ErrInvalidAndPolicy } - children := strings.Split(childrenPolicy, ",") - if len(children) != 2 { - fmt.Println(children) + if len(parts) != 2 { return ErrInvalidAndPolicy } - first, err := parseExpression(children[0]) + first, err := parseExpression(parts[0]) if err != nil { return err } - second, err := parseExpression(children[1]) + second, err := parseExpression(parts[1]) if err != nil { return err } diff --git a/common/descriptor/parser.go b/common/descriptor/parser.go index e473a4d1f..1928b5a99 100644 --- a/common/descriptor/parser.go +++ b/common/descriptor/parser.go @@ -11,6 +11,9 @@ const UnspendableKey = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f281 func ParseTaprootDescriptor(desc string) (*TaprootDescriptor, error) { desc = strings.ReplaceAll(desc, " ", "") + desc = strings.ReplaceAll(desc, "\n", "") + desc = strings.ReplaceAll(desc, "\t", "") + desc = strings.ReplaceAll(desc, "\r", "") if !strings.HasPrefix(desc, "tr(") || !strings.HasSuffix(desc, ")") { return nil, fmt.Errorf("invalid descriptor format") @@ -93,6 +96,8 @@ func splitScriptTree(scriptTreeStr string) ([]string, error) { for _, char := range scriptTreeStr { switch char { + case '{', '}': + continue case '(': depth++ current.WriteRune(char) diff --git a/common/descriptor/parser_test.go b/common/descriptor/parser_test.go index a6e515b38..6d7fc585f 100644 --- a/common/descriptor/parser_test.go +++ b/common/descriptor/parser_test.go @@ -179,6 +179,161 @@ func TestParseTaprootDescriptor(t *testing.T) { }, wantErr: false, }, + { + name: "Multiple level descriptor", + desc: ` + tr( + 0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0, + { + { + { + and(and(pk(873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465), pk(873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465)), pk(873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465)), + and(older(512), and(pk(873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465), pk(873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465))) + }, + { + and(and(pk(873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465), pk(873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465)), pk(873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465)), + and(older(1024), and(pk(873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465), pk(873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465))) + } + }, + { + and(older(512), pk(873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465)), + and(older(512), and(pk(873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465), pk(873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465))) + } + } + ) + `, + expected: descriptor.TaprootDescriptor{ + InternalKey: descriptor.Key{Hex: "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"}, + ScriptTree: []descriptor.Expression{ + &descriptor.And{ + First: &descriptor.And{ + First: &descriptor.PK{ + Key: descriptor.XOnlyKey{ + descriptor.Key{ + Hex: "873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465", + }, + }, + }, + Second: &descriptor.PK{ + Key: descriptor.XOnlyKey{ + descriptor.Key{ + Hex: "873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465", + }, + }, + }, + }, + Second: &descriptor.PK{ + Key: descriptor.XOnlyKey{ + descriptor.Key{ + Hex: "873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465", + }, + }, + }, + }, + &descriptor.And{ + First: &descriptor.Older{ + Timeout: 512, + }, + Second: &descriptor.And{ + First: &descriptor.PK{ + Key: descriptor.XOnlyKey{ + descriptor.Key{ + Hex: "873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465", + }, + }, + }, + Second: &descriptor.PK{ + Key: descriptor.XOnlyKey{ + descriptor.Key{ + Hex: "873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465", + }, + }, + }, + }, + }, + &descriptor.And{ + First: &descriptor.And{ + First: &descriptor.PK{ + Key: descriptor.XOnlyKey{ + descriptor.Key{ + Hex: "873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465", + }, + }, + }, + Second: &descriptor.PK{ + Key: descriptor.XOnlyKey{ + descriptor.Key{ + Hex: "873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465", + }, + }, + }, + }, + Second: &descriptor.PK{ + Key: descriptor.XOnlyKey{ + descriptor.Key{ + Hex: "873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465", + }, + }, + }, + }, + &descriptor.And{ + First: &descriptor.Older{ + Timeout: 1024, + }, + Second: &descriptor.And{ + First: &descriptor.PK{ + Key: descriptor.XOnlyKey{ + descriptor.Key{ + Hex: "873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465", + }, + }, + }, + Second: &descriptor.PK{ + Key: descriptor.XOnlyKey{ + descriptor.Key{ + Hex: "873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465", + }, + }, + }, + }, + }, + &descriptor.And{ + First: &descriptor.Older{ + Timeout: 512, + }, + Second: &descriptor.PK{ + Key: descriptor.XOnlyKey{ + descriptor.Key{ + Hex: "873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465", + }, + }, + }, + }, + &descriptor.And{ + First: &descriptor.Older{ + Timeout: 512, + }, + Second: &descriptor.And{ + First: &descriptor.PK{ + Key: descriptor.XOnlyKey{ + descriptor.Key{ + Hex: "873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465", + }, + }, + }, + Second: &descriptor.PK{ + Key: descriptor.XOnlyKey{ + descriptor.Key{ + Hex: "873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465", + }, + }, + }, + }, + }, + }, + }, + wantErr: false, + }, } for _, tt := range tests {