@@ -15,6 +15,7 @@ import { IMPORT_IN_P } from '../../resources/transactionData/importInP';
1515import { EXPORT_IN_P } from '../../resources/transactionData/exportInP' ;
1616import { IMPORT_IN_C } from '../../resources/transactionData/importInC' ;
1717import { TransactionBuilderFactory , Transaction } from '../../../src/lib' ;
18+ import { secp256k1 , Address } from '@flarenetwork/flarejs' ;
1819
1920describe ( 'Utils' , function ( ) {
2021 let utils : Utils ;
@@ -206,7 +207,8 @@ describe('Utils', function () {
206207 const signature = utils . createSignature ( network , message , privateKey ) ;
207208 const sigOnly = signature . slice ( 0 , 64 ) ;
208209
209- const isValid = utils . verifySignature ( network , message , sigOnly , publicKey ) ;
210+ const messageHash = utils . sha256 ( message ) ;
211+ const isValid = utils . verifySignature ( messageHash , sigOnly , publicKey ) ;
210212 assert . strictEqual ( isValid , true ) ;
211213 } ) ;
212214
@@ -215,7 +217,8 @@ describe('Utils', function () {
215217 const publicKey = Buffer . from ( SEED_ACCOUNT . publicKey , 'hex' ) ;
216218 const invalidSignature = Buffer . alloc ( 64 ) ;
217219
218- const isValid = utils . verifySignature ( network , message , invalidSignature , publicKey ) ;
220+ const messageHash = utils . sha256 ( message ) ;
221+ const isValid = utils . verifySignature ( messageHash , invalidSignature , publicKey ) ;
219222 assert . strictEqual ( isValid , false ) ;
220223 } ) ;
221224
@@ -480,11 +483,12 @@ describe('Utils', function () {
480483 const message = Buffer . from ( SEED_ACCOUNT . message , 'utf8' ) ;
481484 const privateKey = Buffer . from ( SEED_ACCOUNT . privateKey , 'hex' ) ;
482485
483- // Create signature using the same private key
486+ // Create signature using the same private key (createSignature hashes the message internally)
484487 const signature = utils . createSignature ( network , message , privateKey ) ;
485488
486- // Recover public key
487- const recoveredPubKey = utils . recoverySignature ( network , message , signature ) ;
489+ // Recover public key - pass the hashed message since recoverySignature expects pre-hashed
490+ const messageHash = utils . sha256 ( message ) ;
491+ const recoveredPubKey = utils . recoverySignature ( messageHash , signature ) ;
488492
489493 assert . ok ( recoveredPubKey instanceof Buffer ) ;
490494 assert . strictEqual ( recoveredPubKey . length , 33 ) ; // Should be compressed public key (33 bytes)
@@ -495,8 +499,9 @@ describe('Utils', function () {
495499 const privateKey = Buffer . from ( SEED_ACCOUNT . privateKey , 'hex' ) ;
496500 const signature = utils . createSignature ( network , message , privateKey ) ;
497501
498- const pubKey1 = utils . recoverySignature ( network , message , signature ) ;
499- const pubKey2 = utils . recoverySignature ( network , message , signature ) ;
502+ const messageHash = utils . sha256 ( message ) ;
503+ const pubKey1 = utils . recoverySignature ( messageHash , signature ) ;
504+ const pubKey2 = utils . recoverySignature ( messageHash , signature ) ;
500505
501506 assert . deepStrictEqual ( pubKey1 , pubKey2 ) ;
502507 } ) ;
@@ -510,7 +515,8 @@ describe('Utils', function () {
510515
511516 // Create signature and recover public key
512517 const signature = utils . createSignature ( network , message , privateKey ) ;
513- const recoveredPubKey = utils . recoverySignature ( network , message , signature ) ;
518+ const messageHash = utils . sha256 ( message ) ;
519+ const recoveredPubKey = utils . recoverySignature ( messageHash , signature ) ;
514520
515521 // Convert both to hex strings for comparison
516522 assert . strictEqual ( recoveredPubKey . toString ( 'hex' ) , originalPubKey . toString ( 'hex' ) ) ;
@@ -523,23 +529,67 @@ describe('Utils', function () {
523529 const originalPubKey = Buffer . from ( ecc . pointFromScalar ( privateKey , true ) as Uint8Array ) ;
524530
525531 const signature = utils . createSignature ( network , message , privateKey ) ;
526- const recoveredPubKey = utils . recoverySignature ( network , message , signature ) ;
532+ const messageHash = utils . sha256 ( message ) ;
533+ const recoveredPubKey = utils . recoverySignature ( messageHash , signature ) ;
527534
528535 assert . strictEqual ( recoveredPubKey . toString ( 'hex' ) , originalPubKey . toString ( 'hex' ) ) ;
529536 } ) ;
530537
531538 it ( 'should throw error for invalid signature length' , function ( ) {
532- const message = Buffer . from ( SEED_ACCOUNT . message , 'utf8' ) ;
539+ const messageHash = utils . sha256 ( Buffer . from ( SEED_ACCOUNT . message , 'utf8' ) ) ;
533540 const invalidSignature = Buffer . from ( INVALID_SHORT_KEYPAIR_KEY , 'hex' ) ;
534541
535- assert . throws ( ( ) => utils . recoverySignature ( network , message , invalidSignature ) , / F a i l e d t o r e c o v e r s i g n a t u r e / ) ;
542+ assert . throws ( ( ) => utils . recoverySignature ( messageHash , invalidSignature ) , / F a i l e d t o r e c o v e r s i g n a t u r e / ) ;
536543 } ) ;
537544
538545 it ( 'should throw error for signature with invalid recovery parameter' , function ( ) {
539- const message = Buffer . from ( SEED_ACCOUNT . message , 'utf8' ) ;
546+ const messageHash = utils . sha256 ( Buffer . from ( SEED_ACCOUNT . message , 'utf8' ) ) ;
540547 const signature = Buffer . alloc ( 65 ) ; // Valid length but all zeros - invalid signature
541548
542- assert . throws ( ( ) => utils . recoverySignature ( network , message , signature ) , / F a i l e d t o r e c o v e r s i g n a t u r e / ) ;
549+ assert . throws ( ( ) => utils . recoverySignature ( messageHash , signature ) , / F a i l e d t o r e c o v e r s i g n a t u r e / ) ;
550+ } ) ;
551+
552+ it ( 'should recover signature and verify sender address from signed C-chain Export tx' , async function ( ) {
553+ // Transaction from actual build response - C-chain Export tx
554+ const tx =
555+ '0x0000000000010000007278db5c30bed04c05ce209179812850bbb3fe6d46d7eef3744d814c0da55524790000000000000000000000000000000000000000000000000000000000000000000000012a96025ad506b9fbb9023fbdc1665c7f7d7c923f000000000605236658734f94af871c3d131b56131b6fb7a0291eacadd261e69dfb42a9cdf6f7fddd00000000000000000000000158734f94af871c3d131b56131b6fb7a0291eacadd261e69dfb42a9cdf6f7fddd000000070000000006052340000000000000000000000002000000037fa8c7e0c8ad9f09f9179b42b77e94a487c3df758d4ba538f772333ca7bf3668a2fe36648438c79d9b6b77b56effb860eaa430e0e30c4e392f59cd08000000010000000900000001750076e67d9720283a71c6e7a9a88ff662608fefdd3f316f1211957ca1873eee3ee4a74b468bda66176a3e5d3ab54d43a8c0be12348f251a3093c16d9db00cd001c31e9c15' ;
556+ const expectedSenderAddress = '0x2a96025ad506b9fbb9023fbdc1665c7f7d7c923f' ;
557+
558+ const factory = new TransactionBuilderFactory ( coins . get ( 'tflrp' ) ) ;
559+ const txn = ( await factory . from ( tx ) . build ( ) ) as Transaction ;
560+ const signablePayload = txn . signablePayload ;
561+ const signatures = txn . signature ;
562+ const sig = Buffer . from ( utils . removeHexPrefix ( signatures [ 0 ] ) , 'hex' ) ;
563+
564+ // Recover public key from signature (signablePayload is already SHA256 hashed)
565+ const recoveredPubKey = utils . recoverySignature ( signablePayload , sig ) ;
566+
567+ // Get the sender address from the transaction inputs
568+ const txInputs = txn . inputs ;
569+ const senderAddressFromTx = txInputs [ 0 ] . address . toLowerCase ( ) ;
570+
571+ // Verify sender address matches expected
572+ assert . strictEqual (
573+ senderAddressFromTx ,
574+ expectedSenderAddress . toLowerCase ( ) ,
575+ 'Transaction sender address does not match expected'
576+ ) ;
577+
578+ // Derive address from recovered public key
579+ const derivedEvmAddress =
580+ '0x' + Buffer . from ( new Address ( secp256k1 . publicKeyToEthAddress ( recoveredPubKey ) ) . toBytes ( ) ) . toString ( 'hex' ) ;
581+
582+ // Verify the recovered public key matches the sender
583+ assert . strictEqual (
584+ derivedEvmAddress . toLowerCase ( ) ,
585+ senderAddressFromTx ,
586+ 'Recovered public key does not match sender address'
587+ ) ;
588+
589+ // Also verify signature validity
590+ const sigOnly = sig . slice ( 0 , 64 ) ;
591+ const isValid = utils . verifySignature ( signablePayload , sigOnly , recoveredPubKey ) ;
592+ assert . strictEqual ( isValid , true , 'Signature verification failed' ) ;
543593 } ) ;
544594 } ) ;
545595
0 commit comments