在许多逻辑里,我们需要验证交易内是否有Owner参与,比如sUDT的发行等。目前的Owner判定是扫描所有的input_cell内是否有某个cell的lock_hash与Owner的lock_hash是一样的,但如果Owner是acp_lock的话,由于acp的性质,这种Owner判定将完全失去作用。
近期NexisDAO与YokaiSwap开始测试,但同样对接的metamask却有不一样的ckb地址,这种资产割裂让许多用户都感到困惑与不便,经过了解也与acp问题有关,通过修改owner判定,应该可以使这两个地址变得统一。
在与@kylexu讨论了这个问题,即owner的判定除了判断lock_hash相等之外,还必须判定其是否为acp流程。
在原本的acp判定逻辑里,lock只检查Witness的lock域是否为空来判定,如果witness.lock为空,则走acp逻辑,否则走验签逻辑。
int has_signature(int *has_sig) {
int ret;
unsigned char temp[MAX_WITNESS_SIZE];
/* Load witness of first input */
uint64_t witness_len = MAX_WITNESS_SIZE;
ret = ckb_load_witness(temp, &witness_len, 0, 0, CKB_SOURCE_GROUP_INPUT);
if ((ret == CKB_INDEX_OUT_OF_BOUND) ||
(ret == CKB_SUCCESS && witness_len == 0)) {
*has_sig = 0;
return CKB_SUCCESS;
}
if (ret != CKB_SUCCESS) {
return ERROR_SYSCALL;
}
if (witness_len > MAX_WITNESS_SIZE) {
return ERROR_WITNESS_SIZE;
}
/* load signature */
mol_seg_t lock_bytes_seg;
ret = extract_witness_lock(temp, witness_len, &lock_bytes_seg);
if (ret != 0) {
return ERROR_ENCODING;
}
*has_sig = lock_bytes_seg.size > 0;
return CKB_SUCCESS;
}
但新版ckb-production-scripts将这里的逻辑改成了lock要存在并且lock的长度等于signature_size的时候,才走验签逻辑。
/*
* 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;
}
我不知道限制长度的好处是什么,但是这种改动使得acp的判定变得极其复杂且不统一。‘
如果采取最简单的判定,只需要检查对应Witness不为且lock不为空即可,由于Witness经过molecule编码以后的模式为 4Bytes的总长度+4bytes的lock_start+4Bytes的input_start+4bytes的output_start+lock数据+input数据+output数据。所以我们只需要从Witness的第四个字节开始,读取八个字节,如果读到的前四个字节小于后四个字节,即可判定为非acp流程。
pub fn check_owner_mode(args: &Bytes) -> Result<bool, Error> {
// With owner lock script extracted, we will look through each input in the
// current transaction to see if any unlocked cell uses owner lock and not use acp logic.
let is_owner_mode = QueryIter::new(load_cell_lock_hash, Source::Input)
.position(|lock_hash| args[..] == lock_hash[..])
.map(|index| {
let mut buf = [0u8; 8];
let len = load_witness(&mut buf, 4, index, Source::Input)?;
if len < 12 {
None
}
let start = unpack_number(&buf);
let end = unpack_number(&buf[4..]);
if start <= end {
None
}
true
})
.is_some();
Ok(is_owner_mode)
}