Skip to content

Commit

Permalink
Merge pull request METADIUM#36 from lukepark327/feat-vrf
Browse files Browse the repository at this point in the history
Add Verifiable Random Function functionalities to go-WEMIX through pre-compiled contract
  • Loading branch information
sadoci authored May 9, 2023
2 parents 0df0f73 + 6bcac0a commit 3a2a9f7
Show file tree
Hide file tree
Showing 24 changed files with 1,238 additions and 2 deletions.
58 changes: 58 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: feat-vrf-ci

on:
workflow_dispatch:

jobs:
build_test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.ref }}

- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19

- name: Build Go-WEMIX
run: make gwemix.tar.gz
- name: Check Build
run: ls -al build/gwemix.tar.gz

lint_test:
strategy:
fail-fast: false
matrix:
version: [1.17, 1.18, 1.19]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.ref }}

- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.version }}

- name: Check Lint
run: make lint

unit_test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.ref }}

- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19

- name: Check golang Test Cases
run: |
unset ANDROID_HOME
make test-short
12 changes: 12 additions & 0 deletions accounts/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,18 @@ type Wallet interface {

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

// Get pk from sk based on ed25519
EdPubKey(account Account) ([]byte, error)

// Get pk from sk based on ed25519 with account password
EdPubKeyWithPassphrase(account Account, passphrase string) ([]byte, error)

// Get prove
Prove(account Account, message []byte) ([]byte, error)

// Get prove with account password
ProveWithPassphrase(account Account, passphrase string, message []byte) ([]byte, error)
}

// Backend is a "wallet provider" that may contain a batch of accounts they can
Expand Down
16 changes: 16 additions & 0 deletions accounts/external/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,22 @@ func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, pass
return nil, fmt.Errorf("password-operations not supported on external signers")
}

func (api *ExternalSigner) EdPubKey(account accounts.Account) ([]byte, error) {
return nil, fmt.Errorf("vrf-operations not supported on external signers yet") // TODO (lukepark327)
}

func (api *ExternalSigner) EdPubKeyWithPassphrase(account accounts.Account, passphrase string) ([]byte, error) {
return nil, fmt.Errorf("vrf-operations not supported on external signers yet") // TODO (lukepark327)
}

func (api *ExternalSigner) Prove(account accounts.Account, message []byte) ([]byte, error) {
return nil, fmt.Errorf("vrf-operations not supported on external signers yet") // TODO (lukepark327)
}

func (api *ExternalSigner) ProveWithPassphrase(account accounts.Account, passphrase string, message []byte) ([]byte, error) {
return nil, fmt.Errorf("vrf-operations not supported on external signers yet") // TODO (lukepark327)
}

func (api *ExternalSigner) listAccounts() ([]common.Address, error) {
var res []common.Address
if err := api.client.Call(&res, "account_list"); err != nil {
Expand Down
83 changes: 83 additions & 0 deletions accounts/keystore/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package keystore

import (
"crypto/ecdsa"
"crypto/ed25519"
crand "crypto/rand"
"errors"
"math/big"
Expand All @@ -36,13 +37,15 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/vrf"
"github.com/ethereum/go-ethereum/event"
)

var (
ErrLocked = accounts.NewAuthNeededError("password or unlock")
ErrNoMatch = errors.New("no key for given address or file")
ErrDecrypt = errors.New("could not decrypt key with given password")
ErrVrf = errors.New("errors on VRF functions") // TODO (lukepark327): more details

// ErrAccountAlreadyExists is returned if an account attempted to import is
// already present in the keystore.
Expand Down Expand Up @@ -516,3 +519,83 @@ func zeroKey(k *ecdsa.PrivateKey) {
b[i] = 0
}
}

func (ks *KeyStore) EdPubKey(a accounts.Account) ([]byte, error) {
// Look up the key to sign with and abort if it cannot be found
ks.mu.RLock()
defer ks.mu.RUnlock()

unlockedKey, found := ks.unlocked[a.Address]
if !found {
return nil, ErrLocked
}

return edPubKeyFromPrivateKey(unlockedKey.PrivateKey)
}

func (ks *KeyStore) EdPubKeyWithPassphrase(a accounts.Account, passphrase string) ([]byte, error) {
_, key, err := ks.getDecryptedKey(a, passphrase)
if err != nil {
return nil, err
}
defer zeroKey(key.PrivateKey)

return edPubKeyFromPrivateKey(key.PrivateKey)
}

func edPubKeyFromPrivateKey(k *ecdsa.PrivateKey) ([]byte, error) {
// get private key
seed := k.D.Bytes()
sk := ed25519.NewKeyFromSeed(seed)
pk := sk.Public()

xx, ok := pk.(ed25519.PublicKey)
if !ok {
return nil, ErrVrf
}
return xx, nil
}

func (ks *KeyStore) Prove(a accounts.Account, m []byte) ([]byte, error) {
// Look up the key to sign with and abort if it cannot be found
ks.mu.RLock()
defer ks.mu.RUnlock()

unlockedKey, found := ks.unlocked[a.Address]
if !found {
return nil, ErrLocked
}

pi, _, err := prove(unlockedKey.PrivateKey, m)
if err != nil {
return nil, err
}
return pi, err
}

func (ks *KeyStore) ProveWithPassphrase(a accounts.Account, passphrase string, m []byte) ([]byte, error) {
_, key, err := ks.getDecryptedKey(a, passphrase)
if err != nil {
return nil, err
}
defer zeroKey(key.PrivateKey)

pi, _, err := prove(key.PrivateKey, m)
if err != nil {
return nil, err
}
return pi, err
}

func prove(k *ecdsa.PrivateKey, msg []byte) ([]byte, []byte, error) {
// get private key
seed := k.D.Bytes()
sk := ed25519.NewKeyFromSeed(seed)
pk := sk.Public()

xx, ok := pk.(ed25519.PublicKey)
if !ok {
return nil, nil, ErrVrf
}
return vrf.Prove(xx, sk, msg[:])
}
36 changes: 36 additions & 0 deletions accounts/keystore/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,39 @@ func (w *keystoreWallet) SignTxWithPassphrase(account accounts.Account, passphra
// Account seems valid, request the keystore to sign
return w.keystore.SignTxWithPassphrase(account, passphrase, tx, chainID)
}

func (w *keystoreWallet) EdPubKey(account accounts.Account) ([]byte, error) {
// Make sure the requested account is contained within
if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount
}
// Account seems valid, request the keystore to get EdPubKey
return w.keystore.EdPubKey(account)
}

func (w *keystoreWallet) EdPubKeyWithPassphrase(account accounts.Account, passphrase string) ([]byte, error) {
// Make sure the requested account is contained within
if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount
}
// Account seems valid, request the keystore to get EdPubKey
return w.keystore.EdPubKeyWithPassphrase(account, passphrase)
}

func (w *keystoreWallet) Prove(account accounts.Account, message []byte) ([]byte, error) {
// Make sure the requested account is contained within
if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount
}
// Account seems valid, request the keystore to prove
return w.keystore.Prove(account, message)
}

func (w *keystoreWallet) ProveWithPassphrase(account accounts.Account, passphrase string, message []byte) ([]byte, error) {
// Make sure the requested account is contained within
if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount
}
// Account seems valid, request the keystore to prove
return w.keystore.ProveWithPassphrase(account, passphrase, message)
}
16 changes: 16 additions & 0 deletions accounts/scwallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,22 @@ func (w *Wallet) findAccountPath(account accounts.Account) (accounts.DerivationP
return accounts.ParseDerivationPath(parts[1])
}

func (w *Wallet) EdPubKey(account accounts.Account) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

func (w *Wallet) EdPubKeyWithPassphrase(account accounts.Account, passphrase string) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

func (w *Wallet) Prove(account accounts.Account, message []byte) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

func (w *Wallet) ProveWithPassphrase(account accounts.Account, passphrase string, message []byte) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

// Session represents a secured communication session with the wallet.
type Session struct {
Wallet *Wallet // A handle to the wallet that opened the session
Expand Down
16 changes: 16 additions & 0 deletions accounts/usbwallet/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,22 @@ func (w *ledgerDriver) SignTypedMessage(path accounts.DerivationPath, domainHash
return w.ledgerSignTypedMessage(path, domainHash, messageHash)
}

func (w *ledgerDriver) EdPubKey(a accounts.Account) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

func (w *ledgerDriver) EdPubKeyWithPassphrase(a accounts.Account, passphrase string) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

func (w *ledgerDriver) Prove(a accounts.Account, m []byte) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

func (w *ledgerDriver) ProveWithPassphrase(a accounts.Account, passphrase string, m []byte) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

// ledgerVersion retrieves the current version of the Ethereum wallet app running
// on the Ledger wallet.
//
Expand Down
16 changes: 16 additions & 0 deletions accounts/usbwallet/trezor.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,22 @@ func (w *trezorDriver) SignTypedMessage(path accounts.DerivationPath, domainHash
return nil, accounts.ErrNotSupported
}

func (w *trezorDriver) EdPubKey(a accounts.Account) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

func (w *trezorDriver) EdPubKeyWithPassphrase(a accounts.Account, passphrase string) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

func (w *trezorDriver) Prove(a accounts.Account, m []byte) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

func (w *trezorDriver) ProveWithPassphrase(a accounts.Account, passphrase string, m []byte) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

// trezorDerive sends a derivation request to the Trezor device and returns the
// Ethereum address located on that path.
func (w *trezorDriver) trezorDerive(derivationPath []uint32) (common.Address, error) {
Expand Down
24 changes: 24 additions & 0 deletions accounts/usbwallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ type driver interface {
SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error)

SignTypedMessage(path accounts.DerivationPath, messageHash []byte, domainHash []byte) ([]byte, error)

EdPubKey(account accounts.Account) ([]byte, error)

EdPubKeyWithPassphrase(account accounts.Account, passphrase string) ([]byte, error)

Prove(account accounts.Account, message []byte) ([]byte, error)

ProveWithPassphrase(account accounts.Account, passphrase string, message []byte) ([]byte, error)
}

// wallet represents the common functionality shared by all USB hardware
Expand Down Expand Up @@ -638,3 +646,19 @@ func (w *wallet) SignTextWithPassphrase(account accounts.Account, passphrase str
func (w *wallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
return w.SignTx(account, tx, chainID)
}

func (w *wallet) EdPubKey(account accounts.Account) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

func (w *wallet) EdPubKeyWithPassphrase(account accounts.Account, passphrase string) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

func (w *wallet) Prove(account accounts.Account, message []byte) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}

func (w *wallet) ProveWithPassphrase(account accounts.Account, passphrase string, message []byte) ([]byte, error) {
return nil, accounts.ErrNotSupported // TODO
}
2 changes: 2 additions & 0 deletions common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const (
HashLength = 32
// AddressLength is the expected length of the address
AddressLength = 20

EdPubKeyLength = 32
)

var (
Expand Down
Loading

0 comments on commit 3a2a9f7

Please sign in to comment.