Implement Wallet.hpp API
This commit is contained in:
parent
c9688ae051
commit
0911bb605c
5 changed files with 223 additions and 15 deletions
|
|
@ -1 +1 @@
|
||||||
Subproject commit 17b64bb38f73e9bbb9de2c18b6a9dd92bca5b7bd
|
Subproject commit 2593760687e89c8878f0e537ae9b9963fa46b210
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
namespace graphene { namespace utilities {
|
namespace graphene { namespace utilities {
|
||||||
|
|
||||||
|
std::string key_to_wif(const fc::sha256& private_secret );
|
||||||
std::string key_to_wif(const fc::ecc::private_key& key);
|
std::string key_to_wif(const fc::ecc::private_key& key);
|
||||||
fc::optional<fc::ecc::private_key> wif_to_key( const std::string& wif_key );
|
fc::optional<fc::ecc::private_key> wif_to_key( const std::string& wif_key );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,8 @@
|
||||||
|
|
||||||
namespace graphene { namespace utilities {
|
namespace graphene { namespace utilities {
|
||||||
|
|
||||||
std::string key_to_wif(const fc::ecc::private_key& key)
|
std::string key_to_wif(const fc::sha256& secret )
|
||||||
{
|
{
|
||||||
fc::sha256 secret = key.get_secret();
|
|
||||||
const size_t size_of_data_to_hash = sizeof(secret) + 1;
|
const size_t size_of_data_to_hash = sizeof(secret) + 1;
|
||||||
const size_t size_of_hash_bytes = 4;
|
const size_t size_of_hash_bytes = 4;
|
||||||
char data[size_of_data_to_hash + size_of_hash_bytes];
|
char data[size_of_data_to_hash + size_of_hash_bytes];
|
||||||
|
|
@ -34,6 +33,10 @@ std::string key_to_wif(const fc::ecc::private_key& key)
|
||||||
memcpy(data + size_of_data_to_hash, (char*)&digest, size_of_hash_bytes);
|
memcpy(data + size_of_data_to_hash, (char*)&digest, size_of_hash_bytes);
|
||||||
return fc::to_base58(data, sizeof(data));
|
return fc::to_base58(data, sizeof(data));
|
||||||
}
|
}
|
||||||
|
std::string key_to_wif(const fc::ecc::private_key& key)
|
||||||
|
{
|
||||||
|
return key_to_wif( key.get_secret() );
|
||||||
|
}
|
||||||
|
|
||||||
fc::optional<fc::ecc::private_key> wif_to_key( const std::string& wif_key )
|
fc::optional<fc::ecc::private_key> wif_to_key( const std::string& wif_key )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
#include "Wallet.hpp"
|
#include "Wallet.hpp"
|
||||||
|
#include <graphene/utilities/key_conversion.hpp>
|
||||||
#include <fc/crypto/aes.hpp>
|
#include <fc/crypto/aes.hpp>
|
||||||
#include <fc/io/json.hpp>
|
#include <fc/io/json.hpp>
|
||||||
|
|
||||||
|
QString toQString( const std::string& s ) { QString result; result.fromStdString( s ); return result; }
|
||||||
|
QString toQString( public_key_type k ) { return toQString( fc::variant(k).as_string() ); }
|
||||||
|
|
||||||
Wallet::Wallet()
|
Wallet::Wallet()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -19,7 +23,18 @@ bool Wallet::open( QString file_path )
|
||||||
ilog( "Unable to open wallet file '${f}', it does not exist", ("f",p) );
|
ilog( "Unable to open wallet file '${f}', it does not exist", ("f",p) );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_data = fc::json::from_file( p ).as<wallet_file>();
|
||||||
|
|
||||||
|
for( const auto& key : _data.encrypted_private_keys )
|
||||||
|
{
|
||||||
|
if( key.second.label != string() )
|
||||||
|
_label_to_key[toQString(key.second.label)] = toQString( key.first );
|
||||||
|
if( key.second.encrypted_private_key.size() )
|
||||||
|
_available_private_keys.insert( key.first );
|
||||||
|
}
|
||||||
_wallet_file_path = p;
|
_wallet_file_path = p;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,7 +62,10 @@ bool Wallet::save()
|
||||||
bool Wallet::saveAs( QString file_path )
|
bool Wallet::saveAs( QString file_path )
|
||||||
{
|
{
|
||||||
if( !isOpen() ) return false;
|
if( !isOpen() ) return false;
|
||||||
return false;
|
fc::path p(file_path.toStdString());
|
||||||
|
if( fc::exists(p) ) return false;
|
||||||
|
fc::json::save_to_file( _data, _wallet_file_path );
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Wallet::create( QString file_path, QString password, QString brain_key )
|
bool Wallet::create( QString file_path, QString password, QString brain_key )
|
||||||
|
|
@ -164,60 +182,208 @@ bool Wallet::changePassword( QString new_password )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Wallet::getPrivateKey( QString pubkey )
|
||||||
|
{
|
||||||
|
if( !isOpen() ) return QString();
|
||||||
|
if( isLocked() ) return QString();
|
||||||
|
|
||||||
|
auto pub = fc::variant( pubkey.toStdString() ).as<public_key_type>();
|
||||||
|
auto itr = _data.encrypted_private_keys.find( pub );
|
||||||
|
if( itr == _data.encrypted_private_keys.end() )
|
||||||
|
return QString();
|
||||||
|
if( itr->second.encrypted_private_key.size() == 0 )
|
||||||
|
return QString();
|
||||||
|
auto plain = fc::aes_decrypt( _decrypted_master_key, itr->second.encrypted_private_key );
|
||||||
|
return toQString( fc::raw::unpack<std::string>(plain) );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Wallet::getPublicKey( QString wif_private_key )const
|
||||||
|
{
|
||||||
|
auto priv = graphene::utilities::wif_to_key( wif_private_key.toStdString() );
|
||||||
|
if( !priv ) return QString();
|
||||||
|
|
||||||
|
auto pub = public_key_type(priv->get_public_key());
|
||||||
|
return toQString( fc::variant( pub ).as_string() );
|
||||||
|
}
|
||||||
|
|
||||||
QString Wallet::getActivePrivateKey( QString owner_pub_key, uint32_t seq )
|
QString Wallet::getActivePrivateKey( QString owner_pub_key, uint32_t seq )
|
||||||
{
|
{
|
||||||
if( !isOpen() ) return QString();
|
if( !isOpen() ) return QString();
|
||||||
return QString();
|
if( isLocked() ) return QString();
|
||||||
|
|
||||||
|
auto owner_wif_private_key = getPrivateKey( owner_pub_key );
|
||||||
|
if( owner_wif_private_key == QString() ) return QString();
|
||||||
|
|
||||||
|
auto seed = (owner_wif_private_key + " " + QString::number(seq)).toStdString();
|
||||||
|
auto secret = fc::sha256::hash( fc::sha512::hash( seed.c_str(), seed.size() ) );
|
||||||
|
|
||||||
|
auto wif = graphene::utilities::key_to_wif( secret );
|
||||||
|
auto priv_key = graphene::utilities::wif_to_key( wif );
|
||||||
|
if( !priv_key ) return QString();
|
||||||
|
|
||||||
|
public_key_type active_pub_key(priv_key->get_public_key());
|
||||||
|
_data.encrypted_private_keys[active_pub_key].encrypted_private_key = fc::aes_encrypt( _decrypted_master_key, fc::raw::pack( wif ) );
|
||||||
|
_available_private_keys.insert( active_pub_key );
|
||||||
|
|
||||||
|
return toQString(wif);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Wallet::getOwnerPrivateKey( uint32_t seq )
|
QString Wallet::getOwnerPrivateKey( uint32_t seq )
|
||||||
{
|
{
|
||||||
if( !isOpen() ) return QString();
|
if( !isOpen() ) return QString();
|
||||||
return QString();
|
if( isLocked() ) return QString();
|
||||||
|
if( !hasBrainKey() ) return QString();
|
||||||
|
|
||||||
|
auto seed = (getBrainKey() + " " + QString::number(seq)).toStdString();
|
||||||
|
|
||||||
|
auto secret = fc::sha256::hash( fc::sha512::hash( seed.c_str(), seed.size() ) );
|
||||||
|
|
||||||
|
auto wif = graphene::utilities::key_to_wif( secret );
|
||||||
|
auto priv_key = graphene::utilities::wif_to_key( wif );
|
||||||
|
if( !priv_key ) return QString();
|
||||||
|
|
||||||
|
public_key_type owner_pub_key(priv_key->get_public_key());
|
||||||
|
_data.encrypted_private_keys[owner_pub_key].encrypted_private_key = fc::aes_encrypt( _decrypted_master_key, fc::raw::pack( wif ) );
|
||||||
|
_available_private_keys.insert( owner_pub_key );
|
||||||
|
|
||||||
|
return toQString( wif );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Wallet::getActivePublicKey( QString active_pub, uint32_t seq )
|
||||||
|
{
|
||||||
|
return getPublicKey( getActivePrivateKey( active_pub, seq ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Wallet::getOwnerPublicKey( uint32_t seq )
|
||||||
|
{
|
||||||
|
return getPublicKey( getOwnerPrivateKey( seq ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QString Wallet::getKeyLabel( QString pubkey )
|
QString Wallet::getKeyLabel( QString pubkey )
|
||||||
{
|
{
|
||||||
if( !isOpen() ) return QString();
|
if( !isOpen() ) return QString();
|
||||||
return QString();
|
public_key_type key = fc::variant( pubkey.toStdString() ).as<public_key_type>();
|
||||||
|
auto itr = _data.encrypted_private_keys.find( key );
|
||||||
|
if( itr == _data.encrypted_private_keys.end() )
|
||||||
|
return QString();
|
||||||
|
return toQString( itr->second.label );
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* The same label may not be assigned to more than one key, this method will
|
||||||
|
* fail if a key with the same label already exists.
|
||||||
|
*
|
||||||
|
* @return true if the label was set
|
||||||
|
*/
|
||||||
|
bool Wallet::setKeyLabel( QString pubkey, QString label )
|
||||||
|
{
|
||||||
|
if( label == QString() ) // clear the label
|
||||||
|
{
|
||||||
|
auto pub = fc::variant( pubkey.toStdString() ).as<public_key_type>();
|
||||||
|
auto old_label = _data.encrypted_private_keys[pub].label;
|
||||||
|
_data.encrypted_private_keys[pub].label = string();
|
||||||
|
if( old_label.size() )
|
||||||
|
_label_to_key.erase( toQString( old_label ) );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto itr = _label_to_key.find( label );
|
||||||
|
if( itr != _label_to_key.end() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_label_to_key[label] = pubkey;
|
||||||
|
|
||||||
|
auto pub = fc::variant( pubkey.toStdString() ).as<public_key_type>();
|
||||||
|
_data.encrypted_private_keys[pub].label = label.toStdString();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QList<QPair<QString,QString> > Wallet::getAllPublicKeys( bool only_if_private )const
|
||||||
|
{
|
||||||
|
QList< QPair<QString,QString> > result;
|
||||||
|
if( !isOpen() ) return result;
|
||||||
|
|
||||||
|
for( const auto& item : _data.encrypted_private_keys )
|
||||||
|
{
|
||||||
|
if( only_if_private && !item.second.encrypted_private_key.size() ) continue;
|
||||||
|
result.push_back( qMakePair( toQString( item.first ), toQString( item.second.label ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Wallet::getPublicKey( QString label )
|
QString Wallet::getPublicKey( QString label )
|
||||||
{
|
{
|
||||||
if( !isOpen() ) return QString();
|
if( !isOpen() ) return QString();
|
||||||
return QString();
|
|
||||||
|
auto itr = _label_to_key.find( label );
|
||||||
|
if( itr != _label_to_key.end() )
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
return itr->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** imports a public key and assigns it a label */
|
/** imports a public key and assigns it a label */
|
||||||
bool Wallet::importPublicKey( QString pubkey, QString label)
|
bool Wallet::importPublicKey( QString pubkey, QString label )
|
||||||
{
|
{
|
||||||
if( !isOpen() ) return false;
|
return setKeyLabel( pubkey, label );
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param wifkey a private key in (WIF) Wallet Import Format
|
* @param wifkey a private key in (WIF) Wallet Import Format
|
||||||
* @pre !isLocked()
|
* @pre !isLocked()
|
||||||
**/
|
**/
|
||||||
bool Wallet::importPrivateKey( QString wifkey, QString label )
|
bool Wallet::importPrivateKey( QString wifkey, QString label )
|
||||||
{
|
{
|
||||||
if( !isOpen() ) return false;
|
if( !isOpen() ) return false;
|
||||||
return false;
|
if( isLocked() ) return false;
|
||||||
|
|
||||||
|
auto pub = getPublicKey( wifkey );
|
||||||
|
if( pub == QString() ) return false;
|
||||||
|
|
||||||
|
importPublicKey( pub, label );
|
||||||
|
|
||||||
|
auto p = fc::variant( pub.toStdString() ).as<public_key_type>();
|
||||||
|
_data.encrypted_private_keys[p].encrypted_private_key = fc::aes_encrypt( _decrypted_master_key, fc::raw::pack( wifkey.toStdString() ) );
|
||||||
|
_available_private_keys.insert(p);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** removes the key, its lablel and its private key */
|
/** removes the key, its lablel and its private key */
|
||||||
bool Wallet::removePublicKey( QString pubkey )
|
bool Wallet::removePublicKey( QString pubkey )
|
||||||
{
|
{
|
||||||
if( !isOpen() ) return false;
|
if( !isOpen() ) return false;
|
||||||
|
auto pub = fc::variant( pubkey.toStdString() ).as<public_key_type>();
|
||||||
|
_available_private_keys.erase(pub);
|
||||||
|
|
||||||
|
auto itr = _data.encrypted_private_keys.find(pub);
|
||||||
|
if( itr != _data.encrypted_private_keys.end() )
|
||||||
|
{
|
||||||
|
_label_to_key.erase( toQString(itr->second.label) );
|
||||||
|
_data.encrypted_private_keys.erase(itr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** removes only the private key, keeping the public key and label */
|
/** removes only the private key, keeping the public key and label
|
||||||
|
*
|
||||||
|
* @pre isOpen() && !isLocked()
|
||||||
|
**/
|
||||||
bool Wallet::removePrivateKey( QString pubkey )
|
bool Wallet::removePrivateKey( QString pubkey )
|
||||||
{
|
{
|
||||||
if( !isOpen() ) return false;
|
if( !isOpen() ) return false;
|
||||||
return false;
|
if( isLocked() ) return false;
|
||||||
|
|
||||||
|
auto pub = fc::variant( pubkey.toStdString() ).as<public_key_type>();
|
||||||
|
_data.encrypted_private_keys[pub].encrypted_private_key.resize(0);
|
||||||
|
_available_private_keys.erase(pub);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -227,6 +393,28 @@ vector<signature_type> Wallet::signDigest( const digest_type& d,
|
||||||
const set<public_key_type>& keys )const
|
const set<public_key_type>& keys )const
|
||||||
{
|
{
|
||||||
vector<signature_type> result;
|
vector<signature_type> result;
|
||||||
|
if( !isOpen() ) return result;
|
||||||
|
if( isLocked() ) return result;
|
||||||
|
|
||||||
|
result.reserve( keys.size() );
|
||||||
|
|
||||||
|
vector<fc::ecc::private_key> priv_keys;
|
||||||
|
for( const auto& key : keys )
|
||||||
|
{
|
||||||
|
auto itr = _data.encrypted_private_keys.find( key );
|
||||||
|
if( itr == _data.encrypted_private_keys.end() )
|
||||||
|
return vector<signature_type>();
|
||||||
|
if( itr->second.encrypted_private_key.size() == 0 )
|
||||||
|
return vector<signature_type>();
|
||||||
|
|
||||||
|
auto plain_wif = fc::aes_decrypt( _decrypted_master_key, itr->second.encrypted_private_key );
|
||||||
|
auto wif = fc::raw::unpack<std::string>(plain_wif);
|
||||||
|
auto priv = graphene::utilities::wif_to_key( wif );
|
||||||
|
if( !priv ) return vector<signature_type>();
|
||||||
|
|
||||||
|
result.push_back( priv->sign_compact( d ) );
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <graphene/chain/protocol/types.hpp>
|
#include <graphene/chain/protocol/types.hpp>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QList>
|
||||||
|
#include <QPair>
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
@ -17,6 +19,7 @@ using fc::optional;
|
||||||
struct key_data
|
struct key_data
|
||||||
{
|
{
|
||||||
string label; /** unique label assigned to this key */
|
string label; /** unique label assigned to this key */
|
||||||
|
/** encrypted as a packed std::string containing a wif private key */
|
||||||
vector<char> encrypted_private_key;
|
vector<char> encrypted_private_key;
|
||||||
};
|
};
|
||||||
FC_REFLECT( key_data, (label)(encrypted_private_key) );
|
FC_REFLECT( key_data, (label)(encrypted_private_key) );
|
||||||
|
|
@ -42,6 +45,9 @@ FC_REFLECT( wallet_file,
|
||||||
/**
|
/**
|
||||||
* @class Wallet
|
* @class Wallet
|
||||||
* @brief Securely maintains a set of labeled public and private keys
|
* @brief Securely maintains a set of labeled public and private keys
|
||||||
|
*
|
||||||
|
* @note this API is designed for use with QML which does not support exceptions so
|
||||||
|
* all errors are passed via return values.
|
||||||
*/
|
*/
|
||||||
class Wallet : public QObject
|
class Wallet : public QObject
|
||||||
{
|
{
|
||||||
|
|
@ -79,6 +85,7 @@ class Wallet : public QObject
|
||||||
* @return WIF private key
|
* @return WIF private key
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE QString getActivePrivateKey( QString owner_public_key, uint32_t sequence );
|
Q_INVOKABLE QString getActivePrivateKey( QString owner_public_key, uint32_t sequence );
|
||||||
|
Q_INVOKABLE QString getActivePublicKey( QString owner_public_key, uint32_t sequence );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @pre !isLocked();
|
* @pre !isLocked();
|
||||||
|
|
@ -87,9 +94,13 @@ class Wallet : public QObject
|
||||||
* @return WIF private key
|
* @return WIF private key
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE QString getOwnerPrivateKey( uint32_t sequence );
|
Q_INVOKABLE QString getOwnerPrivateKey( uint32_t sequence );
|
||||||
|
Q_INVOKABLE QString getOwnerPublicKey( uint32_t sequence );
|
||||||
|
Q_INVOKABLE QString getPublicKey( QString wif_private_key )const;
|
||||||
|
|
||||||
Q_INVOKABLE QString getKeyLabel( QString pubkey );
|
Q_INVOKABLE QString getKeyLabel( QString pubkey );
|
||||||
|
Q_INVOKABLE bool setKeyLabel( QString pubkey, QString label );
|
||||||
Q_INVOKABLE QString getPublicKey( QString label );
|
Q_INVOKABLE QString getPublicKey( QString label );
|
||||||
|
Q_INVOKABLE QString getPrivateKey( QString pubkey );
|
||||||
|
|
||||||
/** imports a public key and assigns it a label */
|
/** imports a public key and assigns it a label */
|
||||||
Q_INVOKABLE bool importPublicKey( QString pubkey, QString label = QString() );
|
Q_INVOKABLE bool importPublicKey( QString pubkey, QString label = QString() );
|
||||||
|
|
@ -106,6 +117,11 @@ class Wallet : public QObject
|
||||||
/** removes only the private key, keeping the public key and label */
|
/** removes only the private key, keeping the public key and label */
|
||||||
Q_INVOKABLE bool removePrivateKey( QString pubkey );
|
Q_INVOKABLE bool removePrivateKey( QString pubkey );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param only_if_private filter any public keys for which the wallet lacks a private key
|
||||||
|
* @return a list of PUBLICKEY, LABEL for all known public keys
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE QList<QPair<QString,QString> > getAllPublicKeys( bool only_if_private )const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @pre !isLocked()
|
* @pre !isLocked()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue