---
# System prepended metadata

title: PocketChain Pseudo-code

---

# PocketChain Ledger

> Defines the data structure of the *PocketChain* ledger.

https://excalidraw.com/#room=e1bed1f4516380fd122e,QoGoTRwTdhZzyrSIPa2pGA

# Tips mechanism

==How announcers get payed after clients spend tips ??==

Each client $i$ is subscribed to $q$ announcers. To submit a transaction $tx$ for a tip $t_i$, the clients performs the following:

- Generate $k$ proportions:

$$
p_i = \{ \alpha^k: 0 \leq k \leq q, \alpha \in [0,1]\}
$$
- Normalize $p_i$.

$$
p_i = \{ \frac{p^k_i}{\sum_{j} p^j_i} : 0 \leq k \leq q\} 
$$

Calculate tips: 

$$
t^k_i = \begin{cases}
    T \cdot p^k_i & \text{if } t^k_i \geq \text{min_tip} \\
    0 & \text{otherwise}
\end{cases}: \{0 \leq k \leq q \}
$$
----
When submitting a transaction $tx$ for a total tip $\mathcal{T}$, the client $n_c$ follows these steps:
-  Generate Proportions.
    The client generates $k$ proportions according to an exponential decay factor $\alpha \in [0, 1]$:
$$
   p_{n_c} = \{ \alpha^k : 0 \leq k \leq q, \alpha \in [0,1] \}
$$
   These proportions determine how the total tip is distributed, with earlier announcers receiving larger shares.
- Normalize Proportions:  
   The proportions are normalized to ensure the sum of the proportions is 1:
$$
   p_{n_c} = \left\{ \frac{p_{n_c}^{(k)}}{\sum_{i \in |p_{n_c}|} p^{(i)}_{n_c}} : 0 \leq k \leq q \right\}
$$
- Calculate Tips:  
   The total tip $\mathcal{T}$ is distributed across $q$ announcers, with the tip for each announcer $k$ calculated as:
$$
   \mathcal{T}^k = \begin{cases}
   \mathcal{T} \cdot p_{n_c}^k & \text{if } \mathcal{T}^k \geq \text{ $\mathcal{T}_{base}$} \\
   0 & \text{otherwise}
   \end{cases}, \quad 0 \leq k \leq q
$$
   This rule ensures that each announcer receives at least the minimum tip threshold $\mathcal{T}_{base}$. If an announcer’s calculated share of the tip is less than $\mathcal{T}_{base}$, they do not receive any portion of the fee. This prevents the network from being overwhelmed by low-fee transactions that offer insufficient compensation for announcers.


# To improve

- [ ] We need to mention that at least the energy consumption is retrieved from running PC on mobile devices for mobile nodes. 
- [ ] Motivate the use of BRB, what's the issue that it solves and why we chose it. (intro + properties in sys model)
- [ ] Try to avoid general/fuzzy words.
- [x] Possibility to generalize the communicatiom medium use (The system assumes a very narrow transmission model (OFDMA))
- [x] Improve motivation for PC, and clarify that it doesn't exclusively uses mobile/resource-constrained devices.
- [ ] What's the motivation for the endorsment process?
- [ ] Emphasize on the complexity of the consensus algorithm (log) to discuss the network cost of PC.
- [x]  "Pconflict(q) \ "(V BChs),""---Incorrect LaTeX maths mode use. 
- [ ] Improve the conversion function (Tips to PC)
- [ ] Define why and how PC implements fairness.

# To ADD
- [ ] Discuss forks
- [ ] Add a section on the compression mechanisms to prevent the ledger from growing linearly.
- [ ] New devices must not download the entire chain to be able to perform the processor (or announcer) task.
- [ ] Compare PC to some other solutions that can run on mobile. 
- [ ] Clearly mention the limitations of PC.
- [ ] Add security guarantess of PC.
- [ ] Additional experiments (to specify)
- [ ] Can conflicting transactions be used as a DoS attack? --> Security analysis
- [ ] Add a discussion section : Limitations, Potential attacks, Parameters tuning ...


### Questions and Considerations
> - [ ] Should staking Txs be validated without being in a block.
> - [ ] Proof of History
> - [x] How to consider that a tx is confirmed?
*In Bitcoin, the sender's balance is technically updated as soon as the transaction is included in a validated block that is added to the blockchain. *
> - [x] How are rewards distributed among the nodes (Fixed Rewards/ Tx fee sharing)
*==**Bitcoin**== has a fixed block reward. Currently, a miner who successfully creates a new block receives 6.25 BTC. 
All transaction fees from a block go to the miner who successfully mines it.
==**Ethereum**==
**Block Reward**: fixed block reward that gets paid out to the validator who successfully creates a new block.  This reward is currently 2 ETH.
**Transaction Fees**:
    Base Fee: a portion of the transaction fee that is "burned" or permanently removed from circulation. The base fee adjusts dynamically based on network congestion.
    Priority Fee : an optional additional fee that users can include to incentivize validators to include their transactions more quickly. Priority fees go directly to the validating node.*


> **Conditions to endorse:**
> - [ ] Checks if the transactions within the block are valid (e.g., correct format, no double-spending attempts). 
> - [x] Checks if the transactions in the block haven't already been confirmed in the ledger. ==OnTheChain()==
> - [x] Checks if the block's Announcer is a valid announcer and doesn't have another block currently under consensus. ==HasBlockUnderConsensus()==
> - [x] Check Ongoing validations ==HasConflictingTx()==
> 
> > TO ADD
> 
> - For Announcers to endorse a block, the announcer must have `staking_block_expiry > 0`.


### PocketChain Tx
```python=
Class Transaction:
        id: int
        sender: PublicKey  
        recipient: PublicKey # recipient's PubKey
        value: int
        timestamp: GetCurrrentTime ()
        signature: Str
        tip: int
```

### PocketChain Block
```python=
class PCBlock:
  # Header fields
    index: int # depth | vector clock index
    timestamp: int
    hash: str # self called function
    previous_hash: str
    merkle_root: str
    announcer: str
  # Body fields
    transactions: list
  # Block attachments
    endorsements: list
```


### PocketChain Genesis Block
```python=
genesis_block = PCBlock(
    index=0
    hash="000" # SHA256
    previous_block_hash="000"
    merkle_root="000"
    timestamp=1720656000 
    transactions=[coinbase_transaction] initial deposit
    announcer="pk"
    endorsements=[pk]
)
```


# Network Initialization
**Network Configuration:**
```python=
self.config = {
    'q': Number of announcers to subscribe to
    'BRB_SAMPLES': {Gossip_sample, Echo_sample, Ready_sample, Delivery_sample}
    'BRB_THRESHOLDS':{G,E,R,D}
    'MINIMUM_FEE': 1 tip
    'STAKE_AMOUNT': 1 PC coin # function of the PocketChain
    'MIN_STORAGE': 
    'MAX_BLOCK_SIZE': 1 Mb
    'STAKE_EXPIRY': 5
}
```

# ==*Generic Node Class*==

```python=
Class Node:
    key_pair: str  # Public and Private keys
    config: list # Load network configuration
    balance: float # coins exchanged in Pocketchain
    tips: int
    announcer_registry: list
    block_store: list
```

## ==*Client Node*==

```python=
Class Client extends Node:
    super(Node)
```

### Initialize client
```python=
Procedure initialize():
        key_pair = KeyGen() || key_pair
        config = LoadConfiguration()
        balance = InitializeBalance(PubKey)
        tips = InitializeTips(PubKey)
        pendingTransactions = []
        announcer_registry = SubscribeToAnnouncers()
End Procedure
```
### Connect to Announcers
```python=
pubSubSystem = InitializePubSubSystem()

Procedure SubscribeToAnnouncers()
    pubSubSystem.subscribe("ANNOUNCER_AVAILABILITY")
        while length(announcer_registry) < config.q:
            message = pubSubSystem.receive()
            if message.type == "announcer_advertisement":
                if message.announcer not in announcer_registry: 
                    if CheckEligibility(message.announcer):
                        announcer_registry.append(message.announcer)
                    end if
                end if
            end if
End Procedure
```

### Create transaction
```python=
Function CreateTransaction(receiverAddress, transferAmount)
    if balance < transferAmount
        return "Insufficient balance"
    end if
    if tip < minimumFee
        return "Insufficient tip"
    end if
    transaction = new Transaction()
    transaction.id = id
    transaction.sender = address
    transaction.receiver = receiverAddress
    transaction.value = transferAmount
    transaction.tip = CalculateTransactionFee(transaction.id, Tips) #MINIMUM_FEE + extra Tips
    transaction.signature = Sign(transaction, senderPrivateKey)
    transaction.timestamp = GetCurrentTimestamp()
    tips = tips - transaction.tip
    pendingTransactions.append(transaction)
    
    return transaction
```
### Broadcast TX
```python=
Procedure BroadcastTransaction(transaction)
        for each announcer in announcers_registry do:
            announcer.send('TX_SUBMIT',transaction)
        end for
End Procedure
```

### TX Validation Notification
The client listens to $p<q$ confirmation from the network, that his tx is confirmed.
```python=
# Also update the receiver's balance
Upon event <ReceiveTransactionConfirmation(transaction)|announcer>
    if pendingTransactions.contains(transaction) then
        pendingTransactions.remove(transaction)
        # The actual balance is only updated upon confirmation
# executed by sender
        balance -= transaction.amount 
    end if
End Procedure
```

### Update Announcers Registry

Make sure that the $q$ announcers are active at the moment of sending a Tx. 

---

---

## ==*Processor Node*== 
```
Class Processor extends Node:
    super(Node)
    local_blockchain=[]
```

### Initialize Processor
```python=
Procedure initialize():
        key_pair = KeyGen() || key_pair
        config = LoadConfiguration()
        balance = InitializeBalance(PubKey)
        tips = InitializeTips(PubKey)
        #announcer_registry = DiscoverAnnouncers()
        availableStorage= allocateStorage()
End Procedure
```
### Proof-of-Space


### Process blocks (Consensus)
```python=
Upon Event <ReceiveBRBMessage|block|Announcer\Processor>:
    if CheckEligibility(Announcer) 
    and HasEnoughEndorsements(block)
        initiateBRB(Config.BRB_SAMPLES,Config.BRB_THRESHOLDS)
        RelayBRBmessages(block)    
```

```python=
Upon event <Deliver|block>:
    local_blockchain.append(block)
```
---

---

## ==*Announcer Node*==

```python=
Class Announcer extends Node:
    super(Node)
    blockchain : list
    mempool : list
    isEligible: bool #initially false
    staking_block_expiry : int
```

### Initialize Announcer
```python=
Procedure initialize():
        key_pair = KeyGen() || key_pair
        config = LoadConfiguration()
        balance = InitializeBalance(PubKey)
        tips = InitializeTips(PubKey)
        mempool = InitializeMempool()
        announcer_registry = DiscoverAnnouncers()
        isEligible= false
        staking_block_expiry = 0
        availableStorage= allocateStorage()
End Procedure
```
### PoS
```python=
Function CheckEligibility(Announcer): 
    if Announcer.availableStorage >= config.MIN_STORAGE 
    and Announcer.balance >= Config.STAKE_AMOUNT
    and staking_block_expiry >0:
        return True
    else:
        return False
End Function
```
```python=
Procedure Stake()  
    if CheckEligibility():
        receipt= InitiateStakingTransaction()
        ProcessStakingTransaction(receipt)
        AnnounceAvailability(receipt) #Stake_receipt= stake_tx's id or hash
End Procedure
```

```python=
Function InitiateStakingTransaction(amount)
    transaction = Transaction(
            sender = sender.public_key,
            recipient = blockchain.staking_address
            value = sender.balance
            timestamp = GetCurrrentTime ()
            tip = 0
            type = "staking" 
        )
    sendTransaction(transaction) #consensus?
    return transaction.id
End Function
```

```python=
Procedure processStakingTransaction(transaction)
    if validateTransaction(transaction)
        sender.balance -= transaction.value
        sender.staking_block_expiry = Config.STAKE_EXPIRY
End Procedure
```
### Advertise Availability
```python=
Procedure AnnounceAvailability(announcerAddress, Stake_receipt)
    while true do
        message = new Advertisement(announcerAddress, Stake_receipt)
        pubSubSystem.publish("ANNOUNCER_AVAILABILITY", message)
        Sleep(AdvertisementInterval)
    end while
End Procedure
```

### Transaction Verification
```python=
Upon Event <ReceiveTransaction(tx)|client node>
    if isValidTx(tx):
        mempool.addTransaction(tx) #existing tx in mempool are verified
```

```python=
Function isValidTx(tx):
    if hasEnoughTips() and isValidSignature(tx) and isValidBalance(tx) #Verify sender signature, tx fees, enough balance
        return True
    esle:
        return False    
End Function
```

### Candidate Block Formation
```python=
Procedure CreateCandidateBlock()        
    block = new Block(
        timestamp = GetCurrentTimestamp()
        hash = GetHashOfBlock()
        announcer = PubKey
        transactions = selectTransactions(mempool) 
        previous_hash = GetHashOfLastBlock(Blockchain)
        signature = Sign(block, announcer_private_key)
    )
    BroadcastCandidateBlockToAnnouncers(block)
End Procedure
```

```python=
Function selectTransactions()
    transactions = []
    block_size = 0
    while mempool is not empty and block_size <= config.MAX_BLOCK_SIZE do
        transactions.add(transaction)
        block_size += size(transaction)
    end while
    retrun transactions
End Function
```

```python=
Upon Event <ReceiveEndorsement(block)|announcers>
    if length(block.endorsements) >= EndorseThreshold():
        intiateBRB(block)
        staking_block_expiry -=1  
```

```python=
Procedure intiateBRB()
    CreateBroadcastChannel(block)
    InitBroadcastSamples()
    Broadcast(Block)
End Procedure
```



### Endorsements


```python=
Function EndorseThreshold()
    activeAnnouncer = getActiveAnnouncer(announcer_registry)  
    # Simple Majority (50% + 1)
    threshold = (activeAnnouncerCount // 2) + 1
    # or (2/3 + 1)
    # threshold = (2 * activeAnnouncerCount // 3) + 1
    return theshold
End Function

Function ResolveConflict()
```

```python=
Procedure AdvertiseCandidateBlock
    PubSubSystem.publish("ENDORSEMENT", candidateBlock)
End Procedure
```

```python=
Function EndorseBlock(candidateBlock)
    endorsement = SignBlock(Hash(candidateBlock), GetPrivateKey())
    endorsementChannel.publish(endorsement)
    endorsedBlocks.append(candidateBlock)
    return endorsement
End Function
```
```python=
Upon Event <ReceiveCandidateBlock(candidateBlock)|announcer>
    if isValidBlock(candidateBlock)
    not OnTheChain(candidateBlock) and 
    not HasConflictingTransactions(candidateBlock) and
    not HasBlockUnderConsensus(announcer):
        EndorseBlock(candidateBlock)
    else
        SendRejection(candidateBlock)
    end if
```

```python=
Function OnTheChain(candidateBlock):
    for tx in candidateBlock.transactions:
        if blockchain.contains(tx.id):
            return True 
        end if
    end for
    return False 
```

```python=
Function HasConflictingTransactions(candidateBlock)
    for block in endorsedBlocks do #endorsedBlocks[]
        for tx in block.transactions do
            if candidateBlock.transactions.contains(tx):
                return True
            end if
        end for
    end for
End Function
```

```python=
Function HasBlockUnderConsensus(announcer)
    if routingTable.containsChannel(announcer) then
        return true
    end if
    for each block in endorsedBlocks do
        if block.announcer == announcer then
            return true
        end if
    end for
    return false
End Function
```

### After validation, update mempool
```python=
Procedure clearMempool(mempool, validatedBlock)
    for each transaction in validatedBlock.transactions do
        if mempool.contains(transaction) then
            mempool.remove(transaction)
        end if
    end for
End Procedure 
```

# Example
```python=
class BeaconNode:
    def __init__(self, config):
        self.config = config  # Store configuration options
        self.validator_registry = {}  # Key-value store for validator data
        self.block_store = []  # History of recent blocks
        self.peer_manager = PeerManager(self.config)  # Handles network connections

    def start_syncing(self):
        # Logic to connect to other nodes and download the blockchain  

    def process_block(self, block):
        # Logic to validate and store a received block

    def propose_block(self):
        # Logic to create a new block if this node is selected as a proposer

    def create_attestation(self, block):
        # Logic to create an attestation supporting a given block

```


## PUB/SUB test code
```python=
import threading
import time

class Publisher:
    def __init__(self):
        self.subscribers = {}  # Keep track of subscriber node IDs and their message queues
        self.lock = threading.Lock()
        self.advertising_event = threading.Event()

    def advertise(self):
        # Simulate advertising the publisher's address
        print("Publisher advertising...")
        self.advertising_event.set()  # Set the event to notify nodes to subscribe

    def register_subscriber(self, node_id, message_queue):
        with self.lock:
            print(f"Publisher: Node {node_id} subscribed.")
            self.subscribers[node_id] = message_queue
            # Acknowledge the subscription by sending a confirmation message
            message_queue.put(f"Welcome, Node {node_id}!")

    def run(self):
        self.advertise()

    def stop(self):
        self.advertising_event.clear()


class Node(threading.Thread):
    def __init__(self, node_id, publisher):
        super().__init__()
        self.id = node_id
        self.publisher = publisher
        self.message_queue = queue.Queue()

    def subscribe(self):
        # Simulate subscribing to the publisher
        self.publisher.register_subscriber(self.id, self.message_queue)

    def run(self):
        # Wait until the publisher starts advertising
        self.publisher.advertising_event.wait()
        self.subscribe()  # Subscribe to the publisher

        # Listen for a confirmation message from the publisher
        while True:
            message = self.message_queue.get()
            if message == 'END':
                break
            print(f"Node {self.id} received message: {message}")


# Set up the publisher
publisher = Publisher()
# Create and start nodes
nodes = [Node(i, publisher) for i in range(3)]

# Start the publisher
publisher_thread = threading.Thread(target=publisher.run)
publisher_thread.start()

# Start the nodes
for node in nodes:
    node.start()

# Give time for nodes to subscribe and interact with the publisher
time.sleep(5)

# Stop the publisher and nodes
publisher.stop()
for node in nodes:
    node.message_queue.put('END')
    node.join()

publisher_thread.join()
print("Simulation ended.")
```

```python=
def initiateStakingTransaction(sender, amount):
    if amount <= sender.available_balance:
        transaction = Transaction(
            sender=sender.public_key,
            recipient=blockchain.staking_address,  # A special address for staking
            value=amount,
            type="staking" 
        )
        transaction.sign(sender.private_key)
        sendTransaction(transaction)
    else:
        print("Error: Insufficient balance to stake.")

def processStakingTransaction(transaction):
    if validateTransaction(transaction): 
        sender = getAccount(transaction.sender)
        sender.available_balance -= transaction.value
        sender.staked_balance += transaction.value
        sender.staking_lock_expiry = getTimestamp() + config.staking_lock_period
        updateAccountState(sender)
```

```python=
def processStakingTransaction(transaction):
    if validateTransaction(transaction): 
        sender = getAccount(transaction.sender)
        sender.available_balance -= transaction.value
        sender.staked_balance += transaction.value
        sender.staking_lock_expiry = getTimestamp() + config.staking_lock_period
        updateAccountState(sender)
```
__________

```python=
// Initialization 
For each endorser: 
S = {} // Set of endorsed blocks 
T = {} // Set of transactions in endorsed blocks 

// Block Processing 
On receiving block B with endorsement e(B): 
if no_conflicts(B, S) and ledger_consistent(extract_transactions(B)): 
	if B not in S: 
		e(B) += 1 // Initial endorsement for new blocks 
		S.add(B) 
		T.add(extract_transactions(B)) 
	else:
	// Handle higher endorsement updates 
		if received e(B) > local e(B): 
			update local e(B) 
	broadcast(B, endorsement_proof(B))
else: 
	// Conflicts
	ConflictingSet = find_all_conflicting_blocks(B, S)
    C_max = find_strongest_block(ConflictingSet) 

    if e(B) > e(C_max) or (e(B) == e(C_max) and hash(B) < hash(C_max)):
      for C in ConflictingSet:
        S.remove(C)
        T.remove(extract_transactions(C))
      S.add(B)
      T.add(extract_transactions(B))
      e(B) += 1
      broadcast(B, endorsement_proof(B))
    // else discard B 
	
// Finalization 
if e(B) >= threshold: 
	B is ready for consensus 
	S.remove(B) 
	T.remove(transactions in B)
```