diff options
Diffstat (limited to 'Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/mutuala.se')
-rw-r--r-- | Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/mutuala.se | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/mutuala.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/mutuala.se new file mode 100644 index 000000000..3efb0edeb --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/mutuala.se @@ -0,0 +1,187 @@ +# mutuala - subcurrency + +# We want to issue a currency that reduces in value as you store it through negative interest. +# That negative interest would be stored in a commons account. It's like the p2p version of a +# capital tax + +# the same things goes for transactions - you pay as you use the currency. However, the more +# you pay, the more you get to say about what the tax is used for + +# each participant can propose a recipient for a payout to be made out of the commons account, +# others can vote on it by awarding it tax_credits. + +# TODO should proposal have expiration timestamp?, after which the tax_credits are refunded +# TODO multiple proposals can take more credits that available in the Commons, how to handle this +# TODO how to handle lost accounts, after which no longer possible to get 2/3 majority + +shared: + COMMONS = 42 + ADMIN = 666 + CAPITAL_TAX_PER_DAY = 7305 # 5% per year + PAYMENT_TAX = 20 # 5% + + ACCOUNT_LIST_OFFSET = 2^160 + ACCOUNT_MAP_OFFSET = 2^161 + PROPOSAL_LIST_OFFSET = 2^162 + PROPOSAL_MAP_OFFSET = 2^163 + +init: + contract.storage[ADMIN] = msg.sender + contract.storage[ACCOUNT_LIST_OFFSET - 1] = 1 + contract.storage[ACCOUNT_LIST_OFFSET] = msg.sender + contract.storage[ACCOUNT_MAP_OFFSET + msg.sender] = 10^12 + contract.storage[ACCOUNT_MAP_OFFSET + msg.sender + 1] = block.timestamp + +# contract.storage[COMMONS] = balance commons + +# contract.storage[ACCOUNT_LIST_OFFSET - 1] = number of accounts +# contract.storage[ACCOUNT_LIST_OFFSET + n] = account n + +# contract.storage[PROPOSAL_LIST_OFFSET - 1] contains the number of proposals +# contract.storage[PROPOSAL_LIST_OFFSET + n] = proposal n + +# per account: +# contract.storage[ACCOUNT_MAP_OFFSET + account] = balance +# contract.storage[ACCOUNT_MAP_OFFSET + account+1] = timestamp_last_transaction +# contract.storage[ACCOUNT_MAP_OFFSET + account+2] = tax_credits + +# per proposal: +# contract.storage[PROPOSAL_MAP_OFFSET + proposal_id] = recipient +# contract.storage[PROPOSAL_MAP_OFFSET + proposal_id+1] = amount +# contract.storage[PROPOSAL_MAP_OFFSET + proposal_id+2] = total vote credits + +code: + if msg.data[0] == "suicide" and msg.sender == contract.storage[ADMIN]: + suicide(msg.sender) + + elif msg.data[0] == "balance": + addr = msg.data[1] + return(contract.storage[ACCOUNT_MAP_OFFSET + addr]) + + elif msg.data[0] == "pay": + from = msg.sender + fromvalue = contract.storage[ACCOUNT_MAP_OFFSET + from] + to = msg.data[1] + if to == 0 or to >= 2^160: + return([0, "invalid address"], 2) + value = msg.data[2] + tax = value / PAYMENT_TAX + + if fromvalue >= value + tax: + contract.storage[ACCOUNT_MAP_OFFSET + from] = fromvalue - (value + tax) + contract.storage[ACCOUNT_MAP_OFFSET + to] += value + # tax + contract.storage[COMMONS] += tax + contract.storage[ACCOUNT_MAP_OFFSET + from + 2] += tax + + # check timestamp field to see if target account exists + if contract.storage[ACCOUNT_MAP_OFFSET + to + 1] == 0: + # register new account + nr_accounts = contract.storage[ACCOUNT_LIST_OFFSET - 1] + contract.storage[ACCOUNT_LIST_OFFSET + nr_accounts] = to + contract.storage[ACCOUNT_LIST_OFFSET - 1] += 1 + contract.storage[ACCOUNT_MAP_OFFSET + to + 1] = block.timestamp + + return(1) + else: + return([0, "insufficient balance"], 2) + + elif msg.data[0] == "hash": + proposal_id = sha3(msg.data[1]) + return(proposal_id) + + elif msg.data[0] == "propose": + from = msg.sender + # check if sender has an account and has tax credits + if contract.storage[ACCOUNT_MAP_OFFSET + from + 2] == 0: + return([0, "sender has no tax credits"], 2) + + proposal_id = sha3(msg.data[1]) + # check if proposal doesn't already exist + if contract.storage[PROPOSAL_MAP_OFFSET + proposal_id]: + return([0, "proposal already exists"]) + + to = msg.data[2] + # check if recipient is a valid address and has an account (with timestamp) + if to == 0 or to >= 2^160: + return([0, "invalid address"], 2) + if contract.storage[ACCOUNT_MAP_OFFSET + to + 1] == 0: + return([0, "invalid to account"], 2) + + value = msg.data[3] + # check if there is enough money in the commons account + if value > contract.storage[COMMONS]: + return([0, "not enough credits in commons"], 2) + + # record proposal in list + nr_proposals = contract.storage[PROPOSAL_LIST_OFFSET - 1] + contract.storage[PROPOSAL_LIST_OFFSET + nr_proposals] = proposal_id + contract.storage[PROPOSAL_LIST_OFFSET - 1] += 1 + + # record proposal in map + contract.storage[PROPOSAL_MAP_OFFSET + proposal_id] = to + contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 1] = value + + return(proposal_id) + + elif msg.data[0] == "vote": + from = msg.sender + proposal_id = sha3(msg.data[1]) + value = msg.data[2] + # check if sender has an account and has tax credits + if value < contract.storage[ACCOUNT_MAP_OFFSET + from + 2]: + return([0, "sender doesn't have enough tax credits"], 2) + + # check if proposal exist + if contract.storage[PROPOSAL_MAP_OFFSET + proposal_id] == 0: + return([0, "proposal doesn't exist"], 2) + + # increase votes + contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 2] += value + # withdraw tax credits + contract.storage[ACCOUNT_MAP_OFFSET + from + 2] -= value + + # did we reach 2/3 threshold? + if contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 2] >= contract.storage[COMMONS] * 2 / 3: + # got majority + to = contract.storage[PROPOSAL_MAP_OFFSET + proposal_id] + amount = contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 1] + + # adjust balances + contract.storage[ACCOUNT_MAP_OFFSET + to] += amount + contract.storage[COMMONS] -= amount + + # reset proposal + contract.storage[PROPOSAL_MAP_OFFSET + proposal_id] = 0 + contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 1] = 0 + contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 2] = 0 + return(1) + + return(proposal_id) + + elif msg.data[0] == "tick": + nr_accounts = contract.storage[ACCOUNT_LIST_OFFSET - 1] + account_idx = 0 + tax_paid = 0 + # process all accounts and see if they have to pay their daily capital tax + while account_idx < nr_accounts: + cur_account = contract.storage[ACCOUNT_LIST_OFFSET + account_idx] + last_timestamp = contract.storage[ACCOUNT_MAP_OFFSET + cur_account + 1] + time_diff = block.timestamp - last_timestamp + if time_diff >= 86400: + tax_days = time_diff / 86400 + balance = contract.storage[ACCOUNT_MAP_OFFSET + cur_account] + tax = tax_days * (balance / CAPITAL_TAX_PER_DAY) + if tax > 0: + # charge capital tax, but give tax credits in return + contract.storage[ACCOUNT_MAP_OFFSET + cur_account] -= tax + contract.storage[ACCOUNT_MAP_OFFSET + cur_account + 1] += tax_days * 86400 + contract.storage[ACCOUNT_MAP_OFFSET + cur_account + 2] += tax + + contract.storage[COMMONS] += tax + tax_paid += 1 + account_idx += 1 + return(tax_paid) # how many accounts did we charge tax on + + else: + return([0, "unknown command"], 2) |