Lay2 - pw-sdk - Build DApps on CKB and Run Them Everywhere

Team and Background

why are you the right person / team for this project?

Lay2 is a dev team focusing on blockchain protocols and applications. Our members all have 10+ years R&D experiences in various fields of Internet applications, and have been working in blockchain industry for 2+ years. Lay2’s technology stack in blockchain consists of layer 2 technologies such as lightning network and state channel. We believe that making practical blockchain product is hard but worthy, and have developed a framework and several dapps in payment and games.

Project and Justification

why is this a valuable addition to the Nervos Ecosystem?

Nervos is still at the early stage, and the infrastructure is still incomplete no matter for developer or users. This situation has brought Nervos a bilateral cold start problem: if there are not enough qualified developers to construct the ecosystem, it will be hard to attract users. Simultaneously, without a large number of users, it will be difficult to attract qualified developers. Furthermore, some early public blockchains have established a formidable bilateral ecosystem due to their first-mover advantage and wealth effect. This phenomenon, resulting a network effect, has further exacerbated the difficulty of bilateral cold start of Nervos ecosystem.

Fortunately, the Cell Model and the RISC-V based VM of CKB have brought unprecedented flexibility and extensibility, which makes it possible for Nervos to open a new path. There is no need to wait for facilities such as wallets, DApp browsers, to complete the long process of developing, launching, accumulating applications and attracting users. Instead, Nervos can quickly get access to a large number of users by directly adopting other blockchains’ infrastructure, which means that it could enter the application development stage directly without struggling with the bilateral cold start problem. Lay2 is providing CKB with this capability by originally designed product, pw-sdk.

Different from ordinary sdks, DApps developed with pw-sdk (called pw-dapps) can directly run in wallets of Bitcoin, Ethereum, EOS, Tron, etc. There are two meanings of “directly” here: On one hand, no specific development is required of wallet maintainers to support pw-dapps. In fact, they don’t even need to support the CKB chain, e.g. MetaMask can already run pw-dapps now. On the other hand, for blockchain users, any blockchain address is already a valid CKB address, and there is no need to create a CKB wallet before using pw-dapps. Both user experience and security of asset of pw-dapps can be consistent with native DApps on those blockchains.

As for the ecosystem, pw-sdk will bring a large number of mature crypto infrastructures and their user base to Nervos. This allows the construction of Nervos ecosystem to upgrade from a single-threaded serial mode of “infrastructure construction-> product development-> user accumulation-> market feedback" to a multi-threaded mode in which “Infrastructure construction", “product research and development", “market feedback" can advance in parallel. In the same time, it will also largely eliminated the stage of “user accumulation”.It is foreseeable that pw-sdk will greatly accelerate the development of Nervos ecosystem, and It can also show the blockchain world the unique capabilities and great value potential of CKB as a new generation of encrypted economic infrastructure.

Technical Specification and Implementation

how will you implement this successfully?

The pw-sdk includes:

  • pw-lib: cryptographic primitives library

Like Keccak-256 hash library which is used to identify the signature of ethereum format, ECDSA Secp256r1(NIST P-256) library, etc.

  • pw-lock: universal multi-chain lock script

Pw-lock is able to verify the signatures from wallets of Bitcoin, Ethereum, EOS and other blockchains, and each address (distinguish by public key) of those blockchains can be mapped to a valid CKB address.

Pw-lock will be implemented with type id. We will apply to add pw-lock into the short payload format address standard when it’s stable, therefore the mapped addresses from other blockchains will have the same format as the default CKB address.

  • pw-core: front-end sdk

A javascript sdk implemented in Typescript. It will allow developers directly run their CKB DApps in wallets of all blockchains supported by pw-lock, and interact with pw-server.

Timeline / Roadmap

is the timeline reasonable and justified?

It will take about 10 months to get things done, and part of them has been completed so far. As the project progresses, we will continue to release our achievements step by step, so that the community could experience and participate in.


Checkpoint 1: April 2020

pw-lib, including:

  • Keccak-256 support

pw-lock, including:

  • Ethereum signature support
  • EIP-712 support
  • Can be deployed with type id

pw-core, including:

  • API design

Checkpoint 2: June 2020

pw-lib, including:

  • Secp256r1 (ECC P256) support

pw-lock, including:

  • Web Authn support

pw-core, including:

  • Basic API implementation like signing, sending transactions, etc.
  • Basic documents and tutorials
  • DApp demo based on pw-core

Checkpoint 3:August 2020

  • All pw components will support BTC, EOS and more blockchains.

Checkpoint 4:November 2020

  • Full version of pw-lock
  • Full version of p-wallet
  • Full version of pw-core
  • Documents and demos

Special thanks to @stwith for helping with the translation, to @xxuejie @ash @keith for providing technical support and to all test group members for giving valuable feedbacks.

8 Likes

Thanks for submitting the proposal! What will be the upgrade policy of pw-lock, i.e. who can upgrade the pw-lock lock script?

The upgradability of type id is a doube-edged sword, and this is even more true for lock script. There is always a trade-off between upgradability and determinism, and what we can do is try to narrow the gap between the two sides.

For this question, as the pw-lock is still under heavy development, the lock script on Aggron testnet can be upgraded by the cell owner (i.e. the Lay2 team) for now. But we also have some thoughts on possible ways to reduce the risk:

  • Adopt multisig in the lock script of the type id cell, and if any vote dapps come out, we can and also involve the idea of the community.

  • Include formal analysis methods or other test cases in the type script of the type id cell, which can prevent upgrades with poor quality or unwanted behaviors.

  • Reserve a fail-safe lock (e.g. the official lock) in the code of lock script, and make sure it will preserve in any future upgrades.

The discussion on this issue is very important and should be continued. We welcome everyone to contribute ideas and suggestions.

A good UX design may help by allowing users to choose between type_hash and code_hash script reference, leave the upgradability vs. security choice to users as different users have different preferences.

That’s a good point. We can map one external address to two different ckb addresses and give detailed tips to help users make their choice. We can add this feature in the poc and expect some feedbacks.

In this post we will walk through some important technical and functional points mentioned in the abstract, and extend some of them into possible usage scenarios in the future.

pw-lib related

As we all know, customizable cryptographic primitive is such a highlight of CKB that we have heard this feature too many times in core team’s keynotes and AMAs. But why this is important? What cryptographic primitives can we deploy on CKB and how should we make use of them? Let us show you some ideas with some libraries we will introduce in pw-lib.

  • Keccak-256 Hash Algorithm

Keccak, sometimes called SHA-3, is the hashing function of Ethereum. In fact, Keccak-256 is a superset of SHA-3, and the finalized NIST SHA-3 is the standard “SHA-3”, which is different with Ethereum’s hash algorithm.

Keccak-256 provides 256-bit hashes. It is used to generate transaction hash, block hash, and even address in Ethereum. In pw-core, Keccak-256 is used to generate hash to be signed and put in witness. But the real indispensability of Keccak-256 in pw-lock, where we need to recover Ethereum address from the public key. The EIP-712 typed data signature also needs Keccak-256 as the only hash algorithm.

  • SHA-256 Hash Algorithm

SHA-256 stands for Secure Hash Algorithm - 256 bit. Although it looks a lot ‘stronger’ than the SHA-3 algorithm mentioned above, it actually belongs to SHA-2 family, and naturally weaker than SHA-3.

The most famous usage of SHA-256 is Bitcoin mining. It is also used to generate Bitcoin address, and widely used as EOS’s hash algorithm. It is necessary for pw-lock to support both Bitcoin wallets and EOS wallets.

  • Secp256r1 (ECC P-256) Asymmetric Encryption Algorithm

Different from hash algorithms which are used to map data of arbitrary size to a fixed bit string in a one-way form, asymmetric encryption algorithms are used for encrypting or digitally signing data.

ECC stands for Elliptic Curve Cryptography, both Secp256k1 and Secp256r1 belong to this algorithm family. Since Bitcoin first adopted Secp256k1 as the signature algorithm, almost all successors have followed this design, and so has CKB. However, the Secp256r1 algorithm, known as P-256, has a wider acceptance in browsers and smart devices. By adopting P-256, we can open a wider range of application scenarios for CKB.

  • More to expect

As the scenario grows, there will be more demand for cryptographic libraries on CKB, such as SHA3-256 for Tron adaptation, and RSA for interacting with secure web applications. Thanks to the RISC-V vm and the cell model of CKB, we can easily integrate those libraries continuously without causing any painful interruption to both developers and users.

pw-lock related

  • Ethereum Signature Support (including EIP-712)
/* secp256k1_keccak256_sighash_all.c */
keccak_init(&sha3_ctx);
unsigned char eth_prefix[28]= {
  0x19, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x20, 0x53,0x69,
  0x67, 0x6e, 0x65, 0x64, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61,0x67, 0x65,
  0x3a, 0x0a, 0x33, 0x32
};
keccak_update(&sha3_ctx, eth_prefix, 28);
keccak_update(&sha3_ctx, message, 32);
keccak_final(&sha3_ctx, message);
	
/* Personal hash */
ret = verify_signature(message, lock_bytes, args_bytes_seg.ptr);
if(ret == CKB_SUCCESS){
  return CKB_SUCCESS;
}

/* Typed Data hash */
ret = calculate_typed_data(message, message);
if(ret != CKB_SUCCESS){
  return ret;
}
return verify_signature(message, lock_bytes, args_bytes_seg.ptr)

This is part of the code in pw-lock, which shows the logic to verify the signature in witnesses. Among them, ‘keccak_init’ initializes context for hash calculating, ‘keccak_update’ appends messages to original string, and ‘keccak_final’ finishes the calculation. As we can see, the lock first tries to recover and verify public key from the message as a personal_sign signature. If that fails, then the signature is treated as a typed data signature. Full code: https://github.com/lay2dev/ckb-system-scripts/blob/master/c/secp256k1_keccak256_sighash_all.c

  • How to Support EOS Signature

EOS has a different account model from Ethereum. Firstly, EOS accounts are not generated from public key. They are 12 characters long and can contain the letters a-z and the digits 1–5. In fact, the key-pairs act only as password to unlock the account. Secondly, there can be several different keys associated to one account, with different permissions. For example, EOS account ‘zhixiandaren’ has two permissions (the default two):

Associated public keys for EOS account 'zhixiandaren': 
- active: EOS6P9tLLAQYJbmPzoTEG9vpS6GMHJyWW4bUeppqtAwcUuJpZNXYo
- owner : EOS6P9tLLAQYJbmPzoTEG9vpS6GMHJyWW4bUeppqtAwcUuJpZNXYo

In this case there is only one key used, but normally there should be two different keys. The owner key has the highest authority and should not be used frequently, and common operations such as transfers and votes are handled by the active key.

To support EOS signature, pw-lock should compare the recovered key with the active key, which should be passed to the script in args. To be noted, this is for common EOS accounts, other cases like contract permission (@eos.code) are not included for now.

pw-core related

  • How Can We Map Other Blockchain Address to CKB Address

Let’s first talk about what exactly CKB address stands for. As we all know, if Alice want to send crypto currency to Bob, all she needs to know is Bob’s address, much like sending him an email. But how can Bob actually use the received assets, like login to his email account and forward an email? This comes to the lock script - a script which determines whether you are the right person to unlock the assets, just like a script determines whether Bob has the right password to login to the email account ‘[email protected]’.

Our blockchain account has a pair of keys: private key and public key, and the address is often transformed from the public key (e.g. Ethereum address is the last 20 bytes of the public key). When we send a transaction, we have to create a digital signature with our private key and send it along with the message. When the blockchain verifies your access, the public key is firstly recovered from the signature, and then transformed into an address, which will be compared to the address we declared to own.

For common blockchains who have a fixed lock logic, it’s already enough to confirm ownership with signature and address. But CKB is so flexible that everyone can create his lock script with any logic in it. So besides the ‘address’ part from the public key, there is another part to identify which lock script will be used to verify the ‘address’. Plus some other parts like prefix and checksum, and some transformation, we can finally get a valid CKB address, like this:

args:  /* the 'address' transformed from public key */
b39bbc0b3673c7d36450bc14cfcdad2d559c6c64

code_hash: /* which lock script to use */
9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8

address generated:
ckb1jda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3vumhs9nvu786dj9p0q5elx66t24n3kxgj53qk

Let’s take the address apart to get a clearer view:

ckb1 - the human readable part

jda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3vumhs9nvu786dj9p0q5elx66t2 - the Base32 encoded payload part, payload format: 0x02/0x04 | code_hash | args

4n3kxgj53qks- the checksum

More detailed information about CKB address can be found here: https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md

Finally, we have understood that a CKB address contains not only information of the public key, but also the lock script intended to be used. Now we can explain why the Ethereum addresses can be mapped to CKB addresses: We created a lock script which has the same logic with the Ethereum’s lock (the code_hash part), and put the Ethereum address in the args. That’s all. Sounds easy isn’t it? Yes, but only on CKB.

  • How to Sign CKB Transactions With Wallets of Other Blockchains

Let’s take MetaMask as an example:

/* personal_sign */
const DefaultSinger = (from, message) =>
  new Promise((resolve, reject) => {
    const params = [message, from]
    const method = 'personal_sign'

    window.web3.currentProvider.sendAsync({ method, params, from },
      function(err, result) {
        err && reject(err)
        result.error && reject(result.error)
        resolve(result.result)
      })
  }
  
/* signTypedData */
const TypedDataSigner = (from, message, { inputCapacity, outputs }) =>
  new Promise((resolve, reject) => {
    const typedData = buildTypedData(inputCapacity, outputs, message)
    const params = [from, typedData]
    const method = 'eth_signTypedData_v4'

    window.web3.currentProvider.sendAsync({ method, params, from },
      function(err, result) {
        err && reject(err)
        resolve(result.result)
      })
  }

Full code: https://github.com/lay2dev/ckb.pw/blob/master/src/services/eth/core.js

Above is the common method to call signing methods in Ethereum web3 environment. The only difference between signing a CKB transaction with an Ethereum transaction is the ‘message’ part. Now we already know that pw-lock is able to recover Ethereum address from the signature with Keccak-256, so the only thing we have to alter is hashing CKB transaction with Keccak-256, and the we just pass it to the signers above. There is something different when it comes to EIP-712 signature, as we have to assemble the message in form of typed data.

2 Likes

404

应该是最近在重构的原因

1 Like

The branch has been merged into the master, I’ll change it right now, thanks!

1 Like

This grant has now been approved. For more info and the official announcement please check here!