Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Account management implementation #239

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
9ade2ea
account management keystore
dusannosovic-ethernal Jun 7, 2024
020ea48
added manager
dusannosovic-ethernal Jun 7, 2024
3a597ff
some lint fix
dusannosovic-ethernal Jun 7, 2024
c819583
feed test
dusannosovic-ethernal Jun 7, 2024
1432828
linters and test fix
dusannosovic-ethernal Jun 7, 2024
05b1bb0
lint fix
dusannosovic-ethernal Jun 7, 2024
c092e4a
test fix
dusannosovic-ethernal Jun 7, 2024
0240786
test fix
dusannosovic-ethernal Jun 7, 2024
4c4ec19
test fix
dusannosovic-ethernal Jun 7, 2024
99077a2
to abs path
dusannosovic-ethernal Jun 7, 2024
aa371b6
custom json unmarshall
dusannosovic-ethernal Jun 7, 2024
d7dc3d5
commands added
dusannosovic-ethernal Jun 7, 2024
ad3f26f
unnecessary files deletion
dusannosovic-ethernal Jun 7, 2024
14a49f2
interface unnecessary functions
dusannosovic-ethernal Jun 7, 2024
cad8261
manager integration
dusannosovic-ethernal Jun 7, 2024
9d67bf7
skip TestUpdatedKeyFileContents
dusannosovic-ethernal Jun 7, 2024
7c33c2c
introduction of external signer
dusannosovic-ethernal Jun 7, 2024
a8edf4d
lint fix
dusannosovic-ethernal Jun 7, 2024
a71a803
lint
dusannosovic-ethernal Jun 7, 2024
feee013
lint again
dusannosovic-ethernal Jun 7, 2024
01db57b
personal endpoint
dusannosovic-ethernal Jun 7, 2024
b9e70f7
To one key file
dusannosovic-ethernal Jun 7, 2024
67b5fdd
keys to one file and account_cache_test fix
dusannosovic-ethernal Jun 7, 2024
c1ef83a
keystore-test fix
dusannosovic-ethernal Jun 7, 2024
f582ba0
comment delete
dusannosovic-ethernal Jun 7, 2024
72bef8f
lint fix
dusannosovic-ethernal Jun 7, 2024
c889ca0
fix personal
dusannosovic-ethernal Jun 7, 2024
69066f9
linter and personal
dusannosovic-ethernal Jun 8, 2024
42dfb19
lint
dusannosovic-ethernal Jun 9, 2024
707d946
test fix and remove panic
dusannosovic-ethernal Jun 10, 2024
e0d24d0
test fix
dusannosovic-ethernal Jun 10, 2024
6498fd5
unnecessary files deletion
dusannosovic-ethernal Jun 10, 2024
5103d31
jsonrpc functions not exposed
dusannosovic-ethernal Jun 10, 2024
5d9a6f6
command fix
dusannosovic-ethernal Jun 10, 2024
cd88c29
mixed case deleted
dusannosovic-ethernal Jun 10, 2024
c5c7c37
fix some comments
dusannosovic-ethernal Jun 10, 2024
cf452fb
url deleted
dusannosovic-ethernal Jun 11, 2024
5c41a5b
file cache delted
dusannosovic-ethernal Jun 11, 2024
f1d8361
comment fix
dusannosovic-ethernal Jun 12, 2024
9b4172e
forks in time fix
dusannosovic-ethernal Jun 12, 2024
cf0ec7a
in-house pub sub
dusannosovic-ethernal Jun 13, 2024
914b9df
lint and better fork managemt, also interface for manager
dusannosovic-ethernal Jun 14, 2024
c061f26
lint
dusannosovic-ethernal Jun 14, 2024
45cf7dd
some minor changes
dusannosovic-ethernal Jun 14, 2024
7eb6fac
Rebase
dusannosovic-ethernal Jun 14, 2024
0dd3c4c
fix for tests
dusannosovic-ethernal Jun 17, 2024
4dcc34e
small code changes
goran-ethernal Jun 17, 2024
f406099
code optimizations
goran-ethernal Jun 17, 2024
8a8070a
lint fix
goran-ethernal Jun 17, 2024
8b30a8e
commands update
goran-ethernal Jun 17, 2024
1fc79f7
Merge commit '03d19703d4c8a226629204fb45b05f5019a0ddb3' into BLADE-16…
goran-ethernal Jun 17, 2024
17a2516
comment fix
dusannosovic-ethernal Jun 18, 2024
37b0f80
Update accounts/event/event_handler_test.go
dusannosovic-ethernal Jun 18, 2024
be4c884
comment fix and rename keyStore interface functions
dusannosovic-ethernal Jun 19, 2024
e91fa8c
refresh wallets optimization
dusannosovic-ethernal Jun 19, 2024
49e8aa1
comments and change names
dusannosovic-ethernal Jun 19, 2024
564e9bf
small changes
goran-ethernal Jun 19, 2024
326c8e6
lint fix
goran-ethernal Jun 19, 2024
98c4d08
lint fix 2
goran-ethernal Jun 19, 2024
153a4c9
lint fix 2
goran-ethernal Jun 19, 2024
7278f53
tests fix
goran-ethernal Jun 19, 2024
ee43d9d
lint again
goran-ethernal Jun 19, 2024
e66d926
switch to if in personal
dusannosovic-ethernal Jun 20, 2024
9555fad
fuzz test fix
dusannosovic-ethernal Jun 20, 2024
e234646
manager test
dusannosovic-ethernal Jun 20, 2024
4a39c7d
manager unlock change
dusannosovic-ethernal Jun 20, 2024
db33c81
manager test optimziation
dusannosovic-ethernal Jun 20, 2024
6f86da5
readme for account management
dusannosovic-ethernal Jun 20, 2024
39769ef
to mermaid
dusannosovic-ethernal Jun 20, 2024
bc0daea
readme fix
dusannosovic-ethernal Jun 20, 2024
ae95579
Merge branch 'develop' into BLADE-161-account-management-implementation
dusannosovic-ethernal Jun 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions accounts/README.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
Account management
===

## Problem
Account management is a feature that implements the logic for storing private keys for non-validator users. These private keys are used for signing various types of data. Currently, we have implemented file storage on the local disk. The keys are stored in a text file in JSON format. To ensure security, the private keys are encrypted, so opening the file does not compromise sensitive data.

## Account storage
Account storage is component of account management which is used to manage keys file. When system starts it can create a new file if neccessary or load account from existing file.Account cache adds, delete or update accounts in file. Its primary purpose is to keep the key file updated with the latest account data.

## Key storage
Keys are stored in a file in JSON format. All keys are stored in the same file. The data structure used to marshal the keys is map[Address]EncryptedKey, making it very easy to retrieve a keys from the file while keeping the sensitive data secure.

## Keystore
The keystore is a layer between the AccountManager and the AccountStore, where cryptographic operations take place. The keystore maintains its own list of wallets (wrappers around accounts) and notifies the account manager about any changes (such as accounts being added or deleted). In this layer, transactions and data are signed, and private keys are kept unlocked for actions that require an unlocked private key (such as eth_sign).

## Account manager
The account manager is the top layer, and the system interacts with it for every account management task. It handles all types of key storage (currently, we only support local keystore storage) and merges all data from different storage types into one list.

## Interacting with account management
Accounts are required for some features. So our software support two diffent ways of interaction with account management. You can interact with commands or with different json rpc calls.

### Suported json-rpc calls
* **personal_listAccount** - return addresses of all accounts
* **personal_newAccount** - create new account and return address of account
* **personal_importRawKey** - insert hex raw key and storing them in storage
* **personal_unlockAccount** - unlockes account so that can sign transaction with account private key, it used for eth_sign and other calls that doesn't send password for decrypt private key
* **personal_lockAccount** - lock unlocked account
* **personal_updatePassphrase** - change passphrase of existing account

### Supported commands
* **create** - create new account and return address of account
* **insert** - insert command insert existing private key and store in keystore
* **update** - change passphrase of existing account

## ImportRawKey JSON-RPC Flow

``` mermaid
sequenceDiagram
Network->>Personal endpoint:personal_improtRawKey
Personal endpoint->>Manager:WalletsManager()\nKeyStoreType
Manager->>Personal endpoint:KeyStore\n(WalletManager)
Personal endpoint->>KeyStore:ImportECDSA()
KeyStore->>KeyStore:KeyEncrypt()
KeyStore->>AccountStore:add()
KeyStore->>Manager:WalletEvent
Manager->>Manager:Updater
AccountStore->>AccountStore:add
Note right of AccountStore:KeyFile
```

## Insert Command Flow
``` mermaid
sequenceDiagram
Command->>Personal endpoint:personal_improtRawKey
Personal endpoint->>Manager:WalletsManager()\nKeyStoreType
Manager->>Personal endpoint:KeyStore\n(WalletManager)
Personal endpoint->>KeyStore:ImportECDSA()
KeyStore->>KeyStore:KeyEncrypt()
KeyStore->>AccountStore:add()
KeyStore->>Manager:WalletEvent
Manager->>Manager:Updater
AccountStore->>AccountStore:add
Note right of AccountStore:KeyFile
```



## Private keys encryption
Encryption of private keys is crucial for protecting blockchain users from unauthorized actions. We use the AES-128-CTR cryptographic algorithm to encrypt private keys, ensuring a high level of security. AES-128-CTR is a symmetric encryption algorithm, meaning the same key is used for both encryption and decryption.

To enhance security, we introduce an authentication string used to encrypt and decrypt private keys. From this authentication string, we derive an appropriate encryption key, which serves as the private key for AES-128-CTR. To access private keys, users must know their authentication password, which allows for the decryption of the private keys.

Once a private key is decrypted, it is temporarily stored in memory and deleted after each use to maintain security. Every user, when creating a new account or importing an existing key, determines their authentication string and must remember it.





131 changes: 131 additions & 0 deletions accounts/accounts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package accounts

import (
"fmt"
"reflect"

"github.com/0xPolygon/polygon-edge/accounts/event"
"github.com/0xPolygon/polygon-edge/crypto"
"github.com/0xPolygon/polygon-edge/types"
"golang.org/x/crypto/sha3"
)

type Account struct {
Address types.Address `json:"address"`
}

// Wallet represents a software or hardware wallet that might contain one or more
// accounts (derived from the same seed).
type Wallet interface {
// Status returns a textual status to aid the user in the current state of the
// wallet
Status() (string, error)

// Open initializes access to a wallet instance.
Open(passphrase string) error

// Close releases any resources held by an open wallet instance.
Close() error

// Accounts retrieves the list of signing accounts the wallet is currently aware
// of
Accounts() []Account

// Contains returns whether an account is part of this particular wallet or not.
Contains(account Account) bool

// SignData requests the wallet to sign the hash of the given data
SignData(account Account, mimeType string, data []byte) ([]byte, error)

// SignDataWithPassphrase is identical to SignData, but also takes a password
SignDataWithPassphrase(account Account, passphrase, mimeType string, data []byte) ([]byte, error)

// SignText requests the wallet to sign the hash of a given piece of data, prefixed
// by the Ethereum prefix scheme
// This method should return the signature in 'canonical' format, with v 0 or 1.
SignText(account Account, text []byte) ([]byte, error)

// SignTextWithPassphrase is identical to Signtext, but also takes a password
SignTextWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error)

// SignTx requests the wallet to sign the given transaction.
SignTx(account Account, tx *types.Transaction) (*types.Transaction, error)

// SignTxWithPassphrase is identical to SignTx, but also takes a password
SignTxWithPassphrase(account Account, passphrase string,
tx *types.Transaction) (*types.Transaction, error)
}

func TextHash(data []byte) []byte {
hash, _ := textAndHash(data)

return hash
}

func textAndHash(data []byte) ([]byte, string) {
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
hasher := sha3.NewLegacyKeccak256()
hasher.Write([]byte(msg))

return hasher.Sum(nil), msg
}

type WalletEventType int

const (
// WalletArrived is fired when a new wallet is detected either via USB or via
// a filesystem event in the keystore.
WalletArrived WalletEventType = iota

// WalletOpened is fired when a wallet is successfully opened with the purpose
// of starting any background processes such as automatic key derivation.
WalletOpened

// WalletDropped
WalletDropped
)

// WalletEvent is an event fired by an account backend when a wallet arrival or
// departure is detected.
type WalletEvent struct {
Wallet Wallet // Wallet instance arrived or departed
Kind WalletEventType // Event type that happened in the system
}

func (WalletEvent) Type() event.EventType {
return event.WalletEventType
}

type WalletManager interface {
// Wallets retrieves the list of wallets the backend is currently aware of
Wallets() []Wallet

// SetEventHandler set eventHandler on backend to push events
SetEventHandler(eventHandler *event.EventHandler)

// SetManager sets backend manager
SetManager(manager AccountManager)
}

type AccountManager interface {
// Checks for active forks at current block number and return signer
GetSigner() crypto.TxSigner

// Close stop updater in manager
Close() error

// Adds wallet manager to list of wallet managers
AddWalletManager(walletManager WalletManager)

// Return specific type of wallet manager
WalletManagers(kind reflect.Type) []WalletManager

// Return list of all wallets
Wallets() []Wallet

// Return all accounts
Accounts() []types.Address

// Search wallet with specific account
Find(account Account) (Wallet, error)
}
19 changes: 19 additions & 0 deletions accounts/accounts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package accounts

import (
"bytes"
"testing"

"github.com/0xPolygon/polygon-edge/types"
)

func TestTextHash(t *testing.T) {
t.Parallel()

hash := TextHash([]byte("Hello Joe"))
want := types.StringToBytes("0xa080337ae51c4e064c189e113edd0ba391df9206e2f49db658bb32cf2911730b")

if !bytes.Equal(hash, want) {
t.Fatalf("wrong hash: %x", hash)
}
}
61 changes: 61 additions & 0 deletions accounts/event/event_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package event

import "sync"

type EventHandler struct {
subscribers map[string][]chan Event
mu sync.RWMutex
}

func NewEventHandler() *EventHandler {
return &EventHandler{
subscribers: make(map[string][]chan Event),
}
}

func (ps *EventHandler) Subscribe(topic string, event chan Event) {
ps.mu.Lock()
defer ps.mu.Unlock()

ps.subscribers[topic] = append(ps.subscribers[topic], event)
}

func (ps *EventHandler) Publish(topic string, msg Event) {
ps.mu.RLock()
defer ps.mu.RUnlock()

if chans, ok := ps.subscribers[topic]; ok {
for _, ch := range chans {
ch <- msg
}
}
}

func (ps *EventHandler) Unsubscribe(topic string, sub <-chan Event) {
ps.mu.Lock()
defer ps.mu.Unlock()

if chans, ok := ps.subscribers[topic]; ok {
for i, ch := range chans {
if ch == sub {
ps.subscribers[topic] = append(chans[:i], chans[i+1:]...)

close(ch)

break
}
}
}
}

type EventType byte

const (
WalletEventType EventType = 0x01

NewWalletManagerType EventType = 0x02
)

type Event interface {
Type() EventType
}
Loading
Loading