Skip to content

Latest commit

 

History

History
262 lines (192 loc) · 11.6 KB

File metadata and controls

262 lines (192 loc) · 11.6 KB

Updating Lumos for Compatibility With CKB2021

Lumos has been updated for compatibility with the CKB2021 hard fork. It is highly recommended that developers make all the changes noted below immediately to prevent fragmentation in the ecosystem.

Please use Lumos v0.18.0-rc6 or higher!

Table of Contents

Impact After Updating

After updating Lumos and completing the noted updates your dapp, your users may see a new address associated with their account when they visit your site, and their previous assets may be missing. This is because their assets are likely located on the older PW-Lock using the Pre2021 addresses.

The private keys of your users remain unchanges, but multiple different addresses are now controlled by those private keys. In order to recover and migrate assets, you will need to understand how to these addresses are generated in Lumos.

CKB2021 Address Format

Lumos will read addresses in both CKB2021 (new format) and Pre2021 (old format). No changes need to be made in order to read CKB2021 addresses.

// Example of parsing a CKB2021 address to a script.

// Parse and decode an address to a script.
const script = addressToScript('ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdr2z2v7uxcmthzysxqejkanv72w2rx6uq8s7h4s');

// { code_hash:
//    '0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8',
//   hash_type: 'type',
//   args: '0xa35094cf70d8daee2240c0ccadd9b3ca72866d70' }
console.log(script);

To generating a CKB address from a script, the generateAddress() function has been previously used. This method generates a pre-2021 address and is now deprecated. However, it can continue to be used if you need to access the old address. To generate a CKB2021 address, use the new encodeToAddress() function.

// Example of generating a CKB address in different versions from a script.

// Parse and decode an address to a script.
const script = addressToScript('ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdr2z2v7uxcmthzysxqejkanv72w2rx6uq8s7h4s');

// ckt1qyq2x5y5eacd3khwyfqvpn9dmxeu5u5xd4cq656hcv (Pre-2021; deprecated)
console.log(generateAddress(script));

// ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdr2z2v7uxcmthzysxqejkanv72w2rx6uq8s7h4s (CKB2021)
console.log(encodeToAddress(script));

Omni Lock / PW-Lock

Omni Lock addresses follow a similar format to PW-Lock when being used for interoperability. They can be generated by updating the code_hash and args in a script.

The PW-Lock code_hash should be replaced with the Omni Lock code_hash, and the args will need to be modified with a 1-byte interoperability platform code prefix, and a 1-byte suffix of 0x00.

// Example of converting a PW-Lock address into an Omni Lock address.

// A PW-Lock address for ETH address 0xd69282267f92017855d262460d01248da97e7e41.
const pwLockAddress = 'ckt1qpvvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdxxqwkj2pzvlujq9u9t5nzgcxszfyd49l8usghcwxk4';

// ckt1qpvvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdxxqwkj2pzvlujq9u9t5nzgcxszfyd49l8usghcwxk4 (PW-Lock; ETH; CKB2021)
console.log(pwLockAddress);

// Decode the address to a script.
const script = addressToScript(pwLockAddress);

// { code_hash:
//    '0x58c5f491aba6d61678b7cf7edf4910b1f5e00ec0cde2f42e0abb4fd9aff25a63',
//   hash_type: 'type',
//   args: '0xd69282267f92017855d262460d01248da97e7e41' }
console.log(script);

// Update the code_hash and args.
script.code_hash = '0x79f90bb5e892d80dd213439eeab551120eb417678824f282b4ffb5f21bad2e1e'; // Omni Lock code_hash.
// script.hash_type = 'type'; // Hash type is the same.
const platformCode = '01'; // Use code 0x01 for Ethereum. Other codes: https://bit.ly/3uFxAjc
const suffix = '00'; // Always 0x00 for interoperability.
script.args = `0x${platformCode}${script.args.slice(2)}${suffix}`;

// { code_hash:
//    '0x79f90bb5e892d80dd213439eeab551120eb417678824f282b4ffb5f21bad2e1e',
//   hash_type: 'type',
//   args: '0x01d69282267f92017855d262460d01248da97e7e4100' }
console.log(script);

// Encode the script back to an address.
const omniLockAddress = encodeToAddress(script);

// ckt1qpuljza4azfdsrwjzdpea6442yfqadqhv7yzfu5zknlmtusm45hpuqgp66fgyfnljgqhs4wjvfrq6qfy3k5huljpqqf3hfdz (Omni Lock; ETH; CKB2021)
console.log(omniLockAddress);

Addresses Compatibility

Going forward it is recommended that all dapps which relied on PW-Lock to adopt the new CKB2021 address format and Omni Lock. However, existing dapps may need to generate the previous addresses to allow users to migrate properly. In order to properly support both, it is important to understand how the address format and lock may affect users.

When these parameters are used together, given a single ETH address, there are a total of four possible combinations:

  1. Omni Lock; ETH; CKB2021 (recommended)
  2. Omni Lock; ETH; Pre2021
  3. PW-Lock; ETH; CKB2021
  4. PW-Lock; ETH; Pre2021 (previous default)

There are four different addresses, but the combinations have different effects. When changing the address version, it is a change to the off-chain encoding of the human readable format. This means that the on-chain lock script does not change, and assets do not need to be migrated. For example, 1 and 2 both use Omni Lock. If you send assets to an address using combo 1, all the same assets will also be on combo 2 because they are the same lock. The same is true with 3 and 4 because they share the same underlying lock.

Generating all four addresses is possible but only two combinations are the most important. Combo 4 was previously the only one possible, and now only combo 1 is only one recommended.

// How to generate all four different address combinations.

// PW-Lock; ETH; Pre-2021
const pwLockEthPre2021 = 'ckt1q3vvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdx845jsgn8lysp0p2aycjxp5qjfrdf0elyzvfypk7';

// Decode the address to a script.
const script = addressToScript(pwLockEthPre2021);

// PW-Lock; ETH; CKB2021
const pwLockEthCkb2021 = encodeToAddress(script);

// Update the script from PW-Lock to Omni Lock.
script.code_hash = '0x79f90bb5e892d80dd213439eeab551120eb417678824f282b4ffb5f21bad2e1e';
script.args = `0x01${script.args.slice(2)}00`;

// Omni Lock; ETH; Pre-2021
const omniLockEthPre2021 = generateAddress(script);

// Omni Lock; ETH; CKB2021
const omniLockEthCkb2021 = encodeToAddress(script);

// ckt1q3vvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdx845jsgn8lysp0p2aycjxp5qjfrdf0elyzvfypk7 (PW-Lock; ETH; Pre-2021)
console.log(pwLockEthPre2021);

// ckt1qpvvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdxxqwkj2pzvlujq9u9t5nzgcxszfyd49l8usghcwxk4 (PW-Lock; ETH; CKB2021)
console.log(pwLockEthCkb2021);

// ckt1q3uljza4azfdsrwjzdpea6442yfqadqhv7yzfu5zknlmtusm45hpuqwkj2pzvlujq9u9t5nzgcxszfyd49l8usgqfrn2vh (Omni Lock; ETH; Pre-2021)
console.log(omniLockEthPre2021);

// ckt1qpuljza4azfdsrwjzdpea6442yfqadqhv7yzfu5zknlmtusm45hpuqgp66fgyfnljgqhs4wjvfrq6qfy3k5huljpqqf3hfdz (Omni Lock; ETH; CKB2021)
console.log(omniLockEthCkb2021);

WitnessArgs Changes for Omni Lock

When signing a transaction, the signature format for the witness is slightly different for Omni Lock. The signature value must be wrapped in a RcLockWitnessLock structure before being added to WitnessArgs.

// WitnessArgs Structure for PW-Lock
{
    lock: <Signature; 65-bytes>,
    input_type: <empty>,
    output_type: <empty>
}

// WitnessArgs+RcLockWitnessLock Structure for Omni-Lock
{
    lock: {
        signature: <Signature; 65-bytes>,
        rc_identity: <empty>,
        preimage: <empty>
    },
    input_type: <empty>,
    output_type: <empty>
}

Creating a signed witness is done as follows.

// https://github.com/nervosnetwork/lumos/blob/develop/examples/omni-lock-metamask/lib.ts#L157-L163

const signedWitness = new toolkit.Reader(
core.SerializeWitnessArgs({
    lock: SerializeRcLockWitnessLock({
    signature: new toolkit.Reader(signedMessage),
    }),
})
).serializeJson();

Generating a signature placeholder for the witness also requires the same structure prior to signing.

// https://github.com/nervosnetwork/lumos/blob/develop/examples/omni-lock-metamask/lib.ts#L117-L128

const SECP_SIGNATURE_PLACEHOLDER = new toolkit.Reader(
    "0x" +
    "00".repeat(
        SerializeRcLockWitnessLock({
        signature: new toolkit.Reader("0x" + "00".repeat(65)),
        }).byteLength
    )
);
const newWitnessArgs = { lock: SECP_SIGNATURE_PLACEHOLDER };
const witness = new toolkit.Reader(
    core.SerializeWitnessArgs(toolkit.normalizers.NormalizeWitnessArgs(newWitnessArgs))
).serializeJson();

CellDeps Changes for Omni Lock

When Omni Lock is used, the Omni Lock Cell Dep will be required.

// https://github.com/nervosnetwork/lumos/blob/develop/examples/omni-lock-metamask/lib.ts#L93-L112

tx = tx.update("cellDeps", (cellDeps) =>
    cellDeps.push(
        // omni lock dep
        {
            out_point: {
                tx_hash: CONFIG.SCRIPTS.OMNI_LOCK.TX_HASH,
                index: CONFIG.SCRIPTS.OMNI_LOCK.INDEX,
            },
            dep_type: CONFIG.SCRIPTS.OMNI_LOCK.DEP_TYPE,
        },
        // SECP256K1 lock is depended by omni lock
        {
            out_point: {
                tx_hash: CONFIG.SCRIPTS.SECP256K1_BLAKE160.TX_HASH,
                index: CONFIG.SCRIPTS.SECP256K1_BLAKE160.INDEX,
            },
            dep_type: CONFIG.SCRIPTS.SECP256K1_BLAKE160.DEP_TYPE,
        }
    )
);

Examples, Tools, and Further Reading

Examples

Tools

Further Reading

Where to Report Bugs and Request Assistance

If you experience problems with any of the tooling, please report your findings on the appropriate GitHub repository for the tool you are having a problem with.

If you need assistance with upgrading, please join the Nervos Discord and ask your question in the #dev-chat, #lumos, or #pw-core channels.

Sign up here to have important technical updates emailed to you directly in the future.