Commit 78375608 authored by Nick Johnson's avatar Nick Johnson Committed by Guillaume Ballet

accounts, internal: Changes in response to review

parent f7027dd6
...@@ -139,12 +139,12 @@ func (path DerivationPath) MarshalJSON() ([]byte, error) { ...@@ -139,12 +139,12 @@ func (path DerivationPath) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("\"%s\"", path.String())), nil return []byte(fmt.Sprintf("\"%s\"", path.String())), nil
} }
func (dp *DerivationPath) UnmarshalJSON(b []byte) error { func (path *DerivationPath) UnmarshalJSON(b []byte) error {
var path string var dp string
var err error var err error
if err = json.Unmarshal(b, &path); err != nil { if err = json.Unmarshal(b, &dp); err != nil {
return err return err
} }
*dp, err = ParseDerivationPath(path) *path, err = ParseDerivationPath(dp)
return err return err
} }
...@@ -22,20 +22,20 @@ import ( ...@@ -22,20 +22,20 @@ import (
) )
const ( const (
CLA_ISO7816 = 0 claISO7816 = 0
INS_SELECT = 0xA4 insSelect = 0xA4
INS_GET_RESPONSE = 0xC0 insGetResponse = 0xC0
INS_PAIR = 0x12 insPair = 0x12
INS_UNPAIR = 0x13 insUnpair = 0x13
INS_OPEN_SECURE_CHANNEL = 0x10 insOpenSecureChannel = 0x10
INS_MUTUALLY_AUTHENTICATE = 0x11 insMutuallyAuthenticate = 0x11
SW1_GET_RESPONSE = 0x61 sw1GetResponse = 0x61
SW1_OK = 0x90 sw1Ok = 0x90
) )
// CommandAPDU represents an application data unit sent to a smartcard // CommandAPDU represents an application data unit sent to a smartcard.
type CommandAPDU struct { type CommandAPDU struct {
Cla, Ins, P1, P2 uint8 // Class, Instruction, Parameter 1, Parameter 2 Cla, Ins, P1, P2 uint8 // Class, Instruction, Parameter 1, Parameter 2
Data []byte // Command data Data []byte // Command data
...@@ -72,13 +72,13 @@ func (ca CommandAPDU) serialize() ([]byte, error) { ...@@ -72,13 +72,13 @@ func (ca CommandAPDU) serialize() ([]byte, error) {
return buf.Bytes(), nil return buf.Bytes(), nil
} }
// ResponseAPDU represents an application data unit received from a smart card // ResponseAPDU represents an application data unit received from a smart card.
type ResponseAPDU struct { type ResponseAPDU struct {
Data []byte // response data Data []byte // response data
Sw1, Sw2 uint8 // status words 1 and 2 Sw1, Sw2 uint8 // status words 1 and 2
} }
// deserialize deserializes a response APDU // deserialize deserializes a response APDU.
func (ra *ResponseAPDU) deserialize(data []byte) error { func (ra *ResponseAPDU) deserialize(data []byte) error {
ra.Data = make([]byte, len(data)-2) ra.Data = make([]byte, len(data)-2)
......
...@@ -14,6 +14,22 @@ ...@@ -14,6 +14,22 @@
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// This package implements support for smartcard-based hardware wallets such as
// the one written by Status: https://github.com/status-im/hardware-wallet
//
// This implementation of smartcard wallets have a different interaction process
// to other types of hardware wallet. The process works like this:
//
// 1. (First use with a given client) Establish a pairing between hardware
// wallet and client. This requires a secret value called a 'PUK'. You can
// pair with an unpaired wallet with `personal.openWallet(URI, PUK)`.
// 2. (First use only) Initialize the wallet, which generates a keypair, stores
// it on the wallet, and returns it so the user can back it up. You can
// initialize a wallet with `personal.initializeWallet(URI)`.
// 3. Connect to the wallet using the pairing information established in step 1.
// You can connect to a paired wallet with `personal.openWallet(URI, PIN)`.
// 4. Interact with the wallet as normal.
package scwallet package scwallet
import ( import (
...@@ -32,6 +48,7 @@ import ( ...@@ -32,6 +48,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
// Scheme is the URI prefix for smartcard wallets.
const Scheme = "pcsc" const Scheme = "pcsc"
// refreshCycle is the maximum time between wallet refreshes (if USB hotplug // refreshCycle is the maximum time between wallet refreshes (if USB hotplug
...@@ -41,9 +58,9 @@ const refreshCycle = 5 * time.Second ...@@ -41,9 +58,9 @@ const refreshCycle = 5 * time.Second
// refreshThrottling is the minimum time between wallet refreshes to avoid thrashing. // refreshThrottling is the minimum time between wallet refreshes to avoid thrashing.
const refreshThrottling = 500 * time.Millisecond const refreshThrottling = 500 * time.Millisecond
// SmartcardPairing contains information about a smart card we have paired with // smartcardPairing contains information about a smart card we have paired with
// or might pair withub. // or might pair with the hub.
type SmartcardPairing struct { type smartcardPairing struct {
PublicKey []byte `json:"publicKey"` PublicKey []byte `json:"publicKey"`
PairingIndex uint8 `json:"pairingIndex"` PairingIndex uint8 `json:"pairingIndex"`
PairingKey []byte `json:"pairingKey"` PairingKey []byte `json:"pairingKey"`
...@@ -56,7 +73,7 @@ type Hub struct { ...@@ -56,7 +73,7 @@ type Hub struct {
context *scard.Context context *scard.Context
datadir string datadir string
pairings map[string]SmartcardPairing pairings map[string]smartcardPairing
refreshed time.Time // Time instance when the list of wallets was last refreshed refreshed time.Time // Time instance when the list of wallets was last refreshed
wallets map[string]*Wallet // Mapping from reader names to wallet instances wallets map[string]*Wallet // Mapping from reader names to wallet instances
updateFeed event.Feed // Event feed to notify wallet additions/removals updateFeed event.Feed // Event feed to notify wallet additions/removals
...@@ -71,7 +88,7 @@ type Hub struct { ...@@ -71,7 +88,7 @@ type Hub struct {
var HubType = reflect.TypeOf(&Hub{}) var HubType = reflect.TypeOf(&Hub{})
func (hub *Hub) readPairings() error { func (hub *Hub) readPairings() error {
hub.pairings = make(map[string]SmartcardPairing) hub.pairings = make(map[string]smartcardPairing)
pairingFile, err := os.Open(hub.datadir + "/smartcards.json") pairingFile, err := os.Open(hub.datadir + "/smartcards.json")
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
...@@ -84,7 +101,7 @@ func (hub *Hub) readPairings() error { ...@@ -84,7 +101,7 @@ func (hub *Hub) readPairings() error {
if err != nil { if err != nil {
return err return err
} }
var pairings []SmartcardPairing var pairings []smartcardPairing
if err := json.Unmarshal(pairingData, &pairings); err != nil { if err := json.Unmarshal(pairingData, &pairings); err != nil {
return err return err
} }
...@@ -101,7 +118,7 @@ func (hub *Hub) writePairings() error { ...@@ -101,7 +118,7 @@ func (hub *Hub) writePairings() error {
return err return err
} }
pairings := make([]SmartcardPairing, 0, len(hub.pairings)) pairings := make([]smartcardPairing, 0, len(hub.pairings))
for _, pairing := range hub.pairings { for _, pairing := range hub.pairings {
pairings = append(pairings, pairing) pairings = append(pairings, pairing)
} }
...@@ -118,7 +135,7 @@ func (hub *Hub) writePairings() error { ...@@ -118,7 +135,7 @@ func (hub *Hub) writePairings() error {
return pairingFile.Close() return pairingFile.Close()
} }
func (hub *Hub) getPairing(wallet *Wallet) *SmartcardPairing { func (hub *Hub) getPairing(wallet *Wallet) *smartcardPairing {
pairing, ok := hub.pairings[string(wallet.PublicKey)] pairing, ok := hub.pairings[string(wallet.PublicKey)]
if ok { if ok {
return &pairing return &pairing
...@@ -126,7 +143,7 @@ func (hub *Hub) getPairing(wallet *Wallet) *SmartcardPairing { ...@@ -126,7 +143,7 @@ func (hub *Hub) getPairing(wallet *Wallet) *SmartcardPairing {
return nil return nil
} }
func (hub *Hub) setPairing(wallet *Wallet, pairing *SmartcardPairing) error { func (hub *Hub) setPairing(wallet *Wallet, pairing *smartcardPairing) error {
if pairing == nil { if pairing == nil {
delete(hub.pairings, string(wallet.PublicKey)) delete(hub.pairings, string(wallet.PublicKey))
} else { } else {
...@@ -158,7 +175,7 @@ func NewHub(scheme string, datadir string) (*Hub, error) { ...@@ -158,7 +175,7 @@ func NewHub(scheme string, datadir string) (*Hub, error) {
return hub, nil return hub, nil
} }
// Wallets implements accounts.Backend, returning all the currently tracked USB // Wallets implements accounts.Backend, returning all the currently tracked
// devices that appear to be hardware wallets. // devices that appear to be hardware wallets.
func (hub *Hub) Wallets() []accounts.Wallet { func (hub *Hub) Wallets() []accounts.Wallet {
// Make sure the list of wallets is up to date // Make sure the list of wallets is up to date
...@@ -176,7 +193,7 @@ func (hub *Hub) Wallets() []accounts.Wallet { ...@@ -176,7 +193,7 @@ func (hub *Hub) Wallets() []accounts.Wallet {
return cpy return cpy
} }
// refreshWallets scans the USB devices attached to the machine and updates the // refreshWallets scans the devices attached to the machine and updates the
// list of wallets based on the found devices. // list of wallets based on the found devices.
func (hub *Hub) refreshWallets() { func (hub *Hub) refreshWallets() {
elapsed := time.Since(hub.refreshed) elapsed := time.Since(hub.refreshed)
......
...@@ -32,15 +32,15 @@ import ( ...@@ -32,15 +32,15 @@ import (
) )
const ( const (
MAX_PAYLOAD_SIZE = 223 maxPayloadSize = 223
PAIR_P1_FIRST_STEP = 0 pairP1FirstStep = 0
PAIR_P1_LAST_STEP = 1 pairP1LastStep = 1
SC_SECRET_LENGTH = 32 scSecretLength = 32
SC_BLOCK_SIZE = 16 scBlockSize = 16
) )
// SecureChannelSession enables secure communication with a hardware wallet // SecureChannelSession enables secure communication with a hardware wallet.
type SecureChannelSession struct { type SecureChannelSession struct {
card *scard.Card // A handle to the smartcard for communication card *scard.Card // A handle to the smartcard for communication
secret []byte // A shared secret generated from our ECDSA keys secret []byte // A shared secret generated from our ECDSA keys
...@@ -52,7 +52,7 @@ type SecureChannelSession struct { ...@@ -52,7 +52,7 @@ type SecureChannelSession struct {
PairingIndex uint8 // The pairing index PairingIndex uint8 // The pairing index
} }
// NewSecureChannelSession creates a new secure channel for the given card and public key // NewSecureChannelSession creates a new secure channel for the given card and public key.
func NewSecureChannelSession(card *scard.Card, keyData []byte) (*SecureChannelSession, error) { func NewSecureChannelSession(card *scard.Card, keyData []byte) (*SecureChannelSession, error) {
// Generate an ECDSA keypair for ourselves // Generate an ECDSA keypair for ourselves
gen := ecdh.NewEllipticECDH(crypto.S256()) gen := ecdh.NewEllipticECDH(crypto.S256())
...@@ -78,7 +78,7 @@ func NewSecureChannelSession(card *scard.Card, keyData []byte) (*SecureChannelSe ...@@ -78,7 +78,7 @@ func NewSecureChannelSession(card *scard.Card, keyData []byte) (*SecureChannelSe
}, nil }, nil
} }
// Pair establishes a new pairing with the smartcard // Pair establishes a new pairing with the smartcard.
func (s *SecureChannelSession) Pair(sharedSecret []byte) error { func (s *SecureChannelSession) Pair(sharedSecret []byte) error {
secretHash := sha256.Sum256(sharedSecret) secretHash := sha256.Sum256(sharedSecret)
...@@ -87,7 +87,7 @@ func (s *SecureChannelSession) Pair(sharedSecret []byte) error { ...@@ -87,7 +87,7 @@ func (s *SecureChannelSession) Pair(sharedSecret []byte) error {
return err return err
} }
response, err := s.pair(PAIR_P1_FIRST_STEP, challenge) response, err := s.pair(pairP1FirstStep, challenge)
if err != nil { if err != nil {
return err return err
} }
...@@ -107,7 +107,7 @@ func (s *SecureChannelSession) Pair(sharedSecret []byte) error { ...@@ -107,7 +107,7 @@ func (s *SecureChannelSession) Pair(sharedSecret []byte) error {
md.Reset() md.Reset()
md.Write(secretHash[:]) md.Write(secretHash[:])
md.Write(cardChallenge) md.Write(cardChallenge)
response, err = s.pair(PAIR_P1_LAST_STEP, md.Sum(nil)) response, err = s.pair(pairP1LastStep, md.Sum(nil))
if err != nil { if err != nil {
return err return err
} }
...@@ -121,13 +121,13 @@ func (s *SecureChannelSession) Pair(sharedSecret []byte) error { ...@@ -121,13 +121,13 @@ func (s *SecureChannelSession) Pair(sharedSecret []byte) error {
return nil return nil
} }
// Unpair disestablishes an existing pairing // Unpair disestablishes an existing pairing.
func (s *SecureChannelSession) Unpair() error { func (s *SecureChannelSession) Unpair() error {
if s.PairingKey == nil { if s.PairingKey == nil {
return fmt.Errorf("Cannot unpair: not paired") return fmt.Errorf("Cannot unpair: not paired")
} }
_, err := s.TransmitEncrypted(CLA_SCWALLET, INS_UNPAIR, s.PairingIndex, 0, []byte{}) _, err := s.TransmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{})
if err != nil { if err != nil {
return err return err
} }
...@@ -137,7 +137,7 @@ func (s *SecureChannelSession) Unpair() error { ...@@ -137,7 +137,7 @@ func (s *SecureChannelSession) Unpair() error {
return nil return nil
} }
// Open initializes the secure channel // Open initializes the secure channel.
func (s *SecureChannelSession) Open() error { func (s *SecureChannelSession) Open() error {
if s.iv != nil { if s.iv != nil {
return fmt.Errorf("Session already opened") return fmt.Errorf("Session already opened")
...@@ -153,13 +153,13 @@ func (s *SecureChannelSession) Open() error { ...@@ -153,13 +153,13 @@ func (s *SecureChannelSession) Open() error {
md := sha512.New() md := sha512.New()
md.Write(s.secret) md.Write(s.secret)
md.Write(s.PairingKey) md.Write(s.PairingKey)
md.Write(response.Data[:SC_SECRET_LENGTH]) md.Write(response.Data[:scSecretLength])
keyData := md.Sum(nil) keyData := md.Sum(nil)
s.sessionEncKey = keyData[:SC_SECRET_LENGTH] s.sessionEncKey = keyData[:scSecretLength]
s.sessionMacKey = keyData[SC_SECRET_LENGTH : SC_SECRET_LENGTH*2] s.sessionMacKey = keyData[scSecretLength : scSecretLength*2]
// The IV is the last bytes returned from the Open APDU. // The IV is the last bytes returned from the Open APDU.
s.iv = response.Data[SC_SECRET_LENGTH:] s.iv = response.Data[scSecretLength:]
if err := s.mutuallyAuthenticate(); err != nil { if err := s.mutuallyAuthenticate(); err != nil {
return err return err
...@@ -171,12 +171,12 @@ func (s *SecureChannelSession) Open() error { ...@@ -171,12 +171,12 @@ func (s *SecureChannelSession) Open() error {
// mutuallyAuthenticate is an internal method to authenticate both ends of the // mutuallyAuthenticate is an internal method to authenticate both ends of the
// connection. // connection.
func (s *SecureChannelSession) mutuallyAuthenticate() error { func (s *SecureChannelSession) mutuallyAuthenticate() error {
data := make([]byte, SC_SECRET_LENGTH) data := make([]byte, scSecretLength)
if _, err := rand.Read(data); err != nil { if _, err := rand.Read(data); err != nil {
return err return err
} }
response, err := s.TransmitEncrypted(CLA_SCWALLET, INS_MUTUALLY_AUTHENTICATE, 0, 0, data) response, err := s.TransmitEncrypted(claSCWallet, insMutuallyAuthenticate, 0, 0, data)
if err != nil { if err != nil {
return err return err
} }
...@@ -184,18 +184,18 @@ func (s *SecureChannelSession) mutuallyAuthenticate() error { ...@@ -184,18 +184,18 @@ func (s *SecureChannelSession) mutuallyAuthenticate() error {
return fmt.Errorf("Got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2) return fmt.Errorf("Got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2)
} }
if len(response.Data) != SC_SECRET_LENGTH { if len(response.Data) != scSecretLength {
return fmt.Errorf("Response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), SC_SECRET_LENGTH) return fmt.Errorf("Response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), scSecretLength)
} }
return nil return nil
} }
// open is an internal method that sends an open APDU // open is an internal method that sends an open APDU.
func (s *SecureChannelSession) open() (*ResponseAPDU, error) { func (s *SecureChannelSession) open() (*ResponseAPDU, error) {
return transmit(s.card, &CommandAPDU{ return transmit(s.card, &CommandAPDU{
Cla: CLA_SCWALLET, Cla: claSCWallet,
Ins: INS_OPEN_SECURE_CHANNEL, Ins: insOpenSecureChannel,
P1: s.PairingIndex, P1: s.PairingIndex,
P2: 0, P2: 0,
Data: s.publicKey, Data: s.publicKey,
...@@ -203,11 +203,11 @@ func (s *SecureChannelSession) open() (*ResponseAPDU, error) { ...@@ -203,11 +203,11 @@ func (s *SecureChannelSession) open() (*ResponseAPDU, error) {
}) })
} }
// pair is an internal method that sends a pair APDU // pair is an internal method that sends a pair APDU.
func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*ResponseAPDU, error) { func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*ResponseAPDU, error) {
return transmit(s.card, &CommandAPDU{ return transmit(s.card, &CommandAPDU{
Cla: CLA_SCWALLET, Cla: claSCWallet,
Ins: INS_PAIR, Ins: insPair,
P1: p1, P1: p1,
P2: 0, P2: 0,
Data: data, Data: data,
...@@ -215,7 +215,7 @@ func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*ResponseAPDU, error ...@@ -215,7 +215,7 @@ func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*ResponseAPDU, error
}) })
} }
// TransmitEncrypted sends an encrypted message, and decrypts and returns the response // TransmitEncrypted sends an encrypted message, and decrypts and returns the response.
func (s *SecureChannelSession) TransmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*ResponseAPDU, error) { func (s *SecureChannelSession) TransmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*ResponseAPDU, error) {
if s.iv == nil { if s.iv == nil {
return nil, fmt.Errorf("Channel not open") return nil, fmt.Errorf("Channel not open")
...@@ -225,7 +225,7 @@ func (s *SecureChannelSession) TransmitEncrypted(cla, ins, p1, p2 byte, data []b ...@@ -225,7 +225,7 @@ func (s *SecureChannelSession) TransmitEncrypted(cla, ins, p1, p2 byte, data []b
if err != nil { if err != nil {
return nil, err return nil, err
} }
meta := []byte{cla, ins, p1, p2, byte(len(data) + SC_BLOCK_SIZE), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} meta := []byte{cla, ins, p1, p2, byte(len(data) + scBlockSize), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
if err = s.updateIV(meta, data); err != nil { if err = s.updateIV(meta, data); err != nil {
return nil, err return nil, err
} }
...@@ -263,17 +263,17 @@ func (s *SecureChannelSession) TransmitEncrypted(cla, ins, p1, p2 byte, data []b ...@@ -263,17 +263,17 @@ func (s *SecureChannelSession) TransmitEncrypted(cla, ins, p1, p2 byte, data []b
rapdu := &ResponseAPDU{} rapdu := &ResponseAPDU{}
rapdu.deserialize(plainData) rapdu.deserialize(plainData)
if rapdu.Sw1 != SW1_OK { if rapdu.Sw1 != sw1Ok {
return nil, fmt.Errorf("Unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2) return nil, fmt.Errorf("Unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
} }
return rapdu, nil return rapdu, nil
} }
// encryptAPDU is an internal method that serializes and encrypts an APDU // encryptAPDU is an internal method that serializes and encrypts an APDU.
func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) { func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
if len(data) > MAX_PAYLOAD_SIZE { if len(data) > maxPayloadSize {
return nil, fmt.Errorf("Payload of %d bytes exceeds maximum of %d", len(data), MAX_PAYLOAD_SIZE) return nil, fmt.Errorf("Payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize)
} }
data = pad(data, 0x80) data = pad(data, 0x80)
...@@ -288,7 +288,7 @@ func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) { ...@@ -288,7 +288,7 @@ func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
return ret, nil return ret, nil
} }
// pad applies message padding to a 16 byte boundary // pad applies message padding to a 16 byte boundary.
func pad(data []byte, terminator byte) []byte { func pad(data []byte, terminator byte) []byte {
padded := make([]byte, (len(data)/16+1)*16) padded := make([]byte, (len(data)/16+1)*16)
copy(padded, data) copy(padded, data)
...@@ -296,7 +296,7 @@ func pad(data []byte, terminator byte) []byte { ...@@ -296,7 +296,7 @@ func pad(data []byte, terminator byte) []byte {
return padded return padded
} }
// decryptAPDU is an internal method that decrypts and deserializes an APDU // decryptAPDU is an internal method that decrypts and deserializes an APDU.
func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) { func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) {
a, err := aes.NewCipher(s.sessionEncKey) a, err := aes.NewCipher(s.sessionEncKey)
if err != nil { if err != nil {
...@@ -310,7 +310,7 @@ func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) { ...@@ -310,7 +310,7 @@ func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) {
return unpad(ret, 0x80) return unpad(ret, 0x80)
} }
// unpad strips padding from a message // unpad strips padding from a message.
func unpad(data []byte, terminator byte) ([]byte, error) { func unpad(data []byte, terminator byte) ([]byte, error) {
for i := 1; i <= 16; i++ { for i := 1; i <= 16; i++ {
switch data[len(data)-i] { switch data[len(data)-i] {
......
This diff is collapsed.
...@@ -473,6 +473,7 @@ func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args Sen ...@@ -473,6 +473,7 @@ func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args Sen
return s.SendTransaction(ctx, args, passwd) return s.SendTransaction(ctx, args, passwd)
} }
// InitializeWallet initializes a new wallet at the provided URL, by generating and returning a new private key.
func (s *PrivateAccountAPI) InitializeWallet(ctx context.Context, url string) (string, error) { func (s *PrivateAccountAPI) InitializeWallet(ctx context.Context, url string) (string, error) {
wallet, err := s.am.Wallet(url) wallet, err := s.am.Wallet(url)
if err != nil { if err != nil {
...@@ -499,6 +500,7 @@ func (s *PrivateAccountAPI) InitializeWallet(ctx context.Context, url string) (s ...@@ -499,6 +500,7 @@ func (s *PrivateAccountAPI) InitializeWallet(ctx context.Context, url string) (s
} }
} }
// Unpair deletes a pairing between wallet and geth.
func (s *PrivateAccountAPI) Unpair(ctx context.Context, url string, pin string) error { func (s *PrivateAccountAPI) Unpair(ctx context.Context, url string, pin string) error {
wallet, err := s.am.Wallet(url) wallet, err := s.am.Wallet(url)
if err != nil { if err != nil {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment