1
1
use anyhow:: anyhow;
2
-
3
2
use std:: { cmp:: Ordering , collections:: HashSet , convert:: TryFrom } ;
4
3
5
4
use ckb_types:: {
@@ -9,11 +8,27 @@ use ckb_types::{
9
8
prelude:: * ,
10
9
} ;
11
10
12
- use crate :: types:: omni_lock:: OmniLockWitnessLock ;
13
11
use crate :: { traits:: TransactionDependencyProvider , unlock:: omni_lock:: OmniLockFlags } ;
12
+ use crate :: {
13
+ tx_builder:: { gen_script_groups, ScriptGroups } ,
14
+ types:: omni_lock:: OmniLockWitnessLock ,
15
+ } ;
14
16
15
17
use super :: OpenTxError ;
16
18
19
+ /// Check if different
20
+ fn check_script_groups ( group_vec : & [ ScriptGroups ] ) -> Result < ( ) , OpenTxError > {
21
+ let mut keys = HashSet :: new ( ) ;
22
+ for group in group_vec. iter ( ) {
23
+ let len = keys. len ( ) ;
24
+ keys. extend ( group. lock_groups . keys ( ) . clone ( ) ) ;
25
+ if len + group. lock_groups . len ( ) > keys. len ( ) {
26
+ return Err ( OpenTxError :: SameLockInDifferentOpenTx ) ;
27
+ }
28
+ }
29
+ Ok ( ( ) )
30
+ }
31
+
17
32
/// Assemble a transaction from multiple opentransaction, remove duplicate cell deps and header deps.
18
33
/// Alter base input/output index.
19
34
pub fn assemble_new_tx (
@@ -29,21 +44,33 @@ pub fn assemble_new_tx(
29
44
let mut header_deps = HashSet :: new ( ) ;
30
45
let mut base_input_idx = 0usize ;
31
46
let mut base_output_idx = 0usize ;
47
+ let mut base_input_cap = 0usize ;
48
+ let mut base_output_cap = 0usize ;
49
+ let group_vec: Result < Vec < _ > , _ > = transactions
50
+ . iter ( )
51
+ . map ( |tx| gen_script_groups ( tx, provider) )
52
+ . collect ( ) ;
53
+ let group_vec = group_vec?;
54
+ check_script_groups ( & group_vec) ?;
32
55
for tx in transactions. iter ( ) {
33
56
cell_deps. extend ( tx. cell_deps ( ) ) ;
34
57
header_deps. extend ( tx. header_deps ( ) ) ;
35
58
builder = builder. inputs ( tx. inputs ( ) ) ;
59
+ base_input_cap += tx. inputs ( ) . len ( ) ;
60
+ base_output_cap += tx. outputs ( ) . len ( ) ;
36
61
// Handle opentx witness
37
62
for ( input, witness) in tx. inputs ( ) . into_iter ( ) . zip ( tx. witnesses ( ) . into_iter ( ) ) {
38
63
let lock = provider. get_cell ( & input. previous_output ( ) ) ?. lock ( ) ;
39
64
let code_hash = lock. code_hash ( ) ;
40
- if code_hash. cmp ( & opentx_code_hash) == Ordering :: Equal {
65
+ // empty witness should be in a script group
66
+ if !witness. is_empty ( ) && code_hash. cmp ( & opentx_code_hash) == Ordering :: Equal {
41
67
let args = & lock. args ( ) . raw_data ( ) ;
42
- if args. len ( ) >= 22
68
+ let witness_data = witness. raw_data ( ) ;
69
+ if witness_data. len ( ) > 8 // sizeof base_input + sizeof base_output
70
+ && args. len ( ) >= 22
43
71
&& OmniLockFlags :: from_bits_truncate ( args[ 21 ] ) . contains ( OmniLockFlags :: OPENTX )
44
72
{
45
73
// Parse lock data
46
- let witness_data = witness. raw_data ( ) ;
47
74
let current_witness: WitnessArgs =
48
75
WitnessArgs :: from_slice ( witness_data. as_ref ( ) ) ?;
49
76
let lock_field = current_witness
@@ -64,11 +91,17 @@ pub fn assemble_new_tx(
64
91
tmp. copy_from_slice ( & data[ 0 ..4 ] ) ;
65
92
let this_base_input_idx = u32:: from_le_bytes ( tmp)
66
93
+ u32:: try_from ( base_input_idx) . map_err ( |e| anyhow ! ( e) ) ?;
94
+ if this_base_input_idx as usize > base_input_cap {
95
+ return Err ( OpenTxError :: BaseInputIndexOverFlow ) ;
96
+ }
67
97
data[ 0 ..4 ] . copy_from_slice ( & this_base_input_idx. to_le_bytes ( ) ) ;
68
98
69
99
tmp. copy_from_slice ( & data[ 4 ..8 ] ) ;
70
100
let this_base_output_idx = u32:: from_le_bytes ( tmp)
71
101
+ u32:: try_from ( base_output_idx) . map_err ( |e| anyhow ! ( e) ) ?;
102
+ if this_base_output_idx as usize > base_output_cap {
103
+ return Err ( OpenTxError :: BaseOutputIndexOverFlow ) ;
104
+ }
72
105
data[ 4 ..8 ] . copy_from_slice ( & this_base_output_idx. to_le_bytes ( ) ) ;
73
106
74
107
let omnilock_witnesslock = omnilock_witnesslock
0 commit comments