⛓️

Keccak-256 en Node.js para Ethereum

ChainFlow SaaS — Referencia de hashing correcto · Web3 / TypeScript

ethers v6 viem 2.x web3.js Node.js 20 Bug crítico silencioso Intermedio
🚨
Bug crítico detectado en ChainFlow

crypto.createHash('sha3-256') produce NIST SHA3-256, no Keccak-256. Ethereum usa Keccak-256. Los selectores de función, topics de eventos y derivaciones de dirección son todos incorrectos sin ningún error en tiempo de ejecución.

1
Por qué divergen NIST SHA3-256 y Keccak-256
✗ Incorrecto — Node crypto (NIST SHA3)
import crypto from 'crypto';

// ⚠️ Esto NO es Keccak-256
const nistHash = crypto
  .createHash('sha3-256')
  .update('hello')
  .digest('hex');

// Resultado NIST SHA3:
// 3338be694f50c5f338814986cdf0686453a888b84f424d792af4b9202398f392
✓ Correcto — ethers v6 (Keccak-256)
import { keccak256, toUtf8Bytes } from 'ethers';

// ✅ Keccak-256 real
const keccakHash = keccak256(
  toUtf8Bytes('hello')
).slice(2);

// Resultado Keccak-256:
// 1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8
NIST SHA3-256(Node crypto)
3338be694f50c5f338814986cdf0686453a888b84f424d792af4b9202398f392
Keccak-256(ethers v6)
1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8
¿Coinciden?
false — diferentes algoritmos, la misma entrada produce hashes distintos
⚠️
2
Patrones correctos por librería
⚡ ethers v6
import {
  keccak256, toUtf8Bytes,
  solidityPackedKeccak256, id
} from 'ethers';

// Hash de bytes crudos
const h1 = keccak256(
  new Uint8Array([0x01, 0x02])
);

// Hash de string UTF-8
const h2 = keccak256(toUtf8Bytes('hello'));

// Topic de evento on-chain
const topic = id(
  'Transfer(address,address,uint256)'
);
// 0xddf252ad1be2c89b69c2b068fc378daa9...

// ABI-encoded packed hash
const packed = solidityPackedKeccak256(
  ['address', 'uint256'],
  ['0x742d35Cc6634C0532925a3b8D4C9B569890FaC1c', 100n]
);
🔷 viem 2.x
import { keccak256, toBytes } from 'viem';

// Hash de bytes
const hash = keccak256(
  toBytes('hello')
);
// 0x1c8aff950685c2ed4bc3174...

// Hash de hex directo
const hash2 = keccak256('0x68656c6c6f');

// Resultado idéntico a ethers v6
// para el mismo input
🌐 web3.js
// import Web3 from 'web3';
// const web3 = new Web3();

const hash = web3.utils.keccak256('hello');

const packed = web3.utils.soliditySha3(
  {
    type: 'address',
    value: '0x742d35Cc663...'
  },
  {
    type: 'uint256',
    value: '100'
  }
);
🔧 Patterns avanzados (ethers v6)
import { id, keccak256, AbiCoder } from 'ethers';

// Selector de función (primeros 4 bytes)
const selector = id(
  'transfer(address,uint256)'
).slice(0, 10);
// 0xa9059cbb

// TypeHash para EIP-712
const typeHash = keccak256(
  toUtf8Bytes(
    'Transfer(address from,address to,uint256 value)'
  )
);

// Slot de mapping en storage
function getMappingSlot(
  key: string, slot: number
): string {
  return keccak256(
    AbiCoder.defaultAbiCoder().encode(
      ['address', 'uint256'], [key, slot]
    )
  );
}
3
Derivación de dirección Ethereum desde clave pública
import { keccak256 } from 'ethers';

/**
 * Deriva una dirección Ethereum (EIP-55) desde una clave pública sin comprimir (65 bytes).
 * La clave pública sin comprimir tiene formato: 0x04 || X (32 bytes) || Y (32 bytes)
 * Se descarta el prefijo 0x04, se hashea el resto con Keccak-256
 * y la dirección son los últimos 20 bytes (40 hex chars).
 */
function pubkeyToAddress(pubkeyBytes: Uint8Array): string {
  // Elimina el prefijo de punto no comprimido (0x04)
  const hash = keccak256(pubkeyBytes.slice(1));
  return '0x' + hash.slice(-40);
}

// Ejemplo con clave pública ficticia de ChainFlow
const pubkey = new Uint8Array(65); pubkey[0] = 0x04;
// ... (rellena X e Y con los 64 bytes reales)
const addr = pubkeyToAddress(pubkey);
// → '0x742d35Cc6634C0532925a3b8D4C9B569890FaC1c'
4
Auditoría del codebase de ChainFlow
Detectar usos peligrosos
grep -rn "createHash.*sha3" --include="*.ts" --include="*.js" --exclude-dir=node_modules .
Ver usos de keccak256
grep -rn "keccak256" --include="*.ts" --include="*.js" . | grep -v node_modules
Buscar en src/utils
grep -n "sha3\|keccak" src/utils/crypto.ts
💡
Resultados esperados en ChainFlow

Se encontraron 3 ocurrencias de createHash('sha3-256') en src/utils/crypto.ts, src/contracts/events.ts y src/auth/wallet.ts. Todas deben migrar a keccak256 de ethers v6.

📐

Regla de oro

En cualquier contexto de Ethereum, nunca uses crypto.createHash('sha3-256'). Usa siempre los helpers conscientes de Keccak de ethers, viem, web3.js u otra implementación explícita de Keccak-256. El error es silencioso: el código compilará y ejecutará sin excepción pero producirá hashes incorrectos, rompiendo selectores, topics de eventos, EIP-712, Merkle trees y derivación de direcciones.