research/casper4/simple_casper.v.py

448 lines
19 KiB
Python
Raw Normal View History

2017-02-21 12:27:32 +00:00
# Information about validators
validators: public({
2017-02-21 12:27:32 +00:00
# Amount of wei the validator holds
deposit: wei_value,
2017-03-07 02:51:13 +00:00
# The dynasty the validator is joining
dynasty_start: num,
# The dynasty the validator is leaving
dynasty_end: num,
2017-02-21 12:27:32 +00:00
# The timestamp at which the validator can withdraw
withdrawal_time: timestamp,
# The address which the validator's signatures must verify to (to be later replaced with validation code)
addr: address,
# Addess to withdraw to
withdrawal_addr: address,
# The max epoch at which the validator prepared
max_prepared: num,
# The max epoch at which the validator committed
max_committed: num
}[num])
2017-02-21 12:27:32 +00:00
2017-03-07 02:51:13 +00:00
# The current dynasty (validator set changes between dynasties)
dynasty: public(num)
2017-03-07 02:51:13 +00:00
# Amount of wei added to the total deposits in the next dynasty
next_dynasty_wei_delta: wei_value
# Amount of wei added to the total deposits in the dynasty after that
second_next_dynasty_wei_delta: wei_value
# Total deposits during this dynasty
total_deposits: public(wei_value[num])
2017-03-07 02:51:13 +00:00
2017-02-21 12:27:32 +00:00
# Information for use in processing cryptoeconomic commitments
consensus_messages: public({
2017-03-07 02:51:13 +00:00
# How many prepares are there for this hash (hash of message hash + view source) from the current dynasty
2017-02-21 12:27:32 +00:00
prepares: wei_value[bytes32],
2017-03-07 02:51:13 +00:00
# From the previous dynasty
prev_dyn_prepares: wei_value[bytes32],
2017-03-22 20:58:53 +00:00
# Is a prepare referencing the given ancestry hash justified?
ancestry_hash_justified: bool[bytes32],
2017-02-21 12:27:32 +00:00
# Is a commit on the given hash justified?
2017-03-22 20:58:53 +00:00
hash_justified: bool[bytes32],
2017-03-07 02:51:13 +00:00
# How many commits are there for this hash
commits: wei_value[bytes32],
# And from the previous dynasty
prev_dyn_commits: wei_value[bytes32],
# Was the block committed?
committed: bool
}[num]) # index: epoch
2017-02-21 12:27:32 +00:00
# ancestry[x][y] = k > 0: x is a kth generation ancestor of y
ancestry: num[bytes32][bytes32]
# Number of validators
nextValidatorIndex: public(num)
2017-02-21 12:27:32 +00:00
# Constant that guides the size of validator rewards
interest_rate: decimal(1 / sec)
# Time between blocks
block_time: timedelta
# Length of an epoch in blocks
epoch_length: num
# Withdrawal delay
withdrawal_delay: timedelta
# Delay after which a message can be slashed due to absence of justification
insufficiency_slash_delay: timedelta
# Current epoch
current_epoch: public(num)
2017-02-21 12:27:32 +00:00
2017-03-07 02:51:13 +00:00
# Can withdraw destroyed deposits
owner: address
# Total deposits destroyed
total_destroyed: wei_value
# Sighash calculator library address
sighasher: address
2017-03-10 14:47:04 +00:00
2017-03-07 02:51:13 +00:00
def __init__():
2017-03-07 02:55:09 +00:00
# Set Casper parameters
2017-03-22 20:58:53 +00:00
self.interest_rate = 0.000000001
self.block_time = 14
2017-03-07 02:51:13 +00:00
self.epoch_length = 256
2017-03-11 14:43:39 +00:00
# Only ~11.5 days, for testing purposes
self.withdrawal_delay = 1000000
2017-03-22 20:58:53 +00:00
# Only ~1 day, for testing purposes
self.insufficiency_slash_delay = 86400
2017-03-07 02:55:09 +00:00
# Temporary backdoor for testing purposes (to allow recovering destroyed deposits)
2017-03-07 02:51:13 +00:00
self.owner = 0x1db3439a222c519ab44bb1144fc28167b4fa6ee6
2017-03-07 02:55:09 +00:00
# Add an initial validator
2017-03-07 02:51:13 +00:00
self.validators[0] = {
deposit: as_wei_value(3, finney),
2017-03-07 02:51:13 +00:00
dynasty_start: 0,
dynasty_end: 1000000000000000000000000000000,
withdrawal_time: 1000000000000000000000000000000,
addr: 0x1db3439a222c519ab44bb1144fc28167b4fa6ee6,
withdrawal_addr: 0x1db3439a222c519ab44bb1144fc28167b4fa6ee6,
max_prepared: 0,
max_committed: 0
}
self.nextValidatorIndex = 1
2017-03-07 02:55:09 +00:00
# Initialize the epoch counter
2017-03-07 02:51:13 +00:00
self.current_epoch = block.number / self.epoch_length
# Set the sighash calculator address
self.sighasher = 0x38920146f10f3956fc09970beededcb2d9638712
2017-03-22 20:58:53 +00:00
# Set an initial root of the epoch hash chain
self.consensus_messages[0].ancestry_hash_justified[0x0000000000000000000000000000000000000000000000000000000000000000] = True
2017-03-07 02:51:13 +00:00
# Called at the start of any epoch
2017-02-21 12:27:32 +00:00
def initialize_epoch(epoch: num):
computed_current_epoch = block.number / self.epoch_length
assert epoch <= computed_current_epoch and epoch == self.current_epoch + 1
self.current_epoch = epoch
if self.consensus_messages[epoch - 1].committed:
self.dynasty += 1
self.total_deposits[self.dynasty] = self.total_deposits[self.dynasty - 1] + self.next_dynasty_wei_delta
self.next_dynasty_wei_delta = self.second_next_dynasty_wei_delta
self.second_next_dynasty_wei_delta = 0
2017-02-21 12:27:32 +00:00
2017-03-07 02:51:13 +00:00
# Send a deposit to join the validator set
2017-02-21 12:27:32 +00:00
def deposit(validation_addr: address, withdrawal_addr: address):
assert self.current_epoch == block.number / self.epoch_length
self.validators[self.nextValidatorIndex] = {
deposit: msg.value,
2017-03-07 02:51:13 +00:00
dynasty_start: self.dynasty + 2,
dynasty_end: 1000000000000000000000000000000,
2017-02-21 12:27:32 +00:00
withdrawal_time: 1000000000000000000000000000000,
addr: validation_addr,
withdrawal_addr: withdrawal_addr,
max_prepared: 0,
max_committed: 0,
}
self.nextValidatorIndex += 1
2017-03-07 02:51:13 +00:00
self.second_next_dynasty_wei_delta += msg.value
2017-02-21 12:27:32 +00:00
2017-03-07 02:51:13 +00:00
# Exit the validator set, and start the withdrawal procedure
2017-02-21 12:27:32 +00:00
def start_withdrawal(index: num, sig: bytes <= 96):
2017-03-07 02:51:13 +00:00
assert self.current_epoch == block.number / self.epoch_length
2017-02-21 12:27:32 +00:00
# Signature check
assert len(sig) == 96
assert ecrecover(sha3("withdraw"),
as_num256(extract32(sig, 0)),
as_num256(extract32(sig, 32)),
as_num256(extract32(sig, 64))) == self.validators[index].addr
# Check that we haven't already withdrawn
2017-03-07 02:51:13 +00:00
assert self.validators[index].dynasty_end >= self.dynasty + 2
# Set the end dynasty
self.validators[index].dynasty_end = self.dynasty + 2
self.second_next_dynasty_wei_delta -= msg.value
2017-02-21 12:27:32 +00:00
# Set the withdrawal date
#self.validators[index].withdrawal_time = block.timestamp + self.withdrawal_delay
2017-02-21 12:27:32 +00:00
2017-03-07 02:51:13 +00:00
# Withdraw deposited ether
2017-02-21 12:27:32 +00:00
def withdraw(index: num):
2017-03-07 02:51:13 +00:00
# Check that we can withdraw
#assert block.timestamp >= self.validators[index].withdrawal_time
2017-03-07 02:51:13 +00:00
# Withdraw
send(self.validators[index].withdrawal_addr, self.validators[index].deposit)
self.validators[index] = {
deposit: 0,
dynasty_start: 0,
dynasty_end: 0,
withdrawal_time: 0,
addr: None,
withdrawal_addr: None,
max_prepared: 0,
max_committed: 0,
}
2017-02-21 12:27:32 +00:00
2017-03-07 02:51:13 +00:00
# Process a prepare message
def prepare(validator_index: num, prepare_msg: bytes <= 1024):
2017-03-11 14:43:39 +00:00
# Get hash for signature, and implicitly assert that it is an RLP list
# consisting solely of RLP elements
sighash = extract32(raw_call(self.sighasher, prepare_msg, gas=200000, outsize=32), 0)
2017-03-11 14:43:39 +00:00
# Extract parameters
values = RLPList(prepare_msg, [num, bytes32, bytes32, num, bytes32, bytes])
epoch = values[0]
hash = values[1]
ancestry_hash = values[2]
source_epoch = values[3]
source_ancestry_hash = values[4]
sig = values[5]
2017-03-11 14:43:39 +00:00
# For now, the sig is a simple ECDSA sig
2017-03-22 20:58:53 +00:00
# Check the signature
2017-02-21 12:27:32 +00:00
assert len(sig) == 96
assert ecrecover(sighash,
extract32(sig, 0, type=num256),
extract32(sig, 32, type=num256),
extract32(sig, 64, type=num256)) == self.validators[validator_index].addr
2017-02-21 12:27:32 +00:00
# Check that we are in the right epoch
assert self.current_epoch == block.number / self.epoch_length
assert self.current_epoch == epoch
2017-03-07 02:51:13 +00:00
# Check that this validator was active in either the previous dynasty or the current one
ds = self.validators[validator_index].dynasty_start
de = self.validators[validator_index].dynasty_end
2017-03-07 02:51:13 +00:00
dc = self.dynasty
in_current_dynasty = (ds <= dc) and (dc < de)
in_prev_dynasty = (ds <= (dc - 1)) and ((dc - 1) < de)
assert in_current_dynasty or in_prev_dynasty
2017-03-22 20:58:53 +00:00
# Check that the prepare is on top of a justified prepare
assert self.consensus_messages[source_epoch].ancestry_hash_justified[source_ancestry_hash]
2017-02-21 12:27:32 +00:00
# Check that we have not yet prepared for this epoch
2017-03-22 20:58:53 +00:00
#assert self.validators[validator_index].max_prepared == epoch - 1
2017-02-21 12:27:32 +00:00
# Pay the reward if the blockhash is correct
if True: #~blockhash(epoch * self.epoch_length) == hash:
2017-03-22 20:58:53 +00:00
reward = floor(self.validators[validator_index].deposit * self.interest_rate * self.block_time / 2)
self.validators[validator_index].deposit += reward
2017-03-07 02:51:13 +00:00
self.total_deposits[self.dynasty] += reward
2017-02-21 12:27:32 +00:00
# Can't prepare for this epoch again
self.validators[validator_index].max_prepared = epoch
2017-02-21 12:27:32 +00:00
# Record that this prepare took place
new_ancestry_hash = sha3(concat(hash, ancestry_hash))
2017-03-07 02:51:13 +00:00
if in_current_dynasty:
self.consensus_messages[epoch].prepares[sighash] += self.validators[validator_index].deposit
2017-03-07 02:51:13 +00:00
if in_prev_dynasty:
self.consensus_messages[epoch].prev_dyn_prepares[sighash] += self.validators[validator_index].deposit
2017-02-21 12:27:32 +00:00
# If enough prepares with the same epoch_source and hash are made,
# then the hash value is justified for commitment
2017-03-07 02:51:13 +00:00
if (self.consensus_messages[epoch].prepares[sighash] >= self.total_deposits[self.dynasty] * 2 / 3 and \
self.consensus_messages[epoch].prev_dyn_prepares[sighash] >= self.total_deposits[self.dynasty - 1] * 2 / 3) and \
2017-03-22 20:58:53 +00:00
not self.consensus_messages[epoch].ancestry_hash_justified[new_ancestry_hash]:
self.consensus_messages[epoch].ancestry_hash_justified[new_ancestry_hash] = True
self.consensus_messages[epoch].hash_justified[hash] = True
2017-02-21 12:27:32 +00:00
# Add a parent-child relation between ancestry hashes to the ancestry table
self.ancestry[ancestry_hash][new_ancestry_hash] = 1
2017-03-07 02:51:13 +00:00
# Process a commit message
def commit(validator_index: num, commit_msg: bytes <= 1024):
sighash = extract32(raw_call(self.sighasher, commit_msg, gas=200000, outsize=32), 0)
# Extract parameters
values = RLPList(commit_msg, [num, bytes32, bytes])
epoch = values[0]
hash = values[1]
sig = values[2]
2017-03-22 20:58:53 +00:00
# Check the signature
2017-02-21 12:27:32 +00:00
assert len(sig) == 96
assert ecrecover(sighash,
extract32(sig, 0, type=num256),
extract32(sig, 32, type=num256),
extract32(sig, 64, type=num256)) == self.validators[validator_index].addr
2017-02-21 12:27:32 +00:00
# Check that we are in the right epoch
assert self.current_epoch == block.number / self.epoch_length
assert self.current_epoch == epoch
# Check that the commit is justified
2017-03-22 20:58:53 +00:00
assert self.consensus_messages[epoch].hash_justified[hash]
2017-03-07 02:51:13 +00:00
# Check that this validator was active in either the previous dynasty or the current one
ds = self.validators[validator_index].dynasty_start
de = self.validators[validator_index].dynasty_end
2017-03-07 02:51:13 +00:00
dc = self.dynasty
in_current_dynasty = (ds <= dc) and (dc < de)
in_prev_dynasty = (ds <= (dc - 1)) and ((dc - 1) < de)
assert in_current_dynasty or in_prev_dynasty
2017-02-21 12:27:32 +00:00
# Check that we have not yet committed for this epoch
assert self.validators[validator_index].max_committed == epoch - 1
2017-02-21 12:27:32 +00:00
# Pay the reward if the blockhash is correct
if True: #~blockhash(epoch * self.epoch_length) == hash:
2017-03-22 20:58:53 +00:00
reward = floor(self.validators[validator_index].deposit * self.interest_rate * self.block_time / 2)
self.validators[validator_index].deposit += reward
2017-03-07 02:51:13 +00:00
self.total_deposits[self.dynasty] += reward
2017-02-21 12:27:32 +00:00
# Can't commit for this epoch again
self.validators[validator_index].max_committed = epoch
2017-03-07 02:51:13 +00:00
# Record that this commit took place
if in_current_dynasty:
self.consensus_messages[epoch].commits[hash] += self.validators[validator_index].deposit
2017-03-07 02:51:13 +00:00
if in_prev_dynasty:
self.consensus_messages[epoch].prev_dyn_commits[hash] += self.validators[validator_index].deposit
2017-03-07 02:51:13 +00:00
# Record if sufficient commits have been made for the block to be finalized
if (self.consensus_messages[epoch].commits[hash] >= self.total_deposits[self.dynasty] * 2 / 3 and \
self.consensus_messages[epoch].prev_dyn_commits[hash] >= self.total_deposits[self.dynasty - 1] * 2 / 3) and \
2017-03-07 02:51:13 +00:00
not self.consensus_messages[epoch].committed:
self.consensus_messages[epoch].committed = True
2017-02-21 12:27:32 +00:00
2017-03-22 20:58:53 +00:00
# Cannot make two prepares in the same epoch
def double_prepare_slash(validator_index: num, prepare1: bytes <= 1000, prepare2: bytes <= 1000):
# Get hash for signature, and implicitly assert that it is an RLP list
# consisting solely of RLP elements
sighash1 = extract32(raw_call(self.sighasher, prepare1, gas=200000, outsize=32), 0)
sighash2 = extract32(raw_call(self.sighasher, prepare2, gas=200000, outsize=32), 0)
# Extract parameters
values1 = RLPList(prepare1, [num, bytes32, bytes32, num, bytes32, bytes])
values2 = RLPList(prepare2, [num, bytes32, bytes32, num, bytes32, bytes])
epoch1 = values1[0]
sig1 = values1[5]
epoch2 = values2[0]
sig2 = values2[5]
# Check the signatures
2017-02-21 12:27:32 +00:00
assert ecrecover(sighash1,
as_num256(extract32(sig1, 0)),
as_num256(extract32(sig1, 32)),
2017-03-22 20:58:53 +00:00
as_num256(extract32(sig1, 64))) == self.validators[validator_index].addr
2017-02-21 12:27:32 +00:00
assert ecrecover(sighash2,
as_num256(extract32(sig2, 0)),
as_num256(extract32(sig2, 32)),
2017-03-22 20:58:53 +00:00
as_num256(extract32(sig2, 64))) == self.validators[validator_index].addr
# Check that they're from the same epoch
assert epoch1 == epoch2
2017-02-21 12:27:32 +00:00
# Check that they're not the same message
assert sighash1 != sighash2
# Delete the offending validator, and give a 4% "finder's fee"
2017-03-22 20:58:53 +00:00
validator_deposit = self.validators[validator_index].deposit
2017-02-21 12:27:32 +00:00
send(msg.sender, validator_deposit / 25)
2017-03-07 02:51:13 +00:00
self.total_destroyed += validator_deposit * 24 / 25
self.total_deposits[self.dynasty] -= (validator_deposit - validator_deposit / 25)
2017-03-22 20:58:53 +00:00
self.validators[validator_index] = {
2017-02-21 12:27:32 +00:00
deposit: 0,
2017-03-07 02:51:13 +00:00
dynasty_start: 0,
dynasty_end: 0,
2017-02-21 12:27:32 +00:00
withdrawal_time: 0,
addr: None,
withdrawal_addr: None,
max_prepared: 0,
max_committed: 0,
}
2017-03-22 20:58:53 +00:00
def prepare_commit_inconsistency_slash(validator_index: num, prepare_msg: bytes <= 1024, commit_msg: bytes <= 1024):
# Get hash for signature, and implicitly assert that it is an RLP list
# consisting solely of RLP elements
sighash1 = extract32(raw_call(self.sighasher, prepare_msg, gas=200000, outsize=32), 0)
sighash2 = extract32(raw_call(self.sighasher, commit_msg, gas=200000, outsize=32), 0)
# Extract parameters
values1 = RLPList(prepare_msg, [num, bytes32, bytes32, num, bytes32, bytes])
values2 = RLPList(commit_msg, [num, bytes32, bytes])
prepare_epoch = values1[0]
prepare_source_epoch = values1[3]
sig1 = values1[5]
commit_epoch = values2[0]
sig2 = values2[2]
# Check the signatures
2017-02-21 12:27:32 +00:00
assert ecrecover(sighash1,
as_num256(extract32(sig1, 0)),
as_num256(extract32(sig1, 32)),
2017-03-22 20:58:53 +00:00
as_num256(extract32(sig1, 64))) == self.validators[validator_index].addr
2017-02-21 12:27:32 +00:00
assert ecrecover(sighash2,
as_num256(extract32(sig2, 0)),
as_num256(extract32(sig2, 32)),
2017-03-22 20:58:53 +00:00
as_num256(extract32(sig2, 64))) == self.validators[validator_index].addr
2017-02-21 12:27:32 +00:00
# Check that they're not the same message
assert sighash1 != sighash2
# Check that the prepare refers to something older than the commit
assert prepare_source_epoch < commit_epoch
# Check that the prepare is newer than the commit
assert commit_epoch < prepare_epoch
# Delete the offending validator, and give a 4% "finder's fee"
2017-03-22 20:58:53 +00:00
validator_deposit = self.validators[validator_index].deposit
2017-02-21 12:27:32 +00:00
send(msg.sender, validator_deposit / 25)
2017-03-07 02:51:13 +00:00
self.total_destroyed += validator_deposit * 24 / 25
self.total_deposits[self.dynasty] -= validator_deposit
2017-03-22 20:58:53 +00:00
self.validators[validator_index] = {
2017-02-21 12:27:32 +00:00
deposit: 0,
2017-03-07 02:51:13 +00:00
dynasty_start: 0,
dynasty_end: 0,
2017-02-21 12:27:32 +00:00
withdrawal_time: 0,
addr: None,
withdrawal_addr: None,
max_prepared: 0,
max_committed: 0,
}
2017-03-22 20:58:53 +00:00
def commit_non_justification_slash(validator_index: num, commit_msg: bytes <= 1024):
sighash = extract32(raw_call(self.sighasher, commit_msg, gas=200000, outsize=32), 0)
# Extract parameters
values = RLPList(commit_msg, [num, bytes32, bytes])
epoch = values[0]
hash = values[1]
sig = values[2]
# Check the signature
assert len(sig) == 96
2017-02-21 12:27:32 +00:00
assert ecrecover(sighash,
2017-03-22 20:58:53 +00:00
extract32(sig, 0, type=num256),
extract32(sig, 32, type=num256),
extract32(sig, 64, type=num256)) == self.validators[validator_index].addr
2017-02-21 12:27:32 +00:00
# Check that the commit is old enough
assert self.current_epoch == block.number / self.epoch_length
2017-03-22 20:58:53 +00:00
assert (self.current_epoch - epoch) * self.epoch_length * self.block_time > self.insufficiency_slash_delay
assert not self.consensus_messages[epoch].hash_justified[hash]
2017-02-21 12:27:32 +00:00
# Delete the offending validator, and give a 4% "finder's fee"
2017-03-22 20:58:53 +00:00
validator_deposit = self.validators[validator_index].deposit
2017-02-21 12:27:32 +00:00
send(msg.sender, validator_deposit / 25)
2017-03-07 02:51:13 +00:00
self.total_destroyed += validator_deposit * 24 / 25
self.total_deposits[self.dynasty] -= validator_deposit
2017-03-22 20:58:53 +00:00
self.validators[validator_index] = {
2017-02-21 12:27:32 +00:00
deposit: 0,
2017-03-07 02:51:13 +00:00
dynasty_start: 0,
dynasty_end: 0,
2017-02-21 12:27:32 +00:00
withdrawal_time: 0,
addr: None,
withdrawal_addr: None,
max_prepared: 0,
max_committed: 0,
}
# Fill in the table for which hash is what-degree ancestor of which other hash
def derive_ancestry(top: bytes32, middle: bytes32, bottom: bytes32):
assert self.ancestry[middle][top]
assert self.ancestry[bottom][middle]
self.ancestry[bottom][top] = self.ancestry[bottom][middle] + self.ancestry[middle][top]
2017-03-22 20:58:53 +00:00
def prepare_non_justification_slash(validator_index: num, prepare_msg: bytes <= 1024):
# Get hash for signature, and implicitly assert that it is an RLP list
# consisting solely of RLP elements
sighash = extract32(raw_call(self.sighasher, prepare_msg, gas=200000, outsize=32), 0)
# Extract parameters
values = RLPList(prepare_msg, [num, bytes32, bytes32, num, bytes32, bytes])
epoch = values[0]
hash = values[1]
ancestry_hash = values[2]
source_epoch = values[3]
source_ancestry_hash = values[4]
sig = values[5]
# Check the signature
2017-02-21 12:27:32 +00:00
assert ecrecover(sighash,
2017-03-22 20:58:53 +00:00
extract32(sig, 0, type=num256),
extract32(sig, 32, type=num256),
extract32(sig, 64, type=num256)) == self.validators[validator_index].addr
2017-02-21 12:27:32 +00:00
# Check that the view change is old enough
assert self.current_epoch == block.number / self.epoch_length
2017-03-22 20:58:53 +00:00
assert (self.current_epoch - epoch) * self.block_time * self.epoch_length > self.insufficiency_slash_delay
# Check that the source ancestry hash not had enough prepares
assert not self.consensus_messages[source_epoch].ancestry_hash_justified[source_ancestry_hash]
2017-02-21 12:27:32 +00:00
# Delete the offending validator, and give a 4% "finder's fee"
2017-03-22 20:58:53 +00:00
validator_deposit = self.validators[validator_index].deposit
2017-02-21 12:27:32 +00:00
send(msg.sender, validator_deposit / 25)
2017-03-07 02:51:13 +00:00
self.total_destroyed += validator_deposit * 24 / 25
self.total_deposits[self.dynasty] -= validator_deposit
2017-03-22 20:58:53 +00:00
self.validators[validator_index] = {
2017-02-21 12:27:32 +00:00
deposit: 0,
2017-03-07 02:51:13 +00:00
dynasty_start: 0,
dynasty_end: 0,
2017-02-21 12:27:32 +00:00
withdrawal_time: 0,
addr: None,
withdrawal_addr: None,
max_prepared: 0,
max_committed: 0,
}
2017-03-07 02:51:13 +00:00
2017-03-07 02:55:09 +00:00
# Temporary backdoor for testing purposes (to allow recovering destroyed deposits)
2017-03-07 02:51:13 +00:00
def owner_withdraw():
send(self.owner, self.total_destroyed)
self.total_destroyed = 0
2017-03-07 02:55:09 +00:00
# Change backdoor address (set to zero to remove entirely)
def change_owner(new_owner: address):
if self.owner == msg.sender:
self.owner = new_owner