⚒️Technical Implementation

LumiBit's EVM is primarily based on Scroll's ZK rollup solution, and its implementation is largely consistent with most public blockchains. However, there are several key differences.

Address Generation Method

The LumiBit network uses the same address generation method as Bitcoin's P2WPKH to enable interoperability between the Bitcoin and LumiBit networks. This modification allows our platform to become a true Bitcoin Layer2.

Addresses are generated in the same way as Bitcoin. You can use the bitcore-lib library or bitcoinjs for this purpose.

Using bitcore-lib for generation:

// Generate a random key pair with compression enabled
const pair = ECPair.makeRandom({ compressed: true })

// Output the private key in hexadecimal format
console.log(`Private Key: 0x${pair.privateKey?.toString('hex')}`)

// Generate and display a Testnet BTC address
console.log(`Testnet BTC Address: ${bitcore.PublicKey(pair.publicKey, { compressed: true }).toAddress("testnet", "witnesspubkeyhash").toString()}`)

// Generate and display a Live BTC address
const b = bitcore.PublicKey(pair.publicKey, { compressed: true }).toAddress("livenet", "witnesspubkeyhash").toString()

Using bitcoinjs for generation:

const bitcoin = require('bitcoinlib');
const { ECPair } = bitcoin;

// Generate a random key pair with compression
const pair = ECPair.makeRandom({ compressed: true });
const publicKey = pair.publicKey;

// Generate a P2WPKH address using the public key
const { address } = bitcoin.payments.p2wpkh({ pubkey: publicKey });

// Display the generated P2WPKH address
console.log('P2WPKH address:', address);

Transaction Signing Method

The LumiBit network adopts a native signing mode, allowing full control over BTC wallets. You can sign transactions directly using various BTC wallets without needing external solutions like AA wallets for verification. This significantly reduces transaction fees and verification speeds, while also providing the convenience of native signatures.

Details about the signature modifications:

// Sign a Lumibit-specific message asynchronously
async signLumiBitMessage(text: string): Promise<string> {
    const message = new bitcore.Message(text);
    const pKey = new bitcore.PrivateKey(this.ecPair.toWIF());
    return message.sign(pKey);
}

// Asynchronously sign a transaction
async signTransaction(tx: ethers.TransactionRequest): Promise<string> {
    const t = Transaction.from(<TransactionLike<string>>tx);
    const rawTx = _serializeLumiBit(t);
    const signed = Buffer.from(await this.signLumiBitMessage(keccak256(rawTx)), "base64").toString('hex')
    return _serializeLumiBit(t, Signature.from({
        r: `0x${signed.slice(2, 66)}`,
        s: `0x${signed.slice(66, 130)}`,
        v: BigInt(`0x${signed.slice(0, 2)}`) - 4n,
    }))
}

// Serialize the transaction with or without a signature
function _serializeLumiBit(tx: Transaction | LumiBitTransaction, sig?: Signature): string {
    const fields: any = [
        formatNumber(tx.chainId, "chainId"),
        formatNumber(tx.nonce, "nonce"),
        formatNumber(tx.gasPrice || 0, "gasPrice"),
        formatNumber(tx.gasLimit, "gasLimit"),
        (tx.to || "0x"),
        formatNumber(tx.value, "value"),
        tx.data,
        formatAccessList(tx.accessList || [])
    ];

    if (sig) {
        fields.push(toBeArray(sig.v));
        fields.push(toBeArray(sig.r));
        fields.push(toBeArray(sig.s));
    }

    return concat(["0x04", encodeRlp(fields)]);
}

API Modifications

Due to the use of Bitcoin addresses, most RPC requests in the LumiBit network will utilize the P2WPKH address format. This should be considered when addressing differences with traditional EVM RPC data.

// Process a transaction request for JSON-RPC, ensuring proper format adherence
getRpcTransaction(tx: TransactionRequest): JsonRpcTransactionRequest {
    const result: JsonRpcTransactionRequest = {};

    // Ensure numeric values are converted to "quantity" values for JSON-RPC compliance
    ["chainId", "gasLimit", "gasPrice", "type", "maxFeePerGas", "maxPriorityFeePerGas", "nonce", "value"].forEach((key) => {
        if ((<any>tx)[key] == null) { return; }
        let dstKey = key;
        if (key === "

gasLimit") { dstKey = "gas"; }
        (<any>result)[dstKey] = toQuantity(getBigInt((<any>tx)[key], `tx.${ key }`));
    });

    // Ensure addresses and data are in lowercase
    ["from", "to"].forEach((key) => {
        if ((<any>tx)[key] == null) { return; }
        (<any>result)[key] = evmAddressToBech32((<any>tx)[key], this.btcNetwork);
    });

    ["data"].forEach((key) => {
        if ((<any>tx)[key] == null) { return; }
        (<any>result)[key] = hexlify((<any>tx)[key]);
    });

    // Normalize the access list
    if (tx.accessList) {
        result["accessList"] = accessListify(tx.accessList);
    }

    return result;
}

All parameters and returned addresses adhere to the P2WPKH format.

Last updated