-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(indexer): update bitcoin address derivation in multichain indexer (
#610)
- Loading branch information
1 parent
6e944af
commit b528a25
Showing
10 changed files
with
311 additions
and
82 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
apps/backend/migrations/20241106142510_multichain_recreate.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { upSQL } from 'knex-migrate-sql-file'; | ||
|
||
export async function up(knex) { | ||
await upSQL(knex); | ||
} | ||
|
||
export function down() {} |
51 changes: 51 additions & 0 deletions
51
apps/backend/migrations/20241106142510_multichain_recreate.up.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
DROP TABLE multichain_accounts; | ||
|
||
DROP TABLE multichain_transactions; | ||
|
||
CREATE TABLE multichain_accounts ( | ||
account_id TEXT NOT NULL, | ||
chain TEXT NOT NULL, | ||
path TEXT NOT NULL, | ||
derived_address TEXT NOT NULL, | ||
public_key TEXT NOT NULL, | ||
block_height NUMERIC(20, 0) NOT NULL, | ||
block_timestamp BIGINT NOT NULL, | ||
PRIMARY KEY (account_id, chain, path) | ||
); | ||
|
||
CREATE TABLE multichain_transactions ( | ||
id BIGINT GENERATED ALWAYS AS IDENTITY, | ||
receipt_id TEXT NOT NULL, | ||
account_id TEXT NOT NULL, | ||
derived_address TEXT NOT NULL, | ||
public_key TEXT NOT NULL, | ||
chain TEXT NOT NULL, | ||
path TEXT NOT NULL, | ||
derived_transaction TEXT, | ||
block_height NUMERIC(20, 0) NOT NULL, | ||
block_timestamp BIGINT NOT NULL, | ||
PRIMARY KEY (id), | ||
UNIQUE (receipt_id) | ||
); | ||
|
||
CREATE INDEX ma_account_id_idx ON multichain_accounts USING btree (account_id); | ||
|
||
CREATE INDEX ma_derived_address_idx ON multichain_accounts USING btree (derived_address); | ||
|
||
CREATE INDEX ma_block_height_idx ON multichain_accounts USING btree (block_height DESC); | ||
|
||
CREATE INDEX ma_block_timestamp_idx ON multichain_accounts USING btree (block_timestamp DESC); | ||
|
||
CREATE INDEX mt_receipt_id_idx ON multichain_transactions USING btree (receipt_id); | ||
|
||
CREATE INDEX mt_account_id_idx ON multichain_transactions USING btree (account_id); | ||
|
||
CREATE INDEX mt_derived_address_idx ON multichain_transactions USING btree (derived_address); | ||
|
||
CREATE INDEX mt_derived_transaction_idx ON multichain_transactions USING btree (derived_transaction) | ||
WHERE | ||
derived_transaction IS NOT NULL; | ||
|
||
CREATE INDEX mt_block_height_idx ON multichain_transactions USING btree (block_height DESC); | ||
|
||
CREATE INDEX mt_block_timestamp_idx ON multichain_transactions USING btree (block_timestamp DESC); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,59 +1,34 @@ | ||
import crypto from 'node:crypto'; | ||
|
||
import bs58check from 'bs58check'; | ||
import hash from 'hash.js'; | ||
import * as bitcoin from 'bitcoinjs-lib'; | ||
|
||
import { Network } from 'nb-types'; | ||
|
||
import config from '#config'; | ||
import { | ||
deriveChildPublicKey, | ||
najPublicKeyStrToUncompressedHexPoint, | ||
} from '#libs/kdf'; | ||
import { generateCompressedPublicKey } from '#libs/kdf'; | ||
|
||
const network = config.network === Network.MAINNET ? 'bitcoin' : 'testnet'; | ||
const network = | ||
config.network === Network.MAINNET | ||
? bitcoin.networks.bitcoin | ||
: bitcoin.networks.testnet; | ||
|
||
const deriveAddress = async (accountId: string, derivation_path: string) => { | ||
const publicKey = await deriveChildPublicKey( | ||
najPublicKeyStrToUncompressedHexPoint(), | ||
const deriveAddress = async (accountId: string, derivationPath: string) => { | ||
const derivedKey = await generateCompressedPublicKey( | ||
accountId, | ||
derivation_path, | ||
); | ||
const address = await uncompressedHexPointToBtcAddress(publicKey, network); | ||
|
||
return { address, publicKey }; | ||
}; | ||
|
||
const uncompressedHexPointToBtcAddress = async ( | ||
publicKeyHex: string, | ||
network: string, | ||
) => { | ||
// Step 1: SHA-256 hashing of the public key | ||
const publicKeyBytes = Uint8Array.from(Buffer.from(publicKeyHex, 'hex')); | ||
|
||
const sha256HashOutput = await crypto.subtle.digest( | ||
'SHA-256', | ||
publicKeyBytes, | ||
derivationPath, | ||
); | ||
|
||
// Step 2: RIPEMD-160 hashing on the result of SHA-256 | ||
const ripemd160 = hash | ||
.ripemd160() | ||
.update(Buffer.from(sha256HashOutput)) | ||
.digest(); | ||
const publicKeyBuffer = Buffer.from(derivedKey, 'hex'); | ||
|
||
// Step 3: Adding network byte (0x00 for Bitcoin Mainnet) | ||
const network_byte = network === 'bitcoin' ? 0x00 : 0x6f; | ||
const networkByte = Buffer.from([network_byte]); | ||
const networkByteAndRipemd160 = Buffer.concat([ | ||
networkByte, | ||
Buffer.from(ripemd160), | ||
]); | ||
// Use P2WPKH (Bech32) address type | ||
const { address } = bitcoin.payments.p2wpkh({ | ||
network, | ||
pubkey: publicKeyBuffer, | ||
}); | ||
|
||
// Step 4: Base58Check encoding | ||
const address = bs58check.encode(networkByteAndRipemd160); | ||
if (!address) { | ||
throw new Error('Failed to generate Bitcoin address'); | ||
} | ||
|
||
return address; | ||
return { address, publicKey: derivedKey }; | ||
}; | ||
|
||
export default deriveAddress; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { ripemd160, Secp256k1, sha256 } from '@cosmjs/crypto'; | ||
import { fromHex } from '@cosmjs/encoding'; | ||
import { bech32 } from 'bech32'; | ||
|
||
import { generateCompressedPublicKey } from './kdf.js'; | ||
|
||
// implementation is pending becuase prefix is required for cosmos | ||
const deriveAddress = async ( | ||
accountId: string, | ||
derivationPath: string, | ||
prefix: string, | ||
) => { | ||
const derivedKeyHex = await generateCompressedPublicKey( | ||
accountId, | ||
derivationPath, | ||
); | ||
|
||
const publicKey = fromHex(derivedKeyHex); | ||
const address = pubkeyToAddress(publicKey, prefix); | ||
|
||
return { address, publicKey: derivedKeyHex }; | ||
}; | ||
|
||
const pubkeyToAddress = (pubkey: Uint8Array, prefix: string) => { | ||
const pubkeyRaw = | ||
pubkey.length === 33 ? pubkey : Secp256k1.compressPubkey(pubkey); | ||
const sha256Hash = sha256(pubkeyRaw); | ||
const ripemd160Hash = ripemd160(sha256Hash); | ||
|
||
return bech32.encode(prefix, bech32.toWords(ripemd160Hash)); | ||
}; | ||
|
||
export default deriveAddress; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.