Skip to content

Commit 18719d5

Browse files
authored
Merge pull request #4 from 01node/feat/ecsda
Add ecsda encryption and Berachain
2 parents 24a490a + 66b67c9 commit 18719d5

13 files changed

Lines changed: 711 additions & 166 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
./vanity-forge
22
dist/
3+
vanity-forge

constants.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package main
2+
3+
type Encryption int64
4+
5+
const (
6+
Undefined Encryption = iota
7+
Secp256k1
8+
Ethsecp256k1
9+
ECSDA
10+
)
11+
12+
type chain struct {
13+
Name string
14+
Prefix string
15+
PrefixFull string
16+
Encryption
17+
}
18+
19+
type settings struct {
20+
SelectedChain chain // chain selector string or nil
21+
MatcherMode string // starts-with, ends-with, contains
22+
SearchString string // search string
23+
NumAccounts string // number of accounts to generate
24+
RequiredLetters int // number of letters to generate
25+
RequiredDigits int // number of digits to generate
26+
}
27+
28+
type walletgenerator struct {
29+
GenerateWallet func() wallet
30+
}
31+
32+
type matcher struct {
33+
Mode string
34+
SearchString string
35+
Chain chain
36+
RequiredLetters int
37+
RequiredDigits int
38+
}
39+
40+
var (
41+
AvailableChains = []chain{
42+
{
43+
Name: "celestia",
44+
Prefix: "celestia",
45+
PrefixFull: "celestia1",
46+
Encryption: Secp256k1,
47+
},
48+
{
49+
Name: "cosmos",
50+
Prefix: "cosmos",
51+
PrefixFull: "cosmos1",
52+
Encryption: Secp256k1,
53+
},
54+
{
55+
Name: "dydx",
56+
Prefix: "dydx",
57+
PrefixFull: "dydx1",
58+
Encryption: Secp256k1,
59+
},
60+
{
61+
Name: "berachain",
62+
Prefix: "0x",
63+
PrefixFull: "0x",
64+
Encryption: ECSDA,
65+
},
66+
}
67+
MatcherModes = []string{"contains", "starts-with", "ends-with", "regex"}
68+
)

ecsda.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package main
2+
3+
import (
4+
"crypto/ecdsa"
5+
"log"
6+
"strings"
7+
8+
"github.com/ethereum/go-ethereum/crypto"
9+
)
10+
11+
type ecsdaWallet struct {
12+
Chain chain
13+
}
14+
15+
// On Ethereum and other networks compatible with the Ethereum Virtual Machine (EVM), public addresses all share the same format: they begin with 0x, and are followed by 40 alphanumeric characters (numerals and letters), adding up to 42 characters in total. They're also not case sensitive.
16+
17+
// This address is a number, even though it also includes alphabetical characters. This is because the hexadecimal (base 16) system used to generate the address doesn't just use numerals, like our ten-digit decimal system. Instead, the hexadecimal system uses the numerals 0-9 and the letters A-F. This means it has 16 characters at its disposal, hence the name base 16. In computer science and many programming languages, the 0x prefix is used at the start of all hex numbers, as they are known, to differentiate them from decimal values.
18+
19+
// bech16digits is a constant string representing the valid characters in a Bech16 encoding.
20+
const bech16digits = "0123456789"
21+
22+
// bech16letters represents the valid characters for a Bech16 encoding.
23+
const bech16letters = "abcdefABCDEF"
24+
25+
// bech16chars is a constant string that represents the characters used in the bech16 encoding scheme, which includes both digits and letters.
26+
const bech16chars = bech16digits + bech16letters
27+
28+
// bech16Only checks if the given string contains only characters from the bech16 character set.
29+
func (w ecsdaWallet) bech16Only(s string) bool {
30+
return w.countUnionChars(s, bech16chars) == len(s)
31+
}
32+
33+
// countUnionChars counts the number of characters in the input string 's' that are present in the 'letterSet'.
34+
// It returns the count of such characters.
35+
func (w ecsdaWallet) countUnionChars(s string, letterSet string) int {
36+
count := 0
37+
for _, char := range s {
38+
if strings.Contains(letterSet, string(char)) {
39+
count++
40+
}
41+
}
42+
return count
43+
}
44+
45+
// CheckRequiredDigits checks if the given candidate string has the required number of digits.
46+
// It counts the number of union characters between the candidate string and the bech16digits string.
47+
// If the count is less than the required number, it returns false; otherwise, it returns true.
48+
func (w ecsdaWallet) CheckRequiredDigits(candidate string, required int) bool {
49+
if w.countUnionChars(candidate, bech16digits) < required {
50+
return false
51+
}
52+
53+
return true
54+
}
55+
56+
// CheckRequiredLetters checks if a candidate string contains the required number of union characters.
57+
// It returns true if the candidate string meets the requirement, otherwise false.
58+
func (w ecsdaWallet) CheckRequiredLetters(candidate string, required int) bool {
59+
if w.countUnionChars(candidate, bech16letters) < required {
60+
return false
61+
}
62+
63+
return true
64+
}
65+
66+
// ValidateInput validates the input string based on the specified criteria.
67+
// It checks if the input string contains bech16 incompatible characters,
68+
// if it exceeds the maximum length of 40 characters,
69+
// and if the required number of letters and digits are non-negative and do not exceed 40.
70+
// It returns a slice of error messages indicating the validation errors, if any.
71+
func (w ecsdaWallet) ValidateInput(SearchString string, RequiredLetters int, RequiredDigits int) []string {
72+
var errs []string
73+
if !w.bech16Only(SearchString) {
74+
errs = append(errs, "ERROR: "+SearchString+" contains bech16 incompatible characters")
75+
}
76+
if len(SearchString) > 40 {
77+
errs = append(errs, "ERROR: "+SearchString+" is too long. Must be max 40 characters.")
78+
}
79+
if RequiredDigits < 0 || RequiredLetters < 0 {
80+
errs = append(errs, "ERROR: Can't require negative amount of characters.")
81+
}
82+
if RequiredDigits+RequiredLetters > 40 {
83+
errs = append(errs, "ERROR: Can't require more than 40 characters.")
84+
}
85+
return errs
86+
}
87+
88+
// GenerateWallet generates a new wallet by generating a private key and deriving the corresponding public key and address.
89+
// It returns a wallet struct containing the address, public key, and private key bytes.
90+
func (w ecsdaWallet) GenerateWallet() wallet {
91+
privateKey, err := crypto.GenerateKey()
92+
if err != nil {
93+
log.Fatal(err)
94+
}
95+
96+
privateKeyBytes := crypto.FromECDSA(privateKey)
97+
98+
publicKey := privateKey.Public()
99+
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
100+
if !ok {
101+
log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
102+
}
103+
104+
publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA)
105+
106+
address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
107+
108+
return wallet{address, publicKeyBytes, privateKeyBytes}
109+
}

ecsda_test.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestEcsdaWallet_GenerateWallet(t *testing.T) {
10+
wallet := ecsdaWallet{}
11+
w := wallet.GenerateWallet()
12+
assert.NotEmpty(t, w.Address)
13+
assert.NotNil(t, w.PublicKey)
14+
assert.NotNil(t, w.PrivateKey)
15+
}
16+
func TestEcsdaWallet_CountUnionChars(t *testing.T) {
17+
wallet := ecsdaWallet{}
18+
19+
// Test case 1: Counting union characters in a string with valid letter set
20+
s1 := "abcdef123456"
21+
letterSet1 := "abc123"
22+
expectedCount1 := 6
23+
assert.Equal(t, expectedCount1, wallet.countUnionChars(s1, letterSet1), "Expected count of 6 for valid letter set")
24+
25+
// Test case 2: Counting union characters in a string with empty letter set
26+
s2 := "abcdef123456"
27+
letterSet2 := ""
28+
expectedCount2 := 0
29+
assert.Equal(t, expectedCount2, wallet.countUnionChars(s2, letterSet2), "Expected count of 0 for empty letter set")
30+
31+
// Test case 3: Counting union characters in an empty string with valid letter set
32+
s3 := ""
33+
letterSet3 := "abc123"
34+
expectedCount3 := 0
35+
assert.Equal(t, expectedCount3, wallet.countUnionChars(s3, letterSet3), "Expected count of 0 for empty string")
36+
37+
// Test case 4: Counting union characters in a string with invalid letter set
38+
s4 := "abcdef123456"
39+
letterSet4 := "!@#$%"
40+
expectedCount4 := 0
41+
assert.Equal(t, expectedCount4, wallet.countUnionChars(s4, letterSet4), "Expected count of 0 for invalid letter set")
42+
}
43+
func TestEcsdaWallet_Bech16Only(t *testing.T) {
44+
wallet := ecsdaWallet{}
45+
46+
// Test case 1: All characters are valid bech16 characters
47+
s1 := "abcdef123456"
48+
assert.True(t, wallet.bech16Only(s1), "Expected true for valid bech16 characters")
49+
50+
// Test case 2: Some characters are not valid bech16 characters
51+
s2 := "abcde!@#$%"
52+
assert.False(t, wallet.bech16Only(s2), "Expected false for invalid bech16 characters")
53+
54+
// Test case 3: Empty string
55+
s3 := ""
56+
assert.True(t, wallet.bech16Only(s3), "Expected true for empty string")
57+
58+
// Test case 4: String with valid bech16 characters and additional characters
59+
s4 := "abcdef123456!"
60+
assert.False(t, wallet.bech16Only(s4), "Expected false for string with additional characters")
61+
}
62+
func TestEcsdaWallet_CheckRequiredDigits(t *testing.T) {
63+
wallet := ecsdaWallet{}
64+
65+
// Test case 1: Candidate string has less required digits
66+
candidate1 := "abcdef123456"
67+
required1 := 7
68+
assert.False(t, wallet.CheckRequiredDigits(candidate1, required1), "Expected false for candidate string with less required digits")
69+
70+
// Test case 2: Candidate string has exact required digits
71+
candidate2 := "abcdef123456"
72+
required2 := 6
73+
assert.True(t, wallet.CheckRequiredDigits(candidate2, required2), "Expected true for candidate string with exact required digits")
74+
75+
// Test case 3: Candidate string has more than required digits
76+
candidate3 := "abcdef123456"
77+
required3 := 5
78+
assert.True(t, wallet.CheckRequiredDigits(candidate3, required3), "Expected true for candidate string with more than required digits")
79+
80+
// Test case 4: Empty candidate string
81+
candidate4 := ""
82+
required4 := 3
83+
assert.False(t, wallet.CheckRequiredDigits(candidate4, required4), "Expected false for empty candidate string")
84+
}
85+
func TestEcsdaWallet_CheckRequiredLetters(t *testing.T) {
86+
wallet := ecsdaWallet{}
87+
88+
// Test case 1: Candidate string has enough required letters
89+
candidate1 := "abcdef123"
90+
required1 := 5
91+
assert.True(t, wallet.CheckRequiredLetters(candidate1, required1), "Expected true for candidate string with enough required letters")
92+
93+
// Test case 2: Candidate string does not have enough required letters
94+
candidate2 := "abc123"
95+
required2 := 10
96+
assert.False(t, wallet.CheckRequiredLetters(candidate2, required2), "Expected false for candidate string without enough required letters")
97+
98+
// Test case 3: Candidate string is empty
99+
candidate3 := ""
100+
required3 := 5
101+
assert.False(t, wallet.CheckRequiredLetters(candidate3, required3), "Expected false for empty candidate string")
102+
103+
// Test case 4: Required letters is 0
104+
candidate4 := "abc123"
105+
required4 := 0
106+
assert.True(t, wallet.CheckRequiredLetters(candidate4, required4), "Expected true for required letters equal to 0")
107+
}
108+
109+
func TestEcsdaWallet_ValidateInput(t *testing.T) {
110+
wallet := ecsdaWallet{}
111+
112+
// Test case 1: Valid input
113+
searchString1 := "abcdef123456"
114+
requiredLetters1 := 6
115+
requiredDigits1 := 6
116+
expectedErrs1 := []string(nil)
117+
assert.Equal(t, expectedErrs1, wallet.ValidateInput(searchString1, requiredLetters1, requiredDigits1), "Expected no errors for valid input")
118+
119+
// Test case 2: Invalid bech16 characters
120+
searchString2 := "abcde!@#$%"
121+
requiredLetters2 := 6
122+
requiredDigits2 := 6
123+
expectedErrs2 := []string{"ERROR: abcde!@#$% contains bech16 incompatible characters"}
124+
assert.Equal(t, expectedErrs2, wallet.ValidateInput(searchString2, requiredLetters2, requiredDigits2), "Expected error for invalid bech16 characters")
125+
126+
// Test case 3: String too long
127+
searchString3 := "abcdef123456abcdef123456abcdef123456abcdef1234567"
128+
requiredLetters3 := 6
129+
requiredDigits3 := 6
130+
expectedErrs3 := []string{"ERROR: abcdef123456abcdef123456abcdef123456abcdef1234567 is too long. Must be max 40 characters."}
131+
assert.Equal(t, expectedErrs3, wallet.ValidateInput(searchString3, requiredLetters3, requiredDigits3), "Expected error for string too long")
132+
133+
// Test case 4: Negative required characters
134+
searchString4 := "abcdef123456"
135+
requiredLetters4 := -1
136+
requiredDigits4 := 6
137+
expectedErrs4 := []string{"ERROR: Can't require negative amount of characters."}
138+
assert.Equal(t, expectedErrs4, wallet.ValidateInput(searchString4, requiredLetters4, requiredDigits4), "Expected error for negative required characters")
139+
140+
// Test case 5: Required characters exceed string length
141+
searchString5 := "abcdef123456abcdef123456abcdef123456abcdef123456abcdef123456abcdef123456"
142+
requiredLetters5 := 10
143+
requiredDigits5 := 10
144+
expectedErrs5 := []string{"ERROR: abcdef123456abcdef123456abcdef123456abcdef123456abcdef123456abcdef123456 is too long. Must be max 40 characters."}
145+
assert.Equal(t, expectedErrs5, wallet.ValidateInput(searchString5, requiredLetters5, requiredDigits5), "Expected error for required characters exceeding string length")
146+
}

go.mod

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ require (
88
github.com/charmbracelet/huh v0.2.3
99
github.com/charmbracelet/huh/spinner v0.0.0-20240108162426-58163e7b5b2f
1010
github.com/cosmos/cosmos-sdk v0.50.3
11+
github.com/ethereum/go-ethereum v1.13.10
1112
github.com/spf13/pflag v1.0.5
13+
github.com/stretchr/testify v1.8.4
1214
github.com/tendermint/tendermint v0.35.9
1315
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3
1416
)
@@ -19,15 +21,19 @@ require (
1921
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
2022
github.com/aymerick/douceur v0.2.0 // indirect
2123
github.com/btcsuite/btcd v0.22.1 // indirect
24+
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
2225
github.com/catppuccin/go v0.2.0 // indirect
2326
github.com/charmbracelet/bubbles v0.17.1 // indirect
2427
github.com/charmbracelet/bubbletea v0.25.0 // indirect
2528
github.com/charmbracelet/glamour v0.6.0 // indirect
2629
github.com/charmbracelet/lipgloss v0.9.1 // indirect
2730
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
2831
github.com/cosmos/btcutil v1.0.5 // indirect
32+
github.com/davecgh/go-spew v1.1.1 // indirect
33+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
2934
github.com/dlclark/regexp2 v1.10.0 // indirect
3035
github.com/gorilla/css v1.0.0 // indirect
36+
github.com/holiman/uint256 v1.2.4 // indirect
3137
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
3238
github.com/mattn/go-isatty v0.0.20 // indirect
3339
github.com/mattn/go-localereader v0.0.1 // indirect
@@ -39,14 +45,16 @@ require (
3945
github.com/muesli/termenv v0.15.2 // indirect
4046
github.com/olekukonko/tablewriter v0.0.5 // indirect
4147
github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc // indirect
48+
github.com/pmezard/go-difflib v1.0.0 // indirect
4249
github.com/rivo/uniseg v0.4.4 // indirect
4350
github.com/sasha-s/go-deadlock v0.3.1 // indirect
4451
github.com/yuin/goldmark v1.6.0 // indirect
4552
github.com/yuin/goldmark-emoji v1.0.2 // indirect
46-
golang.org/x/crypto v0.16.0 // indirect
53+
golang.org/x/crypto v0.17.0 // indirect
4754
golang.org/x/net v0.19.0 // indirect
48-
golang.org/x/sync v0.4.0 // indirect
55+
golang.org/x/sync v0.5.0 // indirect
4956
golang.org/x/sys v0.15.0 // indirect
5057
golang.org/x/term v0.15.0 // indirect
5158
golang.org/x/text v0.14.0 // indirect
59+
gopkg.in/yaml.v3 v3.0.1 // indirect
5260
)

0 commit comments

Comments
 (0)