/**
 * Sign a message with this address.  This may throw if the address
 * is invalid, the wallet locked or the private key is not owned.
 * @param msg The message that should be signed.
 * @return The message's signature.
 * @throws NoPrivateKey if this address is not owned.
 * @throws std::runtime_error if the address is invalid or the wallet locked.
 */
std::string
CoinInterface::Address::signMessage (const std::string& msg) const
{
  if (!valid)
    throw std::runtime_error ("Can't sign with invalid address.");

  try
    {
      const JsonRpc::JsonData res = rpc->executeRpc ("signmessage", addr, msg);
      return res.asString ();
    }
  catch (const JsonRpc::RpcError& exc)
    {
      switch (exc.getErrorCode ())
        {
        case -13:
          throw std::runtime_error ("Need to unlock the wallet first.");

        case -3:
          {
            std::ostringstream msg;
            msg << "You don't have the private key of " << addr << " in order"
                << " to sign messages with that address.";
            throw NoPrivateKey (msg.str ());
          }

        default:
          throw exc;
        }
    }
}
ZR::BitcoinAddress ZrSatoshiBitcoin::mkOrderAddress( const ZR::ZR_Number & amount )
{
    std::string addr = newAddress();
    if( addr.empty() )
        return addr;

    try{
        JsonRpc rpc( m_settings );
        JsonRpc::JsonData res = rpc.executeRpc ( "sendtoaddress", addr, JsonRpc::JsonData( amount.toDouble() ) );
        // TODO: Error handling
        std::string id = res.asString();

        // make sure the Satoshi client does not touch "id"
        // this will go away if the Satoshi client is restarted
        // may be unnecessary for other implementations of this class, e.g. libbitcoin
        JsonRpc::JsonData addrArray( Json::arrayValue );
        addrArray.append( addr );
        JsonRpc::JsonData res2 = rpc.executeRpc ( "listunspent", 0, 999999, addrArray );
        unsigned int vout = res2[0u][ "vout" ].asUInt();
        JsonRpc::JsonData lockObjArray( Json::arrayValue );
        JsonRpc::JsonData lockObj;
        lockObj[ "txid" ] = id;
        lockObj[ "vout" ] = vout;
        lockObjArray.append( lockObj );
        JsonRpc::JsonData res1 = rpc.executeRpc ( "lockunspent", true, lockObjArray );
    }
    catch( std::runtime_error e ){
        g_ZeroReservePlugin->placeMsg( std::string( "Exception caught at " ) + __func__ + ": " + e.what() + " Cannot make an order address. Insufficient funds? If you have enough, try restarting the Satoshi Client." );
        std::cerr << "Zero Reserve: " << __func__ << ": Exception caught: " << e.what() << std::endl;
        print_stacktrace();
        return std::string();
    }
    return addr;
}
const ZR::BitcoinAddress ZrSatoshiBitcoin::newAddress() const
{
    std::string addr;
    try{
        JsonRpc rpc( m_settings );
        JsonRpc::JsonData resAddr = rpc.executeRpc ( "getnewaddress" );
        addr = resAddr.asString();
    }
    catch( std::runtime_error e ){
        std::cerr << "Zero Reserve: " << __func__ << ": Exception caught: " << e.what() << std::endl;
        g_ZeroReservePlugin->placeMsg( std::string( "Exception caught at " ) + __func__ + ": " + e.what() + " Cannot create new Bitcoin address." );
        print_stacktrace();
    }
    return addr;
}
ZR::MyWallet * ZrSatoshiBitcoin::mkWallet( ZR::MyWallet::WalletType wType )
{
    JsonRpc rpc( m_settings );
    try{
        if( wType == ZR::MyWallet::WIFIMPORT ){
            JsonRpc::JsonData res = rpc.executeRpc ( "getnewaddress" );
            ZR::BitcoinAddress address = res.asString();
            return new SatoshiWallet( address, 0 );
        }
    }
    catch( std::runtime_error e ){
        std::cerr << "Zero Reserve: " << __func__ << ": Exception caught: " << e.what() << std::endl;
        print_stacktrace();
    }
    return NULL;
}
/**
 * Create a new address (as per "getnewaddress") and return it.
 * @return Newly created address.
 */
CoinInterface::Address
CoinInterface::createAddress ()
{
  const JsonRpc::JsonData addr = rpc.executeRpc ("getnewaddress");
  return Address (rpc, addr.asString ());
}