Skip to content
This repository has been archived by the owner on Nov 18, 2022. It is now read-only.

Commit

Permalink
feat: add ETH Address Registry layer2 contract (#100)
Browse files Browse the repository at this point in the history
* Add g_is_using_native_eth_address to distinguish L2TX

new = eth_polyjuice_args_header[7]
    = {'E', 'T', 'H', 'P', 'O', 'L', 'Y'};

* Rename address_to_account_id to short_script_hash_to_account_id

* Add `debug-mode` into Makefile

* feat: add ETH Address Registry layer2 contract

This contract introduces two-ways mappings between `eth_address` and
`gw_script_hash`.
   - As the rightmost 160 bits of a Keccak hash of an ECDSA public key,
     `eth_address` represents an EOA or contract address on Ethereum.
   - Godwoken account script hash(a.k.a. `gw_script_hash`) is a key used for
     locating the account lock. Note that Godwoken enforces one-to-one mapping
     between layer 2 lock script and account ID.
  • Loading branch information
Flouse committed Nov 26, 2021
1 parent 371bda7 commit 26ada29
Show file tree
Hide file tree
Showing 11 changed files with 548 additions and 49 deletions.
50 changes: 41 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ LD := $(TARGET)-gcc
OBJCOPY := $(TARGET)-objcopy

SECP_DIR := deps/secp256k1-fix
SECP256K1_SRC := $(SECP_DIR)/src/ecmult_static_pre_context.h
CFLAGS_CKB_STD = -Ideps/ckb-c-stdlib -Ideps/ckb-c-stdlib/molecule
# CFLAGS_CBMT := -isystem deps/merkle-tree
CFLAGS_SECP := -isystem $(SECP_DIR)/src -isystem $(SECP_DIR)
CFLAGS_INTX := -Ideps/intx/lib/intx -Ideps/intx/include
CFLAGS_BN128 := -Ideps/bn128/include
Expand All @@ -19,11 +19,20 @@ CFLAGS_MBEDTLS := -Ideps/mbedtls/include
CFLAGS_EVMONE := -Ideps/evmone/lib/evmone -Ideps/evmone/include -Ideps/evmone/evmc/include
CFLAGS_SMT := -Ideps/godwoken-scripts/c/deps/sparse-merkle-tree/c
CFLAGS_GODWOKEN := -Ideps/godwoken-scripts/c
CFLAGS := -O3 -Ic/ripemd160 $(CFLAGS_CKB_STD) $(CFLAGS_EVMONE) $(CFLAGS_INTX) $(CFLAGS_BN128) $(CFLAGS_ETHASH) $(CFLAGS_CRYPTO_ALGORITHMS) $(CFLAGS_MBEDTLS) $(CFLAGS_SMT) $(CFLAGS_GODWOKEN) $(CFLAGS_SECP) -Wall -g -fdata-sections -ffunction-sections
CFLAGS := -O3 -Ic/ripemd160 $(CFLAGS_CKB_STD) $(CFLAGS_EVMONE) $(CFLAGS_INTX) $(CFLAGS_BN128) $(CFLAGS_ETHASH) $(CFLAGS_CRYPTO_ALGORITHMS) $(CFLAGS_MBEDTLS) $(CFLAGS_SMT) $(CFLAGS_GODWOKEN) $(CFLAGS_SECP)
CXXFLAGS := $(CFLAGS) -std=c++1z
LDFLAGS := -Wl,--gc-sections

SECP256K1_SRC := $(SECP_DIR)/src/ecmult_static_pre_context.h
# -Wl,<args> Pass the comma separated arguments in args to the linker(GNU linker)
# --gc-sections
# This will perform a garbage collection of code and data never referenced.
# together with -ffunction-sections and -fdata-sections
# -static
# On systems that support dynamic linking, this pre-
# vents linking with the shared libraries. On other
# systems, this option has no effect.
LDFLAGS := -Wl,-static -Wl,--gc-sections -fdata-sections -ffunction-sections -Wall

GENERATOR_FLAGS := -DGW_GENERATOR
VALIDATOR_FLAGS := -DGW_VALIDATOR

MOLC := moleculec
MOLC_VERSION := $(shell cat deps/godwoken-scripts/c/Makefile | egrep "MOLC_VERSION :=" | awk '{print $$3}')
Expand All @@ -41,19 +50,29 @@ VALIDATOR_DEPS := c/validator/secp256k1_helper.h $(BIN_DEPS)
# BUILDER_DOCKER := nervos/ckb-riscv-gnu-toolchain@sha256:aae8a3f79705f67d505d1f1d5ddc694a4fd537ed1c7e9622420a470d59ba2ec3
# docker pull nervos/ckb-riscv-gnu-toolchain:bionic-20190702
BUILDER_DOCKER := nervos/ckb-riscv-gnu-toolchain@sha256:7b168b4b109a0f741078a71b7c4dddaf1d283a5244608f7851f5714fbad273ba
# BUILDER_DOCKER := nervos/ckb-riscv-gnu-toolchain:bionic-20190702-newlib-debug-symbols

all: build/test_contracts build/test_rlp build/generator build/validator build/generator_log build/validator_log build/test_ripemd160 build/blockchain.h build/godwoken.h
all: build/test_contracts build/test_rlp build/generator build/validator build/generator_log build/validator_log build/test_ripemd160 build/blockchain.h build/godwoken.h build/eth-addr-reg-generator build/eth-addr-reg-validator

all-via-docker: generate-protocol
mkdir -p build
docker run --rm -v `pwd`:/code ${BUILDER_DOCKER} bash -c "cd /code && make"
docker run --rm -v `pwd`:/code -w /code ${BUILDER_DOCKER} make
log-version-via-docker: generate-protocol
mkdir -p build
docker run --rm -v `pwd`:/code ${BUILDER_DOCKER} bash -c "cd /code && make build/generator_log && make build/validator_log"
docker run --rm -v `pwd`:/code -w /code ${BUILDER_DOCKER} bash -c "make build/generator_log && make build/validator_log"

# Be aware that a given prerequisite will only be built once per invocation of make, at most.
all-in-debug-mode: LDFLAGS := -g
all-in-debug-mode: $(ALL_OBJS) build/generator_log build/validator_log

all-via-docker-in-debug-mode: generate-protocol
docker run --rm -v `pwd`:/code -w /code ${BUILDER_DOCKER} make all-in-debug-mode
debug-all: CFLAGS += -DCKB_C_STDLIB_PRINTF -O0
debug-all: all

clean-via-docker:
mkdir -p build
docker run --rm -v `pwd`:/code ${BUILDER_DOCKER} bash -c "cd /code && make clean"
docker run --rm -v `pwd`:/code -w /code ${BUILDER_DOCKER} make clean

dist: clean-via-docker all-via-docker

Expand All @@ -74,17 +93,29 @@ build/validator: c/validator.c $(VALIDATOR_DEPS)
build/generator_log: c/generator.c $(GENERATOR_DEPS)
cd $(SECP_DIR) && (git apply workaround-fix-g++-linking.patch || true) && cd - # apply patch
$(CXX) $(CFLAGS) $(LDFLAGS) -Ibuild -o $@ c/generator.c $(ALL_OBJS)
# If we need the whole one for performance analysis, don't separate the executable here
$(OBJCOPY) --only-keep-debug $@ [email protected]
$(OBJCOPY) --strip-debug --strip-all $@
cd $(SECP_DIR) && (git apply -R workaround-fix-g++-linking.patch || true) && cd - # revert patch

build/validator_log: c/validator.c $(VALIDATOR_DEPS)
cd $(SECP_DIR) && (git apply workaround-fix-g++-linking.patch || true) && cd - # apply patch
$(CXX) $(CFLAGS) $(LDFLAGS) -Ibuild -o $@ c/validator.c $(ALL_OBJS)
# If we need the whole one for performance analysis, don't separate the executable here
$(OBJCOPY) --only-keep-debug $@ [email protected]
$(OBJCOPY) --strip-debug --strip-all $@
cd $(SECP_DIR) && (git apply -R workaround-fix-g++-linking.patch || true) && cd - # revert patch

build/eth-addr-reg-generator: c/eth_addr_reg.c
$(CC) $(CFLAGS) $(GENERATOR_FLAGS) $(LDFLAGS) -Ibuild -o $@ $<
$(OBJCOPY) --only-keep-debug $@ $@.debug
$(OBJCOPY) --strip-debug --strip-all $@

build/eth-addr-reg-validator: c/eth_addr_reg.c
$(CC) $(CFLAGS) $(VALIDATOR_FLAGS) $(LDFLAGS) -Ibuild -o $@ $<
$(OBJCOPY) --only-keep-debug $@ $@.debug
$(OBJCOPY) --strip-debug --strip-all $@

build/test_contracts: c/tests/test_contracts.c $(VALIDATOR_DEPS)
cd $(SECP_DIR) && (git apply workaround-fix-g++-linking.patch || true) && cd - # apply patch
$(CXX) $(CFLAGS) $(LDFLAGS) -Ibuild -o $@ c/tests/test_contracts.c $(ALL_OBJS)
Expand Down Expand Up @@ -173,6 +204,7 @@ build/blockchain.h: build/blockchain.mol
${MOLC} --language c --schema-file $< > $@

build/godwoken.h: build/godwoken.mol
cat c/polyjuice.mol >> build/godwoken.mol
${MOLC} --language c --schema-file $< > $@

contract/sudt-erc20-proxy:
Expand Down
95 changes: 95 additions & 0 deletions c/eth_addr_reg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* `ETH Address Registry` layer2 contract
*
* This contract introduces two-ways mappings between `eth_address` and
* `gw_script_hash`.
* - As the rightmost 160 bits of a Keccak hash of an ECDSA public key,
* `eth_address` represents an EOA or contract address on Ethereum.
* - Godwoken account script hash(a.k.a. `gw_script_hash`) is a key used for
* locating the account lock. Note that Godwoken enforces one-to-one mapping
* between layer 2 lock script and account ID.
*
* There are 2 kinds of accounts in Godwoken:
* 1) Typical user accounts denoted by an account lock
* 2) Contract accounts denoted by a backend script
*/

#include "gw_syscalls.h"
#include "polyjuice_utils.h"

#ifdef NO_DEBUG_LOG
int printf(const char *format, ...) { return 0; }
#else
int printf(const char *format, ...) {
ckb_debug(format);
return 0;
}
#endif

/* MSG_TYPE */
#define MSG_QUERY_GW_BY_ETH 0
#define MSG_QUERY_ETH_BY_GW 1

int main() {
#ifndef NO_DEBUG_LOG
// init buffer for debug_print
char buffer[DEBUG_BUFFER_SIZE];
g_debug_buffer = buffer;
#endif
ckb_debug("====== ETH Address Registry ======");

/* initialize context */
gw_context_t ctx = {0};
int ret = gw_context_init(&ctx);
if (ret != 0) {
return ret;
};

/* verify and parse args */
mol_seg_t args_seg;
args_seg.ptr = ctx.transaction_context.args;
args_seg.size = ctx.transaction_context.args_len;
if (MolReader_ETHAddrRegArgs_verify(&args_seg, false) != MOL_OK) {
return GW_FATAL_INVALID_DATA;
}
mol_union_t msg = MolReader_ETHAddrRegArgs_unpack(&args_seg);

/* handle message */
if (msg.item_id == MSG_QUERY_GW_BY_ETH) {
mol_seg_t eth_address_seg = MolReader_EthToGw_get_eth_address(&msg.seg);

uint8_t script_hash[GW_VALUE_BYTES] = {0};

ret = load_script_hash_by_eth_address(&ctx,
eth_address_seg.ptr,
script_hash);
if (ret != 0) {
return ret;
}

ret = ctx.sys_set_program_return_data(&ctx, script_hash, GW_VALUE_BYTES);
if (ret != 0) {
return ret;
}
}
else if (msg.item_id == MSG_QUERY_ETH_BY_GW) {
mol_seg_t script_hash_seg = MolReader_GwToEth_get_gw_script_hash(&msg.seg);

uint8_t eth_address[ETH_ADDRESS_LEN] = {0};
ret = load_eth_address_by_script_hash(&ctx,
script_hash_seg.ptr,
eth_address);
if (ret != 0) {
return ret;
}
ret = ctx.sys_set_program_return_data(&ctx, eth_address, ETH_ADDRESS_LEN);
if (ret != 0) {
return ret;
}
}
else {
return GW_FATAL_UNKNOWN_ARGS;
}

return gw_finalize(&ctx);
}
20 changes: 15 additions & 5 deletions c/polyjuice.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,18 @@ int parse_args(struct evmc_message* msg, uint128_t* gas_price,
uint8_t* args = tx_ctx->args;

/* args[0..8] magic header + call kind */
static const uint8_t polyjuice_args_header[7] = {0xff, 0xff, 0xff, 'P', 'O', 'L', 'Y'};
if (memcmp(polyjuice_args_header, args, 7) != 0) {
static const uint8_t eth_polyjuice_args_header[7]
= {'E', 'T', 'H', 'P', 'O', 'L', 'Y'};
static const uint8_t polyjuice_args_header[7]
= {0xff, 0xff, 0xff, 'P', 'O', 'L', 'Y'};
if (memcmp(eth_polyjuice_args_header, args, 7) == 0) {
// Only access eth_address in this kind of L2TX
g_is_using_native_eth_address = true;
} else if (memcmp(polyjuice_args_header, args, 7) == 0) {
// TODO: deprecate this kind of L2TX
ckb_debug("[WARN] This kind of L2TX will be deprecated.");
g_is_using_native_eth_address = false;
} else {
debug_print_data("invalid polyjuice args header", args, 7);
return -1;
}
Expand Down Expand Up @@ -407,7 +417,7 @@ size_t get_code_size(struct evmc_host_context* context,
ckb_debug("BEGIN get_code_size");
int ret;
uint32_t account_id = 0;
ret = address_to_account_id(context->gw_ctx, address->bytes, &account_id);
ret = short_script_hash_to_account_id(context->gw_ctx, address->bytes, &account_id);
if (ret != 0) {
ckb_debug("get contract account id failed");
return 0;
Expand All @@ -430,7 +440,7 @@ evmc_bytes32 get_code_hash(struct evmc_host_context* context,
evmc_bytes32 hash{0};
int ret;
uint32_t account_id = 0;
ret = address_to_account_id(context->gw_ctx, address->bytes, &account_id);
ret = short_script_hash_to_account_id(context->gw_ctx, address->bytes, &account_id);
if (ret != 0) {
ckb_debug("get contract account id failed");
context->error_code = ret;
Expand Down Expand Up @@ -459,7 +469,7 @@ size_t copy_code(struct evmc_host_context* context, const evmc_address* address,
ckb_debug("BEGIN copy_code");
int ret;
uint32_t account_id = 0;
ret = address_to_account_id(context->gw_ctx, address->bytes, &account_id);
ret = short_script_hash_to_account_id(context->gw_ctx, address->bytes, &account_id);
if (ret != 0) {
ckb_debug("get contract account id failed");
context->error_code = ret;
Expand Down
19 changes: 19 additions & 0 deletions c/polyjuice.mol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// --- ETH Address Registry ---
array Byte20 [byte; 20];
// option ETHAddrOpt (Byte20);

union ETHAddrRegArgs {
EthToGw,
GwToEth,
}

struct EthToGw {
eth_address: Byte20,
}

struct GwToEth {
gw_script_hash: Byte32,
// TODO: should we use gw_short_script_hash?
// a.k.a. godwoken short address (script_hash[0..n])
}
// --- end of ETH Address Registry ---
10 changes: 9 additions & 1 deletion c/polyjuice_globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,19 @@

static uint8_t g_rollup_script_hash[32] = {0};
static uint32_t g_sudt_id = UINT32_MAX;
/* Receipt.contractAddress - The contract address created, if the transaction was a contract creation, otherwise null */

static bool g_is_using_native_eth_address = false;
/**
* Receipt.contractAddress
* The contract address created, if the transaction was a contract creation,
* otherwise null
*/
static uint32_t g_created_id = UINT32_MAX;
static uint8_t g_created_address[20] = {0};

static uint32_t g_creator_account_id = UINT32_MAX;
static evmc_address g_tx_origin = {0};

static uint8_t g_script_code_hash[32] = {0};
static uint8_t g_script_hash_type = 0xff;

Expand Down
Loading

0 comments on commit 26ada29

Please sign in to comment.