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 {}