diff --git a/Cargo.lock b/Cargo.lock index 7f5dfec2..279307d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -351,6 +351,7 @@ dependencies = [ "byteorder", "chrono", "ckb-build-info", + "ckb-chain-spec", "ckb-cli-plugin-protocol", "ckb-crypto", "ckb-dao-utils", diff --git a/Cargo.toml b/Cargo.toml index ed01aeb3..13eed813 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ ckb-types = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.35.0-rc1" ckb-util = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.35.0-rc1" } ckb-resource = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.35.0-rc1" } ckb-dao-utils = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.35.0-rc1" } +ckb-chain-spec = { git = "https://github.com/nervosnetwork/ckb", tag = "v0.35.0-rc1" } ckb-sdk = { path = "ckb-sdk" } ckb-index = { path = "ckb-index" } plugin-protocol = { path = "plugin-protocol", package = "ckb-cli-plugin-protocol" } diff --git a/default.nix b/default.nix index 7174e01f..9bc59b37 100644 --- a/default.nix +++ b/default.nix @@ -1,28 +1,21 @@ -{ pkgs ? import (builtins.fetchTarball { # 2020-02-13 (nixos-19.09) - url = "https://github.com/NixOS/nixpkgs/archive/e02fb6eaf70d4f6db37ce053edf79b731f13c838.tar.gz"; - sha256 = "1dbjbak57vl7kcgpm1y1nm4s74gjfzpfgk33xskdxj9hjphi6mws"; - }) {} +{ pkgsFun ? import (import ./nix/nixpkgs/thunk.nix) -, fetch ? { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }: - if !fetchSubmodules && !private then builtins.fetchTarball { - url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256; - } else (import {}).fetchFromGitHub { - inherit owner repo rev sha256 fetchSubmodules private; - } - -, rustOverlay ? import - "${fetch (builtins.fromJSON (builtins.readFile ./nix/nixpkgs-mozilla/github.json))}/rust-overlay.nix" - pkgs - pkgs +, rustOverlay ? import "${import ./nix/nixpkgs-mozilla/thunk.nix}/rust-overlay.nix" # Rust manifest hash must be updated when rust-toolchain file changes. -, rustPackages ? rustOverlay.rustChannelOf { +, rustPackages ? pkgs.rustChannelOf { date = "2020-05-04"; rustToolchain = ./rust-toolchain; - sha256 = "sha256-Pj5c6sufuY0IZg/NwiVUqOB0z85OlLDrGbWyYYk9tx4="; + sha256 = "07mp7n4n3cmm37mv152frv7p9q58ahjw5k8gcq48vfczrgm5qgiy"; + } + +, pkgs ? pkgsFun { + overlays = [ + rustOverlay + ]; } -, gitignoreNix ? fetch (builtins.fromJSON (builtins.readFile ./nix/gitignore.nix/github.json)) +, gitignoreNix ? import ./nix/gitignore.nix/thunk.nix }: @@ -40,5 +33,5 @@ in rustPlatform.buildRustPackage { verifyCargoDeps = true; # Cargo hash must be updated when Cargo.lock file changes. - cargoSha256 = "039bvmi7ai5zd3k46jfr1r82sfn84x4hg7c34sxsncwifd8p5yc6"; + cargoSha256 = "12kkhbfcl5x2k3n73rfrza7zmjf67s943gbcnvy061l80r6jry2s"; } diff --git a/nix/gitignore.nix/default.nix b/nix/gitignore.nix/default.nix index 0cf822e3..2b4d4ab1 100644 --- a/nix/gitignore.nix/default.nix +++ b/nix/gitignore.nix/default.nix @@ -1,8 +1,2 @@ # DO NOT HAND-EDIT THIS FILE -let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }: - if !fetchSubmodules && !private then builtins.fetchTarball { - url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256; - } else (import {}).fetchFromGitHub { - inherit owner repo rev sha256 fetchSubmodules private; - }; -in import (fetch (builtins.fromJSON (builtins.readFile ./github.json))) +import (import ./thunk.nix) \ No newline at end of file diff --git a/nix/gitignore.nix/github.json b/nix/gitignore.nix/github.json index 4da992e6..4277b320 100644 --- a/nix/gitignore.nix/github.json +++ b/nix/gitignore.nix/github.json @@ -3,6 +3,6 @@ "repo": "gitignore.nix", "branch": "master", "private": false, - "rev": "2ced4519f865341adcb143c5d668f955a2cb997f", - "sha256": "0fc5bgv9syfcblp23y05kkfnpgh3gssz6vn24frs8dzw39algk2z" + "rev": "c4662e662462e7bf3c2a968483478a665d00e717", + "sha256": "1npnx0h6bd0d7ql93ka7azhj40zgjp815fw2r6smg8ch9p7mzdlx" } diff --git a/nix/gitignore.nix/thunk.nix b/nix/gitignore.nix/thunk.nix new file mode 100644 index 00000000..bbf2dc18 --- /dev/null +++ b/nix/gitignore.nix/thunk.nix @@ -0,0 +1,9 @@ +# DO NOT HAND-EDIT THIS FILE +let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }: + if !fetchSubmodules && !private then builtins.fetchTarball { + url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256; + } else (import {}).fetchFromGitHub { + inherit owner repo rev sha256 fetchSubmodules private; + }; + json = builtins.fromJSON (builtins.readFile ./github.json); +in fetch json \ No newline at end of file diff --git a/nix/nixpkgs-mozilla/default.nix b/nix/nixpkgs-mozilla/default.nix index 0cf822e3..2b4d4ab1 100644 --- a/nix/nixpkgs-mozilla/default.nix +++ b/nix/nixpkgs-mozilla/default.nix @@ -1,8 +1,2 @@ # DO NOT HAND-EDIT THIS FILE -let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }: - if !fetchSubmodules && !private then builtins.fetchTarball { - url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256; - } else (import {}).fetchFromGitHub { - inherit owner repo rev sha256 fetchSubmodules private; - }; -in import (fetch (builtins.fromJSON (builtins.readFile ./github.json))) +import (import ./thunk.nix) \ No newline at end of file diff --git a/nix/nixpkgs-mozilla/github.json b/nix/nixpkgs-mozilla/github.json index 67fb042b..2d097ee8 100644 --- a/nix/nixpkgs-mozilla/github.json +++ b/nix/nixpkgs-mozilla/github.json @@ -3,6 +3,6 @@ "repo": "nixpkgs-mozilla", "branch": "master", "private": false, - "rev": "e912ed483e980dfb4666ae0ed17845c4220e5e7c", - "sha256": "08fvzb8w80bkkabc1iyhzd15f4sm7ra10jn32kfch5klgl0gj3j3" + "rev": "efda5b357451dbb0431f983cca679ae3cd9b9829", + "sha256": "11wqrg86g3qva67vnk81ynvqyfj0zxk83cbrf0p9hsvxiwxs8469" } diff --git a/nix/nixpkgs-mozilla/thunk.nix b/nix/nixpkgs-mozilla/thunk.nix new file mode 100644 index 00000000..bbf2dc18 --- /dev/null +++ b/nix/nixpkgs-mozilla/thunk.nix @@ -0,0 +1,9 @@ +# DO NOT HAND-EDIT THIS FILE +let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }: + if !fetchSubmodules && !private then builtins.fetchTarball { + url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256; + } else (import {}).fetchFromGitHub { + inherit owner repo rev sha256 fetchSubmodules private; + }; + json = builtins.fromJSON (builtins.readFile ./github.json); +in fetch json \ No newline at end of file diff --git a/nix/nixpkgs/default.nix b/nix/nixpkgs/default.nix new file mode 100644 index 00000000..2b4d4ab1 --- /dev/null +++ b/nix/nixpkgs/default.nix @@ -0,0 +1,2 @@ +# DO NOT HAND-EDIT THIS FILE +import (import ./thunk.nix) \ No newline at end of file diff --git a/nix/nixpkgs/github.json b/nix/nixpkgs/github.json new file mode 100644 index 00000000..95aa733e --- /dev/null +++ b/nix/nixpkgs/github.json @@ -0,0 +1,8 @@ +{ + "owner": "nixos", + "repo": "nixpkgs", + "branch": "nixos-20.03", + "private": false, + "rev": "252bfe0107587d40092057f338e9ffcf7bbd90cb", + "sha256": "1ljw98lcc04mlz6pprlgd1plinwl5q8fraakk6bx8igkiqlxaadn" +} diff --git a/nix/nixpkgs/thunk.nix b/nix/nixpkgs/thunk.nix new file mode 100644 index 00000000..bbf2dc18 --- /dev/null +++ b/nix/nixpkgs/thunk.nix @@ -0,0 +1,9 @@ +# DO NOT HAND-EDIT THIS FILE +let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }: + if !fetchSubmodules && !private then builtins.fetchTarball { + url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256; + } else (import {}).fetchFromGitHub { + inherit owner repo rev sha256 fetchSubmodules private; + }; + json = builtins.fromJSON (builtins.readFile ./github.json); +in fetch json \ No newline at end of file diff --git a/shell.nix b/shell.nix new file mode 100644 index 00000000..43f9b379 --- /dev/null +++ b/shell.nix @@ -0,0 +1,4 @@ +{ pkgs ? import {}}: +(import ./. {}).overrideAttrs (p: { + nativeBuildInputs = p.nativeBuildInputs ++ [ pkgs.cargo-watch pkgs.rustfmt ]; +}) diff --git a/src/subcommands/api_server.rs b/src/subcommands/api_server.rs index 2d98d6a4..1800e015 100644 --- a/src/subcommands/api_server.rs +++ b/src/subcommands/api_server.rs @@ -441,6 +441,7 @@ impl HttpTransferArgs { tx_fee, to_address: self.to_address, to_data: self.to_data, + is_type_id: false, } } } diff --git a/src/subcommands/util.rs b/src/subcommands/util.rs index ddbac749..2ad67cc9 100644 --- a/src/subcommands/util.rs +++ b/src/subcommands/util.rs @@ -33,7 +33,7 @@ use crate::utils::{ AddressParser, AddressPayloadOption, ArgParser, FilePathParser, FixedHashParser, FromStrParser, HexParser, PrivkeyPathParser, PrivkeyWrapper, PubkeyHexParser, }, - other::{get_address, read_password, serialize_signature}, + other::{get_address, get_network_type, read_password, serialize_signature}, }; use crate::{build_cli, get_version}; @@ -265,6 +265,29 @@ impl<'a> UtilSubCommand<'a> { .validator(|input| DateTime::parse_from_rfc3339(&input).map(|_| ()).map_err(|err| err.to_string())) .about("The locktime in RFC3339 format. Example: 2014-11-28T21:00:00+00:00") ), + App::new("cell-meta") + .about("Query live cell's metadata") + .arg( + Arg::with_name("tx-hash") + .long("tx-hash") + .takes_value(true) + .validator(|input| FixedHashParser::::default().validate(input)) + .required(true) + .about("Tx hash"), + ) + .arg( + Arg::with_name("index") + .long("index") + .takes_value(true) + .validator(|input| FromStrParser::::default().validate(input)) + .required(true) + .about("Output index"), + ) + .arg( + Arg::with_name("with-data") + .long("with-data") + .about("Get live cell with data") + ), App::new("completions") .about("Generates completion scripts for your shell") .arg( @@ -670,6 +693,44 @@ message = "0x" }); Ok(Output::new_output(resp)) } + ("cell-meta", Some(m)) => { + let tx_hash: H256 = + FixedHashParser::::default().from_matches(m, "tx-hash")?; + let index: u32 = FromStrParser::::default().from_matches(m, "index")?; + let with_data = m.is_present("with-data"); + let out_point = packed::OutPoint::new_builder() + .tx_hash(tx_hash.pack()) + .index(index.pack()) + .build(); + let cell_with_status = self.rpc_client.get_live_cell(out_point, true)?; + if cell_with_status.status != "live" { + Ok(Output::new_output(cell_with_status)) + } else { + let network = get_network_type(self.rpc_client)?; + let info = cell_with_status.cell.expect("cell.info"); + let output = info.output; + let data = info.data.expect("info.data"); + let packed_output = packed::CellOutput::from(output.clone()); + let lock_hash: H256 = packed_output.lock().calc_script_hash().unpack(); + let address_payload = AddressPayload::from(packed_output.lock()); + let address = Address::new(network, address_payload); + let type_hash: Option = packed_output + .type_() + .to_opt() + .map(|script| script.calc_script_hash().unpack()); + let mut resp = serde_json::json!({ + "output": output, + "data_hash": data.hash, + "lock_hash": lock_hash, + "type_hash": type_hash, + "address": address.to_string(), + }); + if with_data { + resp["data"] = serde_json::json!(data.content); + } + Ok(Output::new_output(resp)) + } + } ("completions", Some(m)) => { let shell = m.value_of("shell").unwrap(); let version = get_version(); diff --git a/src/subcommands/wallet/mod.rs b/src/subcommands/wallet/mod.rs index d75ee77f..6fd44166 100644 --- a/src/subcommands/wallet/mod.rs +++ b/src/subcommands/wallet/mod.rs @@ -3,12 +3,13 @@ mod index; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; +use ckb_hash::new_blake2b; use ckb_jsonrpc_types as json_types; use ckb_types::{ bytes::Bytes, core::{BlockView, Capacity, ScriptHashType, TransactionView}, h256, - packed::{self, Byte32, CellOutput, OutPoint, Script}, + packed::{self, Byte32, CellOutput, OutPoint, Script, ScriptOpt}, prelude::*, H160, H256, }; @@ -30,6 +31,7 @@ use crate::utils::{ read_password, sync_to_tip, }, }; +use ckb_chain_spec::consensus::TYPE_ID_CODE_HASH; use ckb_index::{with_index_db, IndexDatabase, LiveCellInfo}; use ckb_sdk::{ constants::{ @@ -127,6 +129,11 @@ impl<'a> WalletSubCommand<'a> { .arg(arg::derive_receiving_address_length()) .arg( arg::derive_change_address().conflicts_with(arg::privkey_path().get_name()), + ) + .arg( + Arg::with_name("type-id") + .long("type-id") + .about("Add type id type script to target output cell"), ), App::new("get-capacity") .about("Get capacity by lock script hash or address or lock arg or pubkey") @@ -177,6 +184,7 @@ impl<'a> WalletSubCommand<'a> { tx_fee, to_address, to_data, + is_type_id, } = args; let network_type = get_network_type(self.rpc_client)?; @@ -413,9 +421,34 @@ impl<'a> WalletSubCommand<'a> { skip_check, )?; } + + // Add outputs + let type_script = if is_type_id { + let mut blake2b = new_blake2b(); + let first_cell_input = helper + .transaction() + .inputs() + .into_iter() + .next() + .expect("inputs empty"); + blake2b.update(first_cell_input.as_slice()); + blake2b.update(&0u64.to_le_bytes()); + let mut ret = [0; 32]; + blake2b.finalize(&mut ret); + Some( + Script::new_builder() + .code_hash(TYPE_ID_CODE_HASH.pack()) + .hash_type(ScriptHashType::Type.into()) + .args(Bytes::from(ret[..].to_vec()).pack()) + .build(), + ) + } else { + None + }; let to_output = CellOutput::new_builder() .capacity(Capacity::shannons(to_capacity).pack()) .lock(to_address.payload().into()) + .type_(ScriptOpt::new_builder().set(type_script).build()) .build(); helper.add_output(to_output, to_data); if rest_capacity >= MIN_SECP_CELL_CAPACITY { @@ -566,6 +599,7 @@ impl<'a> CliSubCommand for WalletSubCommand<'a> { .map(|s| s.to_string()), to_address: get_arg_value(m, "to-address")?, to_data: Some(to_data), + is_type_id: m.is_present("type-id"), }; let tx = self.transfer(args, false)?; if debug { @@ -830,6 +864,7 @@ pub struct TransferArgs { pub tx_fee: String, pub to_address: String, pub to_data: Option, + pub is_type_id: bool, } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/test/src/spec/wallet.rs b/test/src/spec/wallet.rs index 04f1dffa..ca9b9bc9 100644 --- a/test/src/spec/wallet.rs +++ b/test/src/spec/wallet.rs @@ -1,6 +1,7 @@ use crate::miner::Miner; use crate::setup::Setup; use crate::spec::Spec; +use ckb_chain_spec::consensus::TYPE_ID_CODE_HASH; use std::fs; use tempfile::tempdir; @@ -122,6 +123,28 @@ impl Spec for WalletTransfer { ACCOUNT2_ADDRESS )); assert!(output.contains(&tx_hash)); + + // create type id cell with transfer + let tx_hash = setup.cli(&format!( + "wallet transfer --privkey-path {} --to-address {} --capacity 20000 --tx-fee 0.00001 --type-id", + miner_privkey, ACCOUNT1_ADDRESS, + )); + log::info!( + "transfer from miner to account1 with 20000 CKB: {}, and with type_id lock script", + tx_hash + ); + setup.miner().generate_blocks(3); + let output = setup.cli(&format!( + "wallet get-capacity --address {}", + ACCOUNT1_ADDRESS + )); + assert_eq!(output, "total: 37999.99999 (CKB)"); + let output = setup.cli(&format!( + "wallet get-live-cells --address {}", + ACCOUNT1_ADDRESS + )); + assert!(output.contains(&tx_hash)); + assert!(output.contains(&format!("{:x}", TYPE_ID_CODE_HASH))); } }