A liveness canary for the Ethereum Alarm Clock, deployed 31 January 2016.
Historical Significance
Nothing on Ethereum runs unless an account sends a transaction, so a contract cannot wake itself at a future block. A canary answers the question that follows from paying strangers to do that work: is anyone still doing it. Because each heartbeat pays for the next one, the contract keeps itself alive only while the service around it functions, and the block number of its last beat is a public record of when that stopped. It is monitoring built out of the thing being monitored, with no server and nobody to ask.
Context
Deployed while the Alarm Clock was the main attempt at scheduled transactions on Ethereum, which the protocol itself does not provide.
Key Facts
Description
A canary for the Ethereum Alarm Clock. It exists to prove the scheduling service is still executing calls: heartbeat records the block it was called in, increments a counter and immediately schedules the next heartbeat through the scheduler contract, so the chain of calls continues on its own for as long as the service works. isAlive reports whether the canary is still going by asking the pending call for its target block and checking it against the current block number, which means a stalled scheduler shows up as a dead canary rather than as silence. The owner can cancel it. Deployed by Piper Merriam from 0xd3cda913deb6f67967b99d67acdfa1712c293601.
Source Verified
Historian Categories
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
Frontier Era
The initial release of Ethereum. A bare-bones implementation for technical users.
Bytecode Overview
Verified Source Available
Source verified through compiler archaeology and exact bytecode matching.
View Verification ProofShow source code (Solidity)
// Submitted by EthereumHistory (ethereumhistory.com)
contract SchedulerInterface {
function scheduleCall(bytes4 abiSignature,
uint targetBlock,
uint requiredGas) public returns (address);
function isKnownCall(address callAddress) constant returns (bool);
}
contract CallContractAPI {
function targetBlock() constant returns (uint);
function cancel() public;
}
contract Canary {
uint public aliveSince;
uint public lastHeartbeat;
uint public heartbeatCount;
SchedulerInterface scheduler;
CallContractAPI callContract;
address public owner;
function schedulerAddress() constant returns (address) {
return address(scheduler);
}
function callContractAddress() constant returns (address) {
return address(callContract);
}
uint16 public frequency;
function Canary(address _scheduler, uint16 _frequency) {
owner = msg.sender;
scheduler = SchedulerInterface(_scheduler);
frequency = _frequency;
}
function() {
if (this.balance < 2 ether) {
owner.send(this.balance);
}
}
function cancel() public {
// not authorized
if (msg.sender != owner) throw;
// need to wait until the
if (callContract.balance > 0) {
callContract.cancel();
}
owner.send(address(this).balance);
}
function initialize() public {
// ensure we are not already initialized.
if (aliveSince != 0) return;
// mark when the canary came to life.
aliveSince = now;
// schedule the first call
scheduleHeartbeat();
}
function scheduleHeartbeat() public {
// schedule the call (~2 hours from now)
address call_address = scheduler.scheduleCall.value(2 ether)(
0x3defb962,
block.number + frequency,
2000000);
if (call_address != 0x0) {
callContract = CallContractAPI(call_address);
}
}
function heartbeat() public {
// Ran out of funds.
if (this.balance < 2 ether) return;
// Not being called by the callContract.
if (msg.sender != address(callContract)) return;
// The canary has died!
if (!isAlive()) return;
// schedule the next call.
scheduleHeartbeat();
// Increment the heartbeat count
heartbeatCount += 1;
lastHeartbeat = now;
}
function isAlive() constant returns (bool) {
return (aliveSince > 0 && block.number < callContract.targetBlock() + 255);
}
}
contract CanaryV7 is Canary {
function CanaryV7() Canary(0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b, 480) {
}
}
contract CanaryV7Testnet is Canary {
function CanaryV7Testnet() Canary(0x26416b12610d26fd31d227456e9009270574038f, 480) {
}
}
contract CanaryV7FastTestnet is Canary {
function CanaryV7FastTestnet() Canary(0x26416b12610d26fd31d227456e9009270574038f, 100) {
}
}External Links
Related contracts
MemberRegistry
Same deployerOwner-controlled dynamic-array member registry.
0x1f3b19...7c161aAugust 13, 2015Members
Same deployerOwner-controlled member registry with array + mapping tracking.
0xaeb2ac...a5fd37August 14, 2015MembershipRoster
Same deployerAn Ethereum Public Trust roster contract for adding, removing, and checking members.
0xf1d327...aa0455August 14, 2015KillMul
Same deployerTest/utility contract with `suicide(address)` kill switch and a `multiply2` helper.
0x3e5e1f...7c78cdAugust 14, 2015Members
Same deployerOwner-controlled member registry using sequential id + mapping storage.
0xc6810e...db4e6fAugust 14, 2015CallerPool
Same deployerThe bonded caller pool of the Ethereum Alarm Clock: callers post a bond, take turns being the designated executor, and forfeit it if they miss.
0x329219...fc3e8bSeptember 22, 2015