Class: Contract
- Inherits:
-
Object
- Object
- Contract
- Includes:
- CryptoHelper, RuntimeHelper, Types
- Defined in:
- lib/rubysol.rb,
lib/rubysol.rb,
lib/rubysol/contract.rb
Overview
add extra setup helpers
Class Attribute Summary collapse
-
.events ⇒ Object
Returns the value of attribute events.
-
.is_abstract_contract ⇒ Object
Returns the value of attribute is_abstract_contract.
-
.parent_contracts ⇒ Object
Sets the attribute parent_contracts.
-
.state_variable_definitions ⇒ Object
state variables.
Class Method Summary collapse
-
.abi ⇒ Object
functions / abis.
-
.abstract ⇒ Object
parent contracts keep abstract - why? why not?.
- .at(address) ⇒ Object
- .construct(*args, **kwargs) ⇒ Object (also: create)
- .define_state_variable(type, args) ⇒ Object
- .enum(class_name, *args) ⇒ Object
- .event(class_name, **attributes) ⇒ Object
- .linearize_contracts(contract) ⇒ Object
- .linearized_parents ⇒ Object (also: parent_contracts)
- .method_added(name) ⇒ Object
-
.nonce ⇒ Object
– use a “number used once” counter for address generation for now note: will count up for now for ALL contracts (uses @@) fix: use a better formula later!!!!.
- .nonce=(value) ⇒ Object
- .public_abi ⇒ Object
- .register(obj) ⇒ Object
-
.registry ⇒ Object
quick hack - add contract registry for lookup by address and class.
- .sig(args = [], *options, returns: nil) ⇒ Object
-
.sigs ⇒ Object
note: sig machinery with method_added MUST come last here.
-
.sigs_exclude ⇒ Object
ignore this methods; do NOT (auto-)generate wrapper method popping (unnamed) sig from stack!!!.
-
.sigs_unnamed ⇒ Object
unnamed sigs stack.
- .storage(**kwargs) ⇒ Object
- .struct(class_name, **attributes) ⇒ Object
Instance Method Summary collapse
-
#__address__ ⇒ Object
“read-only” access for address note: MUST use __address__ - why? why not? otherwise will conflicts with global conversion function when used in contract code e.g.
- #__autoregister__ ⇒ Object
-
#callstack(&block) ⇒ Object
-fix-fix-fix hack for update of msg.sender make more generic and autoupate hack-y callstack to update msg.sender “by hand” for now fix-fix-fix - make it automagic somehow!!.
-
#constructor ⇒ Object
sig [] def constructor end auto-add default constructor in Contract (base) here add sig too - why? why not?.
- #deserialize(state_data) ⇒ Object (also: #load)
- #generate_state ⇒ Object
-
#initialize ⇒ Contract
constructor
A new instance of Contract.
- #public_abi ⇒ Object
- #serialize ⇒ Object (also: #dump)
Methods included from RuntimeHelper
#assert, #block, #current_transaction, #log, #msg, #this
Methods included from CryptoHelper
Constructor Details
#initialize ⇒ Contract
Returns a new instance of Contract.
227 228 229 230 231 232 233 234 235 |
# File 'lib/rubysol/contract.rb', line 227 def initialize unless self.class.abi.generated? ## only generate once? double check self.class.abi.generate_functions end ## rename to generate_storage or such - why? why not? generate_state end |
Class Attribute Details
.events ⇒ Object
Returns the value of attribute events.
6 7 8 |
# File 'lib/rubysol/contract.rb', line 6 def events @events end |
.is_abstract_contract ⇒ Object
Returns the value of attribute is_abstract_contract.
6 7 8 |
# File 'lib/rubysol/contract.rb', line 6 def is_abstract_contract @is_abstract_contract end |
.parent_contracts=(value) ⇒ Object
Sets the attribute parent_contracts
6 7 8 |
# File 'lib/rubysol/contract.rb', line 6 def parent_contracts=(value) @parent_contracts = value end |
.state_variable_definitions ⇒ Object
state variables
56 57 58 |
# File 'lib/rubysol/contract.rb', line 56 def state_variable_definitions @state_variable_definitions end |
Class Method Details
.abi ⇒ Object
functions / abis
123 124 125 |
# File 'lib/rubysol/contract.rb', line 123 def self.abi @abi ||= AbiProxy.new(self) end |
.abstract ⇒ Object
parent contracts
keep abstract - why? why not?
16 17 18 |
# File 'lib/rubysol/contract.rb', line 16 def self.abstract @is_abstract_contract = true end |
.at(address) ⇒ Object
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/rubysol/contract.rb', line 162 def self.at( address ) klass = self puts "==> calling #{klass.name}.at( #{address.pretty_print_inspect })" ## note: support plain strings and typed address for now ## use serialize to get "raw" string value of address ## fix - fix - fix - change back to address? addr_key = address.is_a?( Types::Address ) ? address.serialize : address rec = registry[ addr_key ] if rec obj_klass = rec[0] ## raise type error if not matching class type if obj_klass == klass || obj_klass.parent_contracts.include?( klass ) obj = rec[1] obj else raise TypeError, "#{obj_klass.name} contract found; is NOT a type of #{klass.name}; sorry" end else nil # nothing found end end |
.construct(*args, **kwargs) ⇒ Object Also known as: create
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/rubysol.rb', line 93 def self.construct( *args, **kwargs ) ## todo/fix: check either args or kwargs MUST be empty ## can only use one format puts "[debug] Contract.construct - class -> #{self.name}" puts " args: #{args.inspect}" unless args.empty? puts " kwargs: #{kwargs.inspect}" unless kwargs.empty? contract = new ## (auto-)register before or after calling constructor - why? why not? contract.__autoregister__ contract.constructor( *args, **kwargs ) contract end |
.define_state_variable(type, args) ⇒ Object
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/rubysol/contract.rb', line 61 def self.define_state_variable(type, args) ## note: REMOVE last item from array (use Array#pop) ## make sure name is ALWAYS a symbol!!! name = args.pop.to_sym if state_variable_definitions[name] raise "No shadowing: #{name} is already defined." end ## check for visibility - internal/private/public ## note: make :public default and :internal only if name starting with underscore (_) - why? why not? visibility = name.start_with?( '_' ) ? :internal : :public # note: for now NO support for immutable and constant!!!!! # immutable = false # constant = false ## todo/check - force strict check for double (public/private etc.) use - why? why not? args.each do |arg| case arg when :public, :private, :internal then visibility = arg # when :immutable then immutable = true # when :constant then constant = true else raise ArgumentError, "unknown type qualifier >#{arg}<; sorry for typedef #{type} in #{args.inspect}" end end state_variable_definitions[name] = { type: type, visibility: visibility } # immutable: immutable, # constant: constant ## check - visibility if visibility == :public contract_class = self Generator.getter_function( contract_class, name, type ) end type end |
.enum(class_name, *args) ⇒ Object
138 139 140 141 |
# File 'lib/rubysol/contract.rb', line 138 def self.enum( class_name, *args ) typedclass = Types::Enum.new( class_name, *args, scope: self ) typedclass end |
.event(class_name, **attributes) ⇒ Object
143 144 145 146 |
# File 'lib/rubysol/contract.rb', line 143 def self.event( class_name, **attributes ) typedclass = Types::Event.new( class_name, scope: self, **attributes ) typedclass end |
.linearize_contracts(contract) ⇒ Object
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/rubysol/contract.rb', line 21 def self.linearize_contracts( contract ) ## for now ## include all classes before Contract ## cache result - why? why not? ## ## todo/fix: check if include? Contract ## if not raise error - CANNOT linearize (no contract base found) classes = [] contract.ancestors.each do |ancestor| break if ancestor == Contract if ancestor.instance_of?( Class ) classes << ancestor else ### assume Module puts "[debug] skipping module - #{ancestor.name} : #{ancestor.class.name}" end end classes end |
.linearized_parents ⇒ Object Also known as: parent_contracts
41 42 43 44 |
# File 'lib/rubysol/contract.rb', line 41 def self.linearized_parents ## note: exclude self (that is, cut-off first class) linearize_contracts( self )[1..-1] end |
.method_added(name) ⇒ Object
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
# File 'lib/rubysol/contract.rb', line 363 def self.method_added( name ) if sigs_exclude.include?( name ) puts "--- skip method added hook >#{name}< - found in sigs exclude" return ## do nothing; else puts "==> method added hook >#{name}<... processing..." end ## pp name ## pp name.class.name name = name.to_sym ## note: make sure name is ALWAYS a symbol ## note: method lookup via method needs an object / INSTANCE ## NOT working with class only!!!! # m = method( name ) # pp m.name # pp m.parameters # pp m raise "no unnamed sig(nature) on stack for method >#{name}< in class >#{self.name}<; sorry" if sigs_unnamed.size == 0 sig_unnamed = sigs_unnamed.pop puts " using sig_unnamed: #{sig_unnamed.inspect}" @sigs ||= {} raise "duplicate method sig(nature) for method >#{name}< in class >#{self.name}<; sorry" if @sigs.has_key?( name ) @sigs[ name ] = sig_unnamed puts " generate typed_function >#{name}<" Generator.typed_function( self, name, inputs: sig_unnamed[ :inputs ] ) puts "<== method added hook >#{name}< done." end |
.nonce ⇒ Object
– use a “number used once” counter for address generation for now
note: will count up for now for ALL contracts (uses @@)
fix: use a better formula later!!!!
199 |
# File 'lib/rubysol/contract.rb', line 199 def self.nonce() @@nonce ||= 0; end |
.nonce=(value) ⇒ Object
200 |
# File 'lib/rubysol/contract.rb', line 200 def self.nonce=( value ) @@nonce = value; end |
.public_abi ⇒ Object
127 |
# File 'lib/rubysol/contract.rb', line 127 def self.public_abi() abi.public_api; end |
.register(obj) ⇒ Object
157 158 159 160 |
# File 'lib/rubysol/contract.rb', line 157 def self.register( obj ) registry[ obj.__address__ ] = [obj.class, obj] ## fix: in the future store state NOT object ref!!! obj end |
.registry ⇒ Object
quick hack - add contract registry
for lookup by address and class
156 |
# File 'lib/rubysol/contract.rb', line 156 def self.registry() @@registry ||= {}; end |
.sig(args = [], *options, returns: nil) ⇒ Object
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
# File 'lib/rubysol/contract.rb', line 302 def self.sig( args=[], *, returns: nil ) puts "[debug] add sig args: #{args.inspect}, options: #{.inspect}, returns: #{returns.inspect}" ## use inputs for args) and outputs for returns - why? why not? ## check if include explicit visibility in options if .include?( :public ) || .include?( :private ) || .include?( :internal ) # do nothing / pass-along as is else # auto-add default up-front - :public or :internal if name starting with underscore (_) visibility = name.start_with?( '_' ) ? :internal : :public .unshift( visibility ) end #### # auto-convert args (inputs), returns (outputs) to type (defs) args = args.map { |value| typeof( value ) } ### ## note: turn returns into an array - empty if nil, etc. ## always wrap into array returns = if returns.nil? [] elsif returns.is_a?( ::Array ) returns else ## assume single type [returns] end returns = returns.map { |value| typeof( value ) } @sigs_unnamed ||= [] @sigs_unnamed.push( { inputs: args, outputs: returns, options: } ) end |
.sigs ⇒ Object
note: sig machinery with method_added MUST come last here
283 284 285 |
# File 'lib/rubysol/contract.rb', line 283 def self.sigs @sigs ||= {} end |
.sigs_exclude ⇒ Object
ignore this methods;
do NOT (auto-)generate wrapper method popping (unnamed) sig from stack!!!
293 294 295 296 297 298 |
# File 'lib/rubysol/contract.rb', line 293 def self.sigs_exclude ## note: always exclude globals ## get or can get changed via runtime modules (simulacrm) and such!!! ## e.g. msg.sender, block.timestamp, tx.origin, etc. @sigs_exclude ||= [:block, :tx, :msg] end |
.sigs_unnamed ⇒ Object
unnamed sigs stack
287 288 289 |
# File 'lib/rubysol/contract.rb', line 287 def self.sigs_unnamed ## unnamed sigs stack @sigs_unnamed ||= [] end |
.storage(**kwargs) ⇒ Object
105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/rubysol/contract.rb', line 105 def self.storage( **kwargs ) ## note: assume keys are names and values are types for storage ## note: allow multiple calls of storage!!! kwargs.each do |name, type| type = typeof( type ) ## add support for more args - e.g. visibility or such - why? why not? args = [name] define_state_variable( type, args ) end end |
.struct(class_name, **attributes) ⇒ Object
133 134 135 136 |
# File 'lib/rubysol/contract.rb', line 133 def self.struct( class_name, **attributes ) typedclass = Types::Struct.new( class_name, scope: self, **attributes ) typedclass end |
Instance Method Details
#__address__ ⇒ Object
“read-only” access for address
note: MUST use __address__ - why? why not?
otherwise will conflicts with global conversion function
when used in contract code e.g. address(0) or such
194 |
# File 'lib/rubysol/contract.rb', line 194 def __address__() @__address__; end |
#__autoregister__ ⇒ Object
202 203 204 205 206 207 208 209 |
# File 'lib/rubysol/contract.rb', line 202 def __autoregister__ ## for now use nonce = self.class.nonce += 1 @__address__ = '0x' + 'cc'*16 + ('%08d' % nonce ) ## count puts " new #{self.class.name} contract @ address #{@__address__}" self.class.register( self ) end |
#callstack(&block) ⇒ Object
-fix-fix-fix hack for update of msg.sender
make more generic and autoupate
hack-y callstack to update msg.sender “by hand” for now
fix-fix-fix - make it automagic somehow!!
216 217 218 219 220 221 222 |
# File 'lib/rubysol/contract.rb', line 216 def callstack( &block ) restore = Runtime.msg.sender Runtime.msg.sender = @__address__ ret = block.call Runtime.msg.sender = restore ret end |
#constructor ⇒ Object
sig []
def constructor
end
auto-add default constructor in Contract (base) here add sig too - why? why not?
357 358 359 |
# File 'lib/rubysol/contract.rb', line 357 def constructor puts " calling default (fallback dummy) contract constructor in #{self.class.name}" end |
#deserialize(state_data) ⇒ Object Also known as: load
263 264 265 266 267 268 269 270 271 272 |
# File 'lib/rubysol/contract.rb', line 263 def deserialize(state_data) state_data.each do |name, value| ## todo/fix: make sure ivar is_a? Typed!!!! ## lookup type info definition = self.class.state_variable_definitions[ name ] type = definition[:type] typed = type.new( value ) ## note: always (re)create new typed classes (from literals) instance_variable_set( "@#{name}", typed ) end end |
#generate_state ⇒ Object
238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/rubysol/contract.rb', line 238 def generate_state puts "==> generate_state (ivars) #{self.class.name}" self.class.state_variable_definitions.each do |name, definition| type = definition[:type] puts "[debug] add ivar @#{name} - #{type}" ### note. create Typed instance here (via Type#new_zero) instance_variable_set("@#{name}", type.new_zero ) end end |
#public_abi ⇒ Object
128 |
# File 'lib/rubysol/contract.rb', line 128 def public_abi() self.class.public_abi; end |
#serialize ⇒ Object Also known as: dump
251 252 253 254 255 256 257 258 259 |
# File 'lib/rubysol/contract.rb', line 251 def serialize self.class.state_variable_definitions.keys.reduce({}) do |h, name| ivar = instance_variable_get("@#{name}") ## todo/fix: make sure ivar is_a? Typed!!!! puts "WARN!! no ivar @#{name} found!!! - #{instance_variables}" if ivar.nil? h[name] = ivar.serialize h end end |