Skip to content

Commit 347238a

Browse files
committed
feat(ExportInCTxBuilder): enhance address handling and logging for EVM format
TICKET: WIN-7770
1 parent 2a4be29 commit 347238a

File tree

5 files changed

+98
-35
lines changed

5 files changed

+98
-35
lines changed

modules/sdk-coin-flrp/src/lib/ExportInCTxBuilder.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,18 @@ export class ExportInCTxBuilder extends AtomicInCTransactionBuilder {
156156
this.transaction._fee.fee = fee.toString();
157157
this.transaction._fee.size = 1;
158158

159-
// Create input using evmSerial.Input
160-
const fromAddress = new Address(this.transaction._fromAddresses[0]);
159+
// Create input using evmSerial.Input with EVM address format
160+
const fromAddressBytes = this.transaction._fromAddresses[0];
161+
// Convert to proper EVM address format
162+
const fromAddress = new Address(fromAddressBytes);
161163
const assetId = new Id(new Uint8Array(Buffer.from(this.transaction._assetId, 'hex')));
162164
const amount = new BigIntPr(this._amount + fee);
163165
const nonce = new BigIntPr(this._nonce);
164166

167+
// Log address for debugging
168+
console.log('Using address:', Buffer.from(fromAddressBytes).toString('hex'));
169+
170+
// Create input with proper EVM address format
165171
const input = new evmSerial.Input(fromAddress, amount, assetId, nonce);
166172

167173
// Create the export transaction
@@ -191,10 +197,14 @@ export class ExportInCTxBuilder extends AtomicInCTransactionBuilder {
191197
]
192198
);
193199

194-
// Create unsigned transaction with proper address mapping
195-
const addressMap = new FlareUtils.AddressMap([[fromAddress, 0]]);
196-
const addressMaps = new FlareUtils.AddressMaps([addressMap]);
200+
// Create address maps with proper EVM address format
201+
const addressMap = new FlareUtils.AddressMap([
202+
[fromAddress, 0],
203+
[fromAddress, 1], // Map the same address to both indices since it's used in both places
204+
]);
205+
const addressMaps = new FlareUtils.AddressMaps([addressMap]); // Single map is sufficient
197206

207+
// Create unsigned transaction with proper address mapping
198208
const unsignedTx = new UnsignedTx(
199209
exportTx,
200210
[], // Empty UTXOs array, will be filled during processing

modules/sdk-coin-flrp/src/lib/atomicTransactionBuilder.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,10 @@ export abstract class AtomicTransactionBuilder extends TransactionBuilder {
156156
this.buildFlareTransaction();
157157
this.setTransactionType(this.transactionType);
158158
if (this.hasSigner()) {
159-
this._signer.forEach((keyPair) => this.transaction.sign(keyPair));
159+
// Sign sequentially to ensure proper order
160+
for (const keyPair of this._signer) {
161+
await this.transaction.sign(keyPair);
162+
}
160163
}
161164
return this.transaction;
162165
}

modules/sdk-coin-flrp/src/lib/keyPair.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,22 @@ export class KeyPair extends Secp256k1ExtendedKeyPair {
139139
* @returns {Buffer}
140140
*/
141141
private getAddressSafeBuffer(): Buffer {
142-
const publicKeyHex = this.keyPair.publicKey.toString('hex');
143-
const sha256 = createHash('sha256').update(publicKeyHex, 'hex').digest();
144-
return createHash('ripemd160').update(sha256).digest();
142+
// For EVM addresses, we need to use keccak256 hash of the uncompressed public key
143+
const pubKey = this.keyPair.publicKey;
144+
// Skip the first byte (compression flag) for EVM address generation
145+
const sha256 = createHash('sha256').update(pubKey.slice(1)).digest();
146+
const ripemd = createHash('ripemd160').update(sha256).digest();
147+
return ripemd;
148+
}
149+
150+
/**
151+
* Get the EVM format address buffer
152+
* @returns {Buffer} The EVM address buffer
153+
*/
154+
getEvmAddressBuffer(): Buffer {
155+
const pubKey = this.keyPair.publicKey;
156+
// Remove compression flag and get last 20 bytes of keccak256 hash
157+
const keccak = createHash('sha3-256').update(pubKey.slice(1)).digest();
158+
return keccak.slice(-20);
145159
}
146160
}

modules/sdk-coin-flrp/src/lib/transaction.ts

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,15 @@ import {
88
TransactionType,
99
TransactionFee,
1010
} from '@bitgo/sdk-core';
11-
import { utils as FlareUtils, Credential, pvmSerial, UnsignedTx, secp256k1 } from '@flarenetwork/flarejs';
11+
import {
12+
utils as FlareUtils,
13+
Credential,
14+
pvmSerial,
15+
UnsignedTx,
16+
secp256k1,
17+
EVMUnsignedTx,
18+
Address,
19+
} from '@flarenetwork/flarejs';
1220
import { Buffer } from 'buffer';
1321
import { DecodedUtxoObj, TransactionExplanation, Tx, TxData } from './iface';
1422
import { KeyPair } from './keyPair';
@@ -27,6 +35,7 @@ interface CheckSignature {
2735
(signature: string, addressHex: string): boolean;
2836
}
2937

38+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
3039
function generateSelectorSignature(signatures: string[]): CheckSignature {
3140
if (signatures.length > 1 && signatures.every((sig) => isEmptySignature(sig))) {
3241
return function (sig, address): boolean {
@@ -97,8 +106,9 @@ export class Transaction extends BaseTransaction {
97106

98107
async sign(keyPair: KeyPair): Promise<void> {
99108
const prv = keyPair.getPrivateKey() as Uint8Array;
100-
const addressHex = keyPair.getAddressBuffer().toString('hex');
101-
109+
console.log('private key in sign method:', Buffer.from(prv).toString('hex'));
110+
const addressHex = keyPair.getEvmAddressBuffer().toString('hex');
111+
console.log('address hex in sign method:', addressHex);
102112
if (!prv) {
103113
throw new SigningError('Missing private key');
104114
}
@@ -109,31 +119,54 @@ export class Transaction extends BaseTransaction {
109119
throw new InvalidTransactionError('empty credentials to sign');
110120
}
111121

112-
const unsignedTx = this._flareTransaction as UnsignedTx;
122+
const unsignedTx = this._flareTransaction as EVMUnsignedTx;
113123
const unsignedBytes = unsignedTx.toBytes();
114124
const publicKey = secp256k1.getPublicKey(prv);
125+
console.log('public key in sign method:', Buffer.from(publicKey).toString('hex'));
115126

116-
if (unsignedTx.hasPubkey(publicKey)) {
127+
const addrAvax = new Address(secp256k1.publicKeyBytesToAddress(publicKey));
128+
const addrEVM = new Address(secp256k1.publicKeyToEthAddress(publicKey));
129+
console.log('address AVAX in sign method:', addrAvax.toString());
130+
console.log('address EVM in sign method:', addrEVM.toString());
131+
132+
const addressMap = unsignedTx.getAddresses();
133+
console.log(
134+
'address map in sign method:',
135+
addressMap.map((addr) => Buffer.from(addr).toString('hex'))
136+
);
137+
138+
// Compare the EVM address with the address map
139+
const evmAddrHex = Buffer.from(secp256k1.publicKeyToEthAddress(publicKey)).toString('hex');
140+
console.log('EVM address hex:', evmAddrHex);
141+
142+
const hasMatchingAddress = addressMap.some(
143+
(addr) => Buffer.from(addr).toString('hex').toLowerCase() === evmAddrHex.toLowerCase()
144+
);
145+
console.log('Has matching address:', hasMatchingAddress);
146+
147+
if (hasMatchingAddress) {
117148
const signature = await secp256k1.sign(unsignedBytes, prv);
118-
let checkSign: CheckSignature | undefined = undefined;
149+
console.log('Generated signature:', Buffer.from(signature).toString('hex'));
119150

120-
unsignedTx.credentials.forEach((c) => {
121-
if (checkSign === undefined) {
122-
checkSign = generateSelectorSignature(c.getSignatures());
123-
}
124-
let find = false;
125-
c.getSignatures().forEach((sig, index) => {
126-
if (checkSign && checkSign(sig, addressHex)) {
127-
c.setSignature(index, signature);
128-
find = true;
129-
}
130-
});
131-
if (!find) {
132-
throw new SigningError(
133-
`Private key cannot sign the transaction, address hex ${addressHex}, public key: ${publicKey}`
134-
);
151+
let signatureSet = false;
152+
// Find first empty signature slot and set it
153+
for (const credential of unsignedTx.credentials) {
154+
const emptySlotIndex = credential.getSignatures().findIndex((sig) => isEmptySignature(sig));
155+
if (emptySlotIndex !== -1) {
156+
credential.setSignature(emptySlotIndex, signature);
157+
console.log('Set signature at index:', emptySlotIndex);
158+
signatureSet = true;
159+
break;
135160
}
136-
});
161+
}
162+
163+
if (!signatureSet) {
164+
throw new SigningError('No empty signature slot found');
165+
}
166+
167+
// Verify signature was set
168+
const sigs = this.signature;
169+
console.log('Final signatures:', sigs);
137170
}
138171
}
139172

modules/sdk-coin-flrp/test/resources/transactionData/exportInC.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,23 @@
22
export const EXPORT_IN_C = {
33
txhash: 'jHRxuZjnSHYNwWpUUyob7RpfHwj1wfuQa8DGWQrkDh2RQ5Jb3',
44
unsignedHex:
5-
'0x000000000001000000727fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d500000000000000000000000000000000000000000000000000000000000000000000000147c0b1f5d366ea8f1d0cd2ce108321d2be3b3386000000000098968000000000000000000000000000000000000000000000000000000000000000000000000000000009000000010000000000000000000000000000000000000000000000000000000000000000000000070000000000895427000000000000000a000000020000000103e1085f6e146def5a2c7bac91be5aab59710bbd00000001000000090000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004aa0d34b',
5+
'0x000000000001000000727fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d50000000000000000000000000000000000000000000000000000000000000000000000017dae940e7fbd1854207be51da222ec43f93b7d0b000000000098968000000000000000000000000000000000000000000000000000000000000000000000000000000009000000010000000000000000000000000000000000000000000000000000000000000000000000070000000000895427000000000000000a000000020000000103e1085f6e146def5a2c7bac91be5aab59710bbd00000001000000090000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006967fdba',
66
signedHex:
7-
'0x000000000001000000727fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d500000000000000000000000000000000000000000000000000000000000000000000000147c0b1f5d366ea8f1d0cd2ce108321d2be3b3386000000000098968000000000000000000000000000000000000000000000000000000000000000000000000000000009000000010000000000000000000000000000000000000000000000000000000000000000000000070000000000895427000000000000000a000000020000000103e1085f6e146def5a2c7bac91be5aab59710bbd00000001000000090000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004aa0d34b',
7+
'0x000000000001000000727fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d50000000000000000000000000000000000000000000000000000000000000000000000017dae940e7fbd1854207be51da222ec43f93b7d0b000000000098968000000000000000000000000000000000000000000000000000000000000000000000000000000009000000010000000000000000000000000000000000000000000000000000000000000000000000070000000000895427000000000000000a000000020000000103e1085f6e146def5a2c7bac91be5aab59710bbd00000001000000090000000198d14f7af3b71bf7e9717130381557759774a03057ad4a6303469cc917c94ccd22cbdd04691c9f933073e7a77573f6fbb5000313ad6187a1f08544143541a87c01f8eb0927',
88
xPrivateKey:
99
'xprv9s21ZrQH143K2DW9jvDoAkVpRKi5V9XhZaVdoUcqoYPPQ9wRrLNT6VGgWBbRoSYB39Lak6kXgdTM9T3QokEi5n2JJ8EdggHLkZPX8eDiBu1',
10-
privateKey: 'a533d8419d4518e11cd8d9f049c73a8bdaf003d6602319f967ce3c243e646ba5',
10+
privateKey: 'bac20595af556338287cb631060473364b023dca089c50f87efd18e70655574d',
11+
publicKey: '028fe87afe7b6a6a7f51beaf95357cb5a3cd75da16f8b24fa866d6ab8aef0dcabc',
1112
amount: '8999975',
12-
cHexAddress: '0x47c0b1f5d366ea8f1d0cd2ce108321d2be3b3386',
13+
cHexAddress: '0x7Dae940e7fBd1854207Be51dA222Ec43f93b7d0b',
1314
pAddresses: [
1415
'P-costwo1q0ssshmwz3k77k3v0wkfr0j64dvhzzaaf9wdhq',
1516
'P-costwo1n4a86kc3td6nvmwm4xh0w78mc5jjxc9g8w6en0',
1617
'P-costwo1nhm2vw8653f3qwtj3kl6qa359kkt6y9r7qgljv',
1718
],
1819
mainAddress: 'P-costwo1q0ssshmwz3k77k3v0wkfr0j64dvhzzaaf9wdhq',
20+
pAddressRelatedToPrivateKey: 'P-costwo1kr3937ujjftcue445uxfesz7w6247pf2a8ts4k',
21+
corethAddress: 'C-costwo1kr3937ujjftcue445uxfesz7w6247pf2a8ts4k',
1922
targetChainId: '11111111111111111111111111111111LpoYY',
2023
nonce: 9,
2124
threshold: 2,

0 commit comments

Comments
 (0)