From f84c114f283db7332591840099dc801db879879e Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 27 Jan 2020 15:50:20 -0800 Subject: [PATCH] Revert "Merge pull request #401 from tendermint/cosmos-stdtx" This reverts commit 86fc35629cc5ff5a602ee90f91de0843196a2b4a, reversing changes made to 520727fcfa05f7687f389feb12a31e96b32183ad. Migrating this crate here instead: https://github.com/iqlusioninc/crates/tree/develop/stdtx --- .circleci/config.yml | 7 +- Cargo.lock | 88 --------- Cargo.toml | 6 +- cosmos-stdtx/Cargo.toml | 30 --- cosmos-stdtx/README.md | 64 ------- cosmos-stdtx/src/address.rs | 53 ------ cosmos-stdtx/src/decimal.rs | 130 ------------- cosmos-stdtx/src/error.rs | 102 ---------- cosmos-stdtx/src/lib.rs | 149 --------------- cosmos-stdtx/src/msg.rs | 73 -------- cosmos-stdtx/src/msg/builder.rs | 174 ------------------ cosmos-stdtx/src/msg/field.rs | 43 ----- cosmos-stdtx/src/msg/value.rs | 81 -------- cosmos-stdtx/src/schema.rs | 147 --------------- cosmos-stdtx/src/schema/definition.rs | 78 -------- cosmos-stdtx/src/schema/field.rs | 107 ----------- cosmos-stdtx/src/schema/value_type.rs | 63 ------- cosmos-stdtx/src/stdtx.rs | 122 ------------ cosmos-stdtx/src/stdtx/builder.rs | 102 ---------- cosmos-stdtx/src/type_name.rs | 87 --------- cosmos-stdtx/tests/integration.rs | 19 -- .../tests/support/example_schema.toml | 32 ---- 22 files changed, 3 insertions(+), 1754 deletions(-) delete mode 100644 cosmos-stdtx/Cargo.toml delete mode 100644 cosmos-stdtx/README.md delete mode 100644 cosmos-stdtx/src/address.rs delete mode 100644 cosmos-stdtx/src/decimal.rs delete mode 100644 cosmos-stdtx/src/error.rs delete mode 100644 cosmos-stdtx/src/lib.rs delete mode 100644 cosmos-stdtx/src/msg.rs delete mode 100644 cosmos-stdtx/src/msg/builder.rs delete mode 100644 cosmos-stdtx/src/msg/field.rs delete mode 100644 cosmos-stdtx/src/msg/value.rs delete mode 100644 cosmos-stdtx/src/schema.rs delete mode 100644 cosmos-stdtx/src/schema/definition.rs delete mode 100644 cosmos-stdtx/src/schema/field.rs delete mode 100644 cosmos-stdtx/src/schema/value_type.rs delete mode 100644 cosmos-stdtx/src/stdtx.rs delete mode 100644 cosmos-stdtx/src/stdtx/builder.rs delete mode 100644 cosmos-stdtx/src/type_name.rs delete mode 100644 cosmos-stdtx/tests/integration.rs delete mode 100644 cosmos-stdtx/tests/support/example_schema.toml diff --git a/.circleci/config.yml b/.circleci/config.yml index e539c54..61a4657 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,13 +4,10 @@ jobs: build: docker: - image: tendermint/kms:build-2019-06-05-v0 # bump cache keys when modifying this - environment: - CARGO_INCREMENTAL: 0 - RUSTFLAGS: -D warnings steps: - checkout - restore_cache: - key: cache-2020-01-27-v0 # bump save_cache key below too + key: cache-2019-06-05-v0 # bump save_cache key below too - run: name: Install Rust 1.39.0 # TODO: update Rust in the upstream Docker image command: | @@ -64,7 +61,7 @@ jobs: cargo build --features=softsign TMKMS_BIN=./target/debug/tmkms sh tests/support/run-harness-tests.sh - save_cache: - key: cache-2020-01-27-v0 # bump restore_cache key above too + key: cache-2019-06-05-v0 # bump restore_cache key above too paths: - "~/.cargo" - "./target" diff --git a/Cargo.lock b/Cargo.lock index 1ad0573..cf63d5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -382,24 +382,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "cosmos-stdtx" -version = "0.0.1" -dependencies = [ - "anomaly", - "ecdsa", - "prost-amino", - "prost-amino-derive", - "rust_decimal", - "serde", - "serde_json", - "sha2", - "signatory-secp256k1", - "subtle-encoding 0.5.0", - "thiserror", - "toml", -] - [[package]] name = "crc32fast" version = "1.2.0" @@ -1132,41 +1114,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "num" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f115de20ad793e857f76da2563ff4a09fbcfd6fe93cca0c5d996ab5f3ee38d" -dependencies = [ - "autocfg 1.0.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" -dependencies = [ - "autocfg 1.0.0", - "num-traits", -] - [[package]] name = "num-integer" version = "0.1.42" @@ -1177,29 +1124,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" -dependencies = [ - "autocfg 1.0.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" -dependencies = [ - "autocfg 1.0.0", - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.11" @@ -1638,18 +1562,6 @@ dependencies = [ "libusb1-sys", ] -[[package]] -name = "rust_decimal" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e33d8f7b289776cbd63687e26f5f25ca4b624c8eb1c9d5ea7c2cae2097e7fb" -dependencies = [ - "byteorder", - "bytes", - "num", - "serde", -] - [[package]] name = "rustc-demangle" version = "0.1.16" diff --git a/Cargo.toml b/Cargo.toml index 1d22de6..903a89b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,16 +4,12 @@ description = "Tendermint Key Management System" version = "0.7.1" # Also update html_root_url in lib.rs when bumping this authors = ["Tony Arcieri ", "Ismail Khoffi "] license = "Apache-2.0" -homepage = "https://tendermint.com/" -repository = "https://github.com/tendermint/kms/" +homepage = "https://github.com/tendermint/kms/" readme = "README.md" categories = ["cryptography"] keywords = ["cosmos", "ed25519", "kms", "key-management", "yubihsm"] edition = "2018" -[workspace] -members = [".", "cosmos-stdtx"] - [badges] circle-ci = { repository = "tendermint/kms" } diff --git a/cosmos-stdtx/Cargo.toml b/cosmos-stdtx/Cargo.toml deleted file mode 100644 index 88ce7ec..0000000 --- a/cosmos-stdtx/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "cosmos-stdtx" -description = "Extensible schema-driven Cosmos StdTx builder and serializer" -version = "0.0.1" # Also update html_root_url in lib.rs when bumping this -authors = ["Tony Arcieri "] -license = "Apache-2.0" -repository = "https://github.com/tendermint/kms/tree/master/cosmos-stdtx" -readme = "README.md" -categories = ["cryptography", "encoding"] -keywords = ["crypto", "cosmos", "stdtx", "transaction", "tendermint"] -edition = "2018" - -[badges] -circle-ci = { repository = "tendermint/kms" } - -[dependencies] -anomaly = "0.1" -ecdsa = { version = "0.4", features = ["k256"] } -prost-amino = "0.5" -prost-amino-derive = "0.5" -rust_decimal = "1.1" -serde = { version = "1", features = ["serde_derive"] } -serde_json = "1" -sha2 = "0.8" -subtle-encoding = { version = "0.5", features = ["bech32-preview"] } -thiserror = "1" -toml = "0.5" - -[dev-dependencies] -signatory-secp256k1 = "0.18" diff --git a/cosmos-stdtx/README.md b/cosmos-stdtx/README.md deleted file mode 100644 index 2e9556f..0000000 --- a/cosmos-stdtx/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# cosmos-stdtx.rs 🌌 - -[![Crate][crate-image]][crate-link] -[![Docs][docs-image]][docs-link] -[![Build Status][build-image]][build-link] -[![Safety Dance][safety-image]][safety-link] -[![Apache 2.0 Licensed][license-image]][license-link] -![MSRV][rustc-image] - -Extensible schema-driven [Cosmos] [StdTx] builder and serializer. - -## About - -**cosmos-stdtx.rs** is a Rust library for composing transactions in the [StdTx] -format used by several [Tendermint]-based networks. - -It includes support for cryptographically signing transactions and serializing -them in the [Amino] encoding format. - -Definitions of transaction types are easily extensible, and can be defined at -runtime by loading them from a TOML definition file. This allows -**cosmos-stdtx.rs** to be used with any [Tendermint]-based software which -uses the [StdTx] format without requiring upstream modifications. - -## Minimum Supported Rust Version - -- Rust **1.39+** - -## License - -Copyright © 2020 Tony Arcieri - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -[//]: # (badges) - -[crate-image]: https://img.shields.io/crates/v/cosmos-stdtx.svg -[crate-link]: https://crates.io/crates/cosmos-stdtx -[docs-image]: https://docs.rs/cosmos-stdtx/badge.svg -[docs-link]: https://docs.rs/cosmos-stdtx/ -[build-image]: https://circleci.com/gh/tendermint/kms.svg?style=shield -[build-link]: https://circleci.com/gh/tendermint/kms -[safety-image]: https://img.shields.io/badge/unsafe-forbidden-success.svg -[safety-link]: https://github.com/rust-secure-code/safety-dance/ -[license-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg -[license-link]: https://github.com/tendermint/kms/blob/master/LICENSE -[rustc-image]: https://img.shields.io/badge/rustc-1.39+-blue.svg - -[//]: # (general links) - -[Cosmos]: https://cosmos.network/ -[StdTx]: https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth/types#StdTx -[Tendermint]: https://tendermint.com/ -[Amino]: https://github.com/tendermint/go-amino diff --git a/cosmos-stdtx/src/address.rs b/cosmos-stdtx/src/address.rs deleted file mode 100644 index 51f0a46..0000000 --- a/cosmos-stdtx/src/address.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! Address types (account or validator) - -use crate::error::{Error, ErrorKind}; -use anomaly::ensure; -use std::convert::TryInto; -use subtle_encoding::bech32; - -/// Size of an address -pub const ADDRESS_SIZE: usize = 20; - -/// Address type -#[derive(Clone, Debug)] -pub struct Address(pub [u8; ADDRESS_SIZE]); - -impl Address { - /// Parse an address from its Bech32 form - pub fn from_bech32(addr_bech32: impl AsRef) -> Result<(String, Address), Error> { - let (hrp, addr) = bech32::decode(addr_bech32.as_ref())?; - - ensure!( - addr.len() == ADDRESS_SIZE, - ErrorKind::Address, - "invalid length for decoded address: {} (expected {})", - addr.len(), - ADDRESS_SIZE - ); - - Ok((hrp, Address(addr.as_slice().try_into().unwrap()))) - } - - /// Encode this address as Bech32 - pub fn to_bech32(&self, hrp: &str) -> String { - bech32::encode(hrp, &self.0) - } -} - -impl AsRef<[u8]> for Address { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl From<[u8; ADDRESS_SIZE]> for Address { - fn from(addr: [u8; ADDRESS_SIZE]) -> Address { - Address(addr) - } -} - -impl From
for [u8; ADDRESS_SIZE] { - fn from(addr: Address) -> [u8; ADDRESS_SIZE] { - addr.0 - } -} diff --git a/cosmos-stdtx/src/decimal.rs b/cosmos-stdtx/src/decimal.rs deleted file mode 100644 index b5a4c1f..0000000 --- a/cosmos-stdtx/src/decimal.rs +++ /dev/null @@ -1,130 +0,0 @@ -//! Decimal type providing equivalent semantics to Cosmos [`sdk.Dec`] -//! -//! [`sdk.Dec`]: https://godoc.org/github.com/cosmos/cosmos-sdk/types#Dec - -use crate::error::{Error, ErrorKind}; -use anomaly::{ensure, fail}; -use std::{ - convert::{TryFrom, TryInto}, - fmt::{self, Debug, Display}, - str::FromStr, -}; - -/// Number of decimal places used by `sdk.Dec` -/// See: -pub const PRECISION: u32 = 18; - -/// Maximum value of the decimal part of an `sdk.Dec` -pub const FRACTIONAL_DIGITS_MAX: u64 = 9_999_999_999_999_999_999; - -/// Decimal type which follows Cosmos [`sdk.Dec`] conventions. -/// -/// [`sdk.Dec`]: https://godoc.org/github.com/cosmos/cosmos-sdk/types#Dec -#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] -pub struct Decimal(rust_decimal::Decimal); - -impl Decimal { - /// Create a new [`Decimal`] with the given whole number and decimal - /// parts. The decimal part assumes 18 digits of precision e.g. a - /// decimal with `(1, 1)` is `1.000000000000000001`. - /// - /// 18 digits required by the Cosmos SDK. See: - /// See: - pub fn new(integral_digits: i64, fractional_digits: u64) -> Result { - ensure!( - fractional_digits <= FRACTIONAL_DIGITS_MAX, - ErrorKind::Decimal, - "fractional digits exceed available precision: {}", - fractional_digits - ); - - let integral_digits: rust_decimal::Decimal = integral_digits.into(); - let fractional_digits: rust_decimal::Decimal = fractional_digits.into(); - let precision_exp: rust_decimal::Decimal = 10u64.pow(PRECISION).into(); - - let mut combined_decimal = (integral_digits * precision_exp) + fractional_digits; - combined_decimal.set_scale(PRECISION)?; - Ok(Decimal(combined_decimal)) - } - - /// Serialize this [`Decimal`] as Amino-encoded bytes - pub fn to_amino_bytes(mut self) -> Vec { - self.0 - .set_scale(0) - .expect("can't rescale decimal for Amino serialization"); - self.to_string().into_bytes() - } -} - -impl Debug for Decimal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.0) - } -} - -impl Display for Decimal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl FromStr for Decimal { - type Err = Error; - - fn from_str(s: &str) -> Result { - s.parse::()?.try_into() - } -} - -impl TryFrom for Decimal { - type Error = Error; - - fn try_from(mut decimal_value: rust_decimal::Decimal) -> Result { - match decimal_value.scale() { - 0 => { - let exp: rust_decimal::Decimal = 10u64.pow(PRECISION).into(); - decimal_value *= exp; - decimal_value.set_scale(PRECISION)?; - } - PRECISION => (), - other => fail!( - ErrorKind::Decimal, - "invalid decimal precision: {} (must be 0 or 18)", - other - ), - } - - Ok(Decimal(decimal_value)) - } -} - -macro_rules! impl_from_primitive_int_for_decimal { - ($($int:ty),+) => { - $(impl From<$int> for Decimal { - fn from(num: $int) -> Decimal { - Decimal::new(num as i64, 0).unwrap() - } - })+ - }; -} - -impl_from_primitive_int_for_decimal!(i8, i16, i32, i64, isize); -impl_from_primitive_int_for_decimal!(u8, u16, u32, u64, usize); - -#[cfg(test)] -mod tests { - use super::Decimal; - - /// Used by e.g. JSON - #[test] - fn string_serialization_test() { - let num = Decimal::from(-1i8); - assert_eq!(num.to_string(), "-1.000000000000000000") - } - - #[test] - fn amino_serialization_test() { - let num = Decimal::from(-1i8); - assert_eq!(b"-1000000000000000000", num.to_amino_bytes().as_slice()); - } -} diff --git a/cosmos-stdtx/src/error.rs b/cosmos-stdtx/src/error.rs deleted file mode 100644 index a9c8dd3..0000000 --- a/cosmos-stdtx/src/error.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! Error types - -use anomaly::{BoxError, Context}; -use std::{ - fmt::{self, Display}, - ops::Deref, -}; -use thiserror::Error; - -/// Kinds of errors -#[derive(Copy, Clone, Debug, Error, Eq, PartialEq)] -pub enum ErrorKind { - /// Malformed account or validator address - #[error("address error")] - Address, - - /// Invalid decimal value - #[error("invalid decimal value")] - Decimal, - - /// Input/output errors - #[error("I/O error")] - Io, - - /// Parse error - #[error("parse error")] - Parse, - - /// Signature error - #[error("signature error")] - Signature, - - /// Invalid type - #[error("type error")] - Type, -} - -impl ErrorKind { - /// Add context to an [`ErrorKind`] - pub fn context(self, source: impl Into) -> Context { - Context::new(self, Some(source.into())) - } -} - -/// Error type -#[derive(Debug)] -pub struct Error(Box>); - -impl Deref for Error { - type Target = Context; - - fn deref(&self) -> &Context { - &self.0 - } -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - self.0.source() - } -} - -impl From for Error { - fn from(kind: ErrorKind) -> Self { - Context::new(kind, None).into() - } -} - -impl From> for Error { - fn from(context: Context) -> Self { - Error(Box::new(context)) - } -} - -impl From for Error { - fn from(err: rust_decimal::Error) -> Error { - Context::new(ErrorKind::Decimal, Some(err.into())).into() - } -} - -impl From for Error { - fn from(err: ecdsa::signature::Error) -> Error { - Context::new(ErrorKind::Signature, Some(err.into())).into() - } -} -impl From for Error { - fn from(err: subtle_encoding::Error) -> Error { - Context::new(ErrorKind::Parse, Some(err.into())).into() - } -} - -impl From for Error { - fn from(err: toml::de::Error) -> Error { - Context::new(ErrorKind::Parse, Some(err.into())).into() - } -} diff --git a/cosmos-stdtx/src/lib.rs b/cosmos-stdtx/src/lib.rs deleted file mode 100644 index 34a7974..0000000 --- a/cosmos-stdtx/src/lib.rs +++ /dev/null @@ -1,149 +0,0 @@ -//! Amino serializer for Cosmos SDK-formatted `StdTx` transactions, the -//! standard transaction format used by the Cosmos SDK and other Tendermint -//! blockchains which reuse types from the Cosmos SDK. -//! -//! Uses a TOML-based schema description language for `sdk.Msg` values which -//! should be encoded into the final `StdTx`. -//! -//! Includes a `StdTx` builder capable of constructing `sdk.Msg` values and -//! signing them using any ECDSA secp256k1 signer compatible with the -//! [`ecdsa` crate] (e.g. [`signatory-secp256k1`], [`yubihsm`]). -//! -//! # Equivalent Go code -//! -//! - [`StdTx` (godoc)](https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth/types#StdTx) -//! - [`sdk.Msg` (godoc)](httpshttps://docs.rs/ecdsa://godoc.org/github.com/cosmos/cosmos-sdk/types#Msg) -//! -//! # Usage -//! -//! Below is a self-contained example of how to use [`cosmos_stdtx::Builder`] -//! type to construct a signed [`StdTx`] message: -//! -//! ``` -//! use cosmos_stdtx::Builder; -//! use signatory_secp256k1::{SecretKey, EcdsaSigner}; -//! -//! /// Example account number -//! const ACCOUNT_NUMBER: u64 = 946827; -//! -//! /// Example chain ID -//! const CHAIN_ID: &str = "columbus-3"; -//! -//! /// Example oracle feeder for `oracle/MsgExchangeRateVote` -//! const FEEDER: &str = "terra1t9et8wjeh8d0ewf4lldchterxsmhpcgg5auy47"; -//! -//! /// Example oracle validator for `oracle/MsgExchangeRateVote` -//! const VALIDATOR: &str = "terravaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03x2mfyu7"; -//! -//! /// Example amount of gas to include in transaction -//! const GAS_AMOUNT: u64 = 200000; -//! -//! /// Example StdTx message schema definition. See docs for the -//! /// `cosmos_stdtx::Schema` type for more information: -//! /// -//! /// -//! /// Message types taken from Terra's oracle voter transactions: -//! /// -//! pub const TERRA_SCHEMA: &str = r#" -//! namespace = "core/StdTx" -//! acc_prefix = "terra" -//! val_prefix = "terravaloper" -//! -//! [[definition]] -//! type_name = "oracle/MsgExchangeRatePrevote" -//! fields = [ -//! { name = "hash", type = "string" }, -//! { name = "denom", type = "string" }, -//! { name = "feeder", type = "sdk.AccAddress" }, -//! { name = "validator", type = "sdk.ValAddress" }, -//! ] -//! -//! [[definition]] -//! type_name = "oracle/MsgExchangeRateVote" -//! fields = [ -//! { name = "exchange_rate", type = "sdk.Dec"}, -//! { name = "salt", type = "string" }, -//! { name = "denom", type = "string" }, -//! { name = "feeder", type = "sdk.AccAddress" }, -//! { name = "validator", type = "sdk.ValAddress" }, -//! ] -//! "#; -//! -//! /// Simple error type -//! #[derive(Debug)] -//! struct Error(String); -//! -//! impl From for Error { -//! fn from(err: cosmos_stdtx::Error) -> Error { -//! Error(err.to_string()) -//! } -//! } -//! -//! /// Simple builder for an `oracle/MsgExchangeRateVote` message -//! fn build_vote_msg(schema: &cosmos_stdtx::Schema) -> Result { -//! Ok(cosmos_stdtx::msg::Builder::new(schema, "oracle/MsgExchangeRateVote")? -//! .decimal("exchange_rate", -1i8)? -//! .string("salt", "XXXX")? -//! .string("denom", "ukrw")? -//! .acc_address_bech32("feeder", FEEDER)? -//! .val_address_bech32("validator", VALIDATOR)? -//! .to_msg()) -//! } -//! -//! /// Parse the TOML schema for Terra `sdk.Msg` types -//! let schema = TERRA_SCHEMA.parse::().unwrap(); -//! -//! /// Create ECDSA signer (ordinarily you wouldn't generate a random key -//! /// every time but reuse an existing one) -//! let signer = EcdsaSigner::from(&SecretKey::generate()); -//! -//! /// Create message builder, giving it an account number, chain ID, and a -//! /// boxed ECDSA secp256k1 signer -//! let builder = cosmos_stdtx::Builder::new(schema, ACCOUNT_NUMBER, CHAIN_ID, Box::new(signer)); -//! -//! /// Create message to be included in the `StdTx` using the method defined above -//! let msg = build_vote_msg(builder.schema()).unwrap(); -//! -//! /// Build transaction, returning serialized Amino bytes as a `Vec` -//! let sequence_number = 123456; -//! let fee = cosmos_stdtx::StdFee::for_gas(GAS_AMOUNT); -//! let memo = ""; -//! let amino_bytes = builder -//! .sign_amino_tx(sequence_number, fee, memo, &[msg]) -//! .unwrap(); -//! -//! // `amino_bytes` is now a `Vec` containing an Amino serialized transaction -//! ``` -//! -//! [`ecdsa` crate]: https://docs.rs/ecdsa -//! [`signatory-secp256k1`]: https://docs.rs/signatory-secp256k1 -//! [`yubihsm`]: https://docs.rs/yubihsm -//! [`cosmos_stdtx::Builder`]: https://docs.rs/cosmos-stdtx/latest/cosmos_stdtx/stdtx/struct.Builder.html - -#![doc(html_root_url = "https://docs.rs/cosmos-stdtx/0.0.1")] -#![forbid(unsafe_code)] -#![warn(rust_2018_idioms, missing_docs, unused_qualifications)] - -pub mod address; -pub mod decimal; -pub mod error; -pub mod msg; -pub mod schema; -pub mod stdtx; -pub mod type_name; - -pub use self::{ - address::Address, - decimal::Decimal, - error::Error, - msg::Msg, - schema::Schema, - stdtx::{Builder, StdFee, StdTx}, - type_name::TypeName, -}; - -/// Fixed-width ECDSA secp256k1 signature -pub use ecdsa::curve::secp256k1::FixedSignature as Signature; - -/// Transaction signer for ECDSA secp256k1 signatures -pub type Signer = dyn ecdsa::signature::Signer; diff --git a/cosmos-stdtx/src/msg.rs b/cosmos-stdtx/src/msg.rs deleted file mode 100644 index 2f729b8..0000000 --- a/cosmos-stdtx/src/msg.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! Transaction message type i.e [`sdk.Msg`] -//! -//! [`sdk.Msg`]: https://godoc.org/github.com/cosmos/cosmos-sdk/types#Msg - -mod builder; -mod field; -mod value; - -pub use self::{builder::Builder, field::Field, value::Value}; - -use crate::{Schema, TypeName}; -use prost_amino::encode_length_delimiter as encode_leb128; // Little-endian Base 128 -use std::{collections::BTreeMap, iter::FromIterator}; - -/// Tags are indexes which identify message fields -pub type Tag = u64; - -/// Transaction message type i.e. [`sdk.Msg`]. -/// These serve as the payload for [`StdTx`] transactions. -/// -/// [`StdTx`]: https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth/types#StdTx -/// [`sdk.Msg`]: https://godoc.org/github.com/cosmos/cosmos-sdk/types#Msg -#[derive(Clone, Debug)] -pub struct Msg { - /// Name of the message type - type_name: TypeName, - - /// Fields in the message - fields: Vec, -} - -impl Msg { - /// Compute `serde_json::Value` representing a `sdk.Msg` - pub fn to_json_value(&self, schema: &Schema) -> serde_json::Value { - // `BTreeMap` ensures fields are ordered for Cosmos's Canonical JSON - let mut values = BTreeMap::new(); - - for field in &self.fields { - values.insert( - field.name().to_string(), - field.value().to_json_value(schema), - ); - } - - let mut json = serde_json::Map::new(); - json.insert( - "type".to_owned(), - serde_json::Value::String(self.type_name.to_string()), - ); - json.insert( - "value".to_owned(), - serde_json::Map::from_iter(values.into_iter()).into(), - ); - serde_json::Value::Object(json) - } - - /// Encode this message in the Amino wire format - pub fn to_amino_bytes(&self) -> Vec { - let mut result = self.type_name.amino_prefix(); - - for field in &self.fields { - // Compute the field prefix, which encodes the tag and wire type code - let prefix = field.tag() << 3 | field.value().wire_type(); - encode_leb128(prefix as usize, &mut result).expect("LEB128 encoding error"); - - let mut encoded_value = field.value().to_amino_bytes(); - encode_leb128(encoded_value.len(), &mut result).expect("LEB128 encoding error"); - result.append(&mut encoded_value); - } - - result - } -} diff --git a/cosmos-stdtx/src/msg/builder.rs b/cosmos-stdtx/src/msg/builder.rs deleted file mode 100644 index 43e262a..0000000 --- a/cosmos-stdtx/src/msg/builder.rs +++ /dev/null @@ -1,174 +0,0 @@ -//! Transaction message builder - -use super::{Field, Msg, Value}; -use crate::{ - address::Address, - decimal::Decimal, - error::{Error, ErrorKind}, - schema::{Definition, Schema, ValueType}, - type_name::TypeName, -}; -use anomaly::{ensure, format_err}; -use std::convert::TryInto; - -/// Transaction message builder -pub struct Builder<'a> { - /// Schema for the message we're building - schema_definition: &'a Definition, - - /// Name of the message type - type_name: TypeName, - - /// Bech32 prefix for account addresses - acc_prefix: String, - - /// Bech32 prefix for validator consensus addresses - val_prefix: String, - - /// Fields in the message - fields: Vec, -} - -impl<'a> Builder<'a> { - /// Create a new message builder for the given schema and message type - pub fn new( - schema: &'a Schema, - type_name: impl TryInto, - ) -> Result { - let type_name = type_name.try_into()?; - - let schema_definition = schema.get_definition(&type_name).ok_or_else(|| { - format_err!( - ErrorKind::Type, - "type not found in schema: `{}`", - &type_name - ) - })?; - - Ok(Self { - schema_definition, - type_name, - acc_prefix: schema.acc_prefix().to_owned(), - val_prefix: schema.val_prefix().to_owned(), - fields: vec![], - }) - } - - /// `sdk.AccAddress`: Cosmos SDK account addresses - /// - pub fn acc_address( - &mut self, - field_name: impl TryInto, - address: Address, - ) -> Result<&mut Self, Error> { - let field_name = field_name.try_into()?; - let tag = self - .schema_definition - .get_field_tag(&field_name, ValueType::SdkAccAddress)?; - - let field = Field::new(tag, field_name, Value::SdkAccAddress(address)); - - self.fields.push(field); - Ok(self) - } - - /// `sdk.AccAddress` encoded as Bech32 - pub fn acc_address_bech32( - &mut self, - field_name: impl TryInto, - addr_bech32: impl AsRef, - ) -> Result<&mut Self, Error> { - let (hrp, address) = Address::from_bech32(addr_bech32)?; - - ensure!( - hrp == self.acc_prefix, - ErrorKind::Address, - "invalid account address prefix: `{}` (expected `{}`)", - hrp, - self.acc_prefix, - ); - - self.acc_address(field_name, address) - } - - /// `sdk.Dec`: Cosmos SDK decimals - /// s - pub fn decimal( - &mut self, - field_name: impl TryInto, - value: impl Into, - ) -> Result<&mut Self, Error> { - let field_name = field_name.try_into()?; - - let tag = self - .schema_definition - .get_field_tag(&field_name, ValueType::SdkDecimal)?; - - let field = Field::new(tag, field_name, Value::SdkDecimal(value.into())); - - self.fields.push(field); - Ok(self) - } - - /// `sdk.ValAddress`: Cosmos SDK validator addresses - /// - pub fn val_address( - &mut self, - field_name: impl TryInto, - address: Address, - ) -> Result<&mut Self, Error> { - let field_name = field_name.try_into()?; - let tag = self - .schema_definition - .get_field_tag(&field_name, ValueType::SdkValAddress)?; - - let field = Field::new(tag, field_name, Value::SdkValAddress(address)); - - self.fields.push(field); - Ok(self) - } - - /// `sdk.ValAddress` encoded as Bech32 - pub fn val_address_bech32( - &mut self, - field_name: impl TryInto, - addr_bech32: impl AsRef, - ) -> Result<&mut Self, Error> { - let (hrp, address) = Address::from_bech32(addr_bech32)?; - - ensure!( - hrp == self.val_prefix, - ErrorKind::Address, - "invalid validator address prefix: `{}` (expected `{}`)", - hrp, - self.val_prefix, - ); - - self.val_address(field_name, address) - } - - /// Strings - pub fn string( - &mut self, - field_name: impl TryInto, - s: impl Into, - ) -> Result<&mut Self, Error> { - let field_name = field_name.try_into()?; - let tag = self - .schema_definition - .get_field_tag(&field_name, ValueType::String)?; - - let field = Field::new(tag, field_name, Value::String(s.into())); - - self.fields.push(field); - Ok(self) - } - - /// Consume this builder and output a message - pub fn to_msg(&self) -> Msg { - Msg { - type_name: self.type_name.clone(), - fields: self.fields.clone(), - } - } -} diff --git a/cosmos-stdtx/src/msg/field.rs b/cosmos-stdtx/src/msg/field.rs deleted file mode 100644 index 8494056..0000000 --- a/cosmos-stdtx/src/msg/field.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! Message fields - -use super::{Tag, Value}; -use crate::type_name::TypeName; - -/// Message fields -#[derive(Clone, Debug)] -pub struct Field { - /// Field number to use as the key in an Amino message. - tag: Tag, - - /// Name of this field - name: TypeName, - - /// Amino type to serialize this field as - value: Value, -} - -impl Field { - /// Create a new message field - pub fn new(tag: Tag, name: TypeName, value: impl Into) -> Self { - Self { - tag, - name, - value: value.into(), - } - } - - /// Get this field's [`Tag`] - pub fn tag(&self) -> Tag { - self.tag - } - - /// Get this field's [`TypeName`] - pub fn name(&self) -> &TypeName { - &self.name - } - - /// Get this field's [`Value`] - pub fn value(&self) -> &Value { - &self.value - } -} diff --git a/cosmos-stdtx/src/msg/value.rs b/cosmos-stdtx/src/msg/value.rs deleted file mode 100644 index 73e346e..0000000 --- a/cosmos-stdtx/src/msg/value.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! Message values - -use crate::{ - address::Address, - decimal::Decimal, - schema::{Schema, ValueType}, -}; - -/// Message values - data contained in fields of a message -#[derive(Clone, Debug)] -pub enum Value { - /// `sdk.AccAddress`: Cosmos SDK account addresses - /// - SdkAccAddress(Address), - - /// `sdk.Dec`: Cosmos SDK decimals - /// - SdkDecimal(Decimal), - - /// `sdk.ValAddress`: Cosmos SDK validator addresses - /// - SdkValAddress(Address), - - /// Strings - String(String), -} - -impl Value { - /// Get the type of this value - pub fn value_type(&self) -> ValueType { - match self { - Value::SdkAccAddress(_) => ValueType::SdkAccAddress, - Value::SdkDecimal(_) => ValueType::SdkDecimal, - Value::SdkValAddress(_) => ValueType::SdkValAddress, - Value::String(_) => ValueType::String, - } - } - - /// Get the Amino/Proto wire type for this field - /// See: - pub(super) fn wire_type(&self) -> u64 { - match self { - // Length-delimited types - Value::SdkAccAddress(_) - | Value::SdkDecimal(_) - | Value::SdkValAddress(_) - | Value::String(_) => 2, - } - } - - /// Encode this value as Amino bytes - pub(super) fn to_amino_bytes(&self) -> Vec { - match self { - Value::SdkAccAddress(addr) | Value::SdkValAddress(addr) => addr.as_ref().to_vec(), - Value::SdkDecimal(decimal) => decimal.to_amino_bytes(), - Value::String(s) => s.clone().into_bytes(), - } - } - - /// Encode this value as a [`serde_json::Value`] - pub(super) fn to_json_value(&self, schema: &Schema) -> serde_json::Value { - serde_json::Value::String(match self { - Value::SdkAccAddress(addr) => addr.to_bech32(schema.acc_prefix()), - Value::SdkDecimal(decimal) => decimal.to_string(), - Value::SdkValAddress(addr) => addr.to_bech32(schema.val_prefix()), - Value::String(s) => s.clone(), - }) - } -} - -impl From for Value { - fn from(dec: Decimal) -> Value { - Value::SdkDecimal(dec) - } -} - -impl From for Value { - fn from(s: String) -> Value { - Value::String(s) - } -} diff --git a/cosmos-stdtx/src/schema.rs b/cosmos-stdtx/src/schema.rs deleted file mode 100644 index 95674d9..0000000 --- a/cosmos-stdtx/src/schema.rs +++ /dev/null @@ -1,147 +0,0 @@ -//! Amino schema for an [`sdk.Msg`]. -//! -//! Schema files are similar to Protobuf schemas, but use a TOML-based syntax. -//! -//! # Example TOML File -//! -//! Below is an example TOML file defining an `sdk.Msg`. This example defines -//! a type named `oracle/MsgExchangeRatePrevote`: -//! -//! ```toml -//! # Example StdTx message schema definition. -//! # -//! # Message types taken from Terra's oracle voter transactions: -//! # -//! -//! # StdTx namespace for schema definitions -//! # (e.g. `cosmos-sdk/StdTx` for Cosmos SDK) -//! namespace = "core/StdTx" -//! -//! # Bech32 address prefixes -//! acc_prefix = "terra" -//! val_prefix = "terravaloper" -//! -//! [[definition]] -//! type_name = "oracle/MsgExchangeRatePrevote" -//! fields = [ -//! { name = "hash", type = "string" }, -//! { name = "denom", type = "string" }, -//! { name = "feeder", type = "sdk.AccAddress" }, -//! { name = "validator", type = "sdk.ValAddress" }, -//! ] -//! -//! [[definition]] -//! type_name = "oracle/MsgExchangeRateVote" -//! fields = [ -//! # explicit field tag example - will start from "1" otherwise -//! { name = "exchange_rate", type = "sdk.Dec", tag = 1 }, -//! { name = "salt", type = "string" }, -//! { name = "denom", type = "string" }, -//! { name = "feeder", type = "sdk.AccAddress" }, -//! { name = "validator", type = "sdk.ValAddress" }, -//! ] -//! ``` -//! -//! [`sdk.Msg`]: https://godoc.org/github.com/cosmos/cosmos-sdk/types#Msg - -mod definition; -mod field; -mod value_type; - -pub use self::{definition::Definition, field::Field, value_type::ValueType}; - -use crate::{ - error::{Error, ErrorKind}, - type_name::TypeName, -}; -use anomaly::fail; -use serde::Deserialize; -use std::{fs, path::Path, str::FromStr}; - -/// Schema definition for an [`sdk.Msg`] to be included in an [`StdTx`]. -/// -/// The schema includes information about field identifiers and associated types. -/// -/// [`StdTx`]: https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth/types#StdTx -/// [`sdk.Msg`]: https://godoc.org/github.com/cosmos/cosmos-sdk/types#Msg -#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] -#[serde(deny_unknown_fields)] -pub struct Schema { - /// `StdTx` namespace for schema (e.g. `cosmos-sdk/StdTx`) - namespace: TypeName, - - /// Bech32 prefix for account addresses - acc_prefix: String, - - /// Bech32 prefix for validator consensus addresses - val_prefix: String, - - /// Schema definitions - #[serde(rename = "definition")] - definitions: Vec, -} - -impl Schema { - /// Create a new [`Schema`] with the given `StdTx` namespace and [`Definition`] set - pub fn new( - namespace: TypeName, - acc_prefix: impl Into, - val_prefix: impl Into, - definitions: impl Into>, - ) -> Self { - Self { - namespace, - acc_prefix: acc_prefix.into(), - val_prefix: val_prefix.into(), - definitions: definitions.into(), - } - } - - /// Load a TOML file describing - pub fn load_toml(path: impl AsRef) -> Result { - match fs::read_to_string(path.as_ref()) { - Ok(s) => s.parse(), - Err(e) => fail!( - ErrorKind::Io, - "couldn't open {}: {}", - path.as_ref().display(), - e - ), - } - } - - /// Get the transaction namespace for this schema (e.g. `cosmos-sdk/StdTx`) - pub fn namespace(&self) -> &TypeName { - &self.namespace - } - - /// Get the Bech32 prefix for account addresses - pub fn acc_prefix(&self) -> &str { - self.acc_prefix.as_ref() - } - - /// Get the Bech32 prefix for validator addresses - pub fn val_prefix(&self) -> &str { - self.val_prefix.as_ref() - } - - /// [`Definition`] types found in this [`Schema`] - pub fn definitions(&self) -> &[Definition] { - &self.definitions - } - - /// Get a schema [`Definition`] for the given [`TypeName`] - pub fn get_definition(&self, type_name: &TypeName) -> Option<&Definition> { - self.definitions - .iter() - .find(|def| def.type_name() == type_name) - } -} - -impl FromStr for Schema { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(toml::from_str(s)?) - } -} diff --git a/cosmos-stdtx/src/schema/definition.rs b/cosmos-stdtx/src/schema/definition.rs deleted file mode 100644 index 9de3ee2..0000000 --- a/cosmos-stdtx/src/schema/definition.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! Type definition within a schema - -use super::{field, Field, TypeName, ValueType}; -use crate::{ - error::{Error, ErrorKind}, - msg::Tag, -}; -use anomaly::{fail, format_err}; -use serde::Deserialize; - -/// Definition of a particular type in the schema -#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] -#[serde(deny_unknown_fields)] -pub struct Definition { - /// Name of the type this definition is for - type_name: TypeName, - - /// Fields in this type definition - #[serde(deserialize_with = "field::deserialize_vec")] - fields: Vec, -} - -impl Definition { - /// Create a new schema [`Definition`] with the given type name and fields - pub fn new(type_name: TypeName, fields: impl Into>) -> Result { - let fields = fields.into(); - - if let Err(e) = field::validate(&fields) { - fail!(ErrorKind::Parse, "{}", e); - } - - Ok(Self { type_name, fields }) - } - - /// Get the [`TypeName`] defined by this schema. - pub fn type_name(&self) -> &TypeName { - &self.type_name - } - - /// Get a list of [`Field`] types in this schema. - pub fn fields(&self) -> &[Field] { - self.fields.as_slice() - } - - /// Get a [`Field`] by its [`TypeName`] - pub fn get_field(&self, field_name: &TypeName) -> Option<&Field> { - self.fields.iter().find(|field| field.name() == field_name) - } - - /// Get the [`Tag`] for a [`Field`], ensuring is of the given [`ValueType`] - pub fn get_field_tag( - &self, - field_name: &TypeName, - value_type: ValueType, - ) -> Result { - let field = self.get_field(field_name).ok_or_else(|| { - format_err!( - ErrorKind::Type, - "field name not found in `{}` schema: `{}`", - &self.type_name, - field_name - ) - })?; - - if field.value_type() != value_type { - fail!( - ErrorKind::Type, - "field `{}` of `{}` is not an {} (expected {})", - field_name, - &self.type_name, - value_type, - field.value_type() - ); - } - - Ok(field.tag()) - } -} diff --git a/cosmos-stdtx/src/schema/field.rs b/cosmos-stdtx/src/schema/field.rs deleted file mode 100644 index 8c066c6..0000000 --- a/cosmos-stdtx/src/schema/field.rs +++ /dev/null @@ -1,107 +0,0 @@ -//! Fields in a type definition - -use super::ValueType; -use crate::{msg::Tag, type_name::TypeName}; -use serde::{de, Deserialize}; -use std::collections::BTreeSet as Set; - -/// Fields in an Amino-serialized `sdk.Msg` -#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] -#[serde(deny_unknown_fields)] -pub struct Field { - /// Name of this field - name: TypeName, - - /// Amino type to serialize this field as - #[serde(rename = "type")] - value_type: ValueType, - - /// Field number to use as the key in an Amino message. - /// - /// These are all ensured to be `Some` in the `deserialize_vec` method below. - tag: Option, -} - -impl Field { - /// Create a new [`Field`] with the given tag and [`ValueType`]. - pub fn new(name: TypeName, value_type: ValueType, tag: Tag) -> Self { - Self { - name, - tag: Some(tag), - value_type, - } - } - - /// Get the [`TypeName`] for this [`Field`] - pub fn name(&self) -> &TypeName { - &self.name - } - - /// Get the [`ValueType`] for this [`Field`] - pub fn value_type(&self) -> ValueType { - self.value_type - } - - /// Get the numerical index [`Tag`] for this [`Field`] - pub fn tag(&self) -> Tag { - self.tag.unwrap() - } -} - -/// Deserialize `Vec`, populating their `tag` if unpopulated -pub(super) fn deserialize_vec<'de, D>(deserializer: D) -> Result, D::Error> -where - D: de::Deserializer<'de>, -{ - let mut fields: Vec = Vec::deserialize(deserializer)?; - populate_tags(&mut fields).map_err(de::Error::custom)?; - validate(&fields).map_err(de::Error::custom)?; - Ok(fields) -} - -/// Populate the `tag` for [`Field`] values if unset -fn populate_tags(fields: &mut [Field]) -> Result<(), &str> { - // Tags are 1-indexed - let mut tag = 1; - - for field in fields { - match field.tag { - Some(t) => { - if t == 0 { - // `0` is not allowed as a field tag - return Err("invalid field tag: 0"); - } - - // auto index by last specified tag - tag = t + 1 - } - None => { - field.tag = Some(tag); - tag += 1; - } - } - } - - Ok(()) -} - -/// Ensure field names and tags are unique across all fields -pub(super) fn validate(fields: &[Field]) -> Result<(), String> { - let mut names = Set::new(); - let mut tags = Set::new(); - - for field in fields { - // This invariant is enforced in `populate_tags` and the `Field::new` methods - let tag = field.tag.expect("field with unpopulated tag!"); - - if !names.insert(&field.name) { - return Err(format!("duplicate field name: `{}`", &field.name)); - } - - if !tags.insert(tag) { - return Err(format!("duplicate field tag: {}", tag)); - } - } - - Ok(()) -} diff --git a/cosmos-stdtx/src/schema/value_type.rs b/cosmos-stdtx/src/schema/value_type.rs deleted file mode 100644 index 67cd636..0000000 --- a/cosmos-stdtx/src/schema/value_type.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Types of values that can be present in an `sdk.Msg` - -use crate::error::{Error, ErrorKind}; -use anomaly::fail; -use serde::{de, Deserialize}; -use std::{ - fmt::{self, Display}, - str::FromStr, -}; - -/// Types of Amino values which can be included in a [`sdk.Msg`] -/// -/// [`sdk.Msg`]: https://godoc.org/github.com/cosmos/cosmos-sdk/types#Msg -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum ValueType { - /// `sdk.AccAddress`: Cosmos SDK account addresses - /// - SdkAccAddress, - - /// `sdk.Dec`: Cosmos SDK decimals - /// - SdkDecimal, - - /// `sdk.ValAddress`: Cosmos SDK validator addresses - /// - SdkValAddress, - - /// Strings - String, -} - -impl Display for ValueType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match self { - ValueType::SdkAccAddress => "sdk.AccAddress", - ValueType::SdkDecimal => "sdk.Dec", - ValueType::SdkValAddress => "sdk.ValAddress", - ValueType::String => "string", - }) - } -} - -impl FromStr for ValueType { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "sdk.AccAddress" => ValueType::SdkAccAddress, - "sdk.Dec" => ValueType::SdkDecimal, - "sdk.ValAddress" => ValueType::SdkValAddress, - "string" => ValueType::String, - _ => fail!(ErrorKind::Parse, "unknown value type: `{}`", s), - }) - } -} - -impl<'de> Deserialize<'de> for ValueType { - fn deserialize>(deserializer: D) -> Result { - use de::Error; - let s = String::deserialize(deserializer)?; - s.parse().map_err(D::Error::custom) - } -} diff --git a/cosmos-stdtx/src/stdtx.rs b/cosmos-stdtx/src/stdtx.rs deleted file mode 100644 index 5567cf5..0000000 --- a/cosmos-stdtx/src/stdtx.rs +++ /dev/null @@ -1,122 +0,0 @@ -//! StdTx Amino types - -mod builder; - -pub use self::builder::Builder; - -use crate::{Signature, TypeName}; -use prost_amino::{encode_length_delimiter, Message}; -use prost_amino_derive::Message; -use serde_json::json; - -/// StdTx Amino type -#[derive(Clone, Message)] -pub struct StdTx { - /// Messages in transction - #[prost_amino(bytes, repeated, tag = "1")] - pub msg: Vec>, - - /// Feeds to be paid - #[prost_amino(message)] - pub fee: Option, - - /// Signatures - #[prost_amino(message, repeated)] - pub signatures: Vec, - - /// Memo field - #[prost_amino(string)] - pub memo: String, -} - -impl StdTx { - /// Encode this [`StdTx`] in Amino encoding identifying it with the given - /// type name (e.g. `cosmos-sdk/StdTx`) - pub fn to_amino_bytes(&self, type_name: &TypeName) -> Vec { - let mut amino_tx = type_name.amino_prefix(); - self.encode(&mut amino_tx).expect("LEB128 encoding error"); - - let mut amino_encoded = vec![]; - encode_length_delimiter(amino_tx.len(), &mut amino_encoded).expect("LEB128 encoding error"); - amino_encoded.append(&mut amino_tx); - amino_encoded - } -} - -/// StdFee amino type -#[derive(Clone, Message)] -pub struct StdFee { - /// Fee to be paid - #[prost_amino(message, repeated, tag = "1")] - pub amount: Vec, - - /// Gas requested for transaction - #[prost_amino(uint64)] - pub gas: u64, -} - -impl StdFee { - /// Create a [`StdFee`] for a gas-only transaction - pub fn for_gas(gas: u64) -> Self { - StdFee { - amount: vec![], - gas, - } - } - /// Compute `serde_json::Value` representing this fee - pub fn to_json_value(&self) -> serde_json::Value { - let amount = self - .amount - .iter() - .map(|amt| amt.to_json_value()) - .collect::>(); - - json!({ - "amount": amount, - "gas": self.gas.to_string() - }) - } -} - -/// Coin Amino type -#[derive(Clone, Message)] -pub struct Coin { - /// Denomination of coin - #[prost_amino(string, tag = "1")] - pub denom: String, - - /// Amount of the given denomination - #[prost_amino(string)] - pub amount: String, -} - -impl Coin { - /// Compute `serde_json::Value` representing this coin - pub fn to_json_value(&self) -> serde_json::Value { - json!({ - "denom": self.denom, - "amount": self.amount - }) - } -} - -/// StdSignature amino type -#[derive(Clone, Message)] -pub struct StdSignature { - /// Public key which can verify this signature - #[prost_amino(bytes, tag = "1", amino_name = "tendermint/PubKeySecp256k1")] - pub pub_key: Vec, - - /// Serialized signature - #[prost_amino(bytes)] - pub signature: Vec, -} - -impl From for StdSignature { - fn from(signature: Signature) -> StdSignature { - StdSignature { - pub_key: vec![], - signature: signature.as_ref().to_vec(), - } - } -} diff --git a/cosmos-stdtx/src/stdtx/builder.rs b/cosmos-stdtx/src/stdtx/builder.rs deleted file mode 100644 index c82f8a0..0000000 --- a/cosmos-stdtx/src/stdtx/builder.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! Builder for `StdTx` transactions which handles construction and signing. - -use super::{StdFee, StdSignature, StdTx}; -use crate::{Error, Msg, Schema, Signer}; -use serde_json::json; - -/// [`StdTx`] transaction builder, which handles construction, signing, and -/// Amino serialization. -pub struct Builder { - /// Schema which describes valid transaction types - schema: Schema, - - /// Account number to include in transactions - account_number: u64, - - /// Chain ID - chain_id: String, - - /// Transaction signer - signer: Box, -} - -impl Builder { - /// Create a new transaction builder - pub fn new( - schema: Schema, - account_number: u64, - chain_id: impl Into, - signer: Box, - ) -> Self { - Self { - schema, - account_number, - chain_id: chain_id.into(), - signer, - } - } - - /// Borrow this transaction builder's [`Schema`] - pub fn schema(&self) -> &Schema { - &self.schema - } - - /// Get this transaction builder's account number - pub fn account_number(&self) -> u64 { - self.account_number - } - - /// Borrow this transaction builder's chain ID - pub fn chain_id(&self) -> &str { - &self.chain_id - } - - /// Build and sign a transaction containing the given messages - pub fn sign_tx( - &self, - sequence: u64, - fee: StdFee, - memo: &str, - messages: &[Msg], - ) -> Result { - let sign_msg = self.create_sign_msg(sequence, &fee, memo, messages); - let signature = StdSignature::from(self.signer.try_sign(sign_msg.as_bytes())?); - - Ok(StdTx { - msg: messages.iter().map(|msg| msg.to_amino_bytes()).collect(), - fee: Some(fee), - signatures: vec![signature], - memo: memo.to_owned(), - }) - } - - /// Build, sign, and encode a transaction in Amino format - pub fn sign_amino_tx( - &self, - sequence: u64, - fee: StdFee, - memo: &str, - messages: &[Msg], - ) -> Result, Error> { - let tx = self.sign_tx(sequence, fee, memo, messages)?; - Ok(tx.to_amino_bytes(self.schema.namespace())) - } - - /// Create the JSON message to sign for this transaction - fn create_sign_msg(&self, sequence: u64, fee: &StdFee, memo: &str, messages: &[Msg]) -> String { - let messages = messages - .iter() - .map(|msg| msg.to_json_value(&self.schema)) - .collect::>(); - - json!({ - "account_number": self.account_number.to_string(), - "chain_id": self.chain_id, - "fee": fee.to_json_value(), - "memo": memo, - "msgs": messages, - "sequence": sequence.to_string() - }) - .to_string() - } -} diff --git a/cosmos-stdtx/src/type_name.rs b/cosmos-stdtx/src/type_name.rs deleted file mode 100644 index e28468f..0000000 --- a/cosmos-stdtx/src/type_name.rs +++ /dev/null @@ -1,87 +0,0 @@ -//! Amino type names - -use crate::error::{Error, ErrorKind}; -use anomaly::fail; -use serde::{de, Deserialize}; -use sha2::{Digest, Sha256}; -use std::{ - convert::TryFrom, - fmt::{self, Display}, - str::FromStr, -}; - -/// Name of an Amino type -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct TypeName(String); - -impl TypeName { - /// Create a new `sdk.Msg` type name - pub fn new(name: impl AsRef) -> Result { - name.as_ref().parse() - } - - /// Borrow this [`TypeName`] as a string - pub fn as_str(&self) -> &str { - &self.0 - } - - /// Compute the Amino prefix for this [`TypeName`] - pub fn amino_prefix(&self) -> Vec { - Sha256::digest(self.0.as_bytes()) - .iter() - .filter(|&x| *x != 0x00) - .skip(3) - .filter(|&x| *x != 0x00) - .cloned() - .take(4) - .collect() - } -} - -impl AsRef for TypeName { - fn as_ref(&self) -> &str { - self.as_str() - } -} - -impl Display for TypeName { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } -} - -impl<'de> Deserialize<'de> for TypeName { - fn deserialize>(deserializer: D) -> Result { - use de::Error; - let s = String::deserialize(deserializer)?; - s.parse().map_err(D::Error::custom) - } -} - -impl FromStr for TypeName { - type Err = Error; - - fn from_str(s: &str) -> Result { - for c in s.chars() { - match c { - 'A'..='Z' | 'a'..='z' | '0'..='9' | '/' | '_' => (), - _ => fail!( - ErrorKind::Parse, - "invalid character `{}` in type name: `{}`", - c, - s - ), - } - } - - Ok(TypeName(s.to_owned())) - } -} - -impl TryFrom<&str> for TypeName { - type Error = Error; - - fn try_from(s: &str) -> Result { - s.parse() - } -} diff --git a/cosmos-stdtx/tests/integration.rs b/cosmos-stdtx/tests/integration.rs deleted file mode 100644 index 6dd12b2..0000000 --- a/cosmos-stdtx/tests/integration.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! `cosmos-stdtx` integration tests - -use cosmos_stdtx::Schema; - -/// Path to an example schema TOML file -const EXAMPLE_SCHEMA: &str = "tests/support/example_schema.toml"; - -/// Load an example [`Schema`] from a TOML file -#[test] -fn load_schema() { - let schema = Schema::load_toml(EXAMPLE_SCHEMA).unwrap(); - assert_eq!(schema.definitions().len(), 2); - - for definition in schema.definitions() { - for (i, field) in definition.fields().iter().enumerate() { - assert_eq!(i + 1, field.tag() as usize); - } - } -} diff --git a/cosmos-stdtx/tests/support/example_schema.toml b/cosmos-stdtx/tests/support/example_schema.toml deleted file mode 100644 index 4293f10..0000000 --- a/cosmos-stdtx/tests/support/example_schema.toml +++ /dev/null @@ -1,32 +0,0 @@ -# Example StdTx message schema definition. -# -# Message types taken from Terra's oracle voter transactions: -# - -# StdTx namespace for schema definitions -# (e.g. `cosmos-sdk/StdTx` for Cosmos SDK) -namespace = "core/StdTx" - -# Bech32 address prefixes -acc_prefix = "terra" -val_prefix = "terravaloper" - -[[definition]] -type_name = "oracle/MsgExchangeRatePrevote" -fields = [ - { name = "hash", type = "string" }, - { name = "denom", type = "string" }, - { name = "feeder", type = "sdk.AccAddress" }, - { name = "validator", type = "sdk.ValAddress" }, -] - -[[definition]] -type_name = "oracle/MsgExchangeRateVote" -fields = [ - # explicit field tag example - will start from "1" otherwise - { name = "exchange_rate", type = "sdk.Dec", tag = 1 }, - { name = "salt", type = "string" }, - { name = "denom", type = "string" }, - { name = "feeder", type = "sdk.AccAddress" }, - { name = "validator", type = "sdk.ValAddress" }, -]