Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move Polkadot implementation to Rust #3857

Draft
wants to merge 18 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ members = [
"chains/tw_binance",
"chains/tw_cosmos",
"chains/tw_ethereum",
"chains/tw_internet_computer",
"chains/tw_greenfield",
"chains/tw_internet_computer",
"chains/tw_native_evmos",
"chains/tw_native_injective",
"chains/tw_polkadot",
"chains/tw_ronin",
"chains/tw_solana",
"chains/tw_sui",
Expand All @@ -26,6 +27,7 @@ members = [
"tw_misc",
"tw_number",
"tw_proto",
"tw_ss58_address",
"tw_utxo",
"wallet_core_rs",
]
Expand Down
15 changes: 15 additions & 0 deletions rust/chains/tw_polkadot/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "tw_polkadot"
version = "0.1.0"
edition = "2021"

[dependencies]
lazy_static = "1.4.0"
tw_coin_entry = { path = "../../tw_coin_entry" }
tw_encoding = { path = "../../tw_encoding" }
tw_hash = { path = "../../tw_hash" }
tw_keypair = { path = "../../tw_keypair" }
tw_memory = { path = "../../tw_memory" }
tw_number = { path = "../../tw_number" }
tw_proto = { path = "../../tw_proto" }
tw_ss58_address = { path = "../../tw_ss58_address" }
64 changes: 64 additions & 0 deletions rust/chains/tw_polkadot/src/address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

use std::fmt;
use std::str::FromStr;
use tw_coin_entry::coin_entry::CoinAddress;
use tw_coin_entry::error::prelude::*;
use tw_coin_entry::prefix::AddressPrefix;
use tw_memory::Data;
use tw_ss58_address::{NetworkId, SS58Address};

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct PolkadotPrefix(NetworkId);

impl PolkadotPrefix {
pub fn network(self) -> NetworkId {
self.0
}
}

impl TryFrom<AddressPrefix> for PolkadotPrefix {
type Error = AddressError;

fn try_from(prefix: AddressPrefix) -> Result<Self, Self::Error> {
match prefix {
AddressPrefix::SubstrateNetwork(network) => NetworkId::from_u16(network).map(Self),
_ => Err(AddressError::UnexpectedAddressPrefix),
}
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PolkadotAddress(pub SS58Address);

impl PolkadotAddress {
pub fn with_network_check(self) -> AddressResult<Self> {
if self.0.network() != NetworkId::POLKADOT {
return Err(AddressError::UnexpectedAddressPrefix);
}
Ok(self)
}
}

impl CoinAddress for PolkadotAddress {
#[inline]
fn data(&self) -> Data {
self.0.to_bytes()
}
}

impl FromStr for PolkadotAddress {
type Err = AddressError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
SS58Address::from_str(s).map(PolkadotAddress)
}
}

impl fmt::Display for PolkadotAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
50 changes: 50 additions & 0 deletions rust/chains/tw_polkadot/src/compiler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes};
use tw_coin_entry::error::prelude::*;
use tw_coin_entry::signing_output_error;
use tw_proto::Polkadot::Proto;
use tw_proto::TxCompiler::Proto as CompilerProto;

pub struct PolkadotCompiler;

impl PolkadotCompiler {
#[inline]
pub fn preimage_hashes(
coin: &dyn CoinContext,
input: Proto::SigningInput<'_>,
) -> CompilerProto::PreSigningOutput<'static> {
Self::preimage_hashes_impl(coin, input)
.unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e))
}

fn preimage_hashes_impl(
_coin: &dyn CoinContext,
_input: Proto::SigningInput<'_>,
) -> SigningResult<CompilerProto::PreSigningOutput<'static>> {
todo!()
}

#[inline]
pub fn compile(
coin: &dyn CoinContext,
input: Proto::SigningInput<'_>,
signatures: Vec<SignatureBytes>,
public_keys: Vec<PublicKeyBytes>,
) -> Proto::SigningOutput<'static> {
Self::compile_impl(coin, input, signatures, public_keys)
.unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e))
}

fn compile_impl(
_coin: &dyn CoinContext,
_input: Proto::SigningInput<'_>,
_signatures: Vec<SignatureBytes>,
_public_keys: Vec<PublicKeyBytes>,
) -> SigningResult<Proto::SigningOutput<'static>> {
todo!()
}
}
103 changes: 103 additions & 0 deletions rust/chains/tw_polkadot/src/entry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

use crate::address::{PolkadotAddress, PolkadotPrefix};
use crate::compiler::PolkadotCompiler;
use crate::signer::PolkadotSigner;
use std::str::FromStr;
use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes};
use tw_coin_entry::derivation::Derivation;
use tw_coin_entry::error::prelude::*;
use tw_coin_entry::modules::json_signer::NoJsonSigner;
use tw_coin_entry::modules::message_signer::NoMessageSigner;
use tw_coin_entry::modules::plan_builder::NoPlanBuilder;
use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder;
use tw_coin_entry::modules::wallet_connector::NoWalletConnector;
use tw_keypair::tw::PublicKey;
use tw_proto::Polkadot::Proto;
use tw_proto::TxCompiler::Proto as CompilerProto;
use tw_ss58_address::{NetworkId, SS58Address};

pub struct PolkadotEntry;

impl CoinEntry for PolkadotEntry {
type AddressPrefix = PolkadotPrefix;
type Address = PolkadotAddress;
type SigningInput<'a> = Proto::SigningInput<'a>;
type SigningOutput = Proto::SigningOutput<'static>;
type PreSigningOutput = CompilerProto::PreSigningOutput<'static>;

// Optional modules:
type JsonSigner = NoJsonSigner;
type PlanBuilder = NoPlanBuilder;
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;

#[inline]
fn parse_address(
&self,
_coin: &dyn CoinContext,
address: &str,
_prefix: Option<Self::AddressPrefix>,
) -> AddressResult<Self::Address> {
PolkadotAddress::from_str(address)?.with_network_check()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should allow to verify Kusama, Acala and other addresses via the Polkadot implementation. To achieve this, it's worth to check the address prefix if it's provided and SS58

}

#[inline]
fn parse_address_unchecked(
&self,
_coin: &dyn CoinContext,
address: &str,
) -> AddressResult<Self::Address> {
PolkadotAddress::from_str(address)
}

#[inline]
fn derive_address(
&self,
_coin: &dyn CoinContext,
public_key: PublicKey,
_derivation: Derivation,
prefix: Option<Self::AddressPrefix>,
) -> AddressResult<Self::Address> {
let public_key = public_key
.to_ed25519()
.ok_or(AddressError::PublicKeyTypeMismatch)?;

SS58Address::from_public_key(
public_key,
prefix
.map(PolkadotPrefix::network)
.unwrap_or(NetworkId::POLKADOT),
)
.map(PolkadotAddress)
}

#[inline]
fn sign(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput {
PolkadotSigner::sign(coin, input)
}

#[inline]
fn preimage_hashes(
&self,
coin: &dyn CoinContext,
input: Self::SigningInput<'_>,
) -> Self::PreSigningOutput {
PolkadotCompiler::preimage_hashes(coin, input)
}

#[inline]
fn compile(
&self,
coin: &dyn CoinContext,
input: Self::SigningInput<'_>,
signatures: Vec<SignatureBytes>,
public_keys: Vec<PublicKeyBytes>,
) -> Self::SigningOutput {
PolkadotCompiler::compile(coin, input, signatures, public_keys)
}
}
Loading