http://blog.youkuaiyun.com/fidelhl/article/details/50481859#comments以太坊的整体介绍以及写第一个智能合约
truffle开发环境官网简介http://truffle.readthedocs.io/en/latest/
今天一直在看serpent语言,看起来很简单,可是要自己写却不知道怎么下手,那个浏览器的IDE很好用但是应该不支持serpent吧,solidity会比较好
from serpent_setup import * | |
def test_array_delete(): | |
code = '''\ | |
def delete(data:arr, index): | |
mcopy(data+(items=index), data+(items=(index+1)), items=(len(data) - index - 1)) | |
shrink(data, len(data) - 1) | |
return(data:arr) | |
''' | |
s = t.state() #t is in serpent_setup.py | |
c = s.abi_contract(code) | |
data = range(20) | |
indeces = [0, 1, 14, 18, 19] | |
PRINT('serpent code:') | |
PRINT(code) | |
PRINT() | |
PRINT('lll:') | |
PRINT(serpent.compile_to_lll(code)) | |
for index in indeces: | |
PRINT('Test index %d' % index) | |
PRINT('before delete:', data) #PRINT is defined in serpent_setup.py | |
result = c.delete(data, index) | |
PRINT('after delete: ', result) | |
copy = data[:] | |
copy.pop(index) | |
PRINT('expected: ', copy) | |
if copy == result: | |
PRINT(PASSED) #PASSED and FAILED are defined in serpent_setup.py | |
else: | |
PRINT(FAILED) | |
if __name__ == "__main__": | |
test_array_delete() |
my save_load.py
from serpent_setup import * | |
code = '''\ | |
data junk[](length, data[]) | |
def saveData(key, data:arr): | |
with location = ref(self.junk[key].data[0]): | |
with end = len(data): | |
with i = 0: | |
while(i < end): | |
sstore(location + i, data[i]) | |
i += 1 | |
self.junk[key].length = end | |
return(key) | |
def loadData(key): | |
with location = ref(self.junk[key].data[0]): | |
with count = self.junk[key].length: | |
with result = array(count): | |
with i = 0: | |
while i < count: | |
result[i] = sload(location + i) | |
i += 1 | |
return(result:arr) | |
def appendData(key, item): | |
with temp = alloc(128): | |
temp[0] = 0 #first data structure in the contract | |
temp[1] = key | |
temp[2] = 1 #second member of the structure | |
temp[3] = 0 | |
with location = sha3(temp, items=4): | |
temp[2] = 0 #first member of the structure | |
with len = sha3(temp, items=3): | |
with end = sload(len): | |
sstore(location + end, item) | |
sstore(len, end + 1) | |
def doubleData(key): | |
with location = ref(self.junk[key].data[0]): | |
with end = self.junk[key].length: | |
with i = 0: | |
while i < end: | |
sstore(location + i, sload(location + i) * 2) | |
i += 1 | |
''' | |
PRINT(serpent.compile_to_lll(code)) | |
s = t.state() | |
c = s.abi_contract(code) | |
data = range(10) | |
try: | |
PRINT(c.saveData(1, data, profiling=True)) | |
PRINT(c.loadData(1, profiling=True)) | |
PRINT(c.appendData(1, 10, profiling=True)) | |
PRINT(c.loadData(1, profiling=True)) | |
PRINT(c.doubleData(1, profiling=True)) | |
PRINT(c.loadData(1, profiling=True)) | |
except: | |
PRINT(REDIRECT.getvalue()) |
save_load_tests.py
from serpent_setup import * | |
code1 = '''\ | |
data junk[](length, data[]) | |
def saveData(key, data:arr): | |
self.junk[key].length = len(data) | |
with i = 0: | |
while i < len(data): | |
self.junk[key].data[i] = data[i] | |
i += 1 | |
def loadData(key): | |
with i = 0: | |
with vals = array(self.junk[key].length): | |
while i < len(vals): | |
vals[i] = self.junk[key].data[i] | |
i += 1 | |
return(vals:arr) | |
def appendData(key, item): | |
self.junk[key].data[self.junk[key].length] = item | |
self.junk[key].length += 1 | |
def doubleData(key): | |
with i = 0: | |
while i < self.junk[key].length: | |
self.junk[key].data[i] *= 2 | |
i += 1 | |
''' | |
code2 = '''\ | |
data junk[](length, data[]) | |
def saveData(key, data:arr): | |
with location = ref(self.junk[key].data[0]): | |
with end = len(data): | |
with i = 0: | |
while(i < end): | |
sstore(location + 1, data[i]) | |
i += 1 | |
self.junk[key].length = end | |
return(key) | |
def loadData(key): | |
with location = ref(self.junk[key].data[0]): | |
with count = self.junk[key].length: | |
with result = array(count): | |
with i = 0: | |
while i < count: | |
result[i] = sload(location + i) | |
i += 1 | |
return(result:arr) | |
def appendData(key, item): | |
with location = ref(self.junk[key].data[0]): | |
with end = self.junk[key].length: | |
sstore(location + end, item) | |
self.junk[key].length = end + 1 | |
def doubleData(key): | |
with location = ref(self.junk[key].data[0]): | |
with end = self.junk[key].length: | |
with i = 0: | |
while i < end: | |
sstore(location + i, sload(location + i) * 2) | |
i += 1 | |
''' | |
code3 = '''\ | |
data junk[](length, data[]) | |
def saveData(key, data:arr): | |
self.junk[key].length = len(data) | |
save(self.junk[key].data[0], data, items=len(data)) | |
def loadData(key): | |
return(load(self.junk[key].data[0], items=self.junk[key].length):arr) | |
def appendData(key, item): | |
with data = load(self.junk[key].data[0], items=self.junk[key].length): | |
with new_data = array(len(data) + 1): | |
mcopy(new_data, data, items=len(data)) | |
new_data[len(data)] = item | |
save(self.junk[key].data[0], new_data, items=len(new_data)) | |
self.junk[key].length += 1 | |
def doubleData(key): | |
with i = 0: | |
with vals = load(self.junk[key].data[0], items=self.junk[key].length): | |
while i < self.junk[key].length: | |
vals[i] *= 2 | |
i += 1 | |
save(self.junk[key].data[0], vals, items=len(vals)) | |
''' | |
def code_test(code, name): | |
s = t.state() | |
c = s.abi_contract(code) | |
key = 0xCAFEBABE | |
data = range(10) | |
table = SimpleTable(name + ' - data: ' + str(data), | |
['', 'gas used', 'output', 'runtime']) | |
r1 = c.saveData(key, data, profiling=True) | |
table.add_row(['saveData', r1['gas'], r1['output'], r1['time']]) | |
r2 = c.loadData(key, profiling=True) | |
table.add_row(['loadData', r2['gas'], r2['output'], r2['time']]) | |
r3 = c.appendData(key, 10, profiling=True) | |
table.add_row(['appendData', r3['gas'], r3['output'], r3['time']]) | |
r4 = c.doubleData(key, profiling=True) | |
table.add_row(['doubleData', r4['gas'], r4['output'], r4['time']]) | |
return table | |
def test_save_load(): | |
s = t.state() | |
for i, code in enumerate((code1, code2, code3)): | |
name = 'code%d'%(i+1) | |
table = code_test(code, name) | |
table.print_table() | |
PRINT() | |
if __name__ == '__main__': | |
test_save_load() |
serpent_setup.py
A module for serpent tests without nonsense. | |
Motivation: | |
I write a lot of python scripts for testing serpent code, | |
and I've noticed a few things which bug the heck out of me. | |
One is that the pyethereum EVM prints out nonsense when it | |
runs a contract. I'd have to delve into it's source to figure | |
out how to disable this "feature" and I'm too lazy for that. | |
Another thing that bugs me is that the serpent module prints | |
out warning nonsense that doesn't seem to be disablable. | |
Which leads me to my hacky solution... | |
Method/Madness: | |
The first part is to completely disable warning output. If | |
something is going to break, I'd rather it just break and not | |
complain about it so much and clutter my test output. So let's | |
completely disable warnings! But that doesn't solve the whole | |
problem, because some warnings are hard coded into the C++ code | |
in the serpent module. The there is the EVM nonsense. To fix that | |
I just redirect stdout to /dev/null, and define my own PRINT | |
function that forces data to show in the terminal. And while I'm | |
at it, I might as well throw the usual imports in since every | |
test script I write will import this module. And maybe I should | |
define some useful contants and functions... | |
''' | |
# disable warning crud about eggs and whatnot | |
from warnings import simplefilter; simplefilter('ignore') | |
# tester module! | |
from ethereum import tester | |
import serpent | |
from cStringIO import StringIO | |
import sys, os | |
t = tester | |
STDOUT = sys.stdout | |
REDIRECT = StringIO() | |
PASSED = 'TEST PASSED' | |
FAILED = 'TEST FAILED' | |
sys.stdout = REDIRECT #avoids nonsense when calling contract functions | |
TAB = ' '*3 | |
#all caps cuz why not? :/ | |
def PRINT(*args, **kwds): | |
'''Use this function to print, as stdout is redirected to devnull. | |
All args have str called on them before they are "printed" to STDOUT. | |
Keyword arguments: | |
end -- A string to append to the end of the printed args. | |
This defaults to os.linesep. | |
seq -- A string to join the arguments by. Default is ' '. | |
file -- A file-like object to use instead of STDOUT. | |
Must have both write and flush methods. | |
''' | |
end = kwds.get('end', os.linesep) | |
sep = kwds.get('sep', ' ') | |
fileobj = kwds.get('file', STDOUT) | |
outstr = sep.join(map(str, args)) | |
fileobj.write(outstr + end) | |
fileobj.flush() | |
def _to_str(item): | |
if type(item) == float: | |
return '%.6f'%item | |
else: | |
return str(item) | |
class SimpleTable(object): | |
'''A class for easily making tables and printing them. | |
Tables have a title, column labels, and rows: | |
+----------+----------+----------+ | |
| Table Test | | |
+----------+----------+----------+ | |
+----------+----------+----------+ | |
| LabelA | LabelB | LabelC | | |
+----------+----------+----------+ | |
|1371032652| 666555510|9814208081| | |
+----------+----------+----------+ | |
|1191656453|7918551629|6032785125| | |
+----------+----------+----------+ | |
|1952864215|6186265278|3779520336| | |
+----------+----------+----------+ | |
The rows of a table can also be 'labeled', by having the | |
first column label be the empty string, and each rows 'label' | |
be the first element of the row: | |
+----+----------+----------+----------+ | |
| Table Test 2 | | |
+----+----------+----------+----------+ | |
+----+----------+----------+----------+ | |
| | LabelA | LabelB | LabelC | | |
+----+----------+----------+----------+ | |
|row0|1371032652| 666555510|9814208081| | |
+----+----------+----------+----------+ | |
|row1|1191656453|7918551629|6032785125| | |
+----+----------+----------+----------+ | |
|row2|1952864215|6186265278|3779520336| | |
+----+----------+----------+----------+ | |
''' | |
def __init__(self, title, column_labels): | |
self.title = title | |
self.labels = map(str, column_labels) | |
self.rows = [] | |
self.widths = map(len, self.labels) | |
self.file = STDOUT | |
self.final_message = None | |
def add_row(self, row): | |
'''Appends a row to the table.''' | |
assert len(row) == len(self.labels), 'not enough columns in the row!' | |
self.rows.append(map(_to_str, row)) | |
self.widths = map(max, | |
zip(self.widths, | |
map(len, | |
self.rows[-1]))) | |
def set_file(self, fileobj): | |
'''Sets the file object used for printing the table.''' | |
self.file = fileobj | |
def print_table(self): | |
'''PRINTs table data prettily.''' | |
def p(out): | |
PRINT(out, file=self.file) | |
bar, label_row = [''], [''] | |
for width, label in zip(self.widths, self.labels): | |
bar.append('-' * (width)) | |
label_row.append(str(label).center(width)) | |
bar.append(''); bar = '+'.join(bar) | |
label_row.append(''); label_row = '|'.join(label_row) | |
p('+' + '-'*(len(bar) - 2) + '+') | |
p('|' + self.title.center(len(bar) - 2) + '|') | |
p('+' + '-'*(len(bar) - 2) + '+') | |
p(bar) | |
p(label_row) | |
p(bar) | |
for row in self.rows: | |
adjusted_row = [''] | |
for width, item in zip(self.widths, row): | |
adjusted_row.append(str(item).rjust(width)) | |
adjusted_row.append('') | |
p('|'.join(adjusted_row)) | |
p(bar) | |
if self.final_message: | |
p(bar) | |
p('+' + self.final_message.center(len(bar) - 2) + '+') | |
p(bar) | |
def main(): | |
import random | |
table = SimpleTable('Table Test', | |
['LabelA', 'LabelB', 'LabelC']) | |
for i in range(3): | |
row = [] | |
for j in range(3): | |
row.append(random.randrange(10**10)) | |
table.add_row(row) | |
table.print_table() | |
PRINT() | |
named_rows = SimpleTable(table.title + ' 2', | |
[''] + table.labels) | |
for i, row in enumerate(table.rows): | |
named_rows.add_row(['row%d'%i] + row) | |
named_rows.print_table() | |
if __name__ == '__main__': | |
main() |
参考https://github.com/AugurProject/augur-core/tree/master/tests/serpent_tests