<pre>
  PIP: PIP-0013
  Title: Allow nodes to pull pending operations
  Type: Protocol
  Impact: Soft-Fork
  Author: Herman Schoenfeld <i>&lt;herman@sphere10.com&gt;</i>
  Comments-URI: https://discord.gg/sJqcgtD  (channel #pip-0013)
  Status: Active
  Created: 2018-01-03
</pre>

## Summary

A new network operation called NET_OP_PULL_PENDING is proposed that allows nodes to pull some or all of the pending operations from other connected peers.

## Motivation

Currently a node receives new operations as they flood-fill the network when created and during block propagation. Whilst this is extremely efficient, if a node restarts it loses the current pending pool and is required to wait until the next block in order to become aware of pending/minted operations. This situation can lead to out-of-sequence N_OPERATION errors since a node can forget the correct N_OPERATION of an owned account due to recent pending operations it lost during the resetart. This has led to withdrawal failures at exchanges from Poloniex, a serious user-experience issue.  Whilst this can be mitigated client-side by persisting the pending pool, a comprehensive solution is proposed here. The proposal also benefits mobile and light-clients by allowing them to poll peers for pending operations, as well as other layer-2 scenarios.

## Specification

The proposal here is to add a new network operation. 

### NET_OP_PULL_PENDING

**Arguments** 
- N : DWord  - the number of parameter keys in parameters  
- KeyHash_1 : Bytes[20] - the RIPEMD160 of the first key

  .
  .
  .
 
- KeyHash_N : Bytes[20] - the RIPEMD160 hash of the N'th key

**Output**
- M : DWord - the number of pending operations being returned
- OP_1_SIZE : DWord - the byte length of OP_1
- OP_1 - ByteArray[OP_1_SIZE] - the first pending operation

  .
  .
  .
 
- OP_M_SIZE : DWord - the byte length of OP_M
- OP_M - ByteArray[OP_M_SIZE] - the M'th pending operation

**PSEUDO-CODE**
```pascal
let output = list of operations 
if N = 0 then
  output = PendingPool
else
   for all O in PendingPool
     for all k in (Argument Keys)
       let senderAccount = SafeBox.Accounts[O.Sender]
       let receiverAccount = SafeBox.Accounts[O.Receiver]
       let feePayerAccount = SafeBox.Accounts[O.Signer]
       
       if RIPEMD160(senderAccount.Key) = k OR RIPEMD160(receiverAccount.Key) = k OR RIPEMD160(feePayerAccount.Key) = k then
         output.Add(O)
       else
          if O is ChangeKeyOperation AND RIPEMD160(O.NewKey) = k then
            output.Add(O)
          else if O is BuyAccountOperation and RIPEMD160(O.NewKey) = k then
            output.Add(O)
           else ....
            
return FORMAT(output)
```

**NOTE** the above pseudo-code is only a guide and actual implementation will more scenarios. It is left to the implementor to determine all the scenarios correctly. Errors in implementation will never result in out-of-consensus issues, since this is a non-critical network operation.
 
### DoS Protection

Since this network operation could be used to DoS a peer, peers should include the following protection:
- If 2 NET_OP_PULL_PENDING is received from the **same peer** within 5 seconds, blacklist that peer.

## Rationale

Other approaches were considered involving Bloom Filtering and bit-vectors, but these changes cannot be introduced within the V3 timeline. The above proposal is elegant and simple to implement, and achieves the job until later date. Argument keys are hashed for network efficiency, which is paid for client-side computationally. This is why DoS protection is necessary for this network operation. 

## Backwards Compatibility

This proposal is backwards compatible and can be introduced as a soft-fork.
