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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2a83f038e0a43351f492fc32ea68042559a82782
4
- data.tar.gz: 604e2ed189008bc319d88b2cbb0d1cafbe8bffc1
3
+ metadata.gz: 4ed79bd806a5db0f1d5a92a0dfd2e7f3f3b3db56
4
+ data.tar.gz: 89afee15e5c5800220c13f8f7b58dffb190a4177
5
5
  SHA512:
6
- metadata.gz: 7e27c2c0b46fd2e82bbfa1994656cdc174499efcb23b4f0cbb8304ef497c421ca4c32ab1b778003f0e24470de430ab9126e01dbf0d95c8b9d265b9208a2c4ad1
7
- data.tar.gz: c0c4e8c82b5c4cf32a9d6414b7246a66f0bd675820cdb9c27b6376bfcaba37c028086d43955e61b468f865db0c563eee93cac86f87e7b70f691936be983cc9f1
6
+ metadata.gz: ed45e6ab943f70fcd6a9c43c4faa98ab6f6e606f5808e188276b16efc1464603d393ccb4763484c4f33e8f0386a0644a83a6c0856b6197054240f67f486884bf
7
+ data.tar.gz: f9f0fe016eb2113b1705ec86aed96530690b250e8b83dbff0751b43b7d901ef404dd3bb769ef5e7ae66d75e9ec26cedca6e219fc6561c42e99355e17320be980
@@ -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 initialize( greeting )
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 initialize( initial_supply )
144
+ def setup( initial_supply )
145
145
  @balance_of = Mapping.of( Address => Money )
146
146
  @balance_of[ msg.sender] = initial_supply
147
147
  end
@@ -29,6 +29,7 @@ require 'universum/function'
29
29
  require 'universum/address'
30
30
  require 'universum/type'
31
31
  require 'universum/account'
32
+ require 'universum/storage'
32
33
  require 'universum/contract'
33
34
  require 'universum/transaction'
34
35
  require 'universum/receipt'
@@ -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 = value
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
 
@@ -1,64 +1,61 @@
1
1
  # encoding: utf-8
2
2
 
3
3
 
4
- #############
5
- # FIX/FIX/FIX!!!! - turn Address into a class!!!!
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
- this._sub( value ) # sub(tract) / debit from the sender (current contract)
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
- Account[self].transfer( value )
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
- Account[self].send( value )
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
@@ -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
- include Address ## includes address + send/transfer/balance
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
- def receive ## @payable default fallback - use different name - why? why not? (e.g. handle/process/etc.)
58
- end
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
@@ -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
@@ -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.new( *args ) ## note: balance and this (and msg.send/transfer) NOT available/possible !!!!
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,
@@ -7,8 +7,8 @@
7
7
  class Universum
8
8
 
9
9
  MAJOR = 0
10
- MINOR = 3
11
- PATCH = 1
10
+ MINOR = 4
11
+ PATCH = 0
12
12
  VERSION = [MAJOR,MINOR,PATCH].join('.')
13
13
 
14
14
  def self.version
@@ -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 initialize( num_proposals )
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 )
@@ -3,7 +3,7 @@
3
3
  ######################
4
4
  # Greeter Contract
5
5
 
6
- def initialize( greeting )
6
+ def setup( greeting )
7
7
  @owner = msg.sender
8
8
  @greeting = greeting
9
9
  end
@@ -3,9 +3,9 @@
3
3
  #########################
4
4
  # My Token Contract
5
5
 
6
- def initialize( initial_supply )
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.3.1
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-16 00:00:00.000000000 Z
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