use crate::address::{Address, AddressConvertible, PrivateKey};
#[cfg(feature = "builder")]
use crate::network::ThorNode;
use crate::rlp::{
lstrip, static_left_pad, AsBytes, AsVec, BufMut, Bytes, BytesMut, Decodable, Encodable, Maybe,
RLPError,
};
#[cfg(feature = "builder")]
pub use crate::transaction_builder::{TransactionBuilder, TransactionBuilderError};
use crate::utils::blake2_256;
use crate::{rlp_encodable, U256};
use secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
use secp256k1::{Message, PublicKey, Secp256k1};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
rlp_encodable! {
#[cfg_attr(feature="serde", serde_with::serde_as)]
#[cfg_attr(feature="serde", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Transaction {
#[cfg_attr(feature="serde", serde(rename="chainTag"))]
pub chain_tag: u8,
#[cfg_attr(feature="serde", serde(rename="blockRef"))]
pub block_ref: u64,
pub expiration: u32,
pub clauses: Vec<Clause>,
#[cfg_attr(feature="serde", serde(rename="gasPriceCoef"))]
pub gas_price_coef: u8,
pub gas: u64,
#[cfg_attr(feature="serde", serde(rename="dependsOn"))]
pub depends_on: Option<U256> => AsBytes<U256>,
pub nonce: u64,
pub reserved: Option<Reserved> => AsVec<Reserved>,
#[cfg_attr(feature="serde", serde(with = "serde_with::As::<Option<crate::utils::unhex::Hex>>"))]
pub signature: Option<Bytes> => Maybe<Bytes>,
}
}
impl Transaction {
pub const TRANSACTION_GAS: u64 = 5_000;
pub fn get_signing_hash(&self) -> [u8; 32] {
let mut encoded = Vec::with_capacity(1024);
let mut without_signature = self.clone();
without_signature.signature = None;
without_signature.encode(&mut encoded);
blake2_256(&[encoded])
}
pub fn get_delegate_signing_hash(&self, delegate_for: &Address) -> [u8; 32] {
let main_hash = self.get_signing_hash();
blake2_256(&[&main_hash[..], &delegate_for.to_fixed_bytes()[..]])
}
pub fn sign(self, private_key: &PrivateKey) -> Self {
let hash = self.get_signing_hash();
let signature = Self::sign_hash(hash, private_key);
self.with_signature(Bytes::copy_from_slice(&signature))
.expect("generated signature must be correct")
}
const fn signature_length_valid(&self) -> bool {
match &self.signature {
None => true,
Some(signature) => {
self.is_delegated() && signature.len() == 130
|| !self.is_delegated() && signature.len() == 65
}
}
}
pub fn with_signature(self, signature: Bytes) -> Result<Self, secp256k1::Error> {
let copy = Self {
signature: Some(signature),
..self
};
if copy.signature_length_valid() {
Ok(copy)
} else {
Err(secp256k1::Error::IncorrectSignature)
}
}
pub fn sign_hash(hash: [u8; 32], private_key: &PrivateKey) -> [u8; 65] {
let secp = Secp256k1::signing_only();
let signature =
secp.sign_ecdsa_recoverable(&Message::from_slice(&hash).unwrap(), private_key);
let (recovery_id, bytes) = signature.serialize_compact();
bytes
.into_iter()
.chain([recovery_id.to_i32() as u8])
.collect::<Vec<_>>()
.try_into()
.unwrap()
}
pub fn intrinsic_gas(&self) -> u64 {
let clauses_cost = if self.clauses.is_empty() {
Clause::REGULAR_CLAUSE_GAS
} else {
self.clauses.iter().map(Clause::intrinsic_gas).sum()
};
clauses_cost + Self::TRANSACTION_GAS
}
pub fn origin(&self) -> Result<Option<PublicKey>, secp256k1::Error> {
match &self.signature {
None => Ok(None),
Some(signature) if self.signature_length_valid() => {
let hash = self.get_signing_hash();
let secp = Secp256k1::verification_only();
Ok(Some(secp.recover_ecdsa(
&Message::from_slice(&hash)?,
&RecoverableSignature::from_compact(
&signature[..64],
RecoveryId::from_i32(signature[64] as i32)?,
)?,
)?))
}
_ => Err(secp256k1::Error::IncorrectSignature),
}
}
pub fn delegator(&self) -> Result<Option<PublicKey>, secp256k1::Error> {
if !self.is_delegated() {
return Ok(None);
}
match &self.signature {
None => Ok(None),
Some(signature) if self.signature_length_valid() => {
let hash = self.get_delegate_signing_hash(
&self
.origin()?
.expect("Must be set, already checked signature")
.address(),
);
let secp = Secp256k1::verification_only();
Ok(Some(secp.recover_ecdsa(
&Message::from_slice(&hash)?,
&RecoverableSignature::from_compact(
&signature[65..129],
RecoveryId::from_i32(signature[129] as i32)?,
)?,
)?))
}
_ => Err(secp256k1::Error::IncorrectSignature),
}
}
pub const fn is_delegated(&self) -> bool {
if let Some(reserved) = &self.reserved {
reserved.is_delegated()
} else {
false
}
}
pub fn id(&self) -> Result<Option<[u8; 32]>, secp256k1::Error> {
match self.origin()? {
None => Ok(None),
Some(origin) => Ok(Some(blake2_256(&[
&self.get_signing_hash()[..],
&origin.address().to_fixed_bytes()[..],
]))),
}
}
pub fn has_valid_signature(&self) -> bool {
self._has_valid_signature().unwrap_or(false)
}
fn _has_valid_signature(&self) -> Result<bool, secp256k1::Error> {
if !self.signature_length_valid() {
return Ok(false);
}
match &self.signature {
None => Ok(false),
Some(signature) => {
let hash = self.get_signing_hash();
let secp = Secp256k1::verification_only();
Ok(secp
.recover_ecdsa(
&Message::from_slice(&hash)?,
&RecoverableSignature::from_compact(
&signature[..64],
RecoveryId::from_i32(signature[64] as i32)?,
)?,
)
.is_ok())
}
}
}
pub fn to_broadcastable_bytes(&self) -> Result<Bytes, secp256k1::Error> {
if self.signature.is_some() {
let mut buf = BytesMut::new();
self.encode(&mut buf);
Ok(buf.into())
} else {
Err(secp256k1::Error::IncorrectSignature)
}
}
#[cfg(feature = "builder")]
pub fn build(node: ThorNode) -> TransactionBuilder {
TransactionBuilder::new(node)
}
}
rlp_encodable! {
#[cfg_attr(feature="serde", serde_with::serde_as)]
#[cfg_attr(feature="serde", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Clause {
pub to: Option<Address> => AsBytes<Address>,
pub value: U256,
#[cfg_attr(feature="serde", serde(with = "serde_with::As::<crate::utils::unhex::Hex>"))]
pub data: Bytes,
}
}
impl Clause {
pub const REGULAR_CLAUSE_GAS: u64 = 16_000;
pub const CONTRACT_CREATION_CLAUSE_GAS: u64 = 48_000;
pub const ZERO_DATA_BYTE_GAS_COST: u64 = 4;
pub const NONZERO_DATA_BYTE_GAS_COST: u64 = 68;
pub fn intrinsic_gas(&self) -> u64 {
let clause_gas = if self.to.is_some() {
Self::REGULAR_CLAUSE_GAS
} else {
Self::CONTRACT_CREATION_CLAUSE_GAS
};
let data_gas: u64 = self
.data
.iter()
.map(|&b| {
if b == 0 {
Self::ZERO_DATA_BYTE_GAS_COST
} else {
Self::NONZERO_DATA_BYTE_GAS_COST
}
})
.sum();
clause_gas + data_gas
}
}
#[cfg_attr(feature = "serde", serde_with::serde_as)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Reserved {
pub features: u32,
#[cfg_attr(
feature = "serde",
serde(with = "serde_with::As::<Vec<crate::utils::unhex::Hex>>")
)]
pub unused: Vec<Bytes>,
}
impl Encodable for Reserved {
fn encode(&self, out: &mut dyn BufMut) {
let mut buf = vec![];
self.features.to_be_bytes().encode(&mut buf);
let mut stripped_buf: Vec<_> = [lstrip(&buf[1..])]
.into_iter()
.map(Bytes::from)
.chain(self.unused.clone())
.rev()
.skip_while(Bytes::is_empty)
.collect();
stripped_buf.reverse();
stripped_buf.encode(out)
}
}
impl Decodable for Reserved {
fn decode(buf: &mut &[u8]) -> Result<Self, RLPError> {
if let Some((feature_bytes, unused)) = Vec::<Bytes>::decode(buf)?.split_first() {
Ok(Self {
features: u32::from_be_bytes(static_left_pad(feature_bytes)?),
unused: unused.to_vec(),
})
} else {
Ok(Self::new_empty())
}
}
}
impl Reserved {
pub const DELEGATED_BIT: u32 = 1;
pub const fn new_delegated() -> Self {
Self {
features: Self::DELEGATED_BIT,
unused: vec![],
}
}
pub const fn new_empty() -> Self {
Self {
features: 0,
unused: vec![],
}
}
pub const fn is_delegated(&self) -> bool {
self.features & Self::DELEGATED_BIT != 0
}
pub fn is_empty(&self) -> bool {
self.features == 0 && self.unused.is_empty()
}
}