Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@
"ox": "^0.8.1",
"secp256k1": "5.0.1",
"uuid": "^10.0.0",
"viem": "^2.31.4"
"viem": "^2.31.4",
"zod": "^3.23.8"
},
"devDependencies": {
"@chainsafe/bls-keystore": "^3.1.0",
Expand Down
28 changes: 15 additions & 13 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions src/__test__/e2e/signing/evm-tx.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@ describe('EVM Transaction Signing - Unified Test Suite', () => {
await setupClient();
});

describe.skip('Legacy Transactions', () => {
describe('Legacy Transactions', () => {
LEGACY_VECTORS.forEach((vector, index) => {
it(`${vector.name} (${index + 1}/${LEGACY_VECTORS.length})`, async () => {
await signAndCompareTransaction(vector.tx, vector.name);
});
});
});

describe.skip('EIP-1559 Transactions (Fee Market)', () => {
describe('EIP-1559 Transactions (Fee Market)', () => {
EIP1559_TEST_VECTORS.forEach((vector, index) => {
it(`${vector.name} (${index + 1}/${EIP1559_TEST_VECTORS.length})`, async () => {
await signAndCompareTransaction(vector.tx, vector.name);
});
});
});

describe.skip('EIP-2930 Transactions (Access Lists)', () => {
describe('EIP-2930 Transactions (Access Lists)', () => {
EIP2930_TEST_VECTORS.forEach((vector, index) => {
it(`${vector.name} (${index + 1}/${EIP2930_TEST_VECTORS.length})`, async () => {
await signAndCompareTransaction(vector.tx, vector.name);
Expand All @@ -53,7 +53,7 @@ describe('EVM Transaction Signing - Unified Test Suite', () => {
});
});

describe.skip('Edge Cases & Boundary Conditions', () => {
describe('Edge Cases & Boundary Conditions', () => {
EDGE_CASE_TEST_VECTORS.forEach((vector, index) => {
it(`${vector.name} (${index + 1}/${EDGE_CASE_TEST_VECTORS.length})`, async () => {
await signAndCompareTransaction(vector.tx, vector.name);
Expand Down
2 changes: 1 addition & 1 deletion src/__test__/unit/__snapshots__/decoders.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3609,7 +3609,7 @@ exports[`decoders > sign - generic 1`] = `
"sig": {
"r": "0x640b2c690858ab8d0b9500f9ed64c9aa6b7467b77f1199b061aa96ea780aadaa",
"s": "0x48f830f9290dd1b3eaf1922e08a8c992873be1162bd6d5bef681cf911328abe5",
"v": "0x1",
"v": 1n,
},
}
`;
172 changes: 172 additions & 0 deletions src/__test__/unit/parseGenericSigningResponse.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { describe, expect, it } from 'vitest';
import { Buffer } from 'buffer';
import { parseGenericSigningResponse } from '../../genericSigning';
import { Constants } from '../../index';
import secp256k1 from 'secp256k1';
import { Hash } from 'ox';
import { RLP } from '@ethereumjs/rlp';

describe('parseGenericSigningResponse', () => {
// Helper to create a DER signature
const createDERSignature = (r: Buffer, s: Buffer): Buffer => {
const rLen = r.length;
const sLen = s.length;
const totalLen = 4 + rLen + sLen;
const sig = Buffer.alloc(totalLen + 2);

sig[0] = 0x30; // DER sequence
sig[1] = totalLen;
sig[2] = 0x02; // Integer type
sig[3] = rLen;
r.copy(sig, 4);
sig[4 + rLen] = 0x02; // Integer type
sig[4 + rLen + 1] = sLen;
s.copy(sig, 4 + rLen + 2);

// Pad to 74 bytes (standard for Lattice)
const padded = Buffer.alloc(74);
sig.copy(padded, 0);
return padded;
};

it('should handle generic KECCAK256 message (not EVM transaction)', () => {
// Simulate signing a plain text message "Test!"
const payload = Buffer.from('Test!');
const hash = Buffer.from(Hash.keccak256(payload));

// Create a fake signature
const privateKey = Buffer.from(
'0101010101010101010101010101010101010101010101010101010101010101',
'hex',
);
const sigObj = secp256k1.ecdsaSign(hash, privateKey);
const publicKey = secp256k1.publicKeyCreate(privateKey, false);

// Create DER-encoded signature response
const derSig = createDERSignature(
Buffer.from(sigObj.signature.slice(0, 32)),
Buffer.from(sigObj.signature.slice(32, 64)),
);

// Create mock response buffer
const mockResponse = Buffer.concat([
Buffer.from([0x04]), // Uncompressed pubkey prefix
publicKey.slice(1), // Remove compression prefix from secp256k1 output (64 bytes)
derSig,
]);

const req = {
curveType: Constants.SIGNING.CURVES.SECP256K1,
hashType: Constants.SIGNING.HASHES.KECCAK256,
encodingType: null, // Not EVM encoding
origPayloadBuf: payload,
};

const result = parseGenericSigningResponse(mockResponse, 0, req);

expect(result).toBeDefined();
expect(result.sig).toBeDefined();
expect(result.sig.v).toBeDefined();
expect(typeof result.sig.v).toBe('bigint');

// For non-EVM generic messages, v should be 27 or 28
expect([27n, 28n]).toContain(result.sig.v);
Comment thread
yilmazbahadir marked this conversation as resolved.
});

it('should handle EVM transaction encoding', () => {
// Simulate an unsigned legacy transaction
const unsignedTx = Buffer.from(
'e9808504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080',
'hex',
);
const hash = Buffer.from(Hash.keccak256(unsignedTx));

// Create a fake signature
const privateKey = Buffer.from(
'0101010101010101010101010101010101010101010101010101010101010101',
'hex',
);
const sigObj = secp256k1.ecdsaSign(hash, privateKey);
const publicKey = secp256k1.publicKeyCreate(privateKey, false);

// Create DER-encoded signature response
const derSig = createDERSignature(
Buffer.from(sigObj.signature.slice(0, 32)),
Buffer.from(sigObj.signature.slice(32, 64)),
);

// Create mock response buffer
const mockResponse = Buffer.concat([
Buffer.from([0x04]), // Uncompressed pubkey prefix
publicKey.slice(1), // Remove compression prefix from secp256k1 output (64 bytes)
derSig,
]);

const req = {
curveType: Constants.SIGNING.CURVES.SECP256K1,
hashType: Constants.SIGNING.HASHES.KECCAK256,
encodingType: Constants.SIGNING.ENCODINGS.EVM,
origPayloadBuf: unsignedTx,
};

const result = parseGenericSigningResponse(mockResponse, 0, req);

expect(result).toBeDefined();
expect(result.sig).toBeDefined();
expect(result.sig.v).toBeDefined();
expect(typeof result.sig.v).toBe('bigint');

// For pre-EIP155 transactions, v should be 27 or 28
const vNumber = Number(result.sig.v);
expect([27, 28]).toContain(vNumber);
});

it('should handle RLP-encoded data that looks like a transaction', () => {
// Create an RLP-encoded array with 6+ elements (looks like a transaction)
const txLikeData = [
Buffer.from([0x01]), // nonce
Buffer.from([0x02]), // gasPrice
Buffer.from([0x03]), // gasLimit
Buffer.from([0x04]), // to
Buffer.from([0x05]), // value
Buffer.from([0x06]), // data
];
const rlpEncoded = Buffer.from(RLP.encode(txLikeData));
const hash = Buffer.from(Hash.keccak256(rlpEncoded));

// Create a fake signature
const privateKey = Buffer.from(
'0101010101010101010101010101010101010101010101010101010101010101',
'hex',
);
const sigObj = secp256k1.ecdsaSign(hash, privateKey);
const publicKey = secp256k1.publicKeyCreate(privateKey, false);

// Create DER-encoded signature response
const derSig = createDERSignature(
Buffer.from(sigObj.signature.slice(0, 32)),
Buffer.from(sigObj.signature.slice(32, 64)),
);

// Create mock response buffer
const mockResponse = Buffer.concat([
Buffer.from([0x04]), // Uncompressed pubkey prefix
publicKey.slice(1), // Remove compression prefix from secp256k1 output (64 bytes)
derSig,
]);

const req = {
curveType: Constants.SIGNING.CURVES.SECP256K1,
hashType: Constants.SIGNING.HASHES.KECCAK256,
encodingType: null, // Not explicitly EVM
origPayloadBuf: rlpEncoded,
};

const result = parseGenericSigningResponse(mockResponse, 0, req);

expect(result).toBeDefined();
expect(result.sig).toBeDefined();
expect(result.sig.v).toBeDefined();
expect(typeof result.sig.v).toBe('bigint');
});
});
Loading
Loading