pchain_types/
blockchain.rs

1/*
2    Copyright © 2023, ParallelChain Lab 
3    Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
4*/
5
6//! Types which appear in blocks like [transactions](Transaction) and [receipts](Receipt), and also [blocks](Block) themselves.
7
8use borsh::{BorshSerialize, BorshDeserialize};
9use crate::serialization::{Serializable, Deserializable};
10use crate::cryptography::{Keypair, PublicKey, PublicAddress, SignatureBytes, Signer, Verifier, Sha256Hash, BloomFilter, sha256};
11use crate::runtime::*;
12
13/// A data structure that describes and authorizes the execution of a batch of transactions (state transitions) on the blockchain.
14#[derive(BorshSerialize, BorshDeserialize, Clone)]
15pub struct Block {
16    /// Block header
17    pub header : BlockHeader,
18
19    /// A dynamically sized list of Transactions
20    pub transactions : Vec<Transaction>,
21
22    /// A dynamically sized list of Receipts. If a Block contains a Transaction,
23    /// it must also contain its Receipt. Receipts appear in the order of their Transactions.
24    pub receipts : Vec<Receipt>,
25}
26
27impl Serializable for Block {}
28impl Deserializable for Block {}
29
30/// Block header defines meta information of a block, including evidence for verifying validity of the block.
31#[derive(Clone, BorshSerialize, BorshDeserialize)]
32pub struct BlockHeader {
33    /// Block hash of this block
34    pub hash: Sha256Hash,
35
36    /// The number of Justify-links between this Block and the Genesis Block. 0 for the Genesis Block
37    pub height: u64, 
38
39    /// A QuorumCertificate that points to the Block’s parent
40    pub justify: hotstuff_rs::types::QuorumCertificate,
41
42    /// The SHA256 Hash over content inside the block header
43    pub data_hash: hotstuff_rs::types::CryptoHash,
44
45    /// A number unique to a particular ParallelChain Mainnet-based blockchain. This
46    /// prevents, for example, Blocks from one chain from being published in another chain as evidence
47    /// of malfeasance.
48    pub chain_id: hotstuff_rs::types::ChainID,
49
50    /// The Public Address of the Validator that is the Leader of the View this Block was proposed in
51    pub proposer: PublicAddress,
52
53    /// A Unix timestamp
54    pub timestamp: u32,
55
56    /// The (inclusive) minimum number of Grays that a Transaction included in this Block must pay for every Gas used.
57    pub base_fee: u64,
58
59    /// The total gas used for producing the block.
60    pub gas_used: u64,
61
62    /// Transactions Hash, the Binary Merkle Tree root hash over the Block’s Transactions
63    pub txs_hash: Sha256Hash,
64
65    /// Receipts Hash, the Binary Merkle Tree root hash over the Block’s Receipts
66    pub receipts_hash: Sha256Hash,
67
68    /// State Hash, the SHA256 root hash of the blockchain’s World State Merkle Patricia 
69    /// Trie (MPT) after executing all of this Block’s Transactions
70    pub state_hash: Sha256Hash,
71
72    /// Log Bloom, the 256-byte Block-level Bloom Filter union of all the Bloom Filters of each Log topic from the Block’s Receipts
73    pub log_bloom: BloomFilter,
74}
75
76impl Serializable for BlockHeader {}
77impl Deserializable for BlockHeader {}
78
79/// Digitally signed instructions that tell the ParallelChain state machine to execute a sequence of [commands](Command). 
80/// 
81/// ## Creating a Transaction
82/// 
83/// There are two ways of creating an instance of transaction:
84/// 1. Using the [constructor function](Transaction::new): this takes in the user-provided fields of a transaction and
85///    computes its signature and hash automatically.
86/// 2. Using a struct expression (i.e., `Transaction { signer: ..., }`): this does not check the signature and hash 
87///    fields.
88#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
89pub struct Transaction {
90    /// The public address of the external account which signed this transaction
91    pub signer: PublicAddress,
92
93    /// The number of transactions signed by the signer that have been included on 
94    /// the blockchain before this transaction. This ensures that all of the signer’s transactions are
95    /// included in the blockchain in an expected order, and prevents the same transaction from
96    /// being included in multiple blocks.
97    pub nonce: u64,
98
99    /// A list of execution commands that triggers a sequence of state transitions
100    pub commands: Vec<Command>,
101
102    /// The maximum number of gas units (ref.) that should be used in executing this transaction
103    pub gas_limit: u64,
104
105    /// The maximum number of grays that the signer is willing to burn for a gas unit used in this transaction
106    pub max_base_fee_per_gas: u64,
107
108    /// the number of grays that the signer is willing to pay the block proposer for including this transaction in a block
109    pub priority_fee_per_gas: u64,
110
111    /// the signature formed by signing over content of this transaction by using the signer’s private key
112    pub signature: SignatureBytes,
113
114    /// The cryptographic hash of signature
115    pub hash: Sha256Hash,
116}
117
118impl Transaction {
119    pub fn new(signer: &Keypair, nonce: u64, commands: Vec<Command>, gas_limit: u64, max_base_fee_per_gas: u64, priority_fee_per_gas: u64) -> Transaction {
120        let mut transaction = Transaction {
121            signer: signer.public.to_bytes(),
122            nonce,
123            commands,
124            gas_limit,
125            max_base_fee_per_gas,
126            priority_fee_per_gas,
127            signature: [0u8; 64],
128            hash: [0u8; 32],
129        };
130
131        let signature = signer.sign(&Serializable::serialize(&transaction));
132        transaction.signature = signature.into();
133        
134        let hash = sha256(ed25519_dalek::ed25519::signature::Signature::as_bytes(&signature));
135        transaction.hash = hash;
136
137        transaction
138    }
139
140    /// Check whether the Transaction's:
141    /// 1. Signer is a valid Ed25519 public key.
142    /// 2. Signature is a valid Ed25519 signature.
143    /// 3. Signature is produced by the signer over the intermediate transaction.
144    /// 4. Hash is the SHA256 hash over the signature.
145    pub fn is_cryptographically_correct(&self) -> Result<(), CryptographicallyIncorrectTransactionError> { 
146        // 1.
147        let public_key = PublicKey::from_bytes(&self.signer)
148            .map_err(|_| CryptographicallyIncorrectTransactionError::InvalidSigner)?;
149
150        // 2.
151        let signature = ed25519_dalek::Signature::from_bytes(&self.signature)
152            .map_err(|_| CryptographicallyIncorrectTransactionError::InvalidSignature)?;
153
154        // 3.
155        let signed_msg = {
156            let intermediate_txn = Transaction {
157                signature: [0u8; 64],
158                hash: [0u8; 32],
159                ..self.to_owned()
160            };
161
162            Serializable::serialize(&intermediate_txn)
163        };
164        public_key.verify(&signed_msg, &signature).map_err(|_| CryptographicallyIncorrectTransactionError::WrongSignature)?;
165
166        // 4.
167        if self.hash != sha256(ed25519_dalek::ed25519::signature::Signature::as_bytes(&signature)) {
168            return Err(CryptographicallyIncorrectTransactionError::WrongHash)
169        }
170        
171        Ok(())
172    }
173}
174
175pub enum CryptographicallyIncorrectTransactionError {
176    InvalidSigner,
177    InvalidSignature,
178    WrongSignature,
179    WrongHash,
180}
181
182impl Serializable for Transaction {}
183impl Deserializable for Transaction {}
184
185
186#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
187pub enum Command {
188    Transfer(TransferInput),
189    Deploy(DeployInput),
190    Call(CallInput),
191    CreatePool(CreatePoolInput),
192    SetPoolSettings(SetPoolSettingsInput),
193    DeletePool,
194    CreateDeposit(CreateDepositInput),
195    SetDepositSettings(SetDepositSettingsInput),
196    TopUpDeposit(TopUpDepositInput),
197    WithdrawDeposit(WithdrawDepositInput),
198    StakeDeposit(StakeDepositInput),
199    UnstakeDeposit(UnstakeDepositInput),
200    NextEpoch,
201}
202
203impl Serializable for Command {}
204impl Deserializable for Command {}
205
206/// Log are messages produced by smart contract executions that are persisted on the blockchain
207/// in a cryptographically-provable way. Log produced by transactions that call smart contracts
208/// are stored in the `logs` field of a Block in the order in which they are emitted.
209#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
210pub struct Log { 
211    /// Key of this event. It is created from contract execution.
212    pub topic: Vec<u8>,
213    /// Value of this event. It is created from contract execution.
214    pub value: Vec<u8>,
215}
216
217impl Serializable for Log {}
218impl Deserializable for Log {}
219
220/// Receipt defines the result of transaction execution.
221pub type Receipt = Vec<CommandReceipt>;
222
223/// A CommandReceipt summarizes the result of execution of a [Command].
224#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
225pub struct CommandReceipt {
226    /// Exit status tells whether the corresponding command in the sequence
227    /// succeeded in doing its operation, and, if it failed, whether the 
228    /// failure is because of gas exhaustion or some other reason.
229    pub exit_status: ExitStatus,
230    /// How much gas was used in the execution of the transaction. 
231    /// This will at most be the transaction’s gas limit.
232    pub gas_used: u64,
233    /// The return value of the corresponding command.
234    pub return_values: Vec<u8>,
235    /// The logs emitted during the corresponding call command.
236    pub logs: Vec<Log>,
237}
238
239impl Serializable for CommandReceipt {}
240impl Deserializable for CommandReceipt {}
241
242/// ExitStatus defines the success and error types of receipt.
243#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
244pub enum ExitStatus {
245    /// The Transaction successfully accomplished everything that it could have been expected to do.
246    Success,
247
248    /// The Transaction failed to accomplish the primary operation that Transactions of its kinds are expected to accomplish.
249    Failed,
250
251    /// The Gas Limit was exceeded by a dynamically costed activity in a dynamic-cost Transaction.
252    GasExhausted,
253}
254
255impl Serializable for ExitStatus {}
256impl Deserializable for ExitStatus {}