Problem Statement
Create a simple command-line cryptocurrency wallet that can generate new addresses, sign transactions, and send coins to other addresses on a test blockchain network. The wallet should be able to store the user's private key securely (use a dummy in-memory storage for simplicity) and display the current balance of each address managed by the wallet. Ensure that the wallet is capable of handling multiple addresses and can import existing keys.
The task requires understanding how wallets interact with blockchain networks, including generating key pairs, signing transactions, and using APIs to send transactions.
Concepts
- public and private keys
- transaction signing
- blockchain basics
Constraints
- Use a popular programming language like Python or JavaScript
- Implement in-memory storage for private keys (do not persist to disk)
- Integrate with any public testnet API to check balances and send coins
Security Notes
- Never store private keys on a non-secure medium in production applications
- Always verify signatures and transaction data before broadcasting transactions
Solutions
C Solution
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <curl/curl.h>
#define PRIVATE_KEY_SIZE 64
#define PUBLIC_KEY_SIZE 130
#define ADDRESS_SIZE 52
#define TRANSACTION_API_URL "https://testnet-api.cryptonode.io/v1/transactions"
typedef struct Address {
char private_key[PRIVATE_KEY_SIZE + 1];
char public_key[PUBLIC_KEY_SIZE + 1];
char address[ADDRESS_SIZE + 1];
} Address;
Address addresses[10];
int num_addresses = 0;
void generate_keys(Address *addr) {
EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
if (!EC_KEY_generate_key(eckey)) {
fprintf(stderr, "Error generating key\n");
exit(1);
}
const BIGNUM *priv_bn = EC_KEY_get0_private_key(eckey);
BN_bn2hex(priv_bn, addr->private_key);
unsigned char public_key_bytes[65];
size_t pub_len = EC_POINT_point2buf(EC_KEY_get0_group(eckey), EC_KEY_get0_public_key(eckey), POINT_CONVERSION_UNCOMPRESSED, &public_key_bytes, NULL);
for (size_t i = 0; i < pub_len; ++i) {
sprintf(addr->public_key + i * 2, "%02X", public_key_bytes[i]);
}
// Base58 encoding simplified for demonstration
snprintf(addr->address, sizeof(addr->address), "fake_addr_%s", addr->public_key);
OPENSSL_free(public_key_bytes);
EC_KEY_free(eckey);
}
void import_key(const char *priv_key_hex) {
if (num_addresses >= 10) return;
Address *addr = &addresses[num_addresses++];
strncpy(addr->private_key, priv_key_hex, PRIVATE_KEY_SIZE);
EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
BIGNUM *priv_bn = BN_new();
BN_hex2bn(&priv_bn, priv_key_hex);
if (!EC_KEY_set_private_key(eckey, priv_bn)) {
fprintf(stderr, "Error setting private key\n");
exit(1);
}
unsigned char public_key_bytes[65];
size_t pub_len = EC_POINT_point2buf(EC_KEY_get0_group(eckey), EC_KEY_get0_public_key(eckey), POINT_CONVERSION_UNCOMPRESSED, &public_key_bytes, NULL);
for (size_t i = 0; i < pub_len; ++i) {
sprintf(addr->public_key + i * 2, "%02X", public_key_bytes[i]);
}
// Base58 encoding simplified for demonstration
snprintf(addr->address, sizeof(addr->address), "fake_addr_%s", addr->public_key);
OPENSSL_free(public_key_bytes);
EC_KEY_free(eckey);
}
void display_balances() {
printf("Displaying balances:\n");
for (int i = 0; i < num_addresses; ++i) {
printf("Address: %s, Balance: FAKE_BALANCE\n", addresses[i].address);
}
}
void sign_transaction(const char *to_address, double amount) {
printf("Signing transaction to %s for %.2f coins...\n", to_address, amount);
// Implement signing logic using OpenSSL's ECDSA_sign
}
void send_coins(const char *to_address, double amount) {
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
char post_fields[256];
snprintf(post_fields, sizeof(post_fields), "to=%s&amount=%.2f", to_address, amount);
curl_easy_setopt(curl, CURLOPT_URL, TRANSACTION_API_URL);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_fields);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
curl_global_cleanup();
}
int main() {
Address *addr = &addresses[num_addresses++];
generate_keys(addr);
printf("Generated Address: %s\n", addr->address);
display_balances();
import_key("abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd");
printf("Imported Address: %s\n", addresses[num_addresses - 1].address);
display_balances();
sign_transaction(addresses[1].address, 1.0);
send_coins(addresses[1].address, 1.0);
return 0;
} This C program demonstrates a simple command-line cryptocurrency wallet that can generate new addresses, sign transactions, and send coins to other addresses on a test blockchain network. The wallet uses OpenSSL for cryptographic operations such as key generation and transaction signing. It stores private keys in-memory for simplicity, which is not recommended for production applications due to security risks.
The program defines an Address structure to hold the private key, public key, and address of each managed account. Functions include generate_keys() to create a new key pair, import_key() to add an existing private key, display_balances() to show the balance of each address (mocked in this example), sign_transaction() to sign transactions (partially implemented here), and send_coins() to broadcast transactions using a public testnet API.
Main functionality is demonstrated in the main() function, where it generates a new address, imports an existing one, displays balances, signs a transaction, and sends coins. Note that balance checking and transaction broadcasting are simplified for demonstration purposes.