Skip to content

Commit efb765e

Browse files
twtaylorbitgoperrybitgo
authored andcommitted
TRON-36 - working core recover for trx
1 parent ac30ca3 commit efb765e

2 files changed

Lines changed: 39 additions & 51 deletions

File tree

modules/core/src/v2/coins/trx.ts

Lines changed: 31 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33
*/
44
import * as Bluebird from 'bluebird';
55
import * as crypto from 'crypto';
6-
import { CoinFamily } from '@bitgo/statics';
6+
import { CoinFamily, BaseCoin as StaticsBaseCoin } from '@bitgo/statics';
77
const co = Bluebird.coroutine;
88
import * as bitgoAccountLib from '@bitgo/account-lib';
9-
import { HDNode } from 'bitgo-utxo-lib';
9+
import { HDNode, networks } from 'bitgo-utxo-lib';
1010
import * as request from 'superagent';
1111
import * as common from '../../common';
1212

13-
import { BaseCoin as StaticsBaseCoin } from '@bitgo/statics';
14-
1513
import {
1614
BaseCoin,
1715
KeyPair,
@@ -26,14 +24,11 @@ import {
2624
TransactionPrebuild as BaseTransactionPrebuild,
2725
TransactionExplanation,
2826
} from '../baseCoin';
29-
import * as utxoLib from 'bitgo-utxo-lib';
27+
3028
import { BitGo } from '../../bitgo';
3129
import { NodeCallback } from '../types';
3230
import { TransactionBuilder } from '@bitgo/account-lib';
3331

34-
import debug = require('debug');
35-
import {utxo} from "@bitgo/statics/dist/src/utxo";
36-
3732
export interface TronSignTransactionOptions extends SignTransactionOptions {
3833
txPrebuild: TransactionPrebuild;
3934
prv: string;
@@ -44,6 +39,7 @@ export interface TxInfo {
4439
from: string;
4540
txid: string;
4641
}
42+
4743
export interface TronTransactionExplanation extends TransactionExplanation {
4844
expiration: number;
4945
timestamp: number;
@@ -83,7 +79,6 @@ export enum NodeTypes {
8379
Solidity,
8480
}
8581

86-
8782
export class Trx extends BaseCoin {
8883
protected readonly _staticsCoin: Readonly<StaticsBaseCoin>;
8984

@@ -157,7 +152,7 @@ export class Trx extends BaseCoin {
157152
// random. 512 bits is therefore the maximum entropy and gives us maximum security against cracking.
158153
seed = crypto.randomBytes(512 / 8);
159154
}
160-
const hd = utxoLib.HDNode.fromSeedBuffer(seed);
155+
const hd = HDNode.fromSeedBuffer(seed);
161156
return {
162157
pub: hd.neutered().toBase58(),
163158
prv: hd.toBase58(),
@@ -166,7 +161,7 @@ export class Trx extends BaseCoin {
166161

167162
isValidXpub(xpub: string): boolean {
168163
try {
169-
return utxoLib.HDNode.fromBase58(xpub).isNeutered();
164+
return HDNode.fromBase58(xpub).isNeutered();
170165
} catch (e) {
171166
return false;
172167
}
@@ -284,38 +279,23 @@ export class Trx extends BaseCoin {
284279
throw new Error('invalid xpub');
285280
}
286281

287-
const hdNode = utxoLib.HDNode.fromBase58(xpub, this.bitcoinEncoding());
282+
const hdNode = HDNode.fromBase58(xpub, networks.bitcoin);
288283
return hdNode.keyPair.__Q.getEncoded(false).toString('hex');
289284
}
290285

291286
compressedPubToHexAddress(pub: string): string {
292287
const byteArrayAddr = bitgoAccountLib.Trx.Utils.getByteArrayFromHexAddress(pub);
293288
const rawAddress = bitgoAccountLib.Trx.Utils.getRawAddressFromPubKey(byteArrayAddr);
294-
return Buffer.from(rawAddress).toString('hex').toUpperCase();
289+
return bitgoAccountLib.Trx.Utils.getHexAddressFromByteArray(rawAddress);
295290
}
296291

297292
xprvToCompressedPrv(xprv: string): string {
298293
if (!this.isValidXprv(xprv)) {
299294
throw new Error('invalid xprv');
300295
}
301296

302-
const hdNode = utxoLib.HDNode.fromBase58(xprv, this.bitcoinEncoding());
297+
const hdNode = HDNode.fromBase58(xprv, networks.bitcoin);
303298
return hdNode.keyPair.d.toBuffer(32).toString('hex');
304-
};
305-
306-
bitcoinEncoding(): any {
307-
return {
308-
messagePrefix: '\x18Bitcoin Signed Message:\n',
309-
bech32: 'bc',
310-
bip32: {
311-
public: 0x0488b21e,
312-
private: 0x0488ade4
313-
},
314-
pubKeyHash: 0x00,
315-
scriptHash: 0x05,
316-
wif: 0x80,
317-
coin: 'btc',
318-
};
319299
}
320300

321301
/**
@@ -324,7 +304,7 @@ export class Trx extends BaseCoin {
324304
* @param callback
325305
* @returns {Object} response from Trongrid
326306
*/
327-
recoveryPost(query: { path: string, jsonObj: any, node: NodeTypes }, callback?: NodeCallback<any>): Bluebird<any> {
307+
recoveryPost(query: { path: string; jsonObj: any; node: NodeTypes }, callback?: NodeCallback<any>): Bluebird<any> {
328308
const self = this;
329309
return co(function*() {
330310
let nodeUri = '';
@@ -340,16 +320,19 @@ export class Trx extends BaseCoin {
340320
}
341321

342322
const response = yield request
343-
.post(nodeUri + query.path)
344-
.send(query.jsonObj);
323+
.post(nodeUri + query.path)
324+
.type('json')
325+
.send(query.jsonObj);
345326

346327
if (!response.ok) {
347328
throw new Error('could not reach Tron node');
348329
}
349-
return response.body;
330+
331+
// unfortunately, it doesn't look like most TRON nodes return valid json as body
332+
return JSON.parse(response.text);
350333
})
351-
.call(this)
352-
.asCallback(callback);
334+
.call(this)
335+
.asCallback(callback);
353336
}
354337

355338
/**
@@ -387,15 +370,15 @@ export class Trx extends BaseCoin {
387370
path: '/wallet/createtransaction',
388371
jsonObj: {
389372
to_address: toAddr,
390-
from_address: fromAddr,
391-
amount: amount.toFixed(0),
373+
owner_address: fromAddr,
374+
amount,
392375
},
393376
node: NodeTypes.Full,
394377
});
395-
return result.balance;
378+
return result;
396379
})
397-
.call(this)
398-
.asCallback(callback);
380+
.call(this)
381+
.asCallback(callback);
399382
}
400383

401384
/**
@@ -420,9 +403,9 @@ export class Trx extends BaseCoin {
420403
const bitgoAddress = self.compressedPubToHexAddress(self.xpubToCompressedPub(params.bitgoKey));
421404
const recoveryAddressHex = bitgoAccountLib.Trx.Utils.getHexAddressFromBase58Address(params.recoveryDestination);
422405

406+
// call the node to get our account balance
423407
const recoveryAmount = yield self.getAccountBalanceFromNode(bitgoAddress);
424408

425-
//const userPrv = HDKey.fromExtendedKey(keys[0]).privateKey;
426409
const userXPub = keys[0].neutered().toBase58();
427410
const userXPrv = keys[0].toBase58();
428411
const backupXPub = keys[1].neutered().toBase58();
@@ -431,18 +414,16 @@ export class Trx extends BaseCoin {
431414
const userHexAddr = self.compressedPubToHexAddress(self.xpubToCompressedPub(userXPub));
432415
const backupHexAddr = self.compressedPubToHexAddress(self.xpubToCompressedPub(backupXPub));
433416

434-
// TODO: some checks here about pubs being valid, for this wallet, etc.
435-
436-
// construct the tx
437-
// TODO: recovery amount might not include fees, we'll have to figure out the max we can spend
438-
// sensitive to energy and tx cost amounts
417+
// construct the tx -
439418
// there's an assumption here being made about fees: for a wallet that hasn't been used in awhile, the implication is
440-
// it has maximum bandwidth. thus, a recovery should cost the minimum amount (1e6 sun)
419+
// it has maximum bandwidth. thus, a recovery should cost the minimum amount (1e6 sun or 1 Tron)
441420
if (1e6 > recoveryAmount) {
442421
throw new Error('Amount of funds to recover wouldnt be able to fund a send');
443422
}
444423
const recoveryAmountMinusFees = recoveryAmount - 1e6;
445-
const buildTx = self.getBuildTransaction(recoveryAddressHex, bitgoAddress, recoveryAmountMinusFees);
424+
const buildTx = yield self.getBuildTransaction(recoveryAddressHex, bitgoAddress, recoveryAmountMinusFees);
425+
426+
// TODO: some checks here about pubs being valid, for this wallet, etc. from build transaction
446427

447428
// construct our tx
448429
const txBuilder = new bitgoAccountLib.TransactionBuilder({ coinName: this.getChain() });
@@ -453,11 +434,11 @@ export class Trx extends BaseCoin {
453434
return txBuilder.build().toJson();
454435
}
455436

456-
// sign our tx
457437
txBuilder.sign({ key: userPrv });
458438

439+
// krs recoveries don't get signed
459440
if (!isKrsRecovery) {
460-
const backupXPrv = keys[0].toBase58();
441+
const backupXPrv = keys[1].toBase58();
461442
const backupPrv = self.xprvToCompressedPrv(backupXPrv);
462443

463444
txBuilder.sign({ key: backupPrv });

modules/core/test/v2/unit/recovery.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,14 +566,21 @@ describe('Recovery:', function() {
566566
recoveryNocks.nockTronRecovery();
567567
});
568568

569-
it('should generate TRON recovery tx', co(function *() {
569+
it('should generate TRON recovery tx from user and backup keys', co(function *() {
570570
const recoveryTx = yield baseCoin.recover({
571571
userKey: '{"iv":"QPX3xtGROshqHW8kGPAYCw==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"LJ4YYCyClRE=","ct":"hlJH8lWk/FaciymG8UsscxVFCnOduLRjoWxaK8xU7TjsqUDXsQjj0BpH7aNm64p6ldueaGoU2/VfrrzX9lWrcVmXspFp2oON5EyK45JbI13hirqG2dkOqoT8G8mrMydMp6zG5iOA+EtXRy69kYDCI1Re6mR7k1c="}',
572572
backupKey: '{"iv":"xbOCFaZVnrQLAYKcgMvdNw==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"86cZ+hT+S0Y=","ct":"RH7Uks/JjNARX9wuIw6r4Map2C9FtLlWYk4zjLcrjzkBuNTCUNQgxF7kU5/SWzD+tomVBj6P9CLkXYzPlR1NhVi+aT9mTW6LK9nJ+ErpLKXzbIAxBLezDjJ5xqUS5cGkjoHCtANL7qTZcDBvOfejLDrUjQdw2WQ="}',
573573
bitgoKey: 'xpub661MyMwAqRbcFcaxCPsEyhj79VUuVVThWinZnjhvAPnFLB1SBp7Yk4gvqWsGE3MHdw1tPRLnHRRQLNcrKqaCyBnFK5XTrZUrLyY94LXn4v9',
574574
walletPassphrase: TestBitGo.V2.TEST_RECOVERY_PASSCODE,
575575
recoveryDestination: 'TYBTURKpanKxnx91uyfvvtztNeHE3EQf6G',
576576
});
577+
578+
should.exist(recoveryTx);
579+
580+
recoveryTx.signature[0].should.equal('f4b0b12f1226765c7d4f775244693da0d95726cd4f9dc7c16c67a211a372390ad0fe9dde01dbd418c502c3282c7ac8417132f86544c37b64e1526ffa8b43dead01');
581+
recoveryTx.signature[1].should.equal('1c637c8c317df0b815cdc27637e4403964d74e3b1751efda08c11acdf226c83263ef4c9f4b7731514ded6b19dc846efb58371b653bbadc8040ac18101cafedb501');
582+
recoveryTx.txID.should.equal('55d76a068b97933a98e5d02e6fecd4c2971f1d37f0bb850a919b17def906a239');
583+
recoveryTx.raw_data_hex.should.equal('0a023ffb2208c1647593403d263b40b8b2e6fce72d5a69080112650a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412340a15414d0941161d0f7e1da0c8989b1566c9d9b43e1226121541f3a3d6d514e7d43fbbf632a687acd65aafb8a50c18c0cdd6ac03709deae2fce72d');
577584
}));
578585

579586
it('should generate TRON recovery tx with unencrypted keys', co(function *() {

0 commit comments

Comments
 (0)