diff --git a/Makefile b/Makefile index 52dbf9c..5ea2a29 100644 --- a/Makefile +++ b/Makefile @@ -25,8 +25,8 @@ PASSED_MBEDTLS_CFLAGS := -O3 -fPIC -nostdinc -nostdlib -DCKB_DECLARATION_ONLY -I BUILDER_DOCKER := nervos/ckb-riscv-gnu-toolchain@sha256:aae8a3f79705f67d505d1f1d5ddc694a4fd537ed1c7e9622420a470d59ba2ec3 CLANG_FORMAT_DOCKER := kason223/clang-format@sha256:3cce35b0400a7d420ec8504558a02bdfc12fd2d10e40206f140c4545059cd95d -all: build/simple_udt build/anyone_can_pay build/always_success build/validate_signature_rsa build/xudt_rce build/rce_validator \ - $(SECP256K1_SRC_20210801) build/secp256k1_data_info_20210801.h +all: build/simple_udt build/always_success build/validate_signature_rsa build/xudt_rce build/rce_validator \ + $(SECP256K1_SRC_20210801) build/secp256k1_data_info.h build/secp256k1_data_info_20210801.h all-via-docker: ${PROTOCOL_HEADER} docker run --rm -v `pwd`:/code ${BUILDER_DOCKER} bash -c "cd /code && make" @@ -36,11 +36,6 @@ build/simple_udt: c/simple_udt.c $(OBJCOPY) --only-keep-debug $@ $@.debug $(OBJCOPY) --strip-debug --strip-all $@ -build/anyone_can_pay: c/anyone_can_pay.c ${PROTOCOL_HEADER} c/secp256k1_lock.h build/secp256k1_data_info.h $(SECP256K1_SRC) - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< - $(OBJCOPY) --only-keep-debug $@ $@.debug - $(OBJCOPY) --strip-debug --strip-all $@ - build/always_success: c/always_success.c $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(OBJCOPY) --only-keep-debug $@ $@.debug @@ -168,7 +163,6 @@ package-clean: clean: rm -rf build/simple_udt - rm -rf build/anyone_can_pay rm -rf build/secp256k1_data_info.h build/dump_secp256k1_data rm -rf build/secp256k1_data_info_20210801.h build/dump_secp256k1_data_20210801 rm -rf build/secp256k1_data diff --git a/README.md b/README.md index a3b4747..1fe1852 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ make all-via-docker ### RFC and Deployment * [sUDT](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0025-simple-udt/0025-simple-udt.md) -* [Anyone Can Pay](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0026-anyone-can-pay/0026-anyone-can-pay.md) * [xUDT](https://github.com/nervosnetwork/rfcs/pull/428) ### Omnilock diff --git a/build.rs b/build.rs index af95f5e..0359607 100644 --- a/build.rs +++ b/build.rs @@ -17,10 +17,6 @@ const BINARIES: &[(&str, &str)] = &[ "secp256k1_data", "9799bee251b975b82c45a02154ce28cec89c5853ecc14d12b7b8cccfc19e0af4", ), - ( - "anyone_can_pay", - "cd69ba816f7471e59110058aa37387c362ed9a240cd178f7bb1ecee386cb31e6", - ), ( "simple_udt", "e1e354d6d643ad42724d40967e334984534e0367405c5ae42a9d7d63d77df419", diff --git a/c/anyone_can_pay.c b/c/anyone_can_pay.c deleted file mode 100644 index 889c069..0000000 --- a/c/anyone_can_pay.c +++ /dev/null @@ -1,328 +0,0 @@ -/* UDT anyone-can-pay lock script - * For simplify, we call a cell with anyone-can-pay lock a wallet cell. - * - * Wallet cell can be unlocked without a signature, if: - * - * 1. There is 1 output wallet cell that has the same type hash with the - * unlocked wallet cell. - * 2. The UDT or CKB(if type script is none) in the output wallet is more than - * the unlocked wallet. - * 3. if the type script is none, the cell data is empty. - * - * otherwise, the script perform secp256k1_blake160_sighash_all verification. - */ - -#include "blake2b.h" -#include "blockchain.h" -#include "ckb_syscalls.h" -#include "defs.h" -#include "overflow_add.h" -#include "quick_pow10.h" -#include "secp256k1_helper.h" -#include "secp256k1_lock.h" - -#define BLAKE2B_BLOCK_SIZE 32 -#define SCRIPT_SIZE 32768 -#define CKB_LEN 8 -#define UDT_LEN 16 -#define MAX_WITNESS_SIZE 32768 -#define MAX_TYPE_HASH 256 - -/* anyone can pay errors */ -#define ERROR_OVERFLOW -41 -#define ERROR_OUTPUT_AMOUNT_NOT_ENOUGH -42 -#define ERROR_TOO_MUCH_TYPE_HASH_INPUTS -43 -#define ERROR_NO_PAIR -44 -#define ERROR_DUPLICATED_INPUTS -45 -#define ERROR_DUPLICATED_OUTPUTS -46 - -typedef struct { - int is_ckb_only; - unsigned char type_hash[BLAKE2B_BLOCK_SIZE]; - uint64_t ckb_amount; - uint128_t udt_amount; - uint32_t output_cnt; -} InputWallet; - -int load_type_hash_and_amount(uint64_t cell_index, uint64_t cell_source, - uint8_t type_hash[BLAKE2B_BLOCK_SIZE], - uint64_t *ckb_amount, uint128_t *udt_amount, - int *is_ckb_only) { - uint64_t len = BLAKE2B_BLOCK_SIZE; - int ret = ckb_checked_load_cell_by_field( - type_hash, &len, 0, cell_index, cell_source, CKB_CELL_FIELD_TYPE_HASH); - if (ret == CKB_INDEX_OUT_OF_BOUND) { - return ret; - } - - if (ret == CKB_SUCCESS) { - if (len != BLAKE2B_BLOCK_SIZE) { - return ERROR_ENCODING; - } - } else if (ret != CKB_ITEM_MISSING) { - return ERROR_SYSCALL; - } - - *is_ckb_only = ret == CKB_ITEM_MISSING; - - /* load amount */ - len = CKB_LEN; - ret = - ckb_checked_load_cell_by_field((uint8_t *)ckb_amount, &len, 0, cell_index, - cell_source, CKB_CELL_FIELD_CAPACITY); - if (ret != CKB_SUCCESS) { - *ckb_amount = 0; - return ERROR_SYSCALL; - } - if (len != CKB_LEN) { - return ERROR_ENCODING; - } - len = UDT_LEN; - ret = ckb_load_cell_data((uint8_t *)udt_amount, &len, 0, cell_index, - cell_source); - if (ret != CKB_SUCCESS) { - *udt_amount = 0; - if (ret != CKB_ITEM_MISSING) { - return ERROR_SYSCALL; - } - - return CKB_SUCCESS; - } - - /* check data length */ - if (*is_ckb_only) { - /* ckb only wallet should has no data */ - if (len != 0) { - return ERROR_ENCODING; - } - } else { - if (len < UDT_LEN) { - return ERROR_ENCODING; - } - } - - return CKB_SUCCESS; -} - -int check_payment_unlock(uint64_t min_ckb_amount, uint128_t min_udt_amount) { - unsigned char lock_hash[BLAKE2B_BLOCK_SIZE] = {0}; - InputWallet input_wallets[MAX_TYPE_HASH] = {0}; - uint64_t len = BLAKE2B_BLOCK_SIZE; - /* load wallet lock hash */ - int ret = ckb_load_script_hash(lock_hash, &len, 0); - if (ret != CKB_SUCCESS) { - return ERROR_SYSCALL; - } - if (len > BLAKE2B_BLOCK_SIZE) { - return ERROR_SCRIPT_TOO_LONG; - } - - /* iterate inputs and find input wallet cell */ - int i = 0; - len = BLAKE2B_BLOCK_SIZE; - while (1) { - if (i >= MAX_TYPE_HASH) { - return ERROR_TOO_MUCH_TYPE_HASH_INPUTS; - } - - ret = load_type_hash_and_amount( - i, CKB_SOURCE_GROUP_INPUT, input_wallets[i].type_hash, - &input_wallets[i].ckb_amount, &input_wallets[i].udt_amount, - &input_wallets[i].is_ckb_only); - if (ret == CKB_INDEX_OUT_OF_BOUND) { - break; - } else if (ret != CKB_SUCCESS) { - return ret; - } - - i++; - } - - int input_wallets_cnt = i; - - /* iterate outputs wallet cell */ - i = 0; - while (1) { - uint8_t output_lock_hash[BLAKE2B_BLOCK_SIZE] = {0}; - uint8_t output_type_hash[BLAKE2B_BLOCK_SIZE] = {0}; - uint64_t len = BLAKE2B_BLOCK_SIZE; - /* check lock hash */ - ret = ckb_checked_load_cell_by_field(output_lock_hash, &len, 0, i, - CKB_SOURCE_OUTPUT, - CKB_CELL_FIELD_LOCK_HASH); - if (ret == CKB_INDEX_OUT_OF_BOUND) { - break; - } - if (ret != CKB_SUCCESS) { - return ret; - } - if (len != BLAKE2B_BLOCK_SIZE) { - return ERROR_ENCODING; - } - int has_same_lock = - memcmp(output_lock_hash, lock_hash, BLAKE2B_BLOCK_SIZE) == 0; - - /* skip non ACP lock cells*/ - if (!has_same_lock) { - i++; - continue; - } - - /* load output cell */ - int is_ckb_only = 0; - uint64_t ckb_amount = 0; - uint128_t udt_amount = 0; - ret = load_type_hash_and_amount(i, CKB_SOURCE_OUTPUT, output_type_hash, - &ckb_amount, &udt_amount, &is_ckb_only); - if (ret == CKB_INDEX_OUT_OF_BOUND) { - break; - } else if (ret != CKB_SUCCESS) { - return ret; - } - - /* find input wallet which has same type hash */ - int found_inputs = 0; - for (int j = 0; j < input_wallets_cnt; j++) { - int has_same_type = 0; - /* check type hash */ - if (is_ckb_only) { - has_same_type = input_wallets[j].is_ckb_only; - } else { - has_same_type = memcmp(output_type_hash, input_wallets[j].type_hash, - BLAKE2B_BLOCK_SIZE) == 0; - } - if (!has_same_type) { - continue; - } - /* compare amount */ - uint64_t min_output_ckb_amount = 0; - uint128_t min_output_udt_amount = 0; - int overflow = 0; - overflow = uint64_overflow_add( - &min_output_ckb_amount, input_wallets[j].ckb_amount, min_ckb_amount); - int meet_ckb_cond = !overflow && ckb_amount >= min_output_ckb_amount; - overflow = uint128_overflow_add( - &min_output_udt_amount, input_wallets[j].udt_amount, min_udt_amount); - int meet_udt_cond = !overflow && udt_amount >= min_output_udt_amount; - - /* fail if can't meet both conditions */ - if (!(meet_ckb_cond || meet_udt_cond)) { - return ERROR_OUTPUT_AMOUNT_NOT_ENOUGH; - } - /* output coins must meet condition, or remain the old amount */ - if ((!meet_ckb_cond && ckb_amount != input_wallets[j].ckb_amount) || - (!meet_udt_cond && udt_amount != input_wallets[j].udt_amount)) { - - return ERROR_OUTPUT_AMOUNT_NOT_ENOUGH; - } - - /* increase counter */ - found_inputs++; - input_wallets[j].output_cnt += 1; - if (found_inputs > 1) { - return ERROR_DUPLICATED_INPUTS; - } - if (input_wallets[j].output_cnt > 1) { - return ERROR_DUPLICATED_OUTPUTS; - } - } - - /* one output should pair with one input */ - if (found_inputs == 0) { - return ERROR_NO_PAIR; - } else if (found_inputs > 1) { - return ERROR_DUPLICATED_INPUTS; - } - - i++; - } - - /* check inputs wallet, one input should pair with one output */ - for (int j = 0; j < input_wallets_cnt; j++) { - if (input_wallets[j].output_cnt == 0) { - return ERROR_NO_PAIR; - } else if (input_wallets[j].output_cnt > 1) { - return ERROR_DUPLICATED_OUTPUTS; - } - } - - return CKB_SUCCESS; -} - -int read_args(unsigned char *pubkey_hash, uint64_t *min_ckb_amount, - uint128_t *min_udt_amount) { - int ret; - uint64_t len = 0; - - /* Load args */ - unsigned char script[SCRIPT_SIZE]; - len = SCRIPT_SIZE; - ret = ckb_load_script(script, &len, 0); - if (ret != CKB_SUCCESS) { - return ERROR_SYSCALL; - } - if (len > SCRIPT_SIZE) { - return ERROR_SCRIPT_TOO_LONG; - } - mol_seg_t script_seg; - script_seg.ptr = (uint8_t *)script; - script_seg.size = len; - - if (MolReader_Script_verify(&script_seg, false) != MOL_OK) { - return ERROR_ENCODING; - } - - mol_seg_t args_seg = MolReader_Script_get_args(&script_seg); - mol_seg_t args_bytes_seg = MolReader_Bytes_raw_bytes(&args_seg); - if (args_bytes_seg.size < BLAKE160_SIZE || - args_bytes_seg.size > BLAKE160_SIZE + 2) { - return ERROR_ARGUMENTS_LEN; - } - memcpy(pubkey_hash, args_bytes_seg.ptr, BLAKE160_SIZE); - *min_ckb_amount = 0; - *min_udt_amount = 0; - if (args_bytes_seg.size > BLAKE160_SIZE) { - int x = args_bytes_seg.ptr[BLAKE160_SIZE]; - int is_overflow = quick_pow10(x, min_ckb_amount); - if (is_overflow) { - *min_ckb_amount = MAX_UINT64; - } - } - if (args_bytes_seg.size > BLAKE160_SIZE + 1) { - int x = args_bytes_seg.ptr[BLAKE160_SIZE + 1]; - int is_overflow = uint128_quick_pow10(x, min_udt_amount); - if (is_overflow) { - *min_udt_amount = MAX_UINT128; - } - } - return CKB_SUCCESS; -} - -int main() { - int ret; - /* read script args */ - unsigned char pubkey_hash[BLAKE160_SIZE] = {0}; - uint64_t min_ckb_amount = 0; - uint128_t min_udt_amount = 0; - ret = read_args(pubkey_hash, &min_ckb_amount, &min_udt_amount); - if (ret != CKB_SUCCESS) { - return ret; - } - - /* try load signature */ - unsigned char first_witness[MAX_WITNESS_SIZE]; - uint64_t first_witness_len = 0; - ret = load_secp256k1_first_witness_and_check_signature(first_witness, - &first_witness_len); - int has_sig = ret == CKB_SUCCESS; - - /* ACP verification */ - if (has_sig) { - /* unlock via signature */ - return verify_secp256k1_blake160_sighash_all_with_witness( - pubkey_hash, first_witness, first_witness_len); - } else { - /* unlock via payment */ - return check_payment_unlock(min_ckb_amount, min_udt_amount); - } -} diff --git a/c/defs.h b/c/defs.h deleted file mode 100644 index 3f24b19..0000000 --- a/c/defs.h +++ /dev/null @@ -1,4 +0,0 @@ -#define MAX_UINT64 0xffffffffffffffffUL -#define MAX_UINT128 (((uint128_t)MAX_UINT64 << 64) + MAX_UINT64) - -typedef unsigned __int128 uint128_t; diff --git a/c/overflow_add.h b/c/overflow_add.h deleted file mode 100644 index 48f7e14..0000000 --- a/c/overflow_add.h +++ /dev/null @@ -1,19 +0,0 @@ -#include "defs.h" - -int uint64_overflow_add(uint64_t *result, uint64_t a, uint64_t b) { - if (MAX_UINT64 - a < b) { - /* overflow */ - return 1; - } - *result = a + b; - return 0; -} - -int uint128_overflow_add(uint128_t *result, uint128_t a, uint128_t b) { - if (MAX_UINT128 - a < b) { - /* overflow */ - return 1; - } - *result = a + b; - return 0; -} diff --git a/c/quick_pow10.h b/c/quick_pow10.h deleted file mode 100644 index 752d2e4..0000000 --- a/c/quick_pow10.h +++ /dev/null @@ -1,66 +0,0 @@ -#include "defs.h" - -#define MAX_POW_N 19 -#define UINT128_MAX_POW_N 38 - -int quick_pow10(int n, uint64_t *result) { - static uint64_t pow10[MAX_POW_N + 1] = { - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - 10000000000, - 100000000000, - 1000000000000, - 10000000000000, - 100000000000000, - 1000000000000000, - 10000000000000000, - 100000000000000000, - 1000000000000000000, - 10000000000000000000U, - }; - - if (n > MAX_POW_N) { - return 1; - } - - *result = pow10[n]; - return 0; -} - -int uint128_quick_pow10(int n, uint128_t *result) { - if (n > UINT128_MAX_POW_N) { - return 1; - } - - int ret; - uint64_t mid_result; - if (n <= MAX_POW_N) { - ret = quick_pow10(n, &mid_result); - if (ret) { - return ret; - } - *result = mid_result; - } else { - /* a^(x+y) -> a^x * a^y */ - ret = quick_pow10(n - MAX_POW_N, &mid_result); - if (ret) { - return ret; - } - *result = mid_result; - ret = quick_pow10(MAX_POW_N, &mid_result); - if (ret) { - return ret; - } - *result *= mid_result; - } - - return 0; -} diff --git a/c/secp256k1_helper.h b/c/secp256k1_helper.h deleted file mode 100644 index 0f858ce..0000000 --- a/c/secp256k1_helper.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef CKB_SECP256K1_HELPER_H_ -#define CKB_SECP256K1_HELPER_H_ - -#include "ckb_syscalls.h" -#include "secp256k1_data_info.h" - -#define CKB_SECP256K1_HELPER_ERROR_LOADING_DATA -101 -#define CKB_SECP256K1_HELPER_ERROR_ILLEGAL_CALLBACK -102 -#define CKB_SECP256K1_HELPER_ERROR_ERROR_CALLBACK -103 - -/* - * We are including secp256k1 implementation directly so gcc can strip - * unused functions. For some unknown reasons, if we link in libsecp256k1.a - * directly, the final binary will include all functions rather than those used. - */ -#define HAVE_CONFIG_H 1 -#define USE_EXTERNAL_DEFAULT_CALLBACKS -#include - -void secp256k1_default_illegal_callback_fn(const char* str, void* data) { - (void)str; - (void)data; - ckb_exit(CKB_SECP256K1_HELPER_ERROR_ILLEGAL_CALLBACK); -} - -void secp256k1_default_error_callback_fn(const char* str, void* data) { - (void)str; - (void)data; - ckb_exit(CKB_SECP256K1_HELPER_ERROR_ERROR_CALLBACK); -} - -/* - * data should at least be CKB_SECP256K1_DATA_SIZE big - * so as to hold all loaded data. - */ -int ckb_secp256k1_custom_verify_only_initialize(secp256k1_context* context, - void* data) { - size_t index = 0; - int running = 1; - while (running && index < SIZE_MAX) { - uint64_t len = 32; - uint8_t hash[32]; - - int ret = ckb_load_cell_by_field(hash, &len, 0, index, CKB_SOURCE_CELL_DEP, - CKB_CELL_FIELD_DATA_HASH); - switch (ret) { - case CKB_ITEM_MISSING: - break; - case CKB_SUCCESS: - if (memcmp(ckb_secp256k1_data_hash, hash, 32) == 0) { - /* Found a match, load data here */ - len = CKB_SECP256K1_DATA_SIZE; - ret = ckb_load_cell_data(data, &len, 0, index, CKB_SOURCE_CELL_DEP); - if (ret != CKB_SUCCESS || len != CKB_SECP256K1_DATA_SIZE) { - return CKB_SECP256K1_HELPER_ERROR_LOADING_DATA; - } - running = 0; - } - break; - default: - return CKB_SECP256K1_HELPER_ERROR_LOADING_DATA; - } - if (running) { - index++; - } - } - if (index == SIZE_MAX) { - return CKB_SECP256K1_HELPER_ERROR_LOADING_DATA; - } - - context->illegal_callback = default_illegal_callback; - context->error_callback = default_error_callback; - - secp256k1_ecmult_context_init(&context->ecmult_ctx); - secp256k1_ecmult_gen_context_init(&context->ecmult_gen_ctx); - - /* Recasting data to (uint8_t*) for pointer math */ - uint8_t* p = data; - secp256k1_ge_storage(*pre_g)[] = (secp256k1_ge_storage(*)[])p; - secp256k1_ge_storage(*pre_g_128)[] = - (secp256k1_ge_storage(*)[])(&p[CKB_SECP256K1_DATA_PRE_SIZE]); - context->ecmult_ctx.pre_g = pre_g; - context->ecmult_ctx.pre_g_128 = pre_g_128; - - return 0; -} - -#endif diff --git a/c/secp256k1_lock.h b/c/secp256k1_lock.h deleted file mode 100644 index 83f6caf..0000000 --- a/c/secp256k1_lock.h +++ /dev/null @@ -1,224 +0,0 @@ -#ifndef CKB_LOCK_UTILS_H_ -#define CKB_LOCK_UTILS_H_ - -#define BLAKE2B_BLOCK_SIZE 32 -#define BLAKE160_SIZE 20 -#define PUBKEY_SIZE 33 -#define TEMP_SIZE 32768 -#define RECID_INDEX 64 -/* 32 KB */ -#define MAX_WITNESS_SIZE 32768 -#define SCRIPT_SIZE 32768 -#define SIGNATURE_SIZE 65 - -/* secp256k1 unlock errors */ -#define ERROR_ARGUMENTS_LEN -1 -#define ERROR_ENCODING -2 -#define ERROR_SYSCALL -3 -#define ERROR_SECP_RECOVER_PUBKEY -11 -#define ERROR_SECP_VERIFICATION -12 -#define ERROR_SECP_PARSE_PUBKEY -13 -#define ERROR_SECP_PARSE_SIGNATURE -14 -#define ERROR_SECP_SERIALIZE_PUBKEY -15 -#define ERROR_SCRIPT_TOO_LONG -21 -#define ERROR_WITNESS_SIZE -22 -#define ERROR_INCORRECT_SINCE_FLAGS -23 -#define ERROR_INCORRECT_SINCE_VALUE -24 -#define ERROR_PUBKEY_BLAKE160_HASH -31 - -#if (MAX_WITNESS_SIZE > TEMP_SIZE) || (SCRIPT_SIZE > TEMP_SIZE) -#error "Temp buffer is not big enough!" -#endif - -/* Extract lock from WitnessArgs */ -int extract_witness_lock(uint8_t *witness, uint64_t len, - mol_seg_t *lock_bytes_seg) { - mol_seg_t witness_seg; - witness_seg.ptr = witness; - witness_seg.size = len; - - if (MolReader_WitnessArgs_verify(&witness_seg, false) != MOL_OK) { - return ERROR_ENCODING; - } - mol_seg_t lock_seg = MolReader_WitnessArgs_get_lock(&witness_seg); - - if (MolReader_BytesOpt_is_none(&lock_seg)) { - return ERROR_ENCODING; - } - *lock_bytes_seg = MolReader_Bytes_raw_bytes(&lock_seg); - return CKB_SUCCESS; -} - -/* - * Load secp256k1 first witness and check the signature - * - * This function return CKB_SUCCESS if the witness is a valid WitnessArgs - * and the length of WitnessArgs#lock field is exactly the SIGNATURE_SIZE - * - * Arguments: - * * witness bytes, a buffer to receive the first witness bytes of the input - * cell group - * * witness len, a pointer to receive the first witness length - * - * Witness: - * WitnessArgs with a signature in lock field used to present ownership. - */ -int load_secp256k1_first_witness_and_check_signature( - unsigned char witness_bytes[MAX_WITNESS_SIZE], uint64_t *witness_len) { - int ret; - /* Load witness of first input */ - *witness_len = MAX_WITNESS_SIZE; - ret = ckb_load_witness(witness_bytes, witness_len, 0, 0, - CKB_SOURCE_GROUP_INPUT); - if (ret != CKB_SUCCESS) { - return ERROR_SYSCALL; - } - - if (*witness_len > MAX_WITNESS_SIZE) { - return ERROR_WITNESS_SIZE; - } - - /* load signature */ - mol_seg_t witness_lock_seg; - ret = extract_witness_lock(witness_bytes, *witness_len, &witness_lock_seg); - if (ret != 0) { - return ERROR_ENCODING; - } - - if (witness_lock_seg.size != SIGNATURE_SIZE) { - return ERROR_ARGUMENTS_LEN; - } - return CKB_SUCCESS; -} - -/* - * Arguments: - * * pubkey blake160 hash, blake2b hash of pubkey first 20 bytes, used to - * shield the real pubkey. - * * first witness bytes, the first witness bytes of the input cell group - * * first witness len, length of first witness bytes - */ -int verify_secp256k1_blake160_sighash_all_with_witness( - unsigned char pubkey_hash[BLAKE160_SIZE], - unsigned char first_witness_bytes[MAX_WITNESS_SIZE], - uint64_t first_witness_len) { - int ret; - unsigned char temp[MAX_WITNESS_SIZE]; - unsigned char lock_bytes[SIGNATURE_SIZE]; - - /* copy first witness to temp, we keep the first_witness_bytes untouched in - * this function */ - uint64_t len = first_witness_len; - memcpy(temp, first_witness_bytes, len); - - /* load signature */ - mol_seg_t lock_bytes_seg; - ret = extract_witness_lock(temp, first_witness_len, &lock_bytes_seg); - if (ret != 0) { - return ERROR_ENCODING; - } - - if (lock_bytes_seg.size != SIGNATURE_SIZE) { - return ERROR_ARGUMENTS_LEN; - } - memcpy(lock_bytes, lock_bytes_seg.ptr, lock_bytes_seg.size); - - /* Load tx hash */ - unsigned char tx_hash[BLAKE2B_BLOCK_SIZE]; - len = BLAKE2B_BLOCK_SIZE; - ret = ckb_load_tx_hash(tx_hash, &len, 0); - if (ret != CKB_SUCCESS) { - return ret; - } - if (len != BLAKE2B_BLOCK_SIZE) { - return ERROR_SYSCALL; - } - - /* Prepare sign message */ - unsigned char message[BLAKE2B_BLOCK_SIZE]; - blake2b_state blake2b_ctx; - blake2b_init(&blake2b_ctx, BLAKE2B_BLOCK_SIZE); - blake2b_update(&blake2b_ctx, tx_hash, BLAKE2B_BLOCK_SIZE); - - /* Clear lock field to zero, then digest the first witness */ - memset((void *)lock_bytes_seg.ptr, 0, lock_bytes_seg.size); - blake2b_update(&blake2b_ctx, (char *)&first_witness_len, sizeof(uint64_t)); - blake2b_update(&blake2b_ctx, temp, first_witness_len); - - /* Digest same group witnesses */ - size_t i = 1; - while (1) { - len = MAX_WITNESS_SIZE; - ret = ckb_load_witness(temp, &len, 0, i, CKB_SOURCE_GROUP_INPUT); - if (ret == CKB_INDEX_OUT_OF_BOUND) { - break; - } - if (ret != CKB_SUCCESS) { - return ERROR_SYSCALL; - } - if (len > MAX_WITNESS_SIZE) { - return ERROR_WITNESS_SIZE; - } - blake2b_update(&blake2b_ctx, (char *)&len, sizeof(uint64_t)); - blake2b_update(&blake2b_ctx, temp, len); - i += 1; - } - /* Digest witnesses that not covered by inputs */ - i = ckb_calculate_inputs_len(); - while (1) { - len = MAX_WITNESS_SIZE; - ret = ckb_load_witness(temp, &len, 0, i, CKB_SOURCE_INPUT); - if (ret == CKB_INDEX_OUT_OF_BOUND) { - break; - } - if (ret != CKB_SUCCESS) { - return ERROR_SYSCALL; - } - if (len > MAX_WITNESS_SIZE) { - return ERROR_WITNESS_SIZE; - } - blake2b_update(&blake2b_ctx, (char *)&len, sizeof(uint64_t)); - blake2b_update(&blake2b_ctx, temp, len); - i += 1; - } - blake2b_final(&blake2b_ctx, message, BLAKE2B_BLOCK_SIZE); - - /* Load signature */ - secp256k1_context context; - uint8_t secp_data[CKB_SECP256K1_DATA_SIZE]; - ret = ckb_secp256k1_custom_verify_only_initialize(&context, secp_data); - if (ret != 0) { - return ret; - } - - secp256k1_ecdsa_recoverable_signature signature; - if (secp256k1_ecdsa_recoverable_signature_parse_compact( - &context, &signature, lock_bytes, lock_bytes[RECID_INDEX]) == 0) { - return ERROR_SECP_PARSE_SIGNATURE; - } - - /* Recover pubkey */ - secp256k1_pubkey pubkey; - if (secp256k1_ecdsa_recover(&context, &pubkey, &signature, message) != 1) { - return ERROR_SECP_RECOVER_PUBKEY; - } - - /* Check pubkey hash */ - size_t pubkey_size = PUBKEY_SIZE; - if (secp256k1_ec_pubkey_serialize(&context, temp, &pubkey_size, &pubkey, - SECP256K1_EC_COMPRESSED) != 1) { - return ERROR_SECP_SERIALIZE_PUBKEY; - } - - blake2b_init(&blake2b_ctx, BLAKE2B_BLOCK_SIZE); - blake2b_update(&blake2b_ctx, temp, pubkey_size); - blake2b_final(&blake2b_ctx, temp, BLAKE2B_BLOCK_SIZE); - - if (memcmp(pubkey_hash, temp, BLAKE160_SIZE) != 0) { - return ERROR_PUBKEY_BLAKE160_HASH; - } - - return 0; -} - -#endif /* CKB_LOCK_UTILS_H_ */ diff --git a/docs/ckb-anyone-can-pay.md b/docs/ckb-anyone-can-pay.md deleted file mode 100644 index aa8d113..0000000 --- a/docs/ckb-anyone-can-pay.md +++ /dev/null @@ -1,94 +0,0 @@ -# ckb-anyone-can-pay - -CKB anyone-can-pay lock. - -[RFC Draft](https://talk.nervos.org/t/rfc-anyone-can-pay-lock/4438) - -## Build - -``` sh -make all-via-docker && cargo test -``` - -## Quick start - -### Create - -1, create a cell to receive UDT and CKB: - -``` -Cell { - lock: { - code_hash: - args: - } - data: - type: -} -``` - -2, create a cell to receive only CKB: - -``` -Cell { - lock: { - code_hash: - args: - } - data: - type: -} -``` - -3, we can add minimum amount transfer condition: - -``` -Cell { - lock: { - code_hash: - args: | | - } - data: - type: -} -``` - -`minimum CKB` and `minimum UDT` are two optional args, each occupied a byte, and represent `10 ^ x` minimal amount. The default value is `0` which means anyone can transfer any amount to the cell. A transfer must satisfy the `minimum CKB` **or** `minimum UDT`. - -If the owner only wants to receive `UDT`, the owner can set `minimum CKB` to `255`. - -### Send UDT and CKB - -To transfer coins to an anyone-can-pay lock cell, the sender must build an output cell that has the same `lock_hash` and `type_hash` to the input anyone-can-pay lock cell; if the input anyone-can-pay cell has no `data`, the output cell must also be empty. - -``` -# inputs -Cell { - lock: { - code_hash: - args: | - } - data: - type: - capacity: 100 -} -... - -# outputs -Cell { - lock: { - code_hash: - args: | - } - data: - type: - capacity: 200 -} -... -``` - -### Signature - -The owner can provide a secp256k1 signature to unlock the cell, the signature method is the same as the [P2PH](https://github.com/nervosnetwork/ckb-system-scripts/wiki/How-to-sign-transaction#p2ph). - -Unlock a cell with a signature has no restrictions, which helps owner to manage the cell as he wants. diff --git a/src/lib.rs b/src/lib.rs index d5b0154..b75e210 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,3 @@ include!(concat!(env!("OUT_DIR"), "/bundled.rs")); include!(concat!(env!("OUT_DIR"), "/code_hashes.rs")); - -#[cfg(test)] -mod tests; diff --git a/src/tests/anyone_can_pay.rs b/src/tests/anyone_can_pay.rs deleted file mode 100644 index 060c7a6..0000000 --- a/src/tests/anyone_can_pay.rs +++ /dev/null @@ -1,534 +0,0 @@ -use super::{ - blake160, build_resolved_tx, gen_tx, gen_tx_with_grouped_args, DummyDataLoader, ALWAYS_SUCCESS, - ANYONE_CAN_PAY, ERROR_DUPLICATED_INPUTS, ERROR_DUPLICATED_OUTPUTS, ERROR_ENCODING, - ERROR_NO_PAIR, ERROR_OUTPUT_AMOUNT_NOT_ENOUGH, MAX_CYCLES, -}; -use ckb_crypto::secp::Generator; -use ckb_error::assert_error_eq; -use ckb_script::{ScriptError, TransactionScriptsVerifier}; -use ckb_types::{ - bytes::Bytes, - core::ScriptHashType, - packed::{CellOutput, Script}, - prelude::*, -}; -use rand::thread_rng; - -fn build_anyone_can_pay_script(args: Bytes) -> Script { - let sighash_all_cell_data_hash = CellOutput::calc_data_hash(&ANYONE_CAN_PAY); - Script::new_builder() - .args(args.pack()) - .code_hash(sighash_all_cell_data_hash.clone()) - .hash_type(ScriptHashType::Data.into()) - .build() -} - -fn build_udt_script() -> Script { - let data_hash = CellOutput::calc_data_hash(&ALWAYS_SUCCESS); - Script::new_builder() - .code_hash(data_hash.clone()) - .hash_type(ScriptHashType::Data.into()) - .build() -} - -#[test] -fn test_unlock_by_anyone() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - - let script = build_anyone_can_pay_script(pubkey_hash.to_owned()); - let tx = gen_tx(&mut data_loader, pubkey_hash); - let output = tx.outputs().get(0).unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(Vec::new()) - .set_outputs(vec![output - .as_builder() - .lock(script) - .capacity(44u64.pack()) - .build()]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verifier = TransactionScriptsVerifier::new(&resolved_tx, &data_loader); - let verify_result = verifier.verify(MAX_CYCLES); - verify_result.expect("pass"); -} - -#[test] -fn test_put_output_data() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - - let script = build_anyone_can_pay_script(pubkey_hash.to_owned()); - let tx = gen_tx(&mut data_loader, pubkey_hash); - let output = tx.outputs().get(0).unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(Vec::new()) - .set_outputs(vec![output - .as_builder() - .lock(script) - .capacity(44u64.pack()) - .build()]) - .set_outputs_data(vec![Bytes::from(vec![42u8]).pack()]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verifier = TransactionScriptsVerifier::new(&resolved_tx, &data_loader); - let verify_result = verifier.verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_ENCODING), - ); -} - -#[test] -fn test_wrong_output_args() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - - let script = build_anyone_can_pay_script(pubkey_hash.to_owned()); - let tx = gen_tx(&mut data_loader, pubkey_hash.to_owned()); - let output = tx.outputs().get(0).unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(Vec::new()) - .set_outputs(vec![output - .as_builder() - .lock({ - let mut args = pubkey_hash.to_vec(); - // a valid args - args.push(0); - script.as_builder().args(Bytes::from(args).pack()).build() - }) - .capacity(44u64.pack()) - .build()]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verifier = TransactionScriptsVerifier::new(&resolved_tx, &data_loader); - let verify_result = verifier.verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_NO_PAIR), - ); -} - -#[test] -fn test_split_cell() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - - let script = build_anyone_can_pay_script(pubkey_hash.to_owned()); - let tx = gen_tx(&mut data_loader, pubkey_hash.to_owned()); - let output = tx.outputs().get(0).unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(Vec::new()) - .set_outputs(vec![ - output - .clone() - .as_builder() - .lock(script.clone()) - .capacity(44u64.pack()) - .build(), - output - .as_builder() - .lock(script) - .capacity(44u64.pack()) - .build(), - ]) - .set_outputs_data(vec![ - Bytes::from(Vec::new()).pack(), - Bytes::from(Vec::new()).pack(), - ]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verifier = TransactionScriptsVerifier::new(&resolved_tx, &data_loader); - let verify_result = verifier.verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_DUPLICATED_OUTPUTS), - ); -} - -#[test] -fn test_merge_cell() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - - let script = build_anyone_can_pay_script(pubkey_hash.to_owned()); - let mut rng = thread_rng(); - let tx = gen_tx_with_grouped_args(&mut data_loader, vec![(pubkey_hash, 2)], &mut rng); - let output = tx.outputs().get(0).unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(Vec::new()) - .set_outputs(vec![output - .clone() - .as_builder() - .lock(script.clone()) - .capacity(88u64.pack()) - .build()]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verifier = TransactionScriptsVerifier::new(&resolved_tx, &data_loader); - let verify_result = verifier.verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_DUPLICATED_INPUTS), - ); -} - -#[test] -fn test_insufficient_pay() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - - let script = build_anyone_can_pay_script(pubkey_hash.to_owned()); - let tx = gen_tx(&mut data_loader, pubkey_hash); - let output = tx.outputs().get(0).unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(Vec::new()) - .set_outputs(vec![output - .clone() - .as_builder() - .lock(script.clone()) - .capacity(41u64.pack()) - .build()]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verifier = TransactionScriptsVerifier::new(&resolved_tx, &data_loader); - let verify_result = verifier.verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_OUTPUT_AMOUNT_NOT_ENOUGH), - ); -} - -#[test] -fn test_payment_not_meet_requirement() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - let mut args = pubkey_hash.to_vec(); - args.push(1); - let args = Bytes::from(args); - let script = build_anyone_can_pay_script(args.clone()); - let tx = gen_tx(&mut data_loader, args); - let output = tx.outputs().get(0).unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(Vec::new()) - .set_outputs(vec![output - .clone() - .as_builder() - .lock(script.clone()) - .capacity(44u64.pack()) - .build()]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verifier = TransactionScriptsVerifier::new(&resolved_tx, &data_loader); - let verify_result = verifier.verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_OUTPUT_AMOUNT_NOT_ENOUGH), - ); -} - -#[test] -fn test_no_pair() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - - let another_script = build_anyone_can_pay_script(vec![42].into()); - let tx = gen_tx(&mut data_loader, pubkey_hash.to_owned()); - let output = tx.outputs().get(0).unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(Vec::new()) - .set_outputs(vec![output - .clone() - .as_builder() - .lock(another_script.clone()) - .capacity(44u64.pack()) - .build()]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verifier = TransactionScriptsVerifier::new(&resolved_tx, &data_loader); - let verify_result = verifier.verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_NO_PAIR), - ); -} - -#[test] -fn test_overflow() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - let mut args = pubkey_hash.to_vec(); - args.push(255); - let args = Bytes::from(args); - - let script = build_anyone_can_pay_script(args.to_owned()); - let tx = gen_tx(&mut data_loader, args); - let output = tx.outputs().get(0).unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(Vec::new()) - .set_outputs(vec![output - .as_builder() - .lock(script) - .capacity(44u64.pack()) - .build()]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verifier = TransactionScriptsVerifier::new(&resolved_tx, &data_loader); - let verify_result = verifier.verify(MAX_CYCLES); - - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_OUTPUT_AMOUNT_NOT_ENOUGH), - ); -} - -#[test] -fn test_only_pay_ckb() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - let mut args = pubkey_hash.to_vec(); - args.push(0); - // do not accept UDT transfer - args.push(255); - let args = Bytes::from(args); - - let script = build_anyone_can_pay_script(args.to_owned()); - let tx = gen_tx(&mut data_loader, args); - let input = tx.inputs().get(0).unwrap(); - let (prev_output, _) = data_loader.cells.remove(&input.previous_output()).unwrap(); - let prev_output = prev_output - .as_builder() - .type_(Some(build_udt_script()).pack()) - .build(); - let prev_data = 44u128.to_le_bytes().to_vec().into(); - data_loader - .cells - .insert(input.previous_output(), (prev_output, prev_data)); - let output = tx.outputs().get(0).unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(Vec::new()) - .set_outputs(vec![output - .as_builder() - .lock(script) - .capacity(44u64.pack()) - .type_(Some(build_udt_script()).pack()) - .build()]) - .set_outputs_data(vec![Bytes::from(44u128.to_le_bytes().to_vec()).pack()]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verifier = TransactionScriptsVerifier::new(&resolved_tx, &data_loader); - let verify_result = verifier.verify(MAX_CYCLES); - verify_result.unwrap(); -} - -#[test] -fn test_only_pay_udt() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let mut args = { - let pubkey_hash = blake160(&pubkey.serialize()); - pubkey_hash.to_vec() - }; - args.push(255); - let args = Bytes::from(args); - - let script = build_anyone_can_pay_script(args.to_owned()); - let tx = gen_tx(&mut data_loader, args); - let input = tx.inputs().get(0).unwrap(); - let (prev_output, _) = data_loader.cells.remove(&input.previous_output()).unwrap(); - let input_capacity = prev_output.capacity(); - let prev_output = prev_output - .as_builder() - .type_(Some(build_udt_script()).pack()) - .build(); - let prev_data = 44u128.to_le_bytes().to_vec().into(); - data_loader - .cells - .insert(input.previous_output(), (prev_output, prev_data)); - let output = tx.outputs().get(0).unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(Vec::new()) - .set_outputs(vec![output - .as_builder() - .lock(script) - .capacity(input_capacity) - .type_(Some(build_udt_script()).pack()) - .build()]) - .set_outputs_data(vec![Bytes::from(44u128.to_le_bytes().to_vec()).pack()]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verifier = TransactionScriptsVerifier::new(&resolved_tx, &data_loader); - let verify_result = verifier.verify(MAX_CYCLES); - verify_result.expect("pass"); -} - -#[test] -fn test_udt_unlock_by_anyone() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - - let script = build_anyone_can_pay_script(pubkey_hash.to_owned()); - let tx = gen_tx(&mut data_loader, pubkey_hash); - let input = tx.inputs().get(0).unwrap(); - let (prev_output, _) = data_loader.cells.remove(&input.previous_output()).unwrap(); - let prev_output = prev_output - .as_builder() - .type_(Some(build_udt_script()).pack()) - .build(); - let prev_data = 44u128.to_le_bytes().to_vec().into(); - data_loader - .cells - .insert(input.previous_output(), (prev_output, prev_data)); - let output = tx.outputs().get(0).unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(Vec::new()) - .set_outputs(vec![output - .as_builder() - .lock(script) - .capacity(44u64.pack()) - .type_(Some(build_udt_script()).pack()) - .build()]) - .set_outputs_data(vec![Bytes::from(44u128.to_le_bytes().to_vec()).pack()]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verifier = TransactionScriptsVerifier::new(&resolved_tx, &data_loader); - let verify_result = verifier.verify(MAX_CYCLES); - verify_result.expect("pass"); -} - -#[test] -fn test_udt_overflow() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - let mut args = pubkey_hash.to_vec(); - args.push(1); - args.push(255); - let args = Bytes::from(args); - - let script = build_anyone_can_pay_script(args.to_owned()); - let tx = gen_tx(&mut data_loader, args); - let input = tx.inputs().get(0).unwrap(); - let (prev_output, _) = data_loader.cells.remove(&input.previous_output()).unwrap(); - let prev_output = prev_output - .as_builder() - .type_(Some(build_udt_script()).pack()) - .build(); - let prev_data = 44u128.to_le_bytes().to_vec().into(); - data_loader - .cells - .insert(input.previous_output(), (prev_output, prev_data)); - let output = tx.outputs().get(0).unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(Vec::new()) - .set_outputs(vec![output - .as_builder() - .lock(script) - .capacity(44u64.pack()) - .type_(Some(build_udt_script()).pack()) - .build()]) - .set_outputs_data(vec![Bytes::from(44u128.to_le_bytes().to_vec()).pack()]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verifier = TransactionScriptsVerifier::new(&resolved_tx, &data_loader); - let verify_result = verifier.verify(MAX_CYCLES); - - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_OUTPUT_AMOUNT_NOT_ENOUGH), - ); -} - -#[test] -fn test_extended_udt() { - // we assume the first 16 bytes data represent token amount - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - - let script = build_anyone_can_pay_script(pubkey_hash.to_owned()); - let tx = gen_tx(&mut data_loader, pubkey_hash); - let input = tx.inputs().get(0).unwrap(); - let (prev_output, _) = data_loader.cells.remove(&input.previous_output()).unwrap(); - let prev_output = prev_output - .as_builder() - .type_(Some(build_udt_script()).pack()) - .build(); - let mut prev_data = 44u128.to_le_bytes().to_vec(); - // push junk data - prev_data.push(42); - data_loader - .cells - .insert(input.previous_output(), (prev_output, prev_data.into())); - let output = tx.outputs().get(0).unwrap(); - let mut output_udt = 44u128.to_le_bytes().to_vec(); - // push junk data - output_udt.push(42); - let tx = tx - .as_advanced_builder() - .set_witnesses(Vec::new()) - .set_outputs(vec![output - .as_builder() - .lock(script) - .capacity(44u64.pack()) - .type_(Some(build_udt_script()).pack()) - .build()]) - .set_outputs_data(vec![Bytes::from(output_udt).pack()]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verifier = TransactionScriptsVerifier::new(&resolved_tx, &data_loader); - let verify_result = verifier.verify(MAX_CYCLES); - verify_result.expect("pass"); -} diff --git a/src/tests/mod.rs b/src/tests/mod.rs deleted file mode 100644 index 5839073..0000000 --- a/src/tests/mod.rs +++ /dev/null @@ -1,334 +0,0 @@ -mod anyone_can_pay; -mod secp256k1_compatibility; - -use ckb_crypto::secp::Privkey; -use ckb_script::DataLoader; -use ckb_types::{ - bytes::Bytes, - core::{ - cell::{CellMeta, CellMetaBuilder, ResolvedTransaction}, - BlockExt, Capacity, DepType, EpochExt, HeaderView, ScriptHashType, TransactionBuilder, - TransactionView, - }, - packed::{ - self, Byte32, CellDep, CellInput, CellOutput, OutPoint, Script, WitnessArgs, - WitnessArgsBuilder, - }, - prelude::*, - H256, -}; -use lazy_static::lazy_static; -use rand::{thread_rng, Rng}; -use std::collections::HashMap; - -pub const MAX_CYCLES: u64 = std::u64::MAX; -pub const SIGNATURE_SIZE: usize = 65; - -// errors -pub const ERROR_ENCODING: i8 = -2; -pub const ERROR_PUBKEY_BLAKE160_HASH: i8 = -31; -pub const ERROR_OUTPUT_AMOUNT_NOT_ENOUGH: i8 = -42; -pub const ERROR_NO_PAIR: i8 = -44; -pub const ERROR_DUPLICATED_INPUTS: i8 = -45; -pub const ERROR_DUPLICATED_OUTPUTS: i8 = -46; - -lazy_static! { - pub static ref ANYONE_CAN_PAY: Bytes = - Bytes::from(&include_bytes!("../../build/anyone_can_pay")[..]); - pub static ref SECP256K1_DATA_BIN: Bytes = - Bytes::from(&include_bytes!("../../build/secp256k1_data")[..]); - pub static ref ALWAYS_SUCCESS: Bytes = - Bytes::from(&include_bytes!("../../build/always_success")[..]); -} - -#[derive(Default)] -pub struct DummyDataLoader { - pub cells: HashMap, - pub headers: HashMap, - pub epoches: HashMap, -} - -impl DummyDataLoader { - fn new() -> Self { - Self::default() - } -} - -impl DataLoader for DummyDataLoader { - // load Cell Data - fn load_cell_data(&self, cell: &CellMeta) -> Option<(Bytes, Byte32)> { - cell.mem_cell_data.clone().or_else(|| { - self.cells - .get(&cell.out_point) - .map(|(_, data)| (data.clone(), CellOutput::calc_data_hash(&data))) - }) - } - // load BlockExt - fn get_block_ext(&self, _hash: &Byte32) -> Option { - unreachable!() - } - - // load header - fn get_header(&self, block_hash: &Byte32) -> Option { - self.headers.get(block_hash).cloned() - } - - // load EpochExt - fn get_block_epoch(&self, block_hash: &Byte32) -> Option { - self.epoches.get(block_hash).cloned() - } -} - -pub fn blake160(message: &[u8]) -> Bytes { - Bytes::from(&ckb_hash::blake2b_256(message)[..20]) -} - -pub fn sign_tx(tx: TransactionView, key: &Privkey) -> TransactionView { - let witnesses_len = tx.witnesses().len(); - sign_tx_by_input_group(tx, key, 0, witnesses_len) -} - -pub fn sign_tx_by_input_group( - tx: TransactionView, - key: &Privkey, - begin_index: usize, - len: usize, -) -> TransactionView { - let tx_hash = tx.hash(); - let mut signed_witnesses: Vec = tx - .inputs() - .into_iter() - .enumerate() - .map(|(i, _)| { - if i == begin_index { - let mut blake2b = ckb_hash::new_blake2b(); - let mut message = [0u8; 32]; - blake2b.update(&tx_hash.raw_data()); - // digest the first witness - let witness = WitnessArgs::new_unchecked(tx.witnesses().get(i).unwrap().unpack()); - let zero_lock: Bytes = { - let mut buf = Vec::new(); - buf.resize(SIGNATURE_SIZE, 0); - buf.into() - }; - let witness_for_digest = - witness.clone().as_builder().lock(zero_lock.pack()).build(); - let witness_len = witness_for_digest.as_bytes().len() as u64; - blake2b.update(&witness_len.to_le_bytes()); - blake2b.update(&witness_for_digest.as_bytes()); - ((i + 1)..(i + len)).for_each(|n| { - let witness = tx.witnesses().get(n).unwrap(); - let witness_len = witness.raw_data().len() as u64; - blake2b.update(&witness_len.to_le_bytes()); - blake2b.update(&witness.raw_data()); - }); - blake2b.finalize(&mut message); - let message = H256::from(message); - let sig = key.sign_recoverable(&message).expect("sign"); - witness - .as_builder() - .lock(sig.serialize().pack()) - .build() - .as_bytes() - .pack() - } else { - tx.witnesses().get(i).unwrap_or_default() - } - }) - .collect(); - for i in signed_witnesses.len()..tx.witnesses().len() { - signed_witnesses.push(tx.witnesses().get(i).unwrap()); - } - // calculate message - tx.as_advanced_builder() - .set_witnesses(signed_witnesses) - .build() -} - -pub fn gen_tx(dummy: &mut DummyDataLoader, lock_args: Bytes) -> TransactionView { - let mut rng = thread_rng(); - gen_tx_with_grouped_args(dummy, vec![(lock_args, 1)], &mut rng) -} - -pub fn gen_tx_with_grouped_args( - dummy: &mut DummyDataLoader, - grouped_args: Vec<(Bytes, usize)>, - rng: &mut R, -) -> TransactionView { - // setup sighash_all dep - let sighash_all_out_point = { - let contract_tx_hash = { - let mut buf = [0u8; 32]; - rng.fill(&mut buf); - buf.pack() - }; - OutPoint::new(contract_tx_hash.clone(), 0) - }; - // dep contract code - let sighash_all_cell = CellOutput::new_builder() - .capacity( - Capacity::bytes(ANYONE_CAN_PAY.len()) - .expect("script capacity") - .pack(), - ) - .build(); - let sighash_all_cell_data_hash = CellOutput::calc_data_hash(&ANYONE_CAN_PAY); - dummy.cells.insert( - sighash_all_out_point.clone(), - (sighash_all_cell, ANYONE_CAN_PAY.clone()), - ); - // always success - let always_success_out_point = { - let contract_tx_hash = { - let mut buf = [0u8; 32]; - rng.fill(&mut buf); - buf.pack() - }; - OutPoint::new(contract_tx_hash.clone(), 0) - }; - let always_success_cell = CellOutput::new_builder() - .capacity( - Capacity::bytes(ALWAYS_SUCCESS.len()) - .expect("script capacity") - .pack(), - ) - .build(); - dummy.cells.insert( - always_success_out_point.clone(), - (always_success_cell, ALWAYS_SUCCESS.clone()), - ); - // setup secp256k1_data dep - let secp256k1_data_out_point = { - let tx_hash = { - let mut buf = [0u8; 32]; - rng.fill(&mut buf); - buf.pack() - }; - OutPoint::new(tx_hash, 0) - }; - let secp256k1_data_cell = CellOutput::new_builder() - .capacity( - Capacity::bytes(SECP256K1_DATA_BIN.len()) - .expect("data capacity") - .pack(), - ) - .build(); - dummy.cells.insert( - secp256k1_data_out_point.clone(), - (secp256k1_data_cell, SECP256K1_DATA_BIN.clone()), - ); - // setup default tx builder - let dummy_capacity = Capacity::shannons(42); - let mut tx_builder = TransactionBuilder::default() - .cell_dep( - CellDep::new_builder() - .out_point(sighash_all_out_point) - .dep_type(DepType::Code.into()) - .build(), - ) - .cell_dep( - CellDep::new_builder() - .out_point(always_success_out_point) - .dep_type(DepType::Code.into()) - .build(), - ) - .cell_dep( - CellDep::new_builder() - .out_point(secp256k1_data_out_point) - .dep_type(DepType::Code.into()) - .build(), - ) - .output( - CellOutput::new_builder() - .capacity(dummy_capacity.pack()) - .build(), - ) - .output_data(Bytes::new().pack()); - - for (args, inputs_size) in grouped_args { - // setup dummy input unlock script - for _ in 0..inputs_size { - let previous_tx_hash = { - let mut buf = [0u8; 32]; - rng.fill(&mut buf); - buf.pack() - }; - let previous_out_point = OutPoint::new(previous_tx_hash, 0); - let script = Script::new_builder() - .args(args.pack()) - .code_hash(sighash_all_cell_data_hash.clone()) - .hash_type(ScriptHashType::Data.into()) - .build(); - let previous_output_cell = CellOutput::new_builder() - .capacity(dummy_capacity.pack()) - .lock(script) - .build(); - dummy.cells.insert( - previous_out_point.clone(), - (previous_output_cell.clone(), Bytes::new()), - ); - let mut random_extra_witness = [0u8; 32]; - rng.fill(&mut random_extra_witness); - let witness_args = WitnessArgsBuilder::default() - .extra(Bytes::from(random_extra_witness.to_vec()).pack()) - .build(); - tx_builder = tx_builder - .input(CellInput::new(previous_out_point, 0)) - .witness(witness_args.as_bytes().pack()); - } - } - - tx_builder.build() -} - -pub fn sign_tx_hash(tx: TransactionView, key: &Privkey, tx_hash: &[u8]) -> TransactionView { - // calculate message - let mut blake2b = ckb_hash::new_blake2b(); - let mut message = [0u8; 32]; - blake2b.update(tx_hash); - blake2b.finalize(&mut message); - let message = H256::from(message); - let sig = key.sign_recoverable(&message).expect("sign"); - let witness_args = WitnessArgsBuilder::default() - .lock(Bytes::from(sig.serialize()).pack()) - .build(); - tx.as_advanced_builder() - .set_witnesses(vec![witness_args.as_bytes().pack()]) - .build() -} - -pub fn build_resolved_tx( - data_loader: &DummyDataLoader, - tx: &TransactionView, -) -> ResolvedTransaction { - let resolved_cell_deps = tx - .cell_deps() - .into_iter() - .map(|dep| { - let deps_out_point = dep.clone(); - let (dep_output, dep_data) = - data_loader.cells.get(&deps_out_point.out_point()).unwrap(); - CellMetaBuilder::from_cell_output(dep_output.to_owned(), dep_data.to_owned()) - .out_point(deps_out_point.out_point().clone()) - .build() - }) - .collect(); - - let mut resolved_inputs = Vec::new(); - for i in 0..tx.inputs().len() { - let previous_out_point = tx.inputs().get(i).unwrap().previous_output(); - let (input_output, input_data) = data_loader.cells.get(&previous_out_point).unwrap(); - resolved_inputs.push( - CellMetaBuilder::from_cell_output(input_output.to_owned(), input_data.to_owned()) - .out_point(previous_out_point) - .build(), - ); - } - - ResolvedTransaction { - transaction: tx.clone(), - resolved_cell_deps, - resolved_inputs, - resolved_dep_groups: vec![], - } -} diff --git a/src/tests/secp256k1_compatibility.rs b/src/tests/secp256k1_compatibility.rs deleted file mode 100644 index 1227351..0000000 --- a/src/tests/secp256k1_compatibility.rs +++ /dev/null @@ -1,439 +0,0 @@ -use super::{ - blake160, build_resolved_tx, gen_tx, gen_tx_with_grouped_args, sign_tx, sign_tx_by_input_group, - sign_tx_hash, DummyDataLoader, ERROR_NO_PAIR, ERROR_PUBKEY_BLAKE160_HASH, MAX_CYCLES, -}; -use ckb_crypto::secp::Generator; -use ckb_error::assert_error_eq; -use ckb_script::{ScriptError, TransactionScriptsVerifier}; -use ckb_types::{bytes::Bytes, packed::WitnessArgs, prelude::*, H256}; -use rand::{thread_rng, Rng, SeedableRng}; - -#[test] -fn test_sighash_all_unlock() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - let tx = gen_tx(&mut data_loader, pubkey_hash); - let tx = sign_tx(tx, &privkey); - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(MAX_CYCLES); - verify_result.expect("pass verification"); -} - -#[test] -fn test_sighash_all_unlock_with_args() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let lock_args = { - let mut args = blake160(&pubkey.serialize()).to_vec(); - args.push(42); - args.push(255); - Bytes::from(args) - }; - let mut rng = thread_rng(); - let tx = gen_tx_with_grouped_args(&mut data_loader, vec![(lock_args, 1)], &mut rng); - let tx = sign_tx(tx, &privkey); - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(MAX_CYCLES); - verify_result.expect("pass verification"); -} - -#[test] -fn test_sighash_all_with_extra_witness_unlock() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - let tx = gen_tx(&mut data_loader, pubkey_hash); - let extract_witness = vec![1, 2, 3, 4]; - let tx = tx - .as_advanced_builder() - .set_witnesses(vec![WitnessArgs::new_builder() - .extra(Bytes::from(extract_witness).pack()) - .build() - .as_bytes() - .pack()]) - .build(); - { - let tx = sign_tx(tx.clone(), &privkey); - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(MAX_CYCLES); - verify_result.expect("pass verification"); - } - { - let tx = sign_tx(tx, &privkey); - let wrong_witness = tx - .witnesses() - .get(0) - .map(|w| { - WitnessArgs::new_unchecked(w.unpack()) - .as_builder() - .extra(Bytes::from(vec![0]).pack()) - .build() - }) - .unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(vec![wrong_witness.as_bytes().pack()]) - .build(); - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_PUBKEY_BLAKE160_HASH), - ); - } -} - -#[test] -fn test_sighash_all_with_grouped_inputs_unlock() { - let mut rng = thread_rng(); - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - let tx = gen_tx_with_grouped_args(&mut data_loader, vec![(pubkey_hash, 2)], &mut rng); - { - let tx = sign_tx(tx.clone(), &privkey); - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(MAX_CYCLES); - verify_result.expect("pass verification"); - } - { - let tx = sign_tx(tx.clone(), &privkey); - let wrong_witness = tx - .witnesses() - .get(1) - .map(|w| { - WitnessArgs::new_unchecked(w.unpack()) - .as_builder() - .extra(Bytes::from(vec![0]).pack()) - .build() - }) - .unwrap(); - let tx = tx - .as_advanced_builder() - .set_witnesses(vec![ - tx.witnesses().get(0).unwrap(), - wrong_witness.as_bytes().pack(), - ]) - .build(); - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_PUBKEY_BLAKE160_HASH), - ); - } -} - -#[test] -fn test_sighash_all_with_2_different_inputs_unlock() { - let mut rng = thread_rng(); - let mut data_loader = DummyDataLoader::new(); - // key1 - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - // key2 - let privkey2 = Generator::random_privkey(); - let pubkey2 = privkey2.pubkey().expect("pubkey"); - let pubkey_hash2 = blake160(&pubkey2.serialize()); - - // sign with 2 keys - let tx = gen_tx_with_grouped_args( - &mut data_loader, - vec![(pubkey_hash, 2), (pubkey_hash2, 2)], - &mut rng, - ); - let tx = sign_tx_by_input_group(tx, &privkey, 0, 2); - let tx = sign_tx_by_input_group(tx, &privkey2, 2, 2); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(MAX_CYCLES); - verify_result.expect("pass verification"); -} - -#[test] -fn test_signing_with_wrong_key() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let wrong_privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - let tx = gen_tx(&mut data_loader, pubkey_hash); - let tx = sign_tx(tx, &wrong_privkey); - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_PUBKEY_BLAKE160_HASH), - ); -} - -#[test] -fn test_signing_wrong_tx_hash() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - let tx = gen_tx(&mut data_loader, pubkey_hash); - let tx = { - let mut rand_tx_hash = [0u8; 32]; - let mut rng = thread_rng(); - rng.fill(&mut rand_tx_hash); - sign_tx_hash(tx, &privkey, &rand_tx_hash[..]) - }; - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_PUBKEY_BLAKE160_HASH), - ); -} - -#[test] -fn test_super_long_witness() { - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - let tx = gen_tx(&mut data_loader, pubkey_hash); - let tx_hash = tx.hash(); - - let mut buffer: Vec = vec![]; - buffer.resize(40000, 1); - let super_long_message = Bytes::from(&buffer[..]); - - let mut blake2b = ckb_hash::new_blake2b(); - let mut message = [0u8; 32]; - blake2b.update(&tx_hash.raw_data()); - blake2b.update(&super_long_message[..]); - blake2b.finalize(&mut message); - let message = H256::from(message); - let sig = privkey.sign_recoverable(&message).expect("sign"); - let witness = WitnessArgs::new_builder() - .lock(Bytes::from(sig.serialize()).pack()) - .extra(super_long_message.pack()) - .build(); - let tx = tx - .as_advanced_builder() - .set_witnesses(vec![witness.as_bytes().pack()]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_NO_PAIR), - ); -} - -#[test] -fn test_sighash_all_2_in_2_out_cycles() { - const CONSUME_CYCLES: u64 = 3377980; - - let mut data_loader = DummyDataLoader::new(); - let mut generator = Generator::non_crypto_safe_prng(42); - let mut rng = rand::rngs::SmallRng::seed_from_u64(42); - - // key1 - let privkey = generator.gen_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - // key2 - let privkey2 = generator.gen_privkey(); - let pubkey2 = privkey2.pubkey().expect("pubkey"); - let pubkey_hash2 = blake160(&pubkey2.serialize()); - - // sign with 2 keys - let tx = gen_tx_with_grouped_args( - &mut data_loader, - vec![(pubkey_hash, 1), (pubkey_hash2, 1)], - &mut rng, - ); - let tx = sign_tx_by_input_group(tx, &privkey, 0, 1); - let tx = sign_tx_by_input_group(tx, &privkey2, 1, 1); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(MAX_CYCLES); - let cycles = verify_result.expect("pass verification"); - assert_eq!(CONSUME_CYCLES, cycles) -} - -#[test] -fn test_sighash_all_witness_append_junk_data() { - let mut rng = thread_rng(); - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - - // sign with 2 keys - let tx = gen_tx_with_grouped_args(&mut data_loader, vec![(pubkey_hash, 2)], &mut rng); - let tx = sign_tx_by_input_group(tx, &privkey, 0, 2); - let mut witnesses: Vec<_> = Unpack::>::unpack(&tx.witnesses()); - // append junk data to first witness - let mut witness = Vec::new(); - witness.resize(witnesses[0].len(), 0); - witness.copy_from_slice(&witnesses[0]); - witness.push(0); - witnesses[0] = witness.into(); - - let tx = tx - .as_advanced_builder() - .set_witnesses(witnesses.into_iter().map(|w| w.pack()).collect()) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_NO_PAIR), - ); -} - -#[test] -fn test_sighash_all_witness_args_ambiguity() { - // This test case build tx with WitnessArgs(lock, data, "") - // and try unlock with WitnessArgs(lock, "", data) - // - // this case will fail if contract use a naive function to digest witness. - - let mut rng = thread_rng(); - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - - let tx = gen_tx_with_grouped_args(&mut data_loader, vec![(pubkey_hash, 2)], &mut rng); - let tx = sign_tx_by_input_group(tx, &privkey, 0, 2); - let witnesses: Vec<_> = Unpack::>::unpack(&tx.witnesses()); - // move extra data to type_ - let witnesses: Vec<_> = witnesses - .into_iter() - .map(|witness| { - let witness = WitnessArgs::new_unchecked(witness); - let data = witness.extra().clone(); - witness - .as_builder() - .extra(Bytes::new().pack()) - .type_(data) - .build() - }) - .collect(); - - let tx = tx - .as_advanced_builder() - .set_witnesses(witnesses.into_iter().map(|w| w.as_bytes().pack()).collect()) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_PUBKEY_BLAKE160_HASH), - ); -} - -#[test] -fn test_sighash_all_witnesses_ambiguity() { - // This test case sign tx with [witness1, "", witness2] - // and try unlock with [witness1, witness2, ""] - // - // this case will fail if contract use a naive function to digest witness. - - let mut rng = thread_rng(); - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - - let tx = gen_tx_with_grouped_args(&mut data_loader, vec![(pubkey_hash, 3)], &mut rng); - let witness = Unpack::>::unpack(&tx.witnesses()).remove(0); - let tx = tx - .as_advanced_builder() - .set_witnesses(vec![ - witness.pack(), - Bytes::new().pack(), - Bytes::from(vec![42]).pack(), - ]) - .build(); - let tx = sign_tx_by_input_group(tx, &privkey, 0, 3); - - // exchange witness position - let witness = Unpack::>::unpack(&tx.witnesses()).remove(0); - let tx = tx - .as_advanced_builder() - .set_witnesses(vec![ - witness.pack(), - Bytes::from(vec![42]).pack(), - Bytes::new().pack(), - ]) - .build(); - - assert_eq!(tx.witnesses().len(), tx.inputs().len()); - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(MAX_CYCLES); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_PUBKEY_BLAKE160_HASH), - ); -} - -#[test] -fn test_sighash_all_cover_extra_witnesses() { - let mut rng = thread_rng(); - let mut data_loader = DummyDataLoader::new(); - let privkey = Generator::random_privkey(); - let pubkey = privkey.pubkey().expect("pubkey"); - let pubkey_hash = blake160(&pubkey.serialize()); - - let tx = gen_tx_with_grouped_args(&mut data_loader, vec![(pubkey_hash, 2)], &mut rng); - let witness = Unpack::>::unpack(&tx.witnesses()).remove(0); - let tx = tx - .as_advanced_builder() - .set_witnesses(vec![ - witness.pack(), - Bytes::from(vec![42]).pack(), - Bytes::new().pack(), - ]) - .build(); - let tx = sign_tx_by_input_group(tx, &privkey, 0, 3); - assert!(tx.witnesses().len() > tx.inputs().len()); - - // change last witness - let mut witnesses = Unpack::>::unpack(&tx.witnesses()); - let tx = tx - .as_advanced_builder() - .set_witnesses(vec![ - witnesses.remove(0).pack(), - witnesses.remove(1).pack(), - Bytes::from(vec![0]).pack(), - ]) - .build(); - - let resolved_tx = build_resolved_tx(&data_loader, &tx); - let verify_result = - TransactionScriptsVerifier::new(&resolved_tx, &data_loader).verify(60000000); - assert_error_eq!( - verify_result.unwrap_err(), - ScriptError::ValidationFailure(ERROR_PUBKEY_BLAKE160_HASH), - ); -}