refactor: store a queue of pending updates

buddies-main-deployment
James Prestwich 4 years ago
parent 6536efb2a0
commit afefc55138
No known key found for this signature in database
GPG Key ID: 75A7F5C06D747046
  1. 31
      rust/optics-base/src/abis/ProcessingReplica.abi.json
  2. 8
      rust/optics-base/src/abis/mod.rs
  3. 1
      rust/optics-base/update_abis.sh
  4. 2
      rust/optics-core/src/traits/replica.rs
  5. 26
      solidity/contracts/Queue.sol
  6. 49
      solidity/contracts/Replica.sol

@ -138,7 +138,13 @@
"type": "function"
},
{
"inputs": [],
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "confirmAt",
"outputs": [
{
@ -225,11 +231,16 @@
},
{
"inputs": [],
"name": "optimisticSeconds",
"name": "next_pending",
"outputs": [
{
"internalType": "bytes32",
"name": "_pending",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "",
"name": "_confirmAt",
"type": "uint256"
}
],
@ -238,12 +249,12 @@
},
{
"inputs": [],
"name": "originSLIP44",
"name": "optimisticSeconds",
"outputs": [
{
"internalType": "uint32",
"internalType": "uint256",
"name": "",
"type": "uint32"
"type": "uint256"
}
],
"stateMutability": "view",
@ -251,7 +262,7 @@
},
{
"inputs": [],
"name": "ownSLIP44",
"name": "originSLIP44",
"outputs": [
{
"internalType": "uint32",
@ -264,12 +275,12 @@
},
{
"inputs": [],
"name": "pending",
"name": "ownSLIP44",
"outputs": [
{
"internalType": "bytes32",
"internalType": "uint32",
"name": "",
"type": "bytes32"
"type": "uint32"
}
],
"stateMutability": "view",

@ -124,12 +124,8 @@ impl<M> Replica for ReplicaContract<M>
where
M: ethers_providers::Middleware + 'static,
{
async fn pending(&self) -> Result<Option<(H256, U256)>, ChainCommunicationError> {
let pending = self.contract.pending();
let confirm_at = self.contract.confirm_at();
let res = tokio::try_join!(pending.call(), confirm_at.call());
let (pending, confirm_at) = res?;
async fn next_pending(&self) -> Result<Option<(H256, U256)>, ChainCommunicationError> {
let (pending, confirm_at) = self.contract.next_pending().call().await?;
if confirm_at.is_zero() {
Ok(None)

@ -1,7 +1,6 @@
#!/bin/zsh
cd ../../solidity && \
npm i && \
npm run compile && \
cat artifacts/contracts/Replica.sol/ProcessingReplica.json| jq .abi > ../rust/optics-base/src/abis/ProcessingReplica.abi.json && \
cat artifacts/contracts/Home.sol/Home.json| jq .abi > ../rust/optics-base/src/abis/Home.abi.json

@ -10,7 +10,7 @@ use crate::{
#[async_trait]
pub trait Replica: Common {
/// Return the pending root and time, if any
async fn pending(&self) -> Result<Option<(H256, U256)>, ChainCommunicationError>;
async fn next_pending(&self) -> Result<Option<(H256, U256)>, ChainCommunicationError>;
/// Confirm the next pending root (after its timer has elapsed);
async fn confirm(&self) -> Result<TxOutcome, ChainCommunicationError>;

@ -32,17 +32,31 @@ library QueueLib {
return _q.queue[_q.last];
}
function enqueue(Queue storage _q, bytes32 _item) internal {
uint256 _last = _q.last + 1;
function peek(Queue storage _q) internal view returns (bytes32 _item) {
require(_q.last >= _q.first, "Empty");
_item = _q.queue[_q.first];
}
function enqueue(Queue storage _q, bytes32 _item)
internal
returns (uint256 _last)
{
_last = _q.last + 1;
_q.last = _last;
_q.queue[_last] = _item;
if (_item != bytes32(0)) {
// saves gas if we're queueing 0
_q.queue[_last] = _item;
}
}
function dequeue(Queue storage _q) internal returns (bytes32 item) {
function dequeue(Queue storage _q) internal returns (bytes32 _item) {
uint256 _first = _q.first;
require(_q.last >= _first, "Empty");
item = _q.queue[_first];
delete _q.queue[_first];
_item = _q.queue[_first];
if (_item != bytes32(0)) {
// saves gas if we're dequeuing 0
delete _q.queue[_first];
}
_q.first = _first + 1;
}

@ -4,13 +4,15 @@ pragma solidity >=0.6.11;
import "@summa-tx/memview-sol/contracts/TypedMemView.sol";
import "./Common.sol";
import "./Merkle.sol";
import "./Queue.sol";
abstract contract Replica is Common, QueueManager {
using QueueLib for QueueLib.Queue;
abstract contract Replica is Common {
uint32 public immutable ownSLIP44;
uint256 public optimisticSeconds;
bytes32 public pending;
uint256 public confirmAt;
mapping(bytes32 => uint256) public confirmAt;
constructor(
uint32 _originSLIP44,
@ -18,7 +20,7 @@ abstract contract Replica is Common {
address _updater,
uint256 _optimisticSeconds,
bytes32 _current
) Common(_originSLIP44, _updater, _current) {
) Common(_originSLIP44, _updater, _current) QueueManager() {
ownSLIP44 = _ownSLIP44;
optimisticSeconds = _optimisticSeconds;
current = _current;
@ -34,30 +36,53 @@ abstract contract Replica is Common {
/// Hook for tasks
function _beforeUpdate() internal virtual;
function next_pending()
external
view
returns (bytes32 _pending, uint256 _confirmAt)
{
if (queue.length() != 0) {
_pending = queue.peek();
_confirmAt = confirmAt[_pending];
}
}
// TODO: refactor to queue
function update(
bytes32 _oldRoot,
bytes32 _newRoot,
bytes memory _signature
) external notFailed {
require(current == _oldRoot, "Not current update");
if (queue.length() > 0) {
require(_oldRoot == queue.lastItem(), "Not end of queue");
} else {
require(current == _oldRoot, "Not current update");
}
require(Common.checkSig(_newRoot, _oldRoot, _signature), "Bad sig");
_beforeUpdate();
confirmAt = block.timestamp + optimisticSeconds;
pending = _newRoot;
confirmAt[_newRoot] = block.timestamp + optimisticSeconds;
queue.enqueue(_newRoot);
}
function confirm() external notFailed {
require(confirmAt != 0, "No pending");
require(block.timestamp >= confirmAt, "Not yet");
require(queue.length() != 0, "No pending");
bytes32 _pending;
uint256 _now = block.timestamp;
while (_now >= confirmAt[queue.peek()]) {
_pending = queue.dequeue();
delete confirmAt[_pending];
}
// This condition is hit if the while loop is never executed, because
// the first queue item has not hit its timer yet
require(_pending != bytes32(0), "Not time");
_beforeConfirm();
current = pending;
delete pending;
delete confirmAt;
current = _pending;
}
}

Loading…
Cancel
Save