Skip to content

Commit

Permalink
left tree rotation works
Browse files Browse the repository at this point in the history
  • Loading branch information
pipermerriam committed Sep 15, 2015
1 parent e869c4d commit 869fd61
Show file tree
Hide file tree
Showing 3 changed files with 268 additions and 1 deletion.
145 changes: 145 additions & 0 deletions contracts/Alarm.sol
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ contract Alarm {
}

function getNextCallSibling(bytes32 callKey) public returns (bytes32) {
/*
* Returns the callKey any subsequent calls that have the same
* block number as the provided callKey. If there are no
* subsequent calls with the same block number returns 0x0
*/
var node = call_to_node[callKey];
var call = key_to_calls[callKey];
uint targetBlock = call.targetBlock;
Expand Down Expand Up @@ -275,6 +280,146 @@ contract Alarm {
}
}

function _rotateRight() internal {
/*
* 1. Detatch the left child of the root node. This is the
* new root node.
* 2. Detatch the right child of the new root node.
* 3. Set the old root node as the right child of the new root node.
* 4. Set the detatched right child from the new root node in
* the appropriate location in the tree.
*/
var oldRootNode = call_to_node[rootNodeCallKey];
var newRootNode = call_to_node[oldRootNode.left];
// #1
oldRootNode.left = 0x0;
rootNodeCallKey = newRootNode.callKey;

// #2
bytes32 detatchedChildCallKey = newRootNode.right;
newRootNode.right = 0x0;

// #3
newRootNode.right = oldRootNode.callKey;

// #4
placeCallInTree(detatchedChildCallKey);
}

function _shouldRotateRight() internal returns (bool) {
/*
* Is the left child of the rootNode in the future of the
* current block number.
*/
if (rootNodeCallKey == 0x0) {
return false;
}

var currentRoot = call_to_node[rootNodeCallKey];

// No left child so cant rotate right.
if (currentRoot.left == 0x0) {
return false;
}

// Current root already in the past.
if (key_to_calls[rootNodeCallKey].targetBlock <= block.number) {
return false;
}

return true;
}

function _rotateLeft() internal {
/*
* 1. Detatch the right child of the root node. This is the
* new root node.
* 2. Detatch the left child of the new root node.
* 3. Set the old root node as the left child of the new root node.
* 4. Set the detatched left child from the new root node in
* the appropriate location in the tree.
*/
var oldRootNode = call_to_node[rootNodeCallKey];
var newRootNode = call_to_node[oldRootNode.right];
// #1
oldRootNode.right = 0x0;
rootNodeCallKey = newRootNode.callKey;

// #2
bytes32 detatchedChildCallKey = newRootNode.left;

// #3
newRootNode.left = oldRootNode.callKey;

// #4
if (detatchedChildCallKey != 0x0) {
// First reset the node to not have a callKey,
// otherwise the call to `placeCallInTree` will exit
// early thinking this node is already placed.
var detatchedChildNode = call_to_node[detatchedChildCallKey];
detatchedChildNode.callKey = 0x0;
// Now place it at it's new location in the tree.
placeCallInTree(detatchedChildCallKey);
}
}

function _shouldRotateLeft() internal returns (bool) {
/*
* Is the right child of the rootNode in the future of the
* current block number.
*/
// Empty call tree.
if (rootNodeCallKey == 0x0) {
return false;
}

var currentRoot = call_to_node[rootNodeCallKey];

// No right child so cant rotate left.
if (currentRoot.right == 0x0) {
return false;
}

// Current root already in the future.
if (key_to_calls[rootNodeCallKey].targetBlock >= block.number) {
return false;
}

return true;
}

function rotateTree() public {
/*
* Shifts the root node of the tree so that the root node is
* the tree node prior to the next scheduled call.
*/
if (rootNodeCallKey == 0x0) {
// No root node (empty tree)
return;
}

var currentRoot = call_to_node[rootNodeCallKey];
var rootBlockNumber = key_to_calls[rootNodeCallKey].targetBlock;

// The current root is in the past so we can potentially rotate
// the tree to the left to increase the root block number.
if (rootBlockNumber < block.number) {
while (_shouldRotateLeft()) {
_rotateLeft();
}
return;
}

// The current root is in the future so we can potentially
// rotate the tree to the right to decrease the root block
// number.
if (rootBlockNumber > block.number) {
while (_shouldRotateRight()) {
_rotateRight();
}
}
}

/*
* Call Information API
*/
Expand Down
2 changes: 1 addition & 1 deletion tests/call-ordering/test_get_next_call_sibling.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
geth_max_wait = 45


def test_node_tree_positions(geth_node, rpc_client, deployed_contracts):
def test_get_call_next_sibling(geth_node, rpc_client, deployed_contracts):
"""
8
/ \
Expand Down
122 changes: 122 additions & 0 deletions tests/call-ordering/test_rotating_tree_left.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from populus.utils import (
wait_for_transaction,
wait_for_block,
)


deploy_max_wait = 15
deploy_max_first_block_wait = 180
deploy_wait_for_block = 1

geth_max_wait = 45


def get_tree_state(alarm, calls_to_blocks):
state = set()
for call_key, block_number in calls_to_blocks.items():
if call_key is None:
continue
left = calls_to_blocks[alarm.getCallLeftChild.call(call_key)]
right = calls_to_blocks[alarm.getCallRightChild.call(call_key)]
state.add((block_number, (left, right)))
return state


def test_rotating_tree_left(geth_node, rpc_client, deployed_contracts):
"""
Before left rotation
====================
8
/ \
/ \
/ \
7 1900
/ / \
4 9 2000
/ \ / \
1 4 8 15
/
13
/ \
9 14
After left rotation
===================
1900
/ \
8 2000
/ \
/ \
7 9
/ / \
4 8 15
/ \ /
1 4 13
/ \
9 14
"""
alarm = deployed_contracts.Alarm
client_contract = deployed_contracts.SpecifyBlock

anchor_block = rpc_client.get_block_number()

blocks = (8, 7, 1900, 2000, 4, 1, 4, 9, 15, 8, 13, 14, 9)

call_keys = []

for n in blocks:
wait_for_transaction(rpc_client, client_contract.scheduleIt.sendTransaction(alarm._meta.address, anchor_block + 65 + n))

last_call_key = alarm.getLastCallKey.call()
assert last_call_key is not None

call_keys.append(last_call_key)

assert len(set(call_keys)) == len(blocks)

calls_to_blocks = dict(zip(call_keys, blocks))
calls_to_blocks[None] = None

initial_state = {
(1, (None, None)),
(4, (1, 4)),
(4, (None, None)),
(8, (7, 1900)),
(7, (4, None)),
(1900, (9, 2000)),
(2000, (None, None)),
(9, (8, 15)),
(8, (None, None)),
(15, (13, None)),
(13, (9, 14)),
(9, (None, None)),
(14, (None, None)),
}

assert get_tree_state(alarm, calls_to_blocks) == initial_state

wait_for_block(rpc_client, anchor_block + 65 + 16, max_wait=120)

wait_for_transaction(rpc_client, alarm.rotateTree.sendTransaction())

expected_state = {
(1, (None, None)),
(4, (1, 4)),
(4, (None, None)),
(8, (7, 9)),
(7, (4, None)),
(1900, (8, 2000)),
(2000, (None, None)),
(9, (8, 15)),
(8, (None, None)),
(15, (13, None)),
(13, (9, 14)),
(9, (None, None)),
(14, (None, None)),
}

assert get_tree_state(alarm, calls_to_blocks) == expected_state

0 comments on commit 869fd61

Please sign in to comment.