universum 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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