tetsuo-blockchain-wallet 1.1.5 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,161 @@
1
+ # TETSUO Wallet SDK - Deployment & Testing Guide
2
+
3
+ ## Current Status
4
+
5
+ ✅ **SDK Updates Completed:**
6
+ - Fixed transaction structure (missing script length field)
7
+ - Fixed DER signature encoding
8
+ - Added canonical S value normalization
9
+ - Implemented `/send` CLI command
10
+
11
+ ⏳ **Server Updates Pending:**
12
+ - Added `hexToWif()` function for private key conversion
13
+ - Updated `/api/wallet/sign-and-broadcast` endpoint
14
+
15
+ ---
16
+
17
+ ## Step 1: Deploy Updated Server
18
+
19
+ ### Option A: Using SSH (recommended)
20
+
21
+ Replace the server.js on the remote server:
22
+
23
+ ```bash
24
+ scp /Users/pchmirenko/Desktop/tetsuo-explorer-live/server.js root@165.227.69.246:/root/tetsuo-explorer/server.js
25
+ ```
26
+
27
+ Then restart the service:
28
+
29
+ ```bash
30
+ ssh root@165.227.69.246 "cd /root/tetsuo-explorer && pm2 restart tetsuo-explorer"
31
+ ```
32
+
33
+ ### Option B: Manual Update
34
+
35
+ 1. Open the file on the remote server:
36
+ ```
37
+ /root/tetsuo-explorer/server.js
38
+ ```
39
+
40
+ 2. Find the `sign-and-broadcast` endpoint (around line 729)
41
+
42
+ 3. Replace the endpoint with the updated version that includes `hexToWif()` function
43
+
44
+ 4. Restart the service:
45
+ ```bash
46
+ pm2 restart tetsuo-explorer
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Step 2: Test Locally
52
+
53
+ ### Run the CLI
54
+
55
+ ```bash
56
+ cd /private/tmp/tetsuo-wallet-sdk
57
+ node dist/cli.js
58
+ ```
59
+
60
+ ### Test the `/send` Command
61
+
62
+ In the CLI, run:
63
+
64
+ ```
65
+ /send
66
+ ```
67
+
68
+ Then follow the prompts:
69
+ - Recipient: `TVt1p2fcKTQXZVidshJbDCdYN3wxRnWLES` (or another address)
70
+ - Amount: `0.5` (TETSUO)
71
+ - Confirm: `yes`
72
+
73
+ ### Expected Result
74
+
75
+ If server is updated:
76
+ ```
77
+ ✅ Transaction sent successfully!
78
+ TXID: a1b2c3d4e5f6...
79
+ Check at: https://tetsuoarena.com/tx/a1b2c3d4e5f6...
80
+ ```
81
+
82
+ If server not updated:
83
+ ```
84
+ ✗ Error: Client signing verification failed, using server signature...
85
+ ✗ Error: Invalid private key
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Step 3: What Each Fix Does
91
+
92
+ ### Fix #1: Transaction Structure
93
+ - **File:** `src/transaction.ts` line 236
94
+ - **Issue:** Missing varint length field for input script
95
+ - **Effect:** Transactions no longer get "TX decode failed" error
96
+ - **Status:** ✅ DEPLOYED in SDK 1.2.1
97
+
98
+ ### Fix #2: DER Signature Encoding
99
+ - **File:** `src/transaction.ts` line 372
100
+ - **Issue:** Incorrect length calculation (66 instead of 68 bytes)
101
+ - **Effect:** Signatures no longer get "Non-canonical DER" error
102
+ - **Status:** ✅ DEPLOYED in SDK 1.2.1
103
+
104
+ ### Fix #3: WIF Key Conversion
105
+ - **File:** `server.js` line 730
106
+ - **Issue:** Server expects WIF format, not raw hex
107
+ - **Effect:** Server-side signing fallback will work
108
+ - **Status:** ⏳ PENDING - Needs server deployment
109
+
110
+ ---
111
+
112
+ ## Troubleshooting
113
+
114
+ ### If you get "Invalid private key" error:
115
+ 1. ❌ Server still has old code
116
+ 2. ✅ Solution: Deploy updated server.js
117
+
118
+ ### If you get "Signature must be zero for failed CHECK(MULTI)SIG":
119
+ 1. This means DER encoding is fixed ✅
120
+ 2. But ECDSA signature verification is still failing
121
+ 3. Next step: Debug signature preimage creation
122
+
123
+ ### If CLI won't start:
124
+ ```bash
125
+ # Make sure you're in the right directory
126
+ cd /private/tmp/tetsuo-wallet-sdk
127
+
128
+ # Rebuild if needed
129
+ npm run build
130
+
131
+ # Run CLI
132
+ node dist/cli.js
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Files Modified
138
+
139
+ ```
140
+ /private/tmp/tetsuo-wallet-sdk/
141
+ ├── src/
142
+ │ ├── transaction.ts (FIXED - signature handling)
143
+ │ └── rpc.ts (comments updated)
144
+ ├── package.json (version 1.2.1)
145
+ └── dist/ (built & ready)
146
+
147
+ /Users/pchmirenko/Desktop/tetsuo-explorer-live/
148
+ └── server.js (NEEDS DEPLOYMENT - WIF conversion added)
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Next Steps
154
+
155
+ 1. **Deploy server** (Step 1 above)
156
+ 2. **Test locally** (Step 2 above)
157
+ 3. **Report results**:
158
+ - What error do you get?
159
+ - Is the transaction being broadcast?
160
+ - Does the TXID appear?
161
+
@@ -0,0 +1,91 @@
1
+ # TETSUO Wallet - Key Derivation Fix
2
+
3
+ ## Problem Discovered
4
+
5
+ The wallet SDK was using **HMAC-based key derivation** instead of proper **secp256k1 elliptic curve point multiplication**. This is why client-side signatures are failing.
6
+
7
+ ### What's Happening
8
+
9
+ ```
10
+ Private Key (same): f07313a484a766e1d62f4ad6345d54ef7544784edbad686742cb44df210c3ea6
11
+
12
+ SDK derives (HMAC): 026e9d770f8ba4132a4c7d0b9a14f466bf0b92607e9dfb7abcca3c445c350f9502
13
+ Correct (secp256k1): 024809982b467a559be83af9778784ba6a42a8593d5ead22e757e708db4a334f3d
14
+
15
+ These are DIFFERENT public keys!
16
+ ```
17
+
18
+ ### Why Signatures Fail
19
+
20
+ 1. UTXO scriptPubKey expects signature valid for: `026e9d77...` (stored pubkey)
21
+ 2. We sign with private key, but elliptic derives: `024809...` (correct pubkey)
22
+ 3. Blockchain validates signature against wrong public key
23
+ 4. Signature fails: `"Signature must be zero for failed CHECK(MULTI)SIG"`
24
+
25
+ ## Solution
26
+
27
+ ### Option 1: Create NEW Wallet with Correct Keys (RECOMMENDED)
28
+
29
+ ```bash
30
+ # In CLI, delete old wallets and create new ones
31
+ /delete-wallet # Remove marcus, papa, popo
32
+
33
+ # Create new wallets - these will use correct secp256k1 derivation
34
+ /create-wallet # Create new wallet
35
+ /import-wallet # Or import from mnemonic
36
+ ```
37
+
38
+ Then transfer your TETSUO from old address to new address via another method.
39
+
40
+ ### Option 2: Use Server-Side Signing (Less Secure)
41
+
42
+ ```
43
+ Use /api/wallet/sign-and-broadcast endpoint
44
+ (Requires deploying updated server.js with hexToWif function)
45
+ ```
46
+
47
+ ### Option 3: Keep Old Wallets, Accept HMAC Derivation
48
+
49
+ Accept the HMAC-based derivation as-is. This is not cryptographically correct but maintains compatibility with existing wallets/UTXOs.
50
+
51
+ Status: ✓ Currently implemented for backward compatibility
52
+
53
+ ## What We Fixed
54
+
55
+ ✅ **Transaction Structure** - Fixed missing varint length field for input scripts
56
+ ✅ **DER Encoding** - Fixed signature length calculation
57
+ ✅ **Canonical Signatures** - Added proper S value normalization
58
+ ✅ **Documentation** - Clearly marked HMAC derivation as non-standard
59
+
60
+ ## What Remains
61
+
62
+ The fundamental issue is that **all existing wallets** (marcus, papa, popo) were created with incorrect key derivation. To use proper secp256k1 signing:
63
+
64
+ 1. Create new wallets
65
+ 2. Transfer funds to new addresses
66
+ 3. Use new wallets for all transactions
67
+
68
+ ## Implementation Status
69
+
70
+ | Component | Status | Notes |
71
+ |-----------|--------|-------|
72
+ | Transaction Structure | ✅ FIXED | Input scripts properly sized |
73
+ | DER Encoding | ✅ FIXED | Signatures now canonical |
74
+ | Client Signing | ⏳ BLOCKED | Waiting for new wallets with correct keys |
75
+ | Server Signing | ⏳ PENDING | Needs server deployment + WIF conversion |
76
+
77
+ ## Next Steps
78
+
79
+ 1. **Understand the trade-off**:
80
+ - Keep old wallets = Can spend existing UTXOs but signature validation edge case
81
+ - Create new wallets = Proper crypto but need to transfer funds first
82
+
83
+ 2. **Choose your approach**:
84
+ - Use server-side signing temporarily (less secure but works)
85
+ - Migrate to new wallets (more complex but fully secure)
86
+
87
+ 3. **Test the chosen solution**
88
+
89
+ ---
90
+
91
+ **Note**: The HMAC-based derivation is marked with a TODO for future migration to proper secp256k1. All new SDK versions should use correct elliptic curve math.
package/dist/cli.js CHANGED
@@ -228,8 +228,12 @@ async function getBalance() {
228
228
  console.log(chalk_1.default.yellow('⏳ Fetching balance...'));
229
229
  const rpc = (0, index_1.createRPCClient)(RPC_URL);
230
230
  const balance = await rpc.getBalance(wallet.address);
231
- console.log(chalk_1.default.cyan('\n💰 Balance:'));
232
- console.log(chalk_1.default.green(`${balance.toFixed(8)} TETSUO`));
231
+ console.log(chalk_1.default.cyan('\n💰 Balance Information:'));
232
+ console.log('─'.repeat(50));
233
+ console.log(chalk_1.default.yellow(' Wallet: ') + chalk_1.default.white(wallet.name));
234
+ console.log(chalk_1.default.yellow(' Address: ') + chalk_1.default.white(wallet.address));
235
+ console.log(chalk_1.default.yellow(' Balance: ') + chalk_1.default.green(`${balance.toFixed(8)} TETSUO`));
236
+ console.log('─'.repeat(50));
233
237
  }
234
238
  catch (error) {
235
239
  console.log(chalk_1.default.red(`✗ Error: ${error.message}`));
@@ -247,10 +251,13 @@ async function getTransactions() {
247
251
  const rpc = (0, index_1.createRPCClient)(RPC_URL);
248
252
  const transactions = await rpc.getTransactionHistory(wallet.address);
249
253
  if (transactions.length === 0) {
254
+ console.log(chalk_1.default.cyan('\n📋 Transaction History:'));
255
+ console.log(chalk_1.default.yellow(' Wallet: ') + wallet.name);
250
256
  console.log(chalk_1.default.yellow('No transactions found'));
251
257
  return;
252
258
  }
253
259
  console.log(chalk_1.default.cyan('\n📋 Transaction History:'));
260
+ console.log(chalk_1.default.yellow(' Wallet: ') + wallet.name);
254
261
  console.log('─'.repeat(80));
255
262
  transactions.forEach((tx) => {
256
263
  const type = tx.isIncoming ? chalk_1.default.green('↓ RECEIVE') : chalk_1.default.yellow('↑ SEND');
@@ -323,10 +330,19 @@ async function sendTokens(rl) {
323
330
  // Create and sign transaction
324
331
  console.log(chalk_1.default.yellow('\n⏳ Signing transaction...'));
325
332
  const txHex = (0, index_1.createTransactionHex)(txData.inputs, txData.outputs);
326
- const signedTxHex = (0, index_1.signTransaction)(txHex, wallet.privateKey, txData.inputs);
327
- // Broadcast
333
+ const signedTxHex = (0, index_1.signTransaction)(txHex, wallet.privateKey, txData.inputs, utxos);
334
+ // Broadcast or use server fallback
328
335
  console.log(chalk_1.default.yellow(' Broadcasting...'));
329
- const txid = await rpc.broadcastTransaction(signedTxHex);
336
+ let txid;
337
+ try {
338
+ // Try broadcasting signed transaction
339
+ txid = await rpc.broadcastTransaction(signedTxHex);
340
+ }
341
+ catch (broadcastError) {
342
+ console.log(chalk_1.default.yellow(' Client signing verification failed, using server signature...'));
343
+ // Fallback: Use server to sign and broadcast
344
+ txid = await rpc.signAndBroadcast(txHex, wallet.privateKey);
345
+ }
330
346
  console.log(chalk_1.default.green('\n✅ Transaction sent successfully!'));
331
347
  console.log(chalk_1.default.cyan('\n📊 Transaction Info:'));
332
348
  console.log('─'.repeat(60));
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  export { WalletConfig, GeneratedWallet, ImportedWallet, UTXO, TransactionInput, TransactionOutput, SignedTransaction, TransactionResult, Balance, Transaction, BlockchainInfo, WalletError, InvalidAddressError, InsufficientFundsError, RPCError } from './types';
5
5
  export { sha256, doubleSha256, ripemd160, hash160, randomBytes, toHex, fromHex, toBase58, fromBase58, base58check, base58checkDecode } from './crypto';
6
6
  export { generateAddress, isValidAddress, validateAddress, addressToHash160, getAddressHash } from './address';
7
- export { generateWallet, importFromMnemonic, importFromPrivateKey, derivePublicKey, isValidMnemonic, getSupportedMnemonicLengths } from './wallet';
7
+ export { generateWallet, importFromMnemonic, importFromPrivateKey, derivePublicKey, derivePublicKeyLegacy, isValidMnemonic, getSupportedMnemonicLengths } from './wallet';
8
8
  export { buildTransaction, createTransactionHex, signTransaction, estimateTransactionSize, estimateFee } from './transaction';
9
9
  export { TetsuoRPC, createRPCClient } from './rpc';
10
10
  export declare const VERSION = "1.0.0";
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * TETSUO Wallet SDK - Main Entry Point
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.PACKAGE_INFO = exports.VERSION = exports.createRPCClient = exports.TetsuoRPC = exports.estimateFee = exports.estimateTransactionSize = exports.signTransaction = exports.createTransactionHex = exports.buildTransaction = exports.getSupportedMnemonicLengths = exports.isValidMnemonic = exports.derivePublicKey = exports.importFromPrivateKey = exports.importFromMnemonic = exports.generateWallet = exports.getAddressHash = exports.addressToHash160 = exports.validateAddress = exports.isValidAddress = exports.generateAddress = exports.base58checkDecode = exports.base58check = exports.fromBase58 = exports.toBase58 = exports.fromHex = exports.toHex = exports.randomBytes = exports.hash160 = exports.ripemd160 = exports.doubleSha256 = exports.sha256 = exports.RPCError = exports.InsufficientFundsError = exports.InvalidAddressError = exports.WalletError = void 0;
6
+ exports.PACKAGE_INFO = exports.VERSION = exports.createRPCClient = exports.TetsuoRPC = exports.estimateFee = exports.estimateTransactionSize = exports.signTransaction = exports.createTransactionHex = exports.buildTransaction = exports.getSupportedMnemonicLengths = exports.isValidMnemonic = exports.derivePublicKeyLegacy = exports.derivePublicKey = exports.importFromPrivateKey = exports.importFromMnemonic = exports.generateWallet = exports.getAddressHash = exports.addressToHash160 = exports.validateAddress = exports.isValidAddress = exports.generateAddress = exports.base58checkDecode = exports.base58check = exports.fromBase58 = exports.toBase58 = exports.fromHex = exports.toHex = exports.randomBytes = exports.hash160 = exports.ripemd160 = exports.doubleSha256 = exports.sha256 = exports.RPCError = exports.InsufficientFundsError = exports.InvalidAddressError = exports.WalletError = void 0;
7
7
  // Type exports
8
8
  var types_1 = require("./types");
9
9
  Object.defineProperty(exports, "WalletError", { enumerable: true, get: function () { return types_1.WalletError; } });
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "generateWallet", { enumerable: true, get: functi
36
36
  Object.defineProperty(exports, "importFromMnemonic", { enumerable: true, get: function () { return wallet_1.importFromMnemonic; } });
37
37
  Object.defineProperty(exports, "importFromPrivateKey", { enumerable: true, get: function () { return wallet_1.importFromPrivateKey; } });
38
38
  Object.defineProperty(exports, "derivePublicKey", { enumerable: true, get: function () { return wallet_1.derivePublicKey; } });
39
+ Object.defineProperty(exports, "derivePublicKeyLegacy", { enumerable: true, get: function () { return wallet_1.derivePublicKeyLegacy; } });
39
40
  Object.defineProperty(exports, "isValidMnemonic", { enumerable: true, get: function () { return wallet_1.isValidMnemonic; } });
40
41
  Object.defineProperty(exports, "getSupportedMnemonicLengths", { enumerable: true, get: function () { return wallet_1.getSupportedMnemonicLengths; } });
41
42
  // Transaction exports
package/dist/rpc.d.ts CHANGED
@@ -34,6 +34,10 @@ export declare class TetsuoRPC {
34
34
  * Broadcast a signed transaction
35
35
  */
36
36
  broadcastTransaction(transactionHex: string): Promise<string>;
37
+ /**
38
+ * Sign and broadcast transaction (for fallback when client signing fails)
39
+ */
40
+ signAndBroadcast(unsignedHex: string, privateKey: string): Promise<string>;
37
41
  /**
38
42
  * Estimate fee for transaction
39
43
  */
package/dist/rpc.js CHANGED
@@ -72,7 +72,17 @@ class TetsuoRPC {
72
72
  async getUTXOs(address) {
73
73
  try {
74
74
  const response = await this.client.get(`/api/wallet/utxos/${address}`);
75
- return Array.isArray(response.data.utxos) ? response.data.utxos : [];
75
+ // Map server response to UTXO type (server uses 'amount', SDK expects 'value')
76
+ if (Array.isArray(response.data.utxos)) {
77
+ return response.data.utxos.map((utxo) => ({
78
+ txid: utxo.txid,
79
+ vout: utxo.vout,
80
+ value: utxo.amount ? Math.floor(utxo.amount * 100000000) : 0, // Convert TETSUO to satoshis
81
+ confirmations: utxo.confirmations || 0,
82
+ scriptPubKey: utxo.scriptPubKey
83
+ }));
84
+ }
85
+ return [];
76
86
  }
77
87
  catch (error) {
78
88
  throw this.handleError(`Failed to get UTXOs for ${address}`, error);
@@ -119,7 +129,7 @@ class TetsuoRPC {
119
129
  */
120
130
  async broadcastTransaction(transactionHex) {
121
131
  try {
122
- const response = await this.client.post('/api/transaction/broadcast', {
132
+ const response = await this.client.post('/api/wallet/broadcast', {
123
133
  hex: transactionHex
124
134
  });
125
135
  if (!response.data.txid) {
@@ -131,6 +141,26 @@ class TetsuoRPC {
131
141
  throw this.handleError('Failed to broadcast transaction', error);
132
142
  }
133
143
  }
144
+ /**
145
+ * Sign and broadcast transaction (for fallback when client signing fails)
146
+ */
147
+ async signAndBroadcast(unsignedHex, privateKey) {
148
+ try {
149
+ // The private key can be either hex or WIF format
150
+ // If it's hex (64 chars), send as-is - server will convert it
151
+ const response = await this.client.post('/api/wallet/sign-and-broadcast', {
152
+ unsignedHex,
153
+ privateKey
154
+ });
155
+ if (!response.data.txid) {
156
+ throw new Error('No transaction ID returned');
157
+ }
158
+ return response.data.txid;
159
+ }
160
+ catch (error) {
161
+ throw this.handleError('Failed to sign and broadcast transaction', error);
162
+ }
163
+ }
134
164
  /**
135
165
  * Estimate fee for transaction
136
166
  */
@@ -13,11 +13,11 @@ export declare function buildTransaction(fromAddress: string, toAddress: string,
13
13
  /**
14
14
  * Create transaction hex from inputs and outputs
15
15
  */
16
- export declare function createTransactionHex(inputs: TransactionInput[], outputs: TransactionOutput[]): string;
16
+ export declare function createTransactionHex(inputs: TransactionInput[], outputs: TransactionOutput[], scriptPubKeys?: string[]): string;
17
17
  /**
18
- * Sign a transaction
18
+ * Sign a transaction with proper TETSUO key derivation
19
19
  */
20
- export declare function signTransaction(transactionHex: string, privateKey: string, inputs: TransactionInput[]): string;
20
+ export declare function signTransaction(transactionHex: string, privateKey: string, inputs: TransactionInput[], utxos?: any[]): string;
21
21
  /**
22
22
  * Calculate transaction size
23
23
  */