diff --git a/Cargo.lock b/Cargo.lock index 831eeb55..8cacb59d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -304,45 +304,44 @@ dependencies = [ [[package]] name = "ckb-build-info" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" [[package]] name = "ckb-chain-spec" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" -dependencies = [ - "ckb-crypto 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-dao-utils 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-error 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-hash 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-jsonrpc-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-pow 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-rational 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-resource 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" +dependencies = [ + "ckb-crypto 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-dao-utils 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-error 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-hash 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-jsonrpc-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-pow 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-rational 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-resource 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ckb-cli" -version = "0.26.0" +version = "0.26.1" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "ckb-build-info 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-crypto 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-hash 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-index 0.26.0", - "ckb-jsonrpc-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-resource 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-sdk 0.26.0", - "ckb-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-util 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-build-info 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-crypto 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-hash 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-index 0.26.1", + "ckb-jsonrpc-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-resource 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-sdk 0.26.1", + "ckb-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-util 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "colored 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -372,10 +371,10 @@ dependencies = [ [[package]] name = "ckb-crypto" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ - "ckb-fixed-hash 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-fixed-hash 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "faster-hex 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -385,40 +384,40 @@ dependencies = [ [[package]] name = "ckb-dao-utils" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ckb-error 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-error 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "enum-display-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ckb-error" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ - "ckb-occupied-capacity 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-occupied-capacity 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "enum-display-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ckb-fixed-hash" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ - "ckb-fixed-hash-core 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-fixed-hash-hack 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-fixed-hash-core 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-fixed-hash-hack 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ckb-fixed-hash-core" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "faster-hex 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -427,10 +426,10 @@ dependencies = [ [[package]] name = "ckb-fixed-hash-hack" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ - "ckb-fixed-hash-core 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-fixed-hash-core 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -439,19 +438,19 @@ dependencies = [ [[package]] name = "ckb-hash" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ "blake2b-rs 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ckb-index" -version = "0.26.0" +version = "0.26.1" dependencies = [ "bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ckb-sdk 0.26.0", - "ckb-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-sdk 0.26.1", + "ckb-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rocksdb 0.12.2 (git+https://github.com/nervosnetwork/rust-rocksdb?rev=14d2991)", @@ -461,21 +460,20 @@ dependencies = [ [[package]] name = "ckb-jsonrpc-types" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ - "ckb-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "faster-hex 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ckb-logger" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", @@ -488,34 +486,32 @@ dependencies = [ "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "sentry 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ckb-occupied-capacity" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ - "ckb-occupied-capacity-core 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-occupied-capacity-macros 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-occupied-capacity-core 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-occupied-capacity-macros 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ckb-occupied-capacity-core" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ckb-occupied-capacity-macros" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ - "ckb-occupied-capacity-core 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-occupied-capacity-core 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", @@ -523,83 +519,80 @@ dependencies = [ [[package]] name = "ckb-pow" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ckb-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "eaglesong 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ckb-rational" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ "numext-fixed-uint 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ckb-resource" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ "ckb-system-scripts 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ckb-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "includedir 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "includedir_codegen 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ckb-script" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ckb-chain-spec 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-error 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-hash 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-logger 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-script-data-loader 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-vm 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ckb-chain-spec 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-error 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-hash 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-logger 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-script-data-loader 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-vm 0.18.2 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "faster-hex 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ckb-script-data-loader" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ - "ckb-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", ] [[package]] name = "ckb-sdk" -version = "0.26.0" +version = "0.26.1" dependencies = [ "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "bech32 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitcoin_hashes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "ckb-crypto 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-hash 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-jsonrpc-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-resource 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-script 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-sdk-types 0.26.0", - "ckb-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-crypto 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-hash 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-jsonrpc-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-resource 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-script 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-sdk-types 0.26.1", + "ckb-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "faster-hex 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -619,14 +612,14 @@ dependencies = [ [[package]] name = "ckb-sdk-types" -version = "0.26.0" -dependencies = [ - "ckb-crypto 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-error 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-hash 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-jsonrpc-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-script 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", +version = "0.26.1" +dependencies = [ + "ckb-crypto 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-error 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-hash 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-jsonrpc-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-script 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -645,16 +638,16 @@ dependencies = [ [[package]] name = "ckb-types" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ "bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "ckb-error 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-fixed-hash 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-hash 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-occupied-capacity 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", - "ckb-rational 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)", + "ckb-error 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-fixed-hash 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-hash 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-occupied-capacity 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", + "ckb-rational 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)", "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "merkle-cbt 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -664,8 +657,8 @@ dependencies = [ [[package]] name = "ckb-util" -version = "0.26.0-pre" -source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2#9d0f54bf9ef84a4173b993f20979c7dc199f1340" +version = "0.26.1-pre" +source = "git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1#04c00b0398d7e47ccebae951b3f01a28183672d1" dependencies = [ "linked-hash-map 0.5.1 (git+https://github.com/nervosnetwork/linked-hash-map?rev=df27f21)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -673,13 +666,13 @@ dependencies = [ [[package]] name = "ckb-vm" -version = "0.18.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "ckb-vm-definitions 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ckb-vm-definitions 0.18.2 (registry+https://github.com/rust-lang/crates.io-index)", "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "goblin 0.0.24 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", @@ -688,7 +681,7 @@ dependencies = [ [[package]] name = "ckb-vm-definitions" -version = "0.18.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -3362,30 +3355,30 @@ dependencies = [ "checksum cexpr 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fce5b5fb86b0c57c20c834c1b412fd09c77c8a59b9473f86272709e78874cd1d" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" -"checksum ckb-build-info 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-chain-spec 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-crypto 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-dao-utils 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-error 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-fixed-hash 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-fixed-hash-core 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-fixed-hash-hack 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-hash 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-jsonrpc-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-logger 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-occupied-capacity 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-occupied-capacity-core 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-occupied-capacity-macros 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-pow 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-rational 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-resource 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-script 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-script-data-loader 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" +"checksum ckb-build-info 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-chain-spec 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-crypto 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-dao-utils 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-error 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-fixed-hash 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-fixed-hash-core 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-fixed-hash-hack 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-hash 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-jsonrpc-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-logger 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-occupied-capacity 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-occupied-capacity-core 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-occupied-capacity-macros 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-pow 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-rational 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-resource 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-script 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-script-data-loader 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" "checksum ckb-system-scripts 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "261dd95a93c09ea24397c85b4fbca061e1da2d6573189749aeb99fe840aaf0c9" -"checksum ckb-types 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-util 0.26.0-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.0-rc2)" = "" -"checksum ckb-vm 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4a645990a41708a9a55ea8a90c42c98d20ce9b2d2dd457f51fce33bca8854474" -"checksum ckb-vm-definitions 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bd01bf493cff1315fd73a3be4a3ec135afe04503b81ebc1b531fcb061b42de6c" +"checksum ckb-types 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-util 0.26.1-pre (git+https://github.com/nervosnetwork/ckb?tag=v0.26.1-rc1)" = "" +"checksum ckb-vm 0.18.2 (registry+https://github.com/rust-lang/crates.io-index)" = "684aca846d958bc5c2196b10839b69ec573cc693b14dc701986adda123ffbef0" +"checksum ckb-vm-definitions 0.18.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5b4a65696efd8974a3c785619f2bfc4c40f2cf318ac184c30aa72d0f539a4a4f" "checksum clang-sys 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81de550971c976f176130da4b2978d3b524eaa0fd9ac31f3ceb5ae1231fb4853" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" diff --git a/Cargo.toml b/Cargo.toml index 5dd92b99..44694984 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,20 +1,20 @@ [package] name = "ckb-cli" -version = "0.26.0" +version = "0.26.1" license = "MIT" authors = ["Linfeng Qian ", "Nervos Core Dev "] edition = "2018" [dependencies] -ckb-jsonrpc-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } -ckb-hash = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } -ckb-crypto = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2", features = ["secp"] } -ckb-build-info = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } -ckb-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } -ckb-util = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } +ckb-jsonrpc-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } +ckb-hash = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } +ckb-crypto = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1", features = ["secp"] } +ckb-build-info = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } +ckb-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } +ckb-util = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } ckb-sdk = { path = "ckb-sdk" } ckb-index = { path = "ckb-index" } -ckb-resource = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } +ckb-resource = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } jsonrpc-client-core = "0.5.0" secp256k1 = {version = "0.15.0" } @@ -48,7 +48,7 @@ tui = "0.6.0" termion = "1.5" [build-dependencies] -ckb-build-info = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } +ckb-build-info = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } [workspace] members = ["ckb-sdk", "ckb-index", "ckb-sdk-types"] diff --git a/Makefile b/Makefile index fe2811db..12a43d94 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ ci: fmt clippy test security-audit git diff --exit-code Cargo.lock integration: - bash devtools/ci/integration.sh v0.25.1 + bash devtools/ci/integration.sh v0.26.1-rc1 prod: ## Build binary with release profile. cargo build --release diff --git a/ckb-index/Cargo.toml b/ckb-index/Cargo.toml index 3492f205..4ce1d8e6 100644 --- a/ckb-index/Cargo.toml +++ b/ckb-index/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ckb-index" -version = "0.26.0" +version = "0.26.1" authors = ["Linfeng Qian ", "Nervos Core Dev "] edition = "2018" license = "MIT" @@ -11,7 +11,7 @@ serde_derive = "1.0" bincode = "1.1.4" log = "0.4.6" failure = "0.1.5" -ckb-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } +ckb-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } ckb-sdk = { path = "../ckb-sdk" } [dependencies.rocksdb] diff --git a/ckb-sdk-types/Cargo.toml b/ckb-sdk-types/Cargo.toml index 53ddb5a8..3933bffe 100644 --- a/ckb-sdk-types/Cargo.toml +++ b/ckb-sdk-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ckb-sdk-types" -version = "0.26.0" +version = "0.26.1" authors = ["Linfeng Qian ", "Nervos Core Dev "] edition = "2018" license = "MIT" @@ -9,14 +9,14 @@ license = "MIT" serde = { version = "1.0", features = ["rc"] } serde_derive = "1.0" -ckb-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } -ckb-script = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2", default-features = false } -ckb-jsonrpc-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } -ckb-hash = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } -ckb-error = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } +ckb-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } +ckb-script = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1", default-features = false } +ckb-jsonrpc-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } +ckb-hash = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } +ckb-error = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } [dev-dependencies] -ckb-crypto = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2", features = ["secp"] } +ckb-crypto = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1", features = ["secp"] } [features] default = ["ckb-script/default"] diff --git a/ckb-sdk/Cargo.toml b/ckb-sdk/Cargo.toml index 3619321c..40f6d51d 100644 --- a/ckb-sdk/Cargo.toml +++ b/ckb-sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ckb-sdk" -version = "0.26.0" +version = "0.26.1" authors = ["Linfeng Qian ", "Nervos Core Dev "] edition = "2018" license = "MIT" @@ -27,10 +27,10 @@ chrono = "0.4.6" failure = "0.1.5" lazy_static = "1.4.0" -ckb-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } -ckb-script = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } -ckb-jsonrpc-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } -ckb-hash = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } -ckb-resource = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2" } -ckb-crypto = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.0-rc2", features = ["secp"] } +ckb-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } +ckb-script = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } +ckb-jsonrpc-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } +ckb-hash = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } +ckb-resource = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1" } +ckb-crypto = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.26.1-rc1", features = ["secp"] } ckb-sdk-types = { path = "../ckb-sdk-types" } diff --git a/ckb-sdk/src/lib.rs b/ckb-sdk/src/lib.rs index 0b7f6673..be430e16 100644 --- a/ckb-sdk/src/lib.rs +++ b/ckb-sdk/src/lib.rs @@ -15,7 +15,7 @@ pub use transaction::{ MockCellDep, MockInfo, MockInput, MockResourceLoader, MockTransaction, MockTransactionHelper, ReprMockCellDep, ReprMockInfo, ReprMockInput, ReprMockTransaction, }; -pub use tx_helper::{MultisigConfig, TxHelper}; +pub use tx_helper::{MultisigConfig, SignerFn, TxHelper}; pub use types::{ Address, AddressPayload, AddressType, CodeHashIndex, HumanCapacity, NetworkType, OldAddress, OldAddressFormat, Since, SinceType, diff --git a/ckb-sdk/src/tx_helper.rs b/ckb-sdk/src/tx_helper.rs index b10e64f4..3971650d 100644 --- a/ckb-sdk/src/tx_helper.rs +++ b/ckb-sdk/src/tx_helper.rs @@ -2,7 +2,8 @@ use ckb_hash::{blake2b_256, new_blake2b}; use ckb_types::{ bytes::Bytes, core::{ScriptHashType, TransactionBuilder, TransactionView}, - packed::{self, Byte32, CellInput, CellOutput, OutPoint, Script, WitnessArgs}, + h256, + packed::{self, Byte32, CellDep, CellInput, CellOutput, OutPoint, Script, WitnessArgs}, prelude::*, H160, H256, }; @@ -82,34 +83,35 @@ impl TxHelper { mut get_live_cell: F, genesis_info: &GenesisInfo, ) -> Result<(), String> { - let mut since_value_opt = - since_absolute_epoch_opt.map(|number| Since::new_absolute_epoch(number).value()); - let lock = get_live_cell(out_point.clone(), false)?.lock(); check_lock_script(&lock)?; - if since_value_opt.is_none() { + + let since = if let Some(number) = since_absolute_epoch_opt { + Since::new_absolute_epoch(number).value() + } else { let lock_arg = lock.args().raw_data(); if lock.code_hash() == MULTISIG_TYPE_HASH.pack() && lock_arg.len() == 28 { let mut since_bytes = [0u8; 8]; since_bytes.copy_from_slice(&lock_arg[20..]); - let since_value = u64::from_le_bytes(since_bytes); - since_value_opt = Some(since_value); + u64::from_le_bytes(since_bytes) + } else { + 0 } - } - let since = since_value_opt.unwrap_or(0); + }; + let input = CellInput::new_builder() .previous_output(out_point) .since(since.pack()) .build(); self.transaction = self.transaction.as_advanced_builder().input(input).build(); - let mut cell_deps = Vec::new(); + let mut cell_deps: HashSet = HashSet::default(); for ((code_hash, _), _) in self.input_group(get_live_cell)?.into_iter() { let code_hash: H256 = code_hash.unpack(); if code_hash == SIGHASH_TYPE_HASH { - cell_deps.push(genesis_info.sighash_dep()); + cell_deps.insert(genesis_info.sighash_dep()); } else if code_hash == MULTISIG_TYPE_HASH { - cell_deps.push(genesis_info.multisig_dep()); + cell_deps.insert(genesis_info.multisig_dep()); } else { panic!("Unexpected input code_hash: {:#x}", code_hash); } @@ -117,7 +119,7 @@ impl TxHelper { self.transaction = self .transaction .as_advanced_builder() - .set_cell_deps(cell_deps) + .set_cell_deps(cell_deps.into_iter().collect()) .build(); Ok(()) } @@ -175,9 +177,8 @@ impl TxHelper { && !self.multisig_configs.contains_key(&hash160) { return Err(format!( - "Invalid input(no.{}) lock script args prefix: 0x{}, expected: {:#x}", + "No mutisig config found for input(no.{}) lock_arg prefix: {:#x}", idx + 1, - hex_string(&lock_arg[..20]).unwrap(), hash160, )); } @@ -197,42 +198,13 @@ impl TxHelper { witnesses } - pub fn sign_sighash_inputs( - &self, - mut signer: S, - get_live_cell: C, - ) -> Result, String> - where - S: FnMut(&H160, &H256) -> Result<[u8; SECP_SIGNATURE_SIZE], String>, - C: FnMut(OutPoint, bool) -> Result, - { - let witnesses = self.init_witnesses(); - let mut signatures: HashMap = Default::default(); - for ((code_hash, lock_arg), idxs) in self.input_group(get_live_cell)?.into_iter() { - if code_hash == MULTISIG_TYPE_HASH.pack() { - continue; - } - let hash160 = H160::from_slice(lock_arg.as_ref()).unwrap(); - let signature = build_signature( - &self.transaction.hash(), - &idxs, - &witnesses, - None, - |message: &H256| signer(&hash160, message), - )?; - signatures.insert(lock_arg, signature); - } - Ok(signatures) - } - - pub fn sign_multisig_inputs( + pub fn sign_inputs( &self, - signer_lock_arg: &H160, mut signer: S, get_live_cell: C, ) -> Result, String> where - S: FnMut(&H256) -> Result<[u8; SECP_SIGNATURE_SIZE], String>, + S: FnMut(&HashSet, &H256) -> Result, String>, C: FnMut(OutPoint, bool) -> Result, { let all_sighash_lock_args = self @@ -244,28 +216,27 @@ impl TxHelper { let witnesses = self.init_witnesses(); let mut signatures: HashMap = Default::default(); for ((code_hash, lock_arg), idxs) in self.input_group(get_live_cell)?.into_iter() { - if code_hash == SIGHASH_TYPE_HASH.pack() { - continue; - } - - let hash160 = H160::from_slice(&lock_arg[..20]).unwrap(); - if code_hash == MULTISIG_TYPE_HASH.pack() - && !all_sighash_lock_args - .get(&hash160) + let multisig_hash160 = H160::from_slice(&lock_arg[..20]).unwrap(); + let lock_args = if code_hash == MULTISIG_TYPE_HASH.pack() { + all_sighash_lock_args + .get(&multisig_hash160) .unwrap() - .contains(signer_lock_arg) - { - continue; + .clone() + } else { + let mut lock_args = HashSet::default(); + lock_args.insert(H160::from_slice(lock_arg.as_ref()).unwrap()); + lock_args + }; + if signer(&lock_args, &h256!("0x0"))?.is_some() { + let signature = build_signature( + &self.transaction.hash(), + &idxs, + &witnesses, + self.multisig_configs.get(&multisig_hash160), + |message: &H256| signer(&lock_args, message).map(|sig| sig.unwrap()), + )?; + signatures.insert(lock_arg, signature); } - - let signature = build_signature( - &self.transaction.hash(), - &idxs, - &witnesses, - Some(self.multisig_configs.get(&hash160).unwrap()), - &mut signer, - )?; - signatures.insert(lock_arg, signature); } Ok(signatures) } @@ -277,9 +248,14 @@ impl TxHelper { let mut witnesses = self.init_witnesses(); for ((code_hash, lock_arg), idxs) in self.input_group(get_live_cell)?.into_iter() { let signatures = self.signatures.get(&lock_arg).ok_or_else(|| { + let lock_script = Script::new_builder() + .hash_type(ScriptHashType::Type.into()) + .code_hash(code_hash.clone()) + .args(lock_arg.pack()) + .build(); format!( - "Missing signatures for lock_arg: 0x{}", - hex_string(&lock_arg).unwrap() + "Missing signatures for lock_hash: {:#x}", + lock_script.calc_script_hash() ) })?; let lock_field = if code_hash == MULTISIG_TYPE_HASH.pack() { @@ -366,19 +342,27 @@ impl TxHelper { } } +pub type SignerFn = Box, &H256) -> Result, String>>; + #[derive(Eq, PartialEq, Clone)] pub struct MultisigConfig { - sighash_addresses: HashSet, + sighash_addresses: Vec, require_first_n: u8, threshold: u8, } impl MultisigConfig { pub fn new_with( - sighash_addresses: HashSet, + sighash_addresses: Vec, require_first_n: u8, threshold: u8, ) -> Result { + let mut addr_set: HashSet<&AddressPayload> = HashSet::default(); + for addr in &sighash_addresses { + if !addr_set.insert(addr) { + return Err(format!("Duplicated address: {:?}", addr)); + } + } if threshold as usize > sighash_addresses.len() { return Err(format!( "Invalid threshold {} > {}", @@ -407,7 +391,12 @@ impl MultisigConfig { }) } - pub fn sighash_addresses(&self) -> &HashSet { + pub fn contains_address(&self, target: &AddressPayload) -> bool { + self.sighash_addresses + .iter() + .any(|payload| payload == target) + } + pub fn sighash_addresses(&self) -> &Vec { &self.sighash_addresses } pub fn require_first_n(&self) -> u8 { diff --git a/devtools/azure/windows-dependencies.yml b/devtools/azure/windows-dependencies.yml index bb1a71dc..779e28d7 100644 --- a/devtools/azure/windows-dependencies.yml +++ b/devtools/azure/windows-dependencies.yml @@ -1,14 +1,18 @@ parameters: rustup_toolchain: '' steps: +- powershell: Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh') + displayName: Install scoop - script: | - choco install -y llvm - set "PATH=%PATH%;C:\Program Files\LLVM\bin" - echo "##vso[task.setvariable variable=PATH;]%PATH%;C:\Program Files\LLVM\bin" + set PATH=%PATH%;%USERPROFILE%\scoop\shims + echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\scoop\shims" + scoop help + displayName: Add scoop to path +- script: scoop install llvm displayName: Install LLVM -- script: choco install -y msys2 +- script: scoop install msys2 displayName: Install msys2 -- script: choco install --allow-empty-checksums -y yasm +- script: scoop install yasm displayName: Install yasm - script: | curl -sSf -o rustup-init.exe https://win.rustup.rs diff --git a/src/interactive.rs b/src/interactive.rs index a91d60d1..7947248e 100644 --- a/src/interactive.rs +++ b/src/interactive.rs @@ -13,7 +13,7 @@ use serde_json::json; use crate::subcommands::{ AccountSubCommand, CliSubCommand, IndexController, IndexRequest, MockTxSubCommand, - MoleculeSubCommand, RpcSubCommand, UtilSubCommand, WalletSubCommand, + MoleculeSubCommand, RpcSubCommand, TxSubCommand, UtilSubCommand, WalletSubCommand, }; use crate::utils::{ completer::CkbCompleter, @@ -326,6 +326,14 @@ impl InteractiveEnv { println!("{}", output); Ok(()) } + ("tx", Some(sub_matches)) => { + let genesis_info = self.genesis_info().ok(); + let output = + TxSubCommand::new(&mut self.rpc_client, &mut self.key_store, genesis_info) + .process(&sub_matches, format, color, debug)?; + println!("{}", output); + Ok(()) + } ("util", Some(sub_matches)) => { let output = UtilSubCommand::new(&mut self.rpc_client).process( &sub_matches, diff --git a/src/main.rs b/src/main.rs index 7b2a049b..df1fa43d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use subcommands::TuiSubCommand; use interactive::InteractiveEnv; use subcommands::{ start_index_thread, AccountSubCommand, CliSubCommand, IndexThreadState, MockTxSubCommand, - MoleculeSubCommand, RpcSubCommand, UtilSubCommand, WalletSubCommand, + MoleculeSubCommand, RpcSubCommand, TxSubCommand, UtilSubCommand, WalletSubCommand, }; use utils::other::sync_to_tip; use utils::{ @@ -132,6 +132,14 @@ fn main() -> Result<(), io::Error> { debug, ) }), + ("tx", Some(sub_matches)) => get_key_store(&ckb_cli_dir).and_then(|mut key_store| { + TxSubCommand::new(&mut rpc_client, &mut key_store, None).process( + &sub_matches, + output_format, + color, + debug, + ) + }), ("util", Some(sub_matches)) => { UtilSubCommand::new(&mut rpc_client).process(&sub_matches, output_format, color, debug) } @@ -219,6 +227,7 @@ pub fn build_cli<'a>(version_short: &'a str, version_long: &'a str) -> App<'a, ' .subcommand(RpcSubCommand::subcommand()) .subcommand(AccountSubCommand::subcommand("account")) .subcommand(MockTxSubCommand::subcommand("mock-tx")) + .subcommand(TxSubCommand::subcommand("tx")) .subcommand(UtilSubCommand::subcommand("util")) .subcommand(MoleculeSubCommand::subcommand("molecule")) .subcommand(WalletSubCommand::subcommand()) @@ -320,6 +329,7 @@ pub fn build_interactive() -> App<'static, 'static> { .subcommand(RpcSubCommand::subcommand()) .subcommand(AccountSubCommand::subcommand("account")) .subcommand(MockTxSubCommand::subcommand("mock-tx")) + .subcommand(TxSubCommand::subcommand("tx")) .subcommand(UtilSubCommand::subcommand("util")) .subcommand(MoleculeSubCommand::subcommand("molecule")) .subcommand(WalletSubCommand::subcommand()) diff --git a/src/subcommands/mod.rs b/src/subcommands/mod.rs index 117409d9..2b9f0707 100644 --- a/src/subcommands/mod.rs +++ b/src/subcommands/mod.rs @@ -4,6 +4,7 @@ pub mod molecule; pub mod rpc; #[cfg(unix)] pub mod tui; +pub mod tx; pub mod util; pub mod wallet; @@ -14,6 +15,7 @@ pub use account::AccountSubCommand; pub use mock_tx::MockTxSubCommand; pub use molecule::MoleculeSubCommand; pub use rpc::RpcSubCommand; +pub use tx::TxSubCommand; pub use util::UtilSubCommand; pub use wallet::{ start_index_thread, IndexController, IndexRequest, IndexResponse, IndexThreadState, diff --git a/src/subcommands/tx.rs b/src/subcommands/tx.rs new file mode 100644 index 00000000..bf929194 --- /dev/null +++ b/src/subcommands/tx.rs @@ -0,0 +1,742 @@ +use std::collections::{HashMap, HashSet}; +use std::convert::TryFrom; +use std::fs; +use std::io::Write; +use std::path::PathBuf; +use std::str::FromStr; + +use ckb_jsonrpc_types as json_types; +use ckb_jsonrpc_types::JsonBytes; +use ckb_sdk::{ + constants::{MULTISIG_TYPE_HASH, SECP_SIGNATURE_SIZE}, + wallet::KeyStore, + Address, AddressPayload, CodeHashIndex, GenesisInfo, HttpRpcClient, HumanCapacity, + MultisigConfig, NetworkType, SignerFn, TxHelper, +}; +use ckb_types::{ + bytes::Bytes, + core::Capacity, + h256, + packed::{self, CellOutput, OutPoint, Script}, + prelude::*, + H160, H256, +}; +use clap::{App, Arg, ArgMatches, SubCommand}; +use faster_hex::hex_string; +use serde_derive::{Deserialize, Serialize}; + +use super::CliSubCommand; +use crate::utils::{ + arg, + arg_parser::{ + AddressParser, ArgParser, CapacityParser, FilePathParser, FixedHashParser, FromStrParser, + HexParser, PrivkeyPathParser, PrivkeyWrapper, + }, + other::{ + check_capacity, get_genesis_info, get_live_cell, get_live_cell_with_cache, + get_network_type, get_privkey_signer, get_to_data, read_password, serialize_signature, + }, + printer::{OutputFormat, Printable}, +}; + +pub struct TxSubCommand<'a> { + rpc_client: &'a mut HttpRpcClient, + key_store: &'a mut KeyStore, + genesis_info: Option, +} + +impl<'a> TxSubCommand<'a> { + pub fn new( + rpc_client: &'a mut HttpRpcClient, + key_store: &'a mut KeyStore, + genesis_info: Option, + ) -> TxSubCommand<'a> { + TxSubCommand { + rpc_client, + key_store, + genesis_info, + } + } + + pub fn subcommand(name: &'static str) -> App<'static, 'static> { + let arg_tx_file = Arg::with_name("tx-file") + .long("tx-file") + .takes_value(true) + .validator(|input| FilePathParser::new(false).validate(input)) + .required(true) + .help("Multisig transaction data file (format: json)"); + let arg_sighash_address = Arg::with_name("sighash-address") + .long("sighash-address") + .takes_value(true) + .multiple(true) + .required(true) + .validator(|input| AddressParser::new_sighash().validate(input)) + .help("Normal sighash address"); + let arg_require_first_n = Arg::with_name("require-first-n") + .long("require-first-n") + .takes_value(true) + .default_value("0") + .validator(|input| FromStrParser::::default().validate(input)) + .help("Require first n signatures of corresponding pubkey"); + let arg_threshold = Arg::with_name("threshold") + .long("threshold") + .takes_value(true) + .default_value("1") + .validator(|input| FromStrParser::::default().validate(input)) + .help("Multisig threshold"); + let arg_since_absolute_epoch = Arg::with_name("since-absolute-epoch") + .long("since-absolute-epoch") + .takes_value(true) + .validator(|input| FromStrParser::::default().validate(input)) + .help("Since absolute epoch number"); + + SubCommand::with_name(name) + .about("Handle common sighash/multisig transaction") + .subcommands(vec![ + SubCommand::with_name("init") + .about("Init a common (sighash/multisig) transaction") + .arg(arg_tx_file.clone()), + SubCommand::with_name("add-multisig-config") + .about("Add multisig config") + .arg(arg_sighash_address.clone()) + .arg(arg_require_first_n.clone()) + .arg(arg_threshold.clone()) + .arg(arg_tx_file.clone()), + SubCommand::with_name("clear-field") + .about("Remove all field items in transaction") + .arg( + Arg::with_name("field") + .long("field") + .takes_value(true) + .required(true) + .possible_values(&["inputs", "outputs", "signatures"]) + .help("The transaction field"), + ) + .arg(arg_tx_file.clone()), + SubCommand::with_name("add-input") + .about("Add cell input (with secp/multisig lock)") + .arg( + Arg::with_name("tx-hash") + .long("tx-hash") + .takes_value(true) + .validator(|input| FixedHashParser::::default().validate(input)) + .required(true) + .help("Transaction hash"), + ) + .arg( + Arg::with_name("index") + .long("index") + .takes_value(true) + .validator(|input| FromStrParser::::default().validate(input)) + .required(true) + .help("Transaction output index"), + ) + .arg(arg_since_absolute_epoch.clone()) + .arg(arg_tx_file.clone()), + SubCommand::with_name("add-output") + .about("Add cell output") + .arg( + Arg::with_name("to-sighash-address") + .long("to-sighash-address") + .conflicts_with_all(&[ + "to-short-multisig-address", + "to-long-multisig-address", + ]) + .takes_value(true) + .validator(|input| AddressParser::new_sighash().validate(input)) + .help("To normal sighash address"), + ) + .arg( + Arg::with_name("to-short-multisig-address") + .long("to-short-multisig-address") + .conflicts_with("to-long-multisig-address") + .takes_value(true) + .validator(|input| AddressParser::new_multisig().validate(input)) + .help("To short multisig address"), + ) + .arg( + Arg::with_name("to-long-multisig-address") + .long("to-long-multisig-address") + .takes_value(true) + .validator(|input| { + AddressParser::default() + .set_full_type(MULTISIG_TYPE_HASH) + .validate(input) + }) + .help("To long multisig address (special case, include since)"), + ) + .arg(arg::capacity().required(true)) + .arg(arg::to_data()) + .arg(arg::to_data_path()) + .arg(arg_tx_file.clone()), + SubCommand::with_name("add-signature") + .about("Add signature") + .arg( + Arg::with_name("lock-arg") + .long("lock-arg") + .takes_value(true) + .required(true) + .validator(|input| match HexParser.parse(&input) { + Ok(ref data) if data.len() == 20 || data.len() == 28 => Ok(()), + Ok(ref data) => Err(format!("invalid data length: {}", data.len())), + Err(err) => Err(err.to_string()), + }) + .help("The lock_arg of input lock script (20 bytes or 28 bytes)"), + ) + .arg( + Arg::with_name("signature") + .long("signature") + .takes_value(true) + .required(true) + .validator(|input| match HexParser.parse(&input) { + Ok(ref data) if data.len() == SECP_SIGNATURE_SIZE => Ok(()), + Ok(ref data) => Err(format!("invalid data length: {}", data.len())), + Err(err) => Err(err.to_string()), + }) + .help("The signature"), + ) + .arg(arg_tx_file.clone()), + SubCommand::with_name("info") + .about("Show detail of this multisig transaction (capacity, tx-fee, etc.)") + .arg(arg_tx_file.clone()), + SubCommand::with_name("sign-inputs") + .about("Sign all sighash/multisig inputs in this transaction") + .arg(arg::privkey_path().required_unless(arg::from_account().b.name)) + .arg(arg::from_account().required_unless(arg::privkey_path().b.name)) + .arg(arg_tx_file.clone()) + .arg( + Arg::with_name("add-signatures") + .long("add-signatures") + .help("Sign and add signatures"), + ), + SubCommand::with_name("send") + .about("Send multisig transaction") + .arg(arg_tx_file.clone()) + .arg( + Arg::with_name("max-tx-fee") + .long("max-tx-fee") + .takes_value(true) + .default_value("1.0") + .validator(|input| CapacityParser.validate(input)) + .help("Max transaction fee (unit: CKB)"), + ), + SubCommand::with_name("build-multisig-address") + .about( + "Build multisig address with multisig config and since(optional) argument", + ) + .arg(arg_sighash_address.clone()) + .arg(arg_require_first_n.clone()) + .arg(arg_threshold.clone()) + .arg(arg_since_absolute_epoch.clone()), + ]) + } +} + +impl<'a> CliSubCommand for TxSubCommand<'a> { + fn process( + &mut self, + matches: &ArgMatches, + format: OutputFormat, + color: bool, + debug: bool, + ) -> Result { + let network = get_network_type(self.rpc_client)?; + + match matches.subcommand() { + ("init", Some(m)) => { + let tx_file_opt: Option = + FilePathParser::new(false).from_matches_opt(m, "tx-file", false)?; + let helper = TxHelper::default(); + let repr = ReprTxHelper::new(helper, network); + + if let Some(tx_file) = tx_file_opt { + let mut file = fs::File::create(&tx_file).map_err(|err| err.to_string())?; + let content = + serde_json::to_string_pretty(&repr).map_err(|err| err.to_string())?; + file.write_all(content.as_bytes()) + .map_err(|err| err.to_string())?; + Ok(String::from("ok")) + } else { + Ok(repr.render(format, color)) + } + } + ("clear-field", Some(m)) => { + let tx_file: PathBuf = FilePathParser::new(true).from_matches(m, "tx-file")?; + let field = m.value_of("field").unwrap(); + modify_tx_file(&tx_file, network, |helper| { + match field { + "inputs" => helper.clear_inputs(), + "outputs" => helper.clear_outputs(), + "signatures" => helper.clear_signatures(), + _ => panic!("Invalid clear field: {}", field), + } + Ok(()) + })?; + Ok(String::from("ok")) + } + ("add-input", Some(m)) => { + let tx_file: PathBuf = FilePathParser::new(true).from_matches(m, "tx-file")?; + let tx_hash: H256 = + FixedHashParser::::default().from_matches(m, "tx-hash")?; + let index: u32 = FromStrParser::::default().from_matches(m, "index")?; + let since_absolute_epoch_opt: Option = FromStrParser::::default() + .from_matches_opt(m, "since-absolute-epoch", false)?; + + let genesis_info = get_genesis_info(&self.genesis_info, self.rpc_client)?; + let out_point = OutPoint::new_builder() + .tx_hash(tx_hash.pack()) + .index(index.pack()) + .build(); + let get_live_cell = |out_point, with_data| { + get_live_cell(self.rpc_client, out_point, with_data).map(|(output, _)| output) + }; + modify_tx_file(&tx_file, network, |helper| { + helper.add_input( + out_point, + since_absolute_epoch_opt, + get_live_cell, + &genesis_info, + ) + })?; + + Ok(String::from("ok")) + } + ("add-output", Some(m)) => { + let tx_file: PathBuf = FilePathParser::new(true).from_matches(m, "tx-file")?; + let capacity: u64 = CapacityParser.from_matches(m, "capacity")?; + let to_sighash_address_opt: Option
= AddressParser::new_sighash() + .from_matches_opt(m, "to-sighash-address", false)?; + let to_short_multisig_address_opt: Option
= AddressParser::new_multisig() + .from_matches_opt(m, "to-short-multisig-address", false)?; + let to_long_multisig_address_opt: Option
= AddressParser::default() + .set_full_type(MULTISIG_TYPE_HASH) + .from_matches_opt(m, "to-long-multisig-address", false)?; + + let to_data = get_to_data(m)?; + check_capacity(capacity, to_data.len())?; + if let Some(address) = to_long_multisig_address_opt.as_ref() { + let payload = address.payload(); + if payload.args().len() != 28 { + return Err(format!( + "Invalid address lock_arg length({}) for `to-long-multisig-address`", + payload.args().len() + )); + } + } + let lock_script = to_sighash_address_opt + .or_else(|| to_short_multisig_address_opt) + .or_else(|| to_long_multisig_address_opt) + .map(|address| Script::from(address.payload())) + .ok_or_else(|| "missing target address".to_string())?; + let output = CellOutput::new_builder() + .capacity(Capacity::shannons(capacity).pack()) + .lock(lock_script) + .build(); + + modify_tx_file(&tx_file, network, |helper| { + helper.add_output(output, to_data); + Ok(()) + })?; + + Ok(String::from("ok")) + } + ("add-signature", Some(m)) => { + let tx_file: PathBuf = FilePathParser::new(true).from_matches(m, "tx-file")?; + let lock_arg: Bytes = HexParser.from_matches(m, "lock-arg")?; + let signature: Bytes = HexParser.from_matches(m, "signature")?; + + modify_tx_file(&tx_file, network, |helper| { + helper.add_signature(lock_arg, signature) + })?; + Ok(String::from("ok")) + } + ("add-multisig-config", Some(m)) => { + let tx_file: PathBuf = FilePathParser::new(false).from_matches(m, "tx-file")?; + let sighash_addresses: Vec
= AddressParser::default() + .set_network(network) + .set_short(CodeHashIndex::Sighash) + .from_matches_vec(m, "sighash-address")?; + let require_first_n: u8 = + FromStrParser::::default().from_matches(m, "require-first-n")?; + let threshold: u8 = FromStrParser::::default().from_matches(m, "threshold")?; + + let sighash_addresses = sighash_addresses + .into_iter() + .map(|address| address.payload().clone()) + .collect::>(); + let cfg = MultisigConfig::new_with(sighash_addresses, require_first_n, threshold)?; + modify_tx_file(&tx_file, network, |helper| { + helper.add_multisig_config(cfg); + Ok(()) + })?; + Ok(String::from("ok")) + } + ("info", Some(m)) => { + let tx_file: PathBuf = FilePathParser::new(false).from_matches(m, "tx-file")?; + + let mut live_cell_cache: HashMap<(OutPoint, bool), (CellOutput, Bytes)> = + Default::default(); + let mut get_live_cell = |out_point: OutPoint, with_data: bool| { + get_live_cell_with_cache( + &mut live_cell_cache, + self.rpc_client, + out_point, + with_data, + ) + }; + + let file = fs::File::open(tx_file).map_err(|err| err.to_string())?; + let repr: ReprTxHelper = + serde_json::from_reader(&file).map_err(|err| err.to_string())?; + let helper = TxHelper::try_from(repr)?; + let tx = helper.transaction(); + + let mut input_total = 0; + for input in tx.inputs().into_iter() { + let (output, data) = get_live_cell(input.previous_output(), true)?; + let capacity: u64 = output.capacity().unpack(); + input_total += capacity; + + let type_script_empty = output.type_().to_opt().is_none(); + let prefix = if helper + .signatures() + .contains_key(&output.lock().args().raw_data()) + { + "input(signed)" + } else { + "input" + }; + print_cell_info( + prefix, + network, + output.lock(), + capacity, + data.len(), + type_script_empty, + ); + } + + let mut output_total = 0; + for (output, data) in tx.outputs().into_iter().zip(tx.outputs_data().into_iter()) { + let capacity: u64 = output.capacity().unpack(); + output_total += capacity; + let data_len = data.raw_data().len(); + let type_script_empty = output.type_().is_none(); + print_cell_info( + "output", + network, + output.lock(), + capacity, + data_len, + type_script_empty, + ); + } + let tx_fee_string = if input_total >= output_total { + format!("{:#}", HumanCapacity(input_total - output_total)) + } else { + format!("-{:#}", HumanCapacity(output_total - input_total)) + }; + + let resp = serde_json::json!({ + "input_total": format!("{:#}", HumanCapacity(input_total)), + "output_total": format!("{:#}", HumanCapacity(output_total)), + "tx_fee": tx_fee_string, + }); + Ok(resp.render(format, color)) + } + ("sign-inputs", Some(m)) => { + let tx_file: PathBuf = FilePathParser::new(true).from_matches(m, "tx-file")?; + let privkey_opt: Option = + PrivkeyPathParser.from_matches_opt(m, "privkey-path", false)?; + let account_opt: Option = FixedHashParser::::default() + .from_matches_opt(m, "from-account", false)?; + + let signer = if let Some(privkey) = privkey_opt { + get_privkey_signer(privkey) + } else { + let password = read_password(false, None)?; + let account = account_opt.unwrap(); + let key_store = self.key_store.clone(); + get_keystore_signer(key_store, account, password) + }; + + let mut live_cell_cache: HashMap<(OutPoint, bool), (CellOutput, Bytes)> = + Default::default(); + let get_live_cell = |out_point: OutPoint, with_data: bool| { + get_live_cell_with_cache( + &mut live_cell_cache, + self.rpc_client, + out_point, + with_data, + ) + .map(|(output, _)| output) + }; + + let signatures = modify_tx_file(&tx_file, network, |helper| { + let signatures = helper.sign_inputs(signer, get_live_cell)?; + if m.is_present("add-signatures") { + for (lock_arg, signature) in signatures.clone() { + helper.add_signature(lock_arg, signature)?; + } + } + Ok(signatures) + })?; + let resp = signatures + .into_iter() + .map(|(lock_arg, signature)| { + serde_json::json!({ + "lock-arg": format!("0x{}", hex_string(&lock_arg).unwrap()), + "signature": format!("0x{}", hex_string(&signature).unwrap()), + }) + }) + .collect::>(); + Ok(resp.render(format, color)) + } + ("send", Some(m)) => { + let tx_file: PathBuf = FilePathParser::new(false).from_matches(m, "tx-file")?; + let max_tx_fee: u64 = CapacityParser.from_matches(m, "max-tx-fee")?; + + let mut live_cell_cache: HashMap<(OutPoint, bool), (CellOutput, Bytes)> = + Default::default(); + let mut get_live_cell = |out_point: OutPoint, with_data: bool| { + get_live_cell_with_cache( + &mut live_cell_cache, + self.rpc_client, + out_point, + with_data, + ) + .map(|(output, _)| output) + }; + + let file = fs::File::open(tx_file).map_err(|err| err.to_string())?; + let repr: ReprTxHelper = + serde_json::from_reader(&file).map_err(|err| err.to_string())?; + let helper = TxHelper::try_from(repr)?; + + let (input_total, output_total) = helper.check_tx(&mut get_live_cell)?; + let tx_fee = input_total - output_total; + if tx_fee > max_tx_fee { + return Err(format!( + "Too much transaction fee: {:#}, max: {:#}", + HumanCapacity(tx_fee), + HumanCapacity(max_tx_fee), + )); + } + let tx = helper.build_tx(&mut get_live_cell)?; + let rpc_tx = json_types::Transaction::from(tx.data()); + if debug { + println!("[send transaction]:\n{}", rpc_tx.render(format, color)); + } + let resp = self + .rpc_client + .send_transaction(tx.data()) + .map_err(|err| format!("Send transaction error: {}", err))?; + Ok(resp.render(format, color)) + } + ("build-multisig-address", Some(m)) => { + let sighash_addresses: Vec
= AddressParser::default() + .set_network(network) + .set_short(CodeHashIndex::Sighash) + .from_matches_vec(m, "sighash-address")?; + let require_first_n: u8 = + FromStrParser::::default().from_matches(m, "require-first-n")?; + let threshold: u8 = FromStrParser::::default().from_matches(m, "threshold")?; + let since_absolute_epoch_opt: Option = FromStrParser::::default() + .from_matches_opt(m, "since-absolute-epoch", false)?; + + let sighash_addresses = sighash_addresses + .into_iter() + .map(|address| address.payload().clone()) + .collect::>(); + let cfg = MultisigConfig::new_with(sighash_addresses, require_first_n, threshold)?; + let address_payload = cfg.to_address_payload(since_absolute_epoch_opt); + let lock_script = Script::from(&address_payload); + let resp = serde_json::json!({ + "mainnet": Address::new(NetworkType::Mainnet, address_payload.clone()).to_string(), + "testnet": Address::new(NetworkType::Testnet, address_payload.clone()).to_string(), + "lock-arg": format!("0x{}", hex_string(address_payload.args().as_ref()).unwrap()), + "lock-hash": format!("{:#x}", lock_script.calc_script_hash()) + }); + Ok(resp.render(format, color)) + } + _ => Err(matches.usage().to_owned()), + } + } +} + +fn print_cell_info( + prefix: &str, + network: NetworkType, + lock: packed::Script, + capacity: u64, + data_len: usize, + type_script_empty: bool, +) { + let address_payload = AddressPayload::from(lock); + let lock_kind = if address_payload.code_hash() == MULTISIG_TYPE_HASH.pack() { + if address_payload.args().len() == 20 { + "multisig without since" + } else { + "multisig with since" + } + } else { + "sighash(secp)" + }; + let address = Address::new(network, address_payload); + let type_script_status = if type_script_empty { "none" } else { "some" }; + println!( + "[{}] {} => {}, (data-length: {}, type-script: {}, lock-kind: {})", + prefix, + address, + HumanCapacity(capacity), + data_len, + type_script_status, + lock_kind, + ); +} + +fn get_keystore_signer(key_store: KeyStore, account: H160, password: String) -> SignerFn { + Box::new(move |lock_args: &HashSet, message: &H256| { + if lock_args.contains(&account) { + if message == &h256!("0x0") { + Ok(Some([0u8; 65])) + } else { + key_store + .sign_recoverable_with_password(&account, None, message, password.as_bytes()) + .map(|signature| Some(serialize_signature(&signature))) + .map_err(|err| err.to_string()) + } + } else { + Ok(None) + } + }) +} + +fn modify_tx_file Result>( + path: &PathBuf, + network: NetworkType, + func: F, +) -> Result { + let file = fs::File::open(path).map_err(|err| err.to_string())?; + let repr: ReprTxHelper = serde_json::from_reader(&file).map_err(|err| err.to_string())?; + let mut helper = TxHelper::try_from(repr)?; + + let result = func(&mut helper)?; + + let repr = ReprTxHelper::new(helper, network); + let mut file = fs::File::create(path).map_err(|err| err.to_string())?; + let content = serde_json::to_string_pretty(&repr).map_err(|err| err.to_string())?; + file.write_all(content.as_bytes()) + .map_err(|err| err.to_string())?; + Ok(result) +} + +#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Debug)] +#[serde(deny_unknown_fields)] +struct ReprTxHelper { + transaction: json_types::Transaction, + multisig_configs: HashMap, + signatures: HashMap>, +} + +impl ReprTxHelper { + fn new(tx: TxHelper, network: NetworkType) -> Self { + ReprTxHelper { + transaction: tx.transaction().data().into(), + multisig_configs: tx + .multisig_configs() + .iter() + .map(|(lock_arg, cfg)| { + ( + lock_arg.clone(), + ReprMultisigConfig::new(cfg.clone(), network), + ) + }) + .collect(), + signatures: tx + .signatures() + .iter() + .map(|(lock_arg, signatures)| { + ( + JsonBytes::from_bytes(lock_arg.clone()), + signatures + .iter() + .cloned() + .map(JsonBytes::from_bytes) + .collect(), + ) + }) + .collect(), + } + } +} + +impl TryFrom for TxHelper { + type Error = String; + fn try_from(repr: ReprTxHelper) -> Result { + let transaction = packed::Transaction::from(repr.transaction).into_view(); + let multisig_configs = repr + .multisig_configs + .into_iter() + .map(|(_, repr_cfg)| MultisigConfig::try_from(repr_cfg)) + .collect::, String>>()?; + let signatures: HashMap> = repr + .signatures + .into_iter() + .map(|(lock_arg, signatures)| { + ( + lock_arg.into_bytes(), + signatures.into_iter().map(JsonBytes::into_bytes).collect(), + ) + }) + .collect(); + + let mut tx_helper = TxHelper::new(transaction); + for cfg in multisig_configs { + tx_helper.add_multisig_config(cfg); + } + for (lock_arg, sub_signatures) in signatures { + for sub_signature in sub_signatures { + tx_helper.add_signature(lock_arg.clone(), sub_signature)?; + } + } + Ok(tx_helper) + } +} + +#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Debug)] +#[serde(deny_unknown_fields)] +struct ReprMultisigConfig { + sighash_addresses: Vec, + require_first_n: u8, + threshold: u8, +} + +impl ReprMultisigConfig { + fn new(cfg: MultisigConfig, network: NetworkType) -> Self { + let sighash_addresses = cfg + .sighash_addresses() + .iter() + .map(|payload| Address::new(network, payload.clone()).to_string()) + .collect(); + ReprMultisigConfig { + sighash_addresses, + require_first_n: cfg.require_first_n(), + threshold: cfg.threshold(), + } + } +} + +impl TryFrom for MultisigConfig { + type Error = String; + fn try_from(repr: ReprMultisigConfig) -> Result { + let sighash_addresses = repr + .sighash_addresses + .into_iter() + .map(|address_string| { + Address::from_str(&address_string).map(|addr| addr.payload().clone()) + }) + .collect::, String>>()?; + MultisigConfig::new_with(sighash_addresses, repr.require_first_n, repr.threshold) + } +} diff --git a/src/subcommands/wallet/mod.rs b/src/subcommands/wallet/mod.rs index 42bb618c..8015ab34 100644 --- a/src/subcommands/wallet/mod.rs +++ b/src/subcommands/wallet/mod.rs @@ -1,13 +1,12 @@ mod index; -use std::collections::HashMap; -use std::fs; -use std::io::Read; +use std::collections::{HashMap, HashSet}; use std::path::PathBuf; use ckb_types::{ bytes::Bytes, core::{BlockView, Capacity, EpochNumberWithFraction, ScriptHashType, TransactionView}, + h256, packed::{CellOutput, OutPoint, Script}, prelude::*, H160, H256, @@ -18,10 +17,13 @@ use super::CliSubCommand; use crate::utils::{ arg, arg_parser::{ - AddressParser, ArgParser, CapacityParser, FixedHashParser, FromStrParser, HexParser, + AddressParser, ArgParser, CapacityParser, FixedHashParser, FromStrParser, PrivkeyPathParser, PrivkeyWrapper, }, - other::{get_address, get_live_cell_with_cache, get_network_type, read_password}, + other::{ + check_capacity, get_address, get_live_cell_with_cache, get_network_type, + get_privkey_signer, get_to_data, read_password, serialize_signature, + }, printer::{OutputFormat, Printable}, }; use ckb_index::{with_index_db, IndexDatabase, LiveCellInfo}; @@ -32,7 +34,8 @@ use ckb_sdk::{ SIGHASH_TYPE_HASH, }, wallet::{DerivationPath, KeyStore}, - Address, AddressPayload, GenesisInfo, HttpRpcClient, HumanCapacity, TxHelper, SECP256K1, + Address, AddressPayload, GenesisInfo, HttpRpcClient, HumanCapacity, MultisigConfig, SignerFn, + Since, SinceType, TxHelper, SECP256K1, }; pub use index::{ start_index_thread, CapacityResult, IndexController, IndexRequest, IndexResponse, @@ -110,6 +113,7 @@ impl<'a> WalletSubCommand<'a> { .required_unless(arg::privkey_path().b.name) .conflicts_with(arg::privkey_path().b.name), ) + .arg(arg::from_locked_address()) .arg(arg::to_address().required(true)) .arg(arg::to_data()) .arg(arg::to_data_path()) @@ -150,27 +154,44 @@ impl<'a> WalletSubCommand<'a> { color: bool, debug: bool, ) -> Result { - let from_privkey: Option = + let network_type = get_network_type(self.rpc_client)?; + + let from_privkey_opt: Option = PrivkeyPathParser.from_matches_opt(m, "privkey-path", false)?; - let from_account: Option = - FixedHashParser::::default().from_matches_opt(m, "from-account", false)?; + let from_account_opt: Option = FixedHashParser::::default() + .from_matches_opt(m, "from-account", false) + .or_else(|err| { + let result: Result, String> = AddressParser::new_sighash() + .set_network(network_type) + .from_matches_opt(m, "from-account", false); + result + .map(|address_opt| { + address_opt + .map(|address| H160::from_slice(&address.payload().args()).unwrap()) + }) + .map_err(|_| err) + })?; + let from_locked_address_opt: Option
= AddressParser::default() + .set_network(network_type) + .set_full_type(MULTISIG_TYPE_HASH.clone()) + .from_matches_opt(m, "from-locked-address", false)?; let to_capacity: u64 = CapacityParser.from_matches(m, "capacity")?; let tx_fee: u64 = CapacityParser.from_matches(m, "tx-fee")?; let receiving_address_length: u32 = FromStrParser::::default().from_matches(m, "derive-receiving-address-length")?; - let network_type = get_network_type(self.rpc_client)?; let last_change_address_opt: Option
= AddressParser::default() .set_network(network_type) .from_matches_opt(m, "derive-change-address", false)?; - let (from_address_payload, password) = if let Some(from_privkey) = from_privkey.as_ref() { + let (from_address_payload, password) = if let Some(from_privkey) = from_privkey_opt.as_ref() + { let from_pubkey = secp256k1::PublicKey::from_secret_key(&SECP256K1, from_privkey); (AddressPayload::from_pubkey(&from_pubkey), String::new()) } else { let password = read_password(false, None)?; ( - AddressPayload::from_pubkey_hash(from_account.clone().unwrap()), + AddressPayload::from_pubkey_hash(from_account_opt.clone().unwrap()), password, ) }; @@ -179,20 +200,43 @@ impl<'a> WalletSubCommand<'a> { let to_address: Address = AddressParser::default() .set_network(network_type) .from_matches(m, "to-address")?; - if !(to_address.payload().hash_type() == ScriptHashType::Type - && to_address.payload().code_hash() == SIGHASH_TYPE_HASH.pack() - && to_address.payload().args().len() == 20) - && !(to_address.payload().hash_type() == ScriptHashType::Type - && to_address.payload().code_hash() == MULTISIG_TYPE_HASH.pack() - && (to_address.payload().args().len() == 20 - || to_address.payload().args().len() == 28)) + + if let Some(from_locked_address) = from_locked_address_opt.as_ref() { + let args = from_locked_address.payload().args(); + let err_prefix = "Invalid from-locked-address's args"; + if args.len() != 28 { + return Err(format!("{}: invalid {}", err_prefix, args.len())); + } + let mut since_bytes = [0u8; 8]; + since_bytes.copy_from_slice(&args[20..]); + let since = Since::from_raw_value(u64::from_le_bytes(since_bytes)); + if !since.flags_is_valid() { + return Err(format!("{}: invalid since flags", err_prefix)); + } + if !since.is_absolute() { + return Err(format!("{}: only support absolute since value", err_prefix)); + } + if since.extract_metric().map(|(ty, _)| ty) != Some(SinceType::EpochNumberWithFraction) + { + return Err(format!("{}: only support epoch since value", err_prefix)); + } + } + + let to_address_hash_type = to_address.payload().hash_type(); + let to_address_code_hash: H256 = to_address.payload().code_hash().unpack(); + let to_address_args_len = to_address.payload().args().len(); + if !(to_address_hash_type == ScriptHashType::Type + && to_address_code_hash == SIGHASH_TYPE_HASH + && to_address_args_len == 20) + && !(to_address_hash_type == ScriptHashType::Type + && to_address_code_hash == MULTISIG_TYPE_HASH + && (to_address_args_len == 20 || to_address_args_len == 28)) { return Err(format!("Invalid to-address: {}", to_address)); } - - let to_data = to_data(m)?; - + let to_data = get_to_data(m)?; check_capacity(to_capacity, to_data.len())?; + let genesis_info = self.genesis_info()?; // For check index database is ready @@ -201,8 +245,11 @@ impl<'a> WalletSubCommand<'a> { let genesis_hash = genesis_info.header().hash(); let genesis_info_clone = genesis_info.clone(); - let from_lock_arg = H160::from_slice(from_address.payload().args().as_ref()).unwrap(); + // The lock hashes for search live cells let mut lock_hashes = vec![Script::from(&from_address_payload).calc_script_hash()]; + let mut helper = TxHelper::default(); + + let from_lock_arg = H160::from_slice(from_address.payload().args().as_ref()).unwrap(); let mut path_map: HashMap = Default::default(); let change_address_payload = if let Some(last_change_address) = last_change_address_opt { // Behave like HD wallet @@ -228,11 +275,36 @@ impl<'a> WalletSubCommand<'a> { from_address.payload().clone() }; + if let Some(from_locked_address) = from_locked_address_opt.as_ref() { + lock_hashes.insert( + 0, + Script::from(from_locked_address.payload()).calc_script_hash(), + ); + for lock_arg in std::iter::once(&from_lock_arg).chain(path_map.keys()) { + let mut sighash_addresses = Vec::default(); + sighash_addresses.push(AddressPayload::from_pubkey_hash(lock_arg.clone())); + let require_first_n = 0; + let threshold = 1; + let cfg = MultisigConfig::new_with(sighash_addresses, require_first_n, threshold)?; + if cfg.hash160().as_bytes() == &from_locked_address.payload().args()[0..20] { + helper.add_multisig_config(cfg); + break; + } + } + if helper.multisig_configs().is_empty() { + return Err(String::from( + "from-locked-address is not created from the key or derived keys", + )); + } + } + let max_mature_number = get_max_mature_number(self.rpc_client)?; let mut from_capacity = 0; let mut infos: Vec = Default::default(); let mut terminator = |_, info: &LiveCellInfo| { - if info.type_hashes.is_none() + if from_capacity >= to_capacity + tx_fee { + (true, false) + } else if info.type_hashes.is_none() && info.data_bytes == 0 && is_mature(info, max_mature_number) { @@ -275,11 +347,12 @@ impl<'a> WalletSubCommand<'a> { } let key_store = self.key_store.clone(); - let mut live_cell_cache: HashMap<(OutPoint, bool), CellOutput> = Default::default(); + let mut live_cell_cache: HashMap<(OutPoint, bool), (CellOutput, Bytes)> = + Default::default(); let mut get_live_cell_fn = |out_point: OutPoint, with_data: bool| { get_live_cell_with_cache(&mut live_cell_cache, self.rpc_client, out_point, with_data) + .map(|(output, _)| output) }; - let mut helper = TxHelper::default(); for info in &infos { helper.add_input(info.out_point(), None, &mut get_live_cell_fn, &genesis_info)?; } @@ -295,36 +368,13 @@ impl<'a> WalletSubCommand<'a> { .build(); helper.add_output(change_output, Bytes::default()); } - let signer = |hash160: &H160, message: &H256| { - let signature = if let Some(privkey) = from_privkey.as_ref() { - let message = secp256k1::Message::from_slice(message.as_bytes()) - .expect("Convert to secp256k1 message failed"); - SECP256K1.sign_recoverable(&message, privkey) - } else { - let path = if hash160 == &from_lock_arg { - None - } else { - match path_map.get(hash160) { - Some(path) => Some(path), - None => panic!("No derive path for {:#x}", hash160), - } - }; - key_store - .sign_recoverable_with_password( - &from_lock_arg, - path, - message, - password.as_bytes(), - ) - .map_err(|err| err.to_string())? - }; - let (recov_id, data) = signature.serialize_compact(); - let mut signature_bytes = [0u8; 65]; - signature_bytes[0..64].copy_from_slice(&data[0..64]); - signature_bytes[64] = recov_id.to_i32() as u8; - Ok(signature_bytes) + + let signer = if let Some(from_privkey) = from_privkey_opt { + get_privkey_signer(from_privkey) + } else { + get_keystore_signer(key_store, path_map, from_lock_arg, password) }; - for (lock_arg, signature) in helper.sign_sighash_inputs(signer, &mut get_live_cell_fn)? { + for (lock_arg, signature) in helper.sign_inputs(signer, &mut get_live_cell_fn)? { helper.add_signature(lock_arg, signature)?; } let tx = helper.build_tx(&mut get_live_cell_fn)?; @@ -376,7 +426,15 @@ impl<'a> CliSubCommand for WalletSubCommand<'a> { .from_matches(m, "derive-receiving-address-length")?; let change_address_length: u32 = FromStrParser::::default() .from_matches(m, "derive-change-address-length")?; - let address_payload = get_address(Some(network_type), m)?; + let address_payload = if let Some(address_str) = m.value_of("address") { + AddressParser::default() + .set_network(network_type) + .parse(address_str)? + .payload() + .clone() + } else { + get_address(Some(network_type), m)? + }; let mut lock_hashes = vec![Script::from(&address_payload).calc_script_hash()]; if m.is_present("derived") { let password = read_password(false, None)?; @@ -573,20 +631,37 @@ impl<'a> CliSubCommand for WalletSubCommand<'a> { } } -fn check_capacity(capacity: u64, to_data_len: usize) -> Result<(), String> { - if capacity < MIN_SECP_CELL_CAPACITY { - return Err(format!( - "Capacity can not less than {} shannons", - MIN_SECP_CELL_CAPACITY - )); - } - if capacity < MIN_SECP_CELL_CAPACITY + (to_data_len as u64 * ONE_CKB) { - return Err(format!( - "Capacity can not hold {} bytes of data", - to_data_len - )); - } - Ok(()) +fn get_keystore_signer( + key_store: KeyStore, + path_map: HashMap, + account: H160, + password: String, +) -> SignerFn { + Box::new(move |lock_args: &HashSet, message: &H256| { + let path = if lock_args.contains(&account) { + None + } else { + let mut path_opt = None; + for lock_arg in lock_args { + if let Some(path) = path_map.get(lock_arg) { + path_opt = Some(path); + break; + } + } + if path_opt.is_none() { + return Ok(None); + } + path_opt + }; + if message == &h256!("0x0") { + return Ok(Some([0u8; 65])); + } + let signature = key_store + .sign_recoverable_with_password(&account, path, message, password.as_bytes()) + .map_err(|err| err.to_string())?; + + Ok(Some(serialize_signature(&signature))) + }) } // Get max mature block number @@ -619,21 +694,3 @@ fn is_mature(info: &LiveCellInfo, max_mature_number: u64) -> bool { || info.number == 0 || info.number <= max_mature_number } - -fn to_data(m: &ArgMatches) -> Result { - let to_data_opt: Option = HexParser.from_matches_opt(m, "to-data", false)?; - match to_data_opt { - Some(data) => Ok(data), - None => { - if let Some(path) = m.value_of("to-data-path") { - let mut content = Vec::new(); - let mut file = fs::File::open(path).map_err(|err| err.to_string())?; - file.read_to_end(&mut content) - .map_err(|err| err.to_string())?; - Ok(Bytes::from(content)) - } else { - Ok(Bytes::new()) - } - } - } -} diff --git a/src/utils/arg.rs b/src/utils/arg.rs index c7ebe355..5c9b2c73 100644 --- a/src/utils/arg.rs +++ b/src/utils/arg.rs @@ -83,8 +83,25 @@ pub fn from_account<'a, 'b>() -> Arg<'a, 'b> { Arg::with_name("from-account") .long("from-account") .takes_value(true) - .validator(|input| FixedHashParser::::default().validate(input)) - .help("The account's lock-arg (transfer from this account)") + .validator(|input| { + FixedHashParser::::default() + .validate(input.clone()) + .or_else(|err| { + AddressParser::default() + .validate(input.clone()) + .and_then(|()| AddressParser::new_sighash().validate(input)) + .map_err(|_| err) + }) + }) + .help("The account's lock-arg or sighash address (transfer from this account)") +} + +pub fn from_locked_address<'a, 'b>() -> Arg<'a, 'b> { + Arg::with_name("from-locked-address") + .long("from-locked-address") + .takes_value(true) + .validator(|input| AddressParser::default().validate(input)) + .help("The time locked multisig address to search live cells (which S=0,R=0,M=1,N=1 and have since value)") } pub fn to_address<'a, 'b>() -> Arg<'a, 'b> { diff --git a/src/utils/arg_parser.rs b/src/utils/arg_parser.rs index 8ae3c79c..19c1b56d 100644 --- a/src/utils/arg_parser.rs +++ b/src/utils/arg_parser.rs @@ -236,6 +236,7 @@ impl ArgParser for DirPathParser { } } +#[derive(Clone)] pub struct PrivkeyWrapper(pub secp256k1::SecretKey); // For security purpose @@ -318,7 +319,6 @@ pub enum AddressPayloadOption { Full(Option), #[allow(dead_code)] FullData(Option), - #[allow(dead_code)] FullType(Option), } @@ -341,6 +341,19 @@ impl AddressParser { AddressParser { network, payload } } + pub fn new_sighash() -> Self { + AddressParser { + network: None, + payload: Some(AddressPayloadOption::Short(Some(CodeHashIndex::Sighash))), + } + } + pub fn new_multisig() -> Self { + AddressParser { + network: None, + payload: Some(AddressPayloadOption::Short(Some(CodeHashIndex::Multisig))), + } + } + pub fn set_network(&mut self, network: NetworkType) -> &mut Self { self.network = Some(network); self @@ -366,7 +379,6 @@ impl AddressParser { self.payload = Some(AddressPayloadOption::FullData(Some(code_hash))); self } - #[allow(dead_code)] pub fn set_full_type(&mut self, code_hash: H256) -> &mut Self { self.payload = Some(AddressPayloadOption::FullType(Some(code_hash))); self diff --git a/src/utils/other.rs b/src/utils/other.rs index fa8da120..c05565b8 100644 --- a/src/utils/other.rs +++ b/src/utils/other.rs @@ -1,16 +1,22 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::fs; +use std::io::Read; use std::path::PathBuf; use std::time::{SystemTime, UNIX_EPOCH}; +use ckb_hash::blake2b_256; use ckb_index::{with_index_db, IndexDatabase, VERSION}; use ckb_sdk::{ + constants::{MIN_SECP_CELL_CAPACITY, ONE_CKB}, rpc::AlertMessage, wallet::{KeyStore, ScryptType}, - Address, AddressPayload, CodeHashIndex, GenesisInfo, HttpRpcClient, NetworkType, + Address, AddressPayload, CodeHashIndex, GenesisInfo, HttpRpcClient, NetworkType, SignerFn, + SECP256K1, }; use ckb_types::{ + bytes::Bytes, core::BlockView, + h256, packed::{CellOutput, OutPoint}, prelude::*, H160, H256, @@ -19,7 +25,9 @@ use clap::ArgMatches; use colored::Colorize; use rpassword::prompt_password_stdout; -use super::arg_parser::{AddressParser, ArgParser, FixedHashParser, PubkeyHexParser}; +use super::arg_parser::{ + AddressParser, ArgParser, FixedHashParser, HexParser, PrivkeyWrapper, PubkeyHexParser, +}; pub fn read_password(repeat: bool, prompt: Option<&str>) -> Result { let prompt = prompt.unwrap_or("Password"); @@ -127,11 +135,11 @@ pub fn get_genesis_info( } pub fn get_live_cell_with_cache( - cache: &mut HashMap<(OutPoint, bool), CellOutput>, + cache: &mut HashMap<(OutPoint, bool), (CellOutput, Bytes)>, client: &mut HttpRpcClient, out_point: OutPoint, with_data: bool, -) -> Result { +) -> Result<(CellOutput, Bytes), String> { if let Some(output) = cache.get(&(out_point.clone(), with_data)).cloned() { Ok(output) } else { @@ -145,15 +153,30 @@ pub fn get_live_cell( client: &mut HttpRpcClient, out_point: OutPoint, with_data: bool, -) -> Result { - let cell = client.get_live_cell(out_point, with_data)?; +) -> Result<(CellOutput, Bytes), String> { + let cell = client.get_live_cell(out_point.clone(), with_data)?; if cell.status != "live" { - return Err(format!("Invalid cell status: {}", cell.status)); + return Err(format!( + "Invalid cell status: {}, out_point: {}", + cell.status, out_point + )); } let cell_status = cell.status.clone(); cell.cell - .map(|cell| cell.output.into()) - .ok_or_else(|| format!("Invalid input cell, status: {}", cell_status)) + .map(|cell| { + ( + cell.output.into(), + cell.data + .map(|data| data.content.into_bytes()) + .unwrap_or_default(), + ) + }) + .ok_or_else(|| { + format!( + "Invalid input cell, status: {}, out_point: {}", + cell_status, out_point + ) + }) } pub fn get_network_type(rpc_client: &mut HttpRpcClient) -> Result { @@ -187,3 +210,65 @@ pub fn sync_to_tip(rpc_client: &mut HttpRpcClient, index_dir: &PathBuf) -> Resul } Ok(()) } + +pub fn check_capacity(capacity: u64, to_data_len: usize) -> Result<(), String> { + if capacity < MIN_SECP_CELL_CAPACITY { + return Err(format!( + "Capacity can not less than {} shannons", + MIN_SECP_CELL_CAPACITY + )); + } + if capacity < MIN_SECP_CELL_CAPACITY + (to_data_len as u64 * ONE_CKB) { + return Err(format!( + "Capacity can not hold {} bytes of data", + to_data_len + )); + } + Ok(()) +} + +pub fn get_to_data(m: &ArgMatches) -> Result { + let to_data_opt: Option = HexParser.from_matches_opt(m, "to-data", false)?; + match to_data_opt { + Some(data) => Ok(data), + None => { + if let Some(path) = m.value_of("to-data-path") { + let mut content = Vec::new(); + let mut file = fs::File::open(path).map_err(|err| err.to_string())?; + file.read_to_end(&mut content) + .map_err(|err| err.to_string())?; + Ok(Bytes::from(content)) + } else { + Ok(Bytes::new()) + } + } + } +} + +pub fn get_privkey_signer(privkey: PrivkeyWrapper) -> SignerFn { + let pubkey = secp256k1::PublicKey::from_secret_key(&SECP256K1, &privkey); + let lock_arg = H160::from_slice(&blake2b_256(&pubkey.serialize()[..])[0..20]) + .expect("Generate hash(H160) from pubkey failed"); + Box::new(move |lock_args: &HashSet, message: &H256| { + if lock_args.contains(&lock_arg) { + if message == &h256!("0x0") { + Ok(Some([0u8; 65])) + } else { + let message = secp256k1::Message::from_slice(message.as_bytes()) + .expect("Convert to secp256k1 message failed"); + let signature = SECP256K1.sign_recoverable(&message, &privkey); + Ok(Some(serialize_signature(&signature))) + } + } else { + Ok(None) + } + }) +} + +pub fn serialize_signature(signature: &secp256k1::recovery::RecoverableSignature) -> [u8; 65] { + let (recov_id, data) = signature.serialize_compact(); + let mut signature_bytes = [0u8; 65]; + signature_bytes[0..64].copy_from_slice(&data[0..64]); + signature_bytes[64] = recov_id.to_i32() as u8; + signature_bytes +}