universum 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Manifest.txt +3 -0
- data/README.md +2 -2
- data/lib/universum.rb +1 -0
- data/lib/universum/account.rb +8 -7
- data/lib/universum/address.rb +26 -27
- data/lib/universum/contract.rb +51 -5
- data/lib/universum/receipt.rb +1 -1
- data/lib/universum/storage.rb +48 -0
- data/lib/universum/transaction.rb +3 -3
- data/lib/universum/universum.rb +7 -4
- data/lib/universum/version.rb +2 -2
- data/test/contracts/ballot.rb +1 -1
- data/test/contracts/greeter.rb +1 -1
- data/test/contracts/mytoken.rb +2 -2
- data/test/test_mytoken.rb +33 -0
- data/test/test_storage.rb +36 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ed79bd806a5db0f1d5a92a0dfd2e7f3f3b3db56
|
4
|
+
data.tar.gz: 89afee15e5c5800220c13f8f7b58dffb190a4177
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed45e6ab943f70fcd6a9c43c4faa98ab6f6e606f5808e188276b16efc1464603d393ccb4763484c4f33e8f0386a0644a83a6c0856b6197054240f67f486884bf
|
7
|
+
data.tar.gz: f9f0fe016eb2113b1705ec86aed96530690b250e8b83dbff0751b43b7d901ef404dd3bb769ef5e7ae66d75e9ec26cedca6e219fc6561c42e99355e17320be980
|
data/Manifest.txt
CHANGED
@@ -12,6 +12,7 @@ lib/universum/enum.rb
|
|
12
12
|
lib/universum/event.rb
|
13
13
|
lib/universum/function.rb
|
14
14
|
lib/universum/receipt.rb
|
15
|
+
lib/universum/storage.rb
|
15
16
|
lib/universum/transaction.rb
|
16
17
|
lib/universum/type.rb
|
17
18
|
lib/universum/units_money.rb
|
@@ -26,6 +27,8 @@ test/test_ballot.rb
|
|
26
27
|
test/test_enum.rb
|
27
28
|
test/test_event.rb
|
28
29
|
test/test_greeter.rb
|
30
|
+
test/test_mytoken.rb
|
31
|
+
test/test_storage.rb
|
29
32
|
test/test_units_money.rb
|
30
33
|
test/test_units_time.rb
|
31
34
|
test/test_version.rb
|
data/README.md
CHANGED
@@ -59,7 +59,7 @@ and
|
|
59
59
|
##################
|
60
60
|
# Greeter Contract
|
61
61
|
|
62
|
-
def
|
62
|
+
def setup( greeting )
|
63
63
|
@owner = msg.sender
|
64
64
|
@greeting = greeting
|
65
65
|
end
|
@@ -141,7 +141,7 @@ and
|
|
141
141
|
############################
|
142
142
|
## My Token Contract
|
143
143
|
|
144
|
-
def
|
144
|
+
def setup( initial_supply )
|
145
145
|
@balance_of = Mapping.of( Address => Money )
|
146
146
|
@balance_of[ msg.sender] = initial_supply
|
147
147
|
end
|
data/lib/universum.rb
CHANGED
data/lib/universum/account.rb
CHANGED
@@ -27,25 +27,26 @@ class Account
|
|
27
27
|
def self.all() @@directory.values; end
|
28
28
|
|
29
29
|
|
30
|
+
|
30
31
|
####
|
31
32
|
# account (builtin) services / transaction methods
|
32
|
-
include Address ## includes address + send/transfer/balance
|
33
33
|
|
34
|
+
def address() @address; end
|
35
|
+
def address_hex() @address.hex; end ## add convenience shortcut address_hex - why? why not?
|
36
|
+
|
37
|
+
def balance() @address.balance; end ## note: add convenience shortcut - why? why not?
|
34
38
|
## note: for now allow write access too!!!
|
35
39
|
def balance=( value )
|
36
|
-
@balance
|
40
|
+
@address.send( '@balance=', value )
|
37
41
|
end
|
38
42
|
|
43
|
+
|
39
44
|
attr_reader :tx
|
40
45
|
def _auto_inc_tx() @tx += 1; end ## "internal" method - (auto) increment transaction (tx) counter
|
41
46
|
|
42
|
-
## note: needed by transfer/send
|
43
|
-
def this() Universum.this; end ## returns current contract
|
44
|
-
|
45
47
|
private
|
46
48
|
def initialize( address, balance: 0, tx: 0 )
|
47
|
-
@address = address # type address - (hex) string starts with 0x
|
48
|
-
@balance = balance # uint
|
49
|
+
@address = Address.new( address, balance: balance ) # type address - (hex) string starts with 0x
|
49
50
|
@tx = tx # transaction (tx) count (used for nonce and replay attack protection)
|
50
51
|
end
|
51
52
|
|
data/lib/universum/address.rb
CHANGED
@@ -1,64 +1,61 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
class Address
|
5
|
+
def self.zero() '0x0000'; end
|
6
6
|
|
7
|
+
def initialize( address, balance: 0 )
|
8
|
+
@address = address
|
9
|
+
@balance = balance
|
10
|
+
end
|
11
|
+
|
12
|
+
def hex() @address; end ## return address as a hex string (e.g. '0x1111' etc.)
|
13
|
+
def balance() @balance; end
|
7
14
|
|
8
|
-
module Address
|
9
|
-
def self.zero() '0x0000'; end
|
10
15
|
|
11
|
-
def address
|
12
|
-
if @address
|
13
|
-
@address
|
14
|
-
else
|
15
|
-
if is_a? Contract
|
16
|
-
## fix/todo: use/lookup proper addr from contract
|
17
|
-
## construct address for now from object_id
|
18
|
-
"0x#{(object_id << 1).to_s(16)}"
|
19
|
-
else ## assume Account
|
20
|
-
'0x0000'
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end # method address
|
24
16
|
|
25
17
|
def transfer( value ) ## @payable @public
|
26
18
|
## todo/fix: throw exception if insufficient funds
|
19
|
+
## todo/fix: use assert( send( value )
|
27
20
|
send( value ) # returns true/false
|
28
21
|
end
|
29
22
|
|
30
23
|
def send( value ) ## @payable @public
|
24
|
+
## note: auto-adds "global" from address (using Universum.this)
|
25
|
+
_send( Universum.this, value )
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
#############################
|
30
|
+
### private (internal use only) methods - PLEASE do NOT use (use transfer/send)
|
31
|
+
def _send( from, value )
|
31
32
|
## todo/fix: assert value > 0
|
32
33
|
## todo/fix: add missing -= part in transfer!!!!
|
33
34
|
|
34
35
|
## use this (current contract) for debit (-) ammount
|
35
|
-
|
36
|
+
from._sub( value ) # sub(tract) / debit from the sender (current contract)
|
36
37
|
_add( value ) # add / credit to the recipient
|
37
38
|
end
|
38
39
|
|
39
|
-
def balance
|
40
|
-
@balance ||= 0 ## return 0 if undefined
|
41
|
-
end
|
42
|
-
|
43
|
-
### private (internal use only) methods - PLEASE do NOT use (use transfer/send)
|
44
40
|
def _sub( value )
|
45
|
-
@balance ||= 0 ## return 0 if undefined
|
46
41
|
@balance -= value
|
47
42
|
end
|
48
43
|
|
49
44
|
def _add( value )
|
50
|
-
@balance ||= 0 ## return 0 if undefined
|
51
45
|
@balance += value
|
52
46
|
end
|
53
47
|
end # module Address
|
54
48
|
|
55
49
|
|
56
50
|
|
51
|
+
|
57
52
|
class String
|
58
53
|
def transfer( value )
|
59
54
|
## check if self is an address
|
60
55
|
if self.start_with?( '0x' )
|
61
|
-
|
56
|
+
## fix/fix/fix: use Address[self] lookup!!!!
|
57
|
+
## do NOT use Account any longer
|
58
|
+
Account[self].address.transfer( value )
|
62
59
|
else
|
63
60
|
raise "(Auto-)Type Conversion from Address (Hex) String to Account Failed; Expected String Starting with 0x got #{self}; Contract Halted (Stopped)"
|
64
61
|
end
|
@@ -67,7 +64,9 @@ class String
|
|
67
64
|
def send( value )
|
68
65
|
## check if self is an address
|
69
66
|
if self.start_with?( '0x' )
|
70
|
-
|
67
|
+
## fix/fix/fix: use Address[self] lookup!!!!
|
68
|
+
## do NOT use Account any longer
|
69
|
+
Account[self].address.send( value )
|
71
70
|
else
|
72
71
|
raise "(Auto-)Type Conversion from Address (Hex) String to Account Failed; Expected String Starting with 0x got #{self}; Contract Halted (Stopped)"
|
73
72
|
end
|
data/lib/universum/contract.rb
CHANGED
@@ -17,6 +17,25 @@ class Contract
|
|
17
17
|
end
|
18
18
|
|
19
19
|
code = File.open( path, 'r:bom|utf-8' ) { |f| f.read }
|
20
|
+
|
21
|
+
## auto-patch!!!!
|
22
|
+
## change all ivars to use storage
|
23
|
+
## e.g. @greeting => storage.greeting
|
24
|
+
## @balance_of[ msg.sender ] => storage.balance_of[ msg.sender ]
|
25
|
+
## ...
|
26
|
+
##
|
27
|
+
## todo/fix: check for possible class variables!! (e.g. @@logger or something)
|
28
|
+
## make regex "smarter"
|
29
|
+
code = code.gsub( /(@{1,})([a-z][a-zA-Z0-9_]*)/ ) do |_|
|
30
|
+
if $1.size == 1
|
31
|
+
puts "auto-patching contract code >#{$1}#{$2}< to >storage[ :#{$2} ]<"
|
32
|
+
"storage[ :#{$2} ]"
|
33
|
+
else
|
34
|
+
## assume class variable - skip - keep as is
|
35
|
+
"#{$1}#{$2}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
20
39
|
klass = Class.new( Contract )
|
21
40
|
klass.class_eval( code ) ## note: use class_eval (NOT instance_eval)
|
22
41
|
klass
|
@@ -40,7 +59,35 @@ class Contract
|
|
40
59
|
|
41
60
|
####
|
42
61
|
# account (builtin) services / transaction methods
|
43
|
-
|
62
|
+
def self.create( *args )
|
63
|
+
klass = new( nil, *args )
|
64
|
+
klass.setup( *args )
|
65
|
+
klass
|
66
|
+
end
|
67
|
+
|
68
|
+
def initialize( address=nil, *args )
|
69
|
+
## fix/todo: use/lookup proper addr from contract
|
70
|
+
## construct address for now from object_id
|
71
|
+
address = "0x#{(object_id << 1).to_s(16)}" if address.nil?
|
72
|
+
@address = Address.new( address )
|
73
|
+
@storage = Storage.new
|
74
|
+
|
75
|
+
###########
|
76
|
+
# note: does NOT auto-call setup - why? why not?
|
77
|
+
# use create !!!!!!
|
78
|
+
|
79
|
+
## todo: make initialize private - why? why not?
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def setup
|
84
|
+
# default (built-in) setup; do nothing
|
85
|
+
end
|
86
|
+
|
87
|
+
def address() @address; end
|
88
|
+
def this() @address; end ## returns "embedded" address of current contract (that is, us!)
|
89
|
+
|
90
|
+
def storage() @storage; end
|
44
91
|
|
45
92
|
## function sig(nature) macro for types (dummy for now)
|
46
93
|
# e.g. use like
|
@@ -54,9 +101,9 @@ class Contract
|
|
54
101
|
|
55
102
|
####
|
56
103
|
# todo/double check: auto-add payable default fallback - why? why not?
|
57
|
-
|
58
|
-
|
59
|
-
|
104
|
+
# no - do NOT add, BUT add payable config for receive
|
105
|
+
# def receive ## @payable default fallback - use different name - why? why not? (e.g. handle/process/etc.)
|
106
|
+
# end
|
60
107
|
|
61
108
|
def assert( condition )
|
62
109
|
if condition == true
|
@@ -67,7 +114,6 @@ class Contract
|
|
67
114
|
end
|
68
115
|
|
69
116
|
|
70
|
-
def this() Universum.this; end ## returns current contract
|
71
117
|
def log( event ) Universum.log( event ); end
|
72
118
|
def msg() Universum.msg; end
|
73
119
|
def block() Universum.block; end
|
data/lib/universum/receipt.rb
CHANGED
@@ -36,7 +36,7 @@ class Receipt ## transaction receipt
|
|
36
36
|
|
37
37
|
if contract
|
38
38
|
## note: for easier debugging add class name in () to address (needs to get stripped away in lookup)
|
39
|
-
@contract_address = "#{contract.address} (#{contract.class.name})"
|
39
|
+
@contract_address = "#{contract.address.hex} (#{contract.class.name})"
|
40
40
|
else
|
41
41
|
@contract_address = nil
|
42
42
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#######
|
4
|
+
# keep track of contract state
|
5
|
+
|
6
|
+
class Storage
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@data = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](key)
|
13
|
+
puts " SLOAD( #{key.inspect} )"
|
14
|
+
@data[key]
|
15
|
+
end
|
16
|
+
|
17
|
+
def []=(key,value)
|
18
|
+
puts " SSTORE( #{key.inspect}, #{value.inspect} )"
|
19
|
+
@data[key] = value
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
###############################
|
25
|
+
# todo/future: in the future auto-add getter/setter methods on setup
|
26
|
+
# do NOT use method_missing!!!
|
27
|
+
def method_missing( m, *args, &block )
|
28
|
+
puts "Storage#method_missing( #{m.inspect}), #{args.inspect} )"
|
29
|
+
|
30
|
+
## todo: add support ? for bool
|
31
|
+
## elsif m.end_with?('?') && args.empty?
|
32
|
+
## @storage[m]
|
33
|
+
|
34
|
+
if m =~/\A[a-z][a-zA-Z0-9_]*=\z/ && args.size == 1
|
35
|
+
key = m[0...-1].to_sym ## note: cut-off trailing equal sign (=), use EXCLUSIVE (...) range and NOT INCLUSIVE (..)
|
36
|
+
value = args[0]
|
37
|
+
puts " SSTORE( #{key.inspect}, #{value.inspect} )"
|
38
|
+
@data[key] = value
|
39
|
+
elsif m =~/\A[a-z][a-zA-Z0-9_]*\z/ && args.empty? ## todo/fix: check for valid identifier
|
40
|
+
key = m
|
41
|
+
puts " SLOAD( #{key.inspect} )"
|
42
|
+
@data[key]
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end ## class Storage
|
@@ -18,12 +18,12 @@ class Transaction
|
|
18
18
|
account = Account.at( from ) ## lookup account by address
|
19
19
|
end
|
20
20
|
|
21
|
-
@from = account.address
|
21
|
+
@from = account.address.hex
|
22
22
|
|
23
23
|
if to.is_a?( Contract )
|
24
|
-
@to = "#{to.address} (#{to.class.name})"
|
24
|
+
@to = "#{to.address.hex} (#{to.class.name})"
|
25
25
|
elsif to.is_a?( Account ) ## note: to allows Contracts AND Accounts
|
26
|
-
@to = to.address
|
26
|
+
@to = to.address.hex
|
27
27
|
else
|
28
28
|
@to = to # might be a contract or account (pass through for now)
|
29
29
|
end
|
data/lib/universum/universum.rb
CHANGED
@@ -31,6 +31,9 @@ class Universum ## Uni short for Universum
|
|
31
31
|
counter = @@counter ||= 0 ## total tx counter for debugging (start with 0)
|
32
32
|
@@counter += 1
|
33
33
|
|
34
|
+
## todo/fix:
|
35
|
+
## allow/add auto-create account for convenience (and easy testing)!!!
|
36
|
+
|
34
37
|
## note: always lookup (use) account for now
|
35
38
|
if from.is_a?( Account )
|
36
39
|
account = from
|
@@ -39,7 +42,7 @@ class Universum ## Uni short for Universum
|
|
39
42
|
end
|
40
43
|
|
41
44
|
## setup contract msg context
|
42
|
-
self.msg = { sender: account.address, value: value }
|
45
|
+
self.msg = { sender: account.address.hex, value: value }
|
43
46
|
|
44
47
|
|
45
48
|
## allow shortcut for Class (without ctor arguments) - no need to wrap in array
|
@@ -54,7 +57,7 @@ class Universum ## Uni short for Universum
|
|
54
57
|
args = data[1..-1] ## arguments
|
55
58
|
|
56
59
|
puts "** tx ##{counter} (block ##{block.number}): #{tx.log_str}"
|
57
|
-
contract = klass.
|
60
|
+
contract = klass.create( *args ) ## note: balance and this (and msg.send/transfer) NOT available/possible !!!!
|
58
61
|
|
59
62
|
if value > 0
|
60
63
|
## move value to msg (todo/fix: restore if exception)
|
@@ -62,9 +65,9 @@ class Universum ## Uni short for Universum
|
|
62
65
|
contract._add( value ) # add / credit to the recipient
|
63
66
|
end
|
64
67
|
|
65
|
-
puts " new #{contract.class.name} contract adddress: #{contract.address.inspect}"
|
68
|
+
puts " new #{contract.class.name} contract adddress: #{contract.address.hex.inspect}"
|
66
69
|
## add new contract to (lookup) directory
|
67
|
-
Contract.store( contract.address, contract )
|
70
|
+
Contract.store( contract.address.hex, contract )
|
68
71
|
|
69
72
|
## issue (mined) transaction receipt
|
70
73
|
receipt = Receipt.new( tx: tx,
|
data/lib/universum/version.rb
CHANGED
data/test/contracts/ballot.rb
CHANGED
@@ -9,7 +9,7 @@ Voter = Struct.new( weight: 0,
|
|
9
9
|
Proposal = Struct.new( vote_count: 0 )
|
10
10
|
|
11
11
|
## Create a new ballot with $(num_proposals) different proposals.
|
12
|
-
def
|
12
|
+
def setup( num_proposals )
|
13
13
|
@chairperson = msg.sender
|
14
14
|
@voters = Mapping.of( Address => Voter )
|
15
15
|
@proposals = Array.of( Proposal, num_proposals )
|
data/test/contracts/greeter.rb
CHANGED
data/test/contracts/mytoken.rb
CHANGED
@@ -3,9 +3,9 @@
|
|
3
3
|
#########################
|
4
4
|
# My Token Contract
|
5
5
|
|
6
|
-
def
|
6
|
+
def setup( initial_supply )
|
7
7
|
@balance_of = Mapping.of( Address => Money )
|
8
|
-
@balance_of[ msg.sender] = initial_supply
|
8
|
+
@balance_of[ msg.sender ] = initial_supply
|
9
9
|
end
|
10
10
|
|
11
11
|
def transfer( to, value )
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
###
|
4
|
+
# to run use
|
5
|
+
# ruby -I ./lib -I ./test test/test_mytoken.rb
|
6
|
+
|
7
|
+
|
8
|
+
require 'helper'
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
class TestMyToken < MiniTest::Test
|
13
|
+
|
14
|
+
MyToken = Contract.load( "#{Universum.root}/test/contracts/mytoken" )
|
15
|
+
|
16
|
+
def test_uni
|
17
|
+
Account['0x1111']
|
18
|
+
Account['0xaaaa']
|
19
|
+
Account['0xbbbb']
|
20
|
+
|
21
|
+
tx = Uni.send_transaction( from: '0x1111', data: [MyToken, 100_000_000] )
|
22
|
+
pp tx
|
23
|
+
pp tx.receipt
|
24
|
+
|
25
|
+
token = tx.receipt.contract
|
26
|
+
|
27
|
+
Uni.send_transaction( from: '0x1111', to: token, data: [:transfer, '0xaaaa', 100] )
|
28
|
+
Uni.send_transaction( from: '0x1111', to: token, data: [:transfer, '0xbbbb', 200] )
|
29
|
+
Uni.send_transaction( from: '0xbbbb', to: token, data: [:transfer, '0xaaaa', 10] )
|
30
|
+
pp token
|
31
|
+
end
|
32
|
+
|
33
|
+
end # class TestMyToken
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
###
|
4
|
+
# to run use
|
5
|
+
# ruby -I ./lib -I ./test test/test_storage.rb
|
6
|
+
|
7
|
+
|
8
|
+
require 'helper'
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
class TestStorage < MiniTest::Test
|
14
|
+
|
15
|
+
def test_storage
|
16
|
+
|
17
|
+
s = Storage.new
|
18
|
+
s[:balance]
|
19
|
+
s[:balance] = {}
|
20
|
+
s[:balance]['0xaaaa'] = 100
|
21
|
+
s[:balance]['0xbbbb'] = 200
|
22
|
+
pp s
|
23
|
+
|
24
|
+
balance_exp = { '0xaaaa'=>100, '0xbbbb'=>200 }
|
25
|
+
assert_equal balance_exp, s.balance
|
26
|
+
assert_equal 100, s.balance['0xaaaa']
|
27
|
+
assert_equal 200, s.balance['0xbbbb']
|
28
|
+
|
29
|
+
s.balance['0xcccc'] = 300
|
30
|
+
assert_equal 300, s.balance['0xcccc']
|
31
|
+
|
32
|
+
s.owner = '0x1111'
|
33
|
+
assert_equal '0x1111', s.owner
|
34
|
+
end
|
35
|
+
|
36
|
+
end # class TestStorage
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: universum
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-02-
|
11
|
+
date: 2019-02-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: safestruct
|
@@ -79,6 +79,7 @@ files:
|
|
79
79
|
- lib/universum/event.rb
|
80
80
|
- lib/universum/function.rb
|
81
81
|
- lib/universum/receipt.rb
|
82
|
+
- lib/universum/storage.rb
|
82
83
|
- lib/universum/transaction.rb
|
83
84
|
- lib/universum/type.rb
|
84
85
|
- lib/universum/units_money.rb
|
@@ -93,6 +94,8 @@ files:
|
|
93
94
|
- test/test_enum.rb
|
94
95
|
- test/test_event.rb
|
95
96
|
- test/test_greeter.rb
|
97
|
+
- test/test_mytoken.rb
|
98
|
+
- test/test_storage.rb
|
96
99
|
- test/test_units_money.rb
|
97
100
|
- test/test_units_time.rb
|
98
101
|
- test/test_version.rb
|