Update project structure and build process

This commit is contained in:
Juan Pablo Civile
2025-05-13 11:10:08 -03:00
parent 124e9fa1bc
commit d9f3e925a4
277 changed files with 15321 additions and 930 deletions

128
libwallet/hdpath/hdpath.go Normal file
View File

@@ -0,0 +1,128 @@
package hdpath
import (
"fmt"
"regexp"
"strconv"
"strings"
"github.com/btcsuite/btcutil/hdkeychain"
)
type Path string
const HardenedSymbol = "'"
var re = regexp.MustCompile("^(m?|\\/|(([a-z]+:)?\\d+'?))(\\/([a-z]+:)?\\d+'?)*$")
func Parse(s string) (Path, error) {
if !re.MatchString(s) {
return "", fmt.Errorf("path is not valid: `%s`", s)
}
return Path(s), nil
}
func MustParse(s string) Path {
p, err := Parse(s)
if err != nil {
panic(err)
}
return p
}
func (p Path) Child(i uint32) Path {
isChildHardened := i >= hdkeychain.HardenedKeyStart
if isChildHardened {
i -= hdkeychain.HardenedKeyStart
return Path(fmt.Sprintf("%s/%d'", p, i))
}
return Path(fmt.Sprintf("%s/%d", p, i))
}
func (p Path) NamedChild(name string, i uint32) Path {
isChildHardened := i >= hdkeychain.HardenedKeyStart
if isChildHardened {
i -= hdkeychain.HardenedKeyStart
return Path(fmt.Sprintf("%s/%s:%d'", p, name, i))
}
return Path(fmt.Sprintf("%s/%s:%d", p, name, i))
}
func (p Path) String() string {
return string(p)
}
type PathIndex struct {
Index uint32
Hardened bool
Name string
}
// Indexes returns the derivation indexes corresponding to this path.
//
// The following paths are valid:
//
// "" (root key)
// "m" (root key)
// "/" (root key)
// "m/0'" (hardened child #0 of the root key)
// "/0'" (hardened child #0 of the root key)
// "0'" (hardened child #0 of the root key)
// "m/44'/1'/2'" (BIP44 testnet account #2)
// "/44'/1'/2'" (BIP44 testnet account #2)
// "44'/1'/2'" (BIP44 testnet account #2)
// "m/schema:1'" (Muun schema path)
//
// The following paths are invalid:
//
// "m / 0 / 1" (contains spaces)
// "m/b/c" (alphabetical characters instead of numerical indexes)
// "m/1.2^3" (contains illegal characters)
func (p Path) Indexes() []PathIndex {
path := string(p)
if path == "m" || path == "/" || path == "" {
return make([]PathIndex, 0)
}
var indexes []PathIndex
path = strings.TrimPrefix(path, "m")
path = strings.TrimPrefix(path, "/")
for _, chunk := range strings.Split(path, "/") {
hardened := false
indexText := chunk
if strings.HasSuffix(indexText, HardenedSymbol) {
hardened = true
indexText = strings.TrimSuffix(indexText, HardenedSymbol)
}
parts := strings.Split(indexText, ":")
if len(parts) > 2 {
panic("path is malformed: " + path)
}
var name string
if len(parts) == 2 {
name = parts[0]
}
index, err := strconv.ParseUint(parts[len(parts)-1], 10, 32)
if err != nil {
panic("path is malformed: " + err.Error())
}
indexes = append(indexes, PathIndex{
Index: uint32(index),
Hardened: hardened,
Name: name,
})
}
return indexes
}
// IndexesFrom returns the indexes starting from the given parent path.
func (p Path) IndexesFrom(parentPath Path) []PathIndex {
return p.Indexes()[len(parentPath.Indexes()):]
}

View File

@@ -0,0 +1,82 @@
package hdpath
import (
"reflect"
"testing"
)
var (
rootPath = make([]PathIndex, 0)
shortPath = []PathIndex{
PathIndex{Index: 0, Hardened: true},
}
longPath = []PathIndex{
PathIndex{Index: 44, Hardened: true},
PathIndex{Index: 1, Hardened: true},
PathIndex{Index: 2, Hardened: false},
}
shortMuunPath = []PathIndex{
PathIndex{Index: 1, Hardened: true, Name: "schema"},
}
longMuunPath = []PathIndex{
PathIndex{Index: 1, Hardened: true, Name: "schema"},
PathIndex{Index: 1, Hardened: true, Name: "recovery"},
}
)
func TestBuild(t *testing.T) {
p, err := Parse("m/1/1")
if err != nil {
t.Fatal(err)
}
p = p.Child(0)
p = p.NamedChild("foo", 1)
if p.String() != "m/1/1/0/foo:1" {
t.Fatalf("expected path to be m/1/1/0/foo:1, got %s instead", p.String())
}
}
func TestParsingAndValidation(t *testing.T) {
type args struct {
path string
}
tests := []struct {
name string
args args
want []PathIndex
wantErr bool
}{
{name: "root1", args: args{path: ""}, want: rootPath},
{name: "root2", args: args{path: "m"}, want: rootPath},
{name: "root3", args: args{path: "/"}, want: rootPath},
{name: "short1", args: args{path: "m/0'"}, want: shortPath},
{name: "short2", args: args{path: "0'"}, want: shortPath},
{name: "short3", args: args{path: "/0'"}, want: shortPath},
{name: "long1", args: args{path: "m/44'/1'/2"}, want: longPath},
{name: "long2", args: args{path: "/44'/1'/2"}, want: longPath},
{name: "long3", args: args{path: "44'/1'/2"}, want: longPath},
{name: "shortMuun", args: args{path: "m/schema:1'"}, want: shortMuunPath},
{name: "longMuun", args: args{path: "m/schema:1'/recovery:1'"}, want: longMuunPath},
{name: "has spaces", args: args{path: "m / 0 / 1"}, wantErr: true},
{name: "has no indexes", args: args{path: "m/b/c"}, wantErr: true},
{name: "has weird chars", args: args{path: "m/1.2^3"}, wantErr: true},
{name: "has several :", args: args{path: "m/recovery:1:1"}, wantErr: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
path, err := Parse(tt.args.path)
if (err != nil) != tt.wantErr {
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
}
if tt.wantErr {
return
}
indexes := path.Indexes()
if !reflect.DeepEqual(indexes, tt.want) {
t.Errorf("Indexes() = %v, want %v", indexes, tt.want)
}
})
}
}