Skip to content

Commit

Permalink
support more hdwallets for one account
Browse files Browse the repository at this point in the history
  • Loading branch information
fanhousanbu committed Sep 19, 2024
1 parent d1a1aa0 commit 457a53d
Show file tree
Hide file tree
Showing 19 changed files with 306 additions and 208 deletions.
1 change: 1 addition & 0 deletions internal/common_util/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package common_util
import (
"crypto/ecdsa"
"encoding/hex"

"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
Expand Down
26 changes: 14 additions & 12 deletions internal/common_util/crypto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,21 @@ func TestEthereumSignHexStr(t *testing.T) {
if err != nil {
t.Fatal(err)
}
privateKeyStr := hdWallet.PrivateKey
address := hdWallet.Address
t.Logf("address: %s", address)
t.Logf("privateKeyStr: %s", privateKeyStr)
privateKeyECDSA, err := crypto.HexToECDSA(privateKeyStr)
if err != nil {
t.Fatal(err)
for w := range hdWallet {
privateKeyStr := hdWallet[w].PrivateKey
address := hdWallet[w].Address
t.Logf("address: %s", address)
t.Logf("privateKeyStr: %s", privateKeyStr)
privateKeyECDSA, err := crypto.HexToECDSA(privateKeyStr)
if err != nil {
t.Fatal(err)
}
publicKey := privateKeyECDSA.Public()
publicKeyECDSA, _ := publicKey.(*ecdsa.PublicKey)
addressAno := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
t.Logf("addressAno: %s", addressAno)
//sign, err := EthereumSignHexStr(private
}
publicKey := privateKeyECDSA.Public()
publicKeyECDSA, _ := publicKey.(*ecdsa.PublicKey)
addressAno := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
t.Logf("addressAno: %s", addressAno)
//sign, err := EthereumSignHexStr(private
}

func TestSignMessage(t *testing.T) {
Expand Down
5 changes: 4 additions & 1 deletion internal/community/account/hdwallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ type HierarchicalPath string

// HierarchicalPath_ETH is the default path for Mainet / Eth / TestNet
const HierarchicalPath_ETH HierarchicalPath = "m/44'/60'/0'/0/0"
const HierarchicalPath_ETH_FMT string = "m/44'/60'/0'/0/%d"

type HdWallet struct {
Mnemonic string `json:"mnemonic"`
Address string `json:"address"`
PrivateKey string `json:"privateKey"`
Used bool `json:"used"`
Primary bool `json:"primary"`
}

func newWallet() (*hdwallet.Wallet, *string, error) {
Expand All @@ -39,7 +42,7 @@ func NewHdWallet(hierarchicalPath ...HierarchicalPath) ([]HdWallet, error) {
} else {
hdwallets := make([]HdWallet, 0)
for p := range hierarchicalPath {
path := hdwallet.MustParseDerivationPath(string(p))
path := hdwallet.MustParseDerivationPath(string(hierarchicalPath[p]))
account, err := wallet.Derive(path, false)
if err != nil {
return nil, err
Expand Down
10 changes: 3 additions & 7 deletions internal/community/account/hdwallet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,14 @@ func TestNewHdWallet(t *testing.T) {
"m/44'/60'/0'/0/1",
"m/44'/60'/0'/0/2",
}
for _, hierarchicalPath := range paths {
wallet, err := NewHdWallet(hierarchicalPath)
wallets, err := NewHdWallet(paths...)
for _, wallet := range wallets {

if err != nil {
t.Errorf("unexpected error: %v", err)
return
}

if wallet == nil {
t.Error("expected wallet to be created, but got nil")
return
}

if wallet.Mnemonic == "" {
t.Error("expected mnemonic to be set, but got empty string")
}
Expand Down
14 changes: 8 additions & 6 deletions internal/community/account/impl/alchemy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ func TestAlchemyProvider_CreateAccount(t *testing.T) {
t.Errorf("Failed to create account: %v", err)
}

account, err := provider.CreateAccount(w)
if err != nil {
t.Errorf("Failed to create account: %v", err)
}
for i := range w {
account, err := provider.CreateAccount(&w[i])
if err != nil {
t.Errorf("Failed to create account: %v", err)
}

if account == "" {
t.Error("Expected account to be created, but got empty string")
if account == "" {
t.Error("Expected account to be created, but got empty string")
}
}
}
21 changes: 11 additions & 10 deletions internal/community/chain/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,21 @@ func TestCreateAccount(t *testing.T) {

os.Chdir("../../../")

w, err := account.NewHdWallet(account.HierarchicalPath(account.HierarchicalPath_ETH))
wallets, err := account.NewHdWallet(account.HierarchicalPath(account.HierarchicalPath_ETH))
if err != nil {
t.Errorf("Failed to create account: %v", err)
}

address, initCode, err := CreateSmartAccount(w, seedworks.OptimismSepolia)
if err != nil {
t.Errorf("Failed to create account: %v", err)
for _, w := range wallets {
address, initCode, err := CreateSmartAccount(&w, seedworks.OptimismSepolia)
if err != nil {
t.Errorf("Failed to create account: %v", err)
}
if address == "" {
t.Error("Expected account to be created, but got empty string")
}
t.Logf("address: %v", address)
t.Logf("initCode: %v", initCode)
}
if address == "" {
t.Error("Expected account to be created, but got empty string")
}
t.Logf("address: %v", address)
t.Logf("initCode: %v", initCode)

// test code
}
2 changes: 1 addition & 1 deletion plugins/passkey_relay_party/account_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (relay *RelayParty) getAccountInfo(ctx *gin.Context) {
response.GetResponse().WithDataSuccess(ctx, seedworks.AccountInfo{
InitCode: *initCode,
AA: *aaAddr,
EOA: user.GetEOA(),
EOA: user.GetPrimaryEOA(),
Email: email,
})
return
Expand Down
2 changes: 1 addition & 1 deletion plugins/passkey_relay_party/create_aa.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func createAA(relay *RelayParty, user *seedworks.User, ctx *gin.Context) {
response.InternalServerError(ctx, err.Error())
return
} else {
if err := relay.db.SaveAccounts(user, req.Network, req.Alias); err != nil {
if err := relay.db.SaveAccounts(user); err != nil {
if errors.Is(err, seedworks.ErrUserAlreadyExists{}) {
response.BadRequest(ctx, err.Error())
} else {
Expand Down
6 changes: 6 additions & 0 deletions plugins/passkey_relay_party/seedworks/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ type ErrInvalidCaptcha struct{}
type ErrUserAlreadyExists struct{}
type ErrWalletNotFound struct{}
type ErrChainNotFound struct{}
type ErrNoAvailableWallet struct{}

var _ error = ErrUserNotFound{}
var _ error = ErrEmailEmpty{}
var _ error = ErrInvalidCaptcha{}
var _ error = ErrUserAlreadyExists{}
var _ error = ErrWalletNotFound{}
var _ error = ErrChainNotFound{}
var _ error = ErrNoAvailableWallet{}

func (e ErrUserNotFound) Error() string {
return string("user not found")
Expand All @@ -37,3 +39,7 @@ func (e ErrWalletNotFound) Error() string {
func (e ErrChainNotFound) Error() string {
return string("chain not found")
}

func (e ErrNoAvailableWallet) Error() string {
return string("no available wallet")
}
6 changes: 1 addition & 5 deletions plugins/passkey_relay_party/seedworks/reg.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package seedworks

import consts "another_node/internal/seedworks"

type RegistrationByEmailPrepare struct {
Email string `json:"email"`
}
Expand All @@ -14,7 +12,5 @@ type RegistrationByEmail struct {

type FinishRegistrationByEmail struct {
RegistrationByEmailPrepare
Origin string `json:"origin"`
Network consts.Chain `json:"network"`
Alias string `json:"alias"`
Origin string `json:"origin"`
}
123 changes: 78 additions & 45 deletions plugins/passkey_relay_party/seedworks/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,45 @@ import (
"github.com/go-webauthn/webauthn/webauthn"
)

type userChain struct {
InitCode string
AA_Addr string
Alias string
Name consts.Chain
type UserChain struct {
InitCode string
AA_Addr string
Alias string
Name consts.Chain
FromHdWallet *account.HdWallet
}

type User struct {
id []byte
credentials []webauthn.Credential
email string
wallet *account.HdWallet
chainAddresses map[string]userChain
wallets []account.HdWallet
chainAddresses map[string]UserChain
}

func (user *User) GetEOA() string {
return user.wallet.Address
func (user *User) GetPrimaryEOA() string {
if user.wallets == nil {
return ""
}
for i := range user.wallets {
if user.wallets[i].Primary {
return user.wallets[i].Address
}
}
return ""
}

func (user *User) TryCreateAA(network consts.Chain, alias string) (err error) {
var w *account.HdWallet
if user.wallet == nil || len(user.wallet.PrivateKey) == 0 {
if w, err = account.NewHdWallet(account.HierarchicalPath_ETH); err != nil {
return
} else {
user.wallet = w
for i := range user.wallets {
if !user.wallets[i].Used {
w = &user.wallets[i]
break
}
} else {
w = user.wallet
}

if w == nil {
return &ErrNoAvailableWallet{}
}

if _, aaAddr := user.GetChainAddresses(network, alias); aaAddr != nil {
Expand All @@ -52,38 +62,45 @@ func (user *User) TryCreateAA(network consts.Chain, alias string) (err error) {
if err != nil {
return err
}
user.SetAAWallet(&initCode, &aa_address, alias, network)
return nil

user.SetAAWallet(w, &initCode, &aa_address, alias, network)

return
}

func newUser(email string) *User {
return &User{
id: []byte(email),
email: email,
chainAddresses: make(map[string]userChain),
chainAddresses: make(map[string]UserChain),
}
}

func MappingUser(airaccount *model.AirAccount, getFromVault func() (string, error)) (*User, error) {
func MappingUser(airaccount *model.AirAccount, getFromVault func(vault *string) (string, error)) (*User, error) {
user := &User{
id: []byte(airaccount.Email),
email: airaccount.Email,
wallets: make([]account.HdWallet, 0),
credentials: make([]webauthn.Credential, 0),
chainAddresses: make(map[string]userChain),
chainAddresses: make(map[string]UserChain),
}

if hdwalletStr, err := getFromVault(); err != nil {
return nil, err
} else {
if hdwalletStr != "" {
var hdwallet account.HdWallet
if err := json.Unmarshal([]byte(hdwalletStr), &hdwallet); err != nil {
return nil, err
for i := range airaccount.HdWallet {
if hdwalletStr, err := getFromVault(&airaccount.HdWallet[i].WalletVault); err != nil {
return nil, err
} else {
if hdwalletStr != "" {
var hdwallet account.HdWallet
if err := json.Unmarshal([]byte(hdwalletStr), &hdwallet); err != nil {
return nil, err
} else {
hdwallet.Used = airaccount.HdWallet[i].Used
hdwallet.Primary = airaccount.HdWallet[i].Primary
user.wallets = append(user.wallets, hdwallet)
}
} else {
user.wallet = &hdwallet
return nil, &ErrWalletNotFound{}
}
} else {
return nil, &ErrWalletNotFound{}
}
}

Expand All @@ -96,7 +113,7 @@ func MappingUser(airaccount *model.AirAccount, getFromVault func() (string, erro

for i := range airaccount.AirAccountChains {
chain := airaccount.AirAccountChains[i]
user.chainAddresses[chain.ChainName+":"+chain.Alias] = userChain{
user.chainAddresses[chain.ChainName+":"+chain.Alias] = UserChain{
InitCode: chain.InitCode,
AA_Addr: chain.AA_Address,
Name: consts.Chain(chain.ChainName),
Expand All @@ -120,10 +137,14 @@ func (user *User) GetAccounts() (email, facebook, twitter string) {
return
}

func (user *User) GetChains() map[string]userChain {
func (user *User) GetChains() map[string]UserChain {
return user.chainAddresses
}

func (user *User) GetWallets() []account.HdWallet {
return user.wallets
}

func (user *User) GetChainAddresses(chain consts.Chain, alias string) (initCode, aaAddr *string) {
if len(chain) == 0 {
return nil, nil
Expand All @@ -139,15 +160,25 @@ func (user *User) GetChainAddresses(chain consts.Chain, alias string) (initCode,
return
}

func (user *User) GetPrivateKeyStr() string {
return user.wallet.PrivateKey
}
func (user *User) GetPrivateKeyEcdsa() (*ecdsa.PrivateKey, error) {
return crypto.HexToECDSA(user.GetPrivateKeyStr())
for i := range user.wallets {
if user.wallets[i].Primary {
return crypto.HexToECDSA(user.wallets[i].PrivateKey)
}
}
panic("no primary wallet")
}

func (user *User) WalletMarshal() ([]byte, error) {
return json.Marshal(user.wallet)
func (user *User) WalletMarshal() ([][]byte, error) {
rlt := make([][]byte, 0)
for i := range user.wallets {
if wallet, err := json.Marshal(user.wallets[i]); err != nil {
return nil, err
} else {
rlt = append(rlt, wallet)
}
}
return rlt, nil
}

func (user *User) WebAuthnID() []byte {
Expand Down Expand Up @@ -176,11 +207,13 @@ func (user *User) AddCredential(cred *webauthn.Credential) {
user.credentials = append(user.credentials, *cred)
}

func (user *User) SetAAWallet(init_code, aa_address *string, alias string, network consts.Chain) {
user.chainAddresses[string(network)+":"+alias] = userChain{
InitCode: *init_code,
AA_Addr: *aa_address,
Alias: alias,
Name: network,
func (user *User) SetAAWallet(wallet *account.HdWallet, init_code, aa_address *string, alias string, network consts.Chain) {
wallet.Used = true
user.chainAddresses[string(network)+":"+alias] = UserChain{
InitCode: *init_code,
AA_Addr: *aa_address,
Alias: alias,
Name: network,
FromHdWallet: wallet,
}
}
Loading

0 comments on commit 457a53d

Please sign in to comment.