ciri 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/Gemfile.lock +6 -6
- data/README.md +68 -48
- data/Rakefile +16 -11
- data/ciri-crypto/lib/ciri/crypto/signature.rb +7 -5
- data/ciri-rlp/ciri-rlp.gemspec +1 -1
- data/ciri-rlp/lib/ciri/rlp.rb +1 -1
- data/ciri-rlp/lib/ciri/rlp/decode.rb +23 -7
- data/ciri-rlp/lib/ciri/rlp/encode.rb +8 -2
- data/ciri-rlp/lib/ciri/rlp/serializable.rb +14 -4
- data/ciri-rlp/lib/ciri/rlp/version.rb +1 -1
- data/ciri-rlp/spec/ciri/fixture_spec.rb +2 -2
- data/ciri-utils/Gemfile.lock +1 -1
- data/ciri-utils/lib/ciri/utils/version.rb +1 -1
- data/ciri.gemspec +4 -4
- data/lib/ciri/chain/header.rb +13 -11
- data/lib/ciri/chain/header_chain.rb +1 -10
- data/lib/ciri/chain/transaction.rb +5 -23
- data/lib/ciri/db/account_db.rb +0 -4
- data/lib/ciri/db/backend/errors.rb +27 -0
- data/lib/ciri/db/backend/memory.rb +10 -12
- data/lib/ciri/db/backend/rocks.rb +13 -6
- data/lib/ciri/db/backend/rocks_db.rb +4 -0
- data/lib/ciri/evm.rb +6 -1
- data/lib/ciri/evm/execution_context.rb +6 -2
- data/lib/ciri/evm/instruction.rb +0 -1
- data/lib/ciri/evm/op.rb +11 -5
- data/lib/ciri/evm/op/errors.rb +37 -0
- data/lib/ciri/evm/state.rb +1 -1
- data/lib/ciri/evm/sub_state.rb +6 -6
- data/lib/ciri/evm/vm.rb +53 -33
- data/lib/ciri/forks.rb +4 -0
- data/lib/ciri/forks/base.rb +28 -0
- data/lib/ciri/forks/byzantium.rb +45 -11
- data/lib/ciri/forks/byzantium/opcodes.rb +37 -0
- data/lib/ciri/forks/constantinople.rb +29 -0
- data/lib/ciri/forks/frontier.rb +49 -29
- data/lib/ciri/forks/frontier/cost.rb +91 -95
- data/lib/ciri/forks/frontier/opcodes.rb +99 -0
- data/lib/ciri/forks/frontier/transaction.rb +62 -0
- data/lib/ciri/forks/homestead.rb +31 -7
- data/lib/ciri/forks/homestead/opcodes.rb +37 -0
- data/lib/ciri/forks/homestead/transaction.rb +43 -0
- data/lib/ciri/forks/spurious_dragon.rb +55 -0
- data/lib/ciri/forks/spurious_dragon/cost.rb +91 -0
- data/lib/ciri/forks/spurious_dragon/transaction.rb +44 -0
- data/lib/ciri/forks/tangerine_whistle.rb +37 -0
- data/lib/ciri/forks/tangerine_whistle/cost.rb +98 -0
- data/lib/ciri/rlp/decode.rb +4 -4
- data/lib/ciri/rlp/encode.rb +2 -2
- data/lib/ciri/version.rb +1 -1
- metadata +22 -10
- data/lib/ciri/forks/homestead/cost.rb +0 -195
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 20618c56ec07fac482c96e6a4bd5cdb3f3bfb45402be5548e748994283246c99
|
4
|
+
data.tar.gz: d76e2848328eb6a42b4bd0705aaf20885a44cbb0b487b1d4a485fff92f0dd42a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 665f5f8f3d6437c24993c4e2f573b805886906a70f9a0fec5610eec0a5c870ce5d8d8b72558212b5600ba3c9f23f9a29287d2d53bdc85c91623caf8cc487dbc5
|
7
|
+
data.tar.gz: b16a58be1b92f544c2670a2c6956b2943d14f5f5cc1ffc05a3be3f148578049754a2bfc146372f20e493fb127bda8668c2e2174d304a82bb42cd27d26dc7b1e4
|
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ciri (0.0.
|
4
|
+
ciri (0.0.4)
|
5
5
|
bitcoin-secp256k1 (~> 0.4.0)
|
6
6
|
ciri-crypto (~> 0.1.1)
|
7
|
-
ciri-rlp (~> 0.
|
8
|
-
ciri-utils (~> 0.2.
|
7
|
+
ciri-rlp (~> 0.2.1)
|
8
|
+
ciri-utils (~> 0.2.1)
|
9
9
|
concurrent-ruby (~> 1.0.5)
|
10
10
|
ffi (~> 1.9.23)
|
11
11
|
lru_redux (~> 1.1.0)
|
@@ -13,9 +13,9 @@ PATH
|
|
13
13
|
ciri-crypto (0.1.1)
|
14
14
|
bitcoin-secp256k1 (~> 0.4.0)
|
15
15
|
ciri-utils (~> 0.2.0)
|
16
|
-
ciri-rlp (0.
|
17
|
-
ciri-utils (~> 0.2.
|
18
|
-
ciri-utils (0.2.
|
16
|
+
ciri-rlp (0.2.1)
|
17
|
+
ciri-utils (~> 0.2.1)
|
18
|
+
ciri-utils (0.2.1)
|
19
19
|
digest-sha3 (~> 1.1.0)
|
20
20
|
|
21
21
|
GEM
|
data/README.md
CHANGED
@@ -3,32 +3,79 @@ Ciri
|
|
3
3
|
[![Build Status](https://travis-ci.org/ciri-ethereum/ciri.svg?branch=master)](https://travis-ci.org/ciri-ethereum/ciri)
|
4
4
|
[![Gitter](https://badges.gitter.im/join.svg)](https://gitter.im/ciri-ethereum/Lobby)
|
5
5
|
|
6
|
-
Ciri
|
6
|
+
Ciri is an ongoing Ethereum implementation.
|
7
7
|
|
8
|
-
It aims to be a feature complete
|
9
|
-
As you see it'
|
8
|
+
It aims to be a feature complete Ethereum implementation and expects to achieve both research-friendly and high-performance.
|
9
|
+
As you see, it's still under development.
|
10
10
|
|
11
|
-
|
11
|
+
See [How to learn Ethereum and contribute to Ciri](https://github.com/ciri-ethereum/ciri/wiki#how-to-learn-ethereum-and-contribute-to-ciri).
|
12
12
|
|
13
13
|
See [projects](https://github.com/ciri-ethereum/ciri/projects) and [milestones](https://github.com/ciri-ethereum/ciri/milestones) for current development status.
|
14
14
|
|
15
15
|
Roadmap
|
16
16
|
---------------
|
17
17
|
|
18
|
-
See [
|
19
|
-
|
20
|
-
Documentation
|
21
|
-
---------------
|
22
|
-
|
23
|
-
[YARD documentation](https://www.rubydoc.info/github/ciri-ethereum/ciri/master)
|
18
|
+
See [Roadmap](https://github.com/ciri-ethereum/ciri/wiki) on Wiki
|
24
19
|
|
25
20
|
Development
|
26
21
|
---------------
|
27
22
|
|
28
23
|
Ciri depends on [rocksdb](https://github.com/facebook/rocksdb), [secp256k1](https://github.com/bitcoin-core/secp256k1), [ethash](https://github.com/ethereum/ethash) and [snappy](https://github.com/google/snappy).
|
29
24
|
|
30
|
-
It's recommendation to [setup with docker](#setup-with-docker), it will save lots of time.
|
25
|
+
It's a recommendation to [setup with docker](#setup-with-docker), it will help to save lots of time.
|
26
|
+
|
27
|
+
### Setup with docker
|
28
|
+
|
29
|
+
Use docker command to pull image:
|
30
|
+
|
31
|
+
``` bash
|
32
|
+
docker pull ciriethereum/ciri
|
33
|
+
```
|
34
|
+
|
35
|
+
Or you can use our prepared rake tasks if you're not familiar with docker:
|
36
|
+
|
37
|
+
clone repo and submodules
|
38
|
+
|
39
|
+
``` bash
|
40
|
+
git clone --recursive https://github.com/ciri-ethereum/ciri.git
|
41
|
+
cd ciri
|
42
|
+
```
|
43
|
+
|
44
|
+
make sure we have installed docker, ruby and rake
|
45
|
+
``` bash
|
46
|
+
# make sure we have installed docker, ruby and rake
|
47
|
+
docker -v
|
48
|
+
gem install rake
|
49
|
+
```
|
50
|
+
|
51
|
+
#### Pull docker image
|
52
|
+
|
53
|
+
``` bash
|
54
|
+
# pull Ciri docker image
|
55
|
+
rake docker:pull
|
56
|
+
```
|
57
|
+
|
58
|
+
#### Build docker image
|
59
|
+
(it will take a few minutes)
|
60
|
+
``` bash
|
61
|
+
# build Ciri docker image
|
62
|
+
rake docker:build
|
63
|
+
```
|
64
|
+
|
65
|
+
#### Run tests in docker
|
66
|
+
``` bash
|
67
|
+
# run tests
|
68
|
+
rake docker:quick
|
69
|
+
```
|
70
|
+
|
71
|
+
#### Other usages
|
72
|
+
``` bash
|
73
|
+
# open a shell for developing
|
74
|
+
rake docker:shell
|
31
75
|
|
76
|
+
# type 'rake -T' see other supported tasks
|
77
|
+
rake -T
|
78
|
+
```
|
32
79
|
|
33
80
|
### Manually Setup
|
34
81
|
|
@@ -40,7 +87,7 @@ git clone --recursive https://github.com/ciri-ethereum/ciri.git
|
|
40
87
|
|
41
88
|
#### Install dependencies
|
42
89
|
|
43
|
-
On mac you can install `rocksdb` and `snappy` with homebrew
|
90
|
+
On a mac you can install `rocksdb` and `snappy` with homebrew
|
44
91
|
|
45
92
|
``` bash
|
46
93
|
brew install rocksdb snappy
|
@@ -58,7 +105,7 @@ secp256k1
|
|
58
105
|
cd secp256k1 && ./autogen.sh && ./configure --enable-module-recovery --enable-experimental --enable-module-ecdh && make && make install
|
59
106
|
```
|
60
107
|
|
61
|
-
For linux
|
108
|
+
For linux users, remember to check [Dockerfile](/docker) instructions for hint.
|
62
109
|
|
63
110
|
then run:
|
64
111
|
``` bash
|
@@ -67,49 +114,22 @@ bundle install
|
|
67
114
|
|
68
115
|
run tests:
|
69
116
|
``` bash
|
70
|
-
RUBY_THREAD_VM_STACK_SIZE=52428800 bundle exec rake
|
117
|
+
RUBY_THREAD_VM_STACK_SIZE=52428800 bundle exec rake quick
|
71
118
|
```
|
72
119
|
|
120
|
+
Why Ruby?
|
121
|
+
---------------
|
73
122
|
|
74
|
-
|
123
|
+
> Because Ruby has built-in block support!
|
75
124
|
|
76
|
-
|
125
|
+
Seriously,
|
77
126
|
|
78
|
-
|
79
|
-
``` bash
|
80
|
-
# make sure we have installed docker, ruby and rake
|
81
|
-
docker -v
|
82
|
-
gem install rake
|
83
|
-
```
|
127
|
+
Ruby is a scripting language which makes it easy to write prototype or research code (like the official python Ethereum implementation intended).
|
84
128
|
|
85
|
-
|
86
|
-
pull latest released Ciri docker image
|
87
|
-
``` bash
|
88
|
-
# pull Ciri docker image
|
89
|
-
rake docker:pull
|
90
|
-
```
|
91
|
-
|
92
|
-
#### Build docker image
|
93
|
-
build local docker image (it will take few minutes)
|
94
|
-
``` bash
|
95
|
-
# build Ciri docker image
|
96
|
-
rake docker:build
|
97
|
-
```
|
129
|
+
According to the several performance research projects in Ruby community (JIT, JRuby, TruffleRuby), we are highly looking forward to seeing improvement of this language performance in the future.
|
98
130
|
|
99
|
-
|
100
|
-
``` bash
|
101
|
-
# run tests
|
102
|
-
rake docker:test
|
103
|
-
```
|
131
|
+
Due to Ruby, we could expect to achieve both research-friendly and high-performance in our implementation.
|
104
132
|
|
105
|
-
#### Other usage
|
106
|
-
``` bash
|
107
|
-
# open a shell for develop
|
108
|
-
rake docker:shell
|
109
|
-
|
110
|
-
# cool, type 'rake -T' see other supported tasks
|
111
|
-
rake -T
|
112
|
-
```
|
113
133
|
|
114
134
|
Contributors
|
115
135
|
---------------
|
data/Rakefile
CHANGED
@@ -14,8 +14,8 @@ end
|
|
14
14
|
|
15
15
|
SUB_COMPONENTS = %w{ciri-utils ciri-rlp ciri-crypto}
|
16
16
|
|
17
|
-
desc 'run
|
18
|
-
task :
|
17
|
+
desc 'run quick spec'
|
18
|
+
task :quick do
|
19
19
|
exit(1) unless check_env
|
20
20
|
run("rspec -t ~slow_tests")
|
21
21
|
SUB_COMPONENTS.each do |dir|
|
@@ -23,8 +23,8 @@ task :test do
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
desc 'run all
|
27
|
-
task :"
|
26
|
+
desc 'run all specs, include extreme slow tests'
|
27
|
+
task :"spec:all" do
|
28
28
|
exit(1) unless check_env
|
29
29
|
run("rspec")
|
30
30
|
SUB_COMPONENTS.each do |dir|
|
@@ -33,7 +33,7 @@ task :"test:all" do
|
|
33
33
|
end
|
34
34
|
|
35
35
|
namespace :docker do
|
36
|
-
base_image = 'ciriethereum/
|
36
|
+
base_image = 'ciriethereum/ciri'
|
37
37
|
|
38
38
|
desc 'pull docker image'
|
39
39
|
task :pull do
|
@@ -46,6 +46,11 @@ namespace :docker do
|
|
46
46
|
run("docker build . -f docker/Dockerfile -t #{base_image}:latest")
|
47
47
|
end
|
48
48
|
|
49
|
+
desc 'push docker image'
|
50
|
+
task :push do
|
51
|
+
run("docker push #{base_image}:latest")
|
52
|
+
end
|
53
|
+
|
49
54
|
desc 'open Ciri develop container shell'
|
50
55
|
task :shell do
|
51
56
|
container_name = 'ciri-develop'
|
@@ -57,14 +62,14 @@ namespace :docker do
|
|
57
62
|
end
|
58
63
|
end
|
59
64
|
|
60
|
-
desc 'run
|
61
|
-
task :
|
62
|
-
run("docker run -v `pwd`:/app --rm #{base_image}:latest rake
|
65
|
+
desc 'run quick specs in docker'
|
66
|
+
task :quick do
|
67
|
+
run("docker run -v `pwd`:/app --rm #{base_image}:latest rake quick")
|
63
68
|
end
|
64
69
|
|
65
|
-
desc 'run all
|
66
|
-
task :"
|
67
|
-
run("docker run -v `pwd`:/app --rm #{base_image}:latest rake
|
70
|
+
desc 'run all specs(include slow tests) in docker'
|
71
|
+
task :"spec:all" do
|
72
|
+
run("docker run -v `pwd`:/app --rm #{base_image}:latest rake spec:all")
|
68
73
|
end
|
69
74
|
|
70
75
|
private
|
@@ -50,20 +50,22 @@ module Ciri
|
|
50
50
|
|
51
51
|
def signature
|
52
52
|
@signature ||= Utils.big_endian_encode(@r, "\x00".b, size: 32) +
|
53
|
-
|
54
|
-
|
53
|
+
Utils.big_endian_encode(@s, "\x00".b, size: 32) +
|
54
|
+
Utils.big_endian_encode(@v, "\x00".b)
|
55
55
|
end
|
56
56
|
|
57
57
|
alias to_s signature
|
58
58
|
|
59
59
|
def valid?
|
60
60
|
v <= 1 &&
|
61
|
-
|
62
|
-
|
61
|
+
r < SECP256K1N && r >= 1 &&
|
62
|
+
s < SECP256K1N && s >= 1
|
63
63
|
end
|
64
64
|
|
65
|
+
# high s value is invalid after Homestead fork
|
66
|
+
# see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md#specification
|
65
67
|
def low_s?
|
66
|
-
s
|
68
|
+
s <= (SECP256K1N / 2)
|
67
69
|
end
|
68
70
|
end
|
69
71
|
|
data/ciri-rlp/ciri-rlp.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
|
-
spec.add_dependency "ciri-utils", "~> 0.2.
|
24
|
+
spec.add_dependency "ciri-utils", "~> 0.2.1"
|
25
25
|
spec.add_development_dependency "bundler", "~> 1.16"
|
26
26
|
spec.add_development_dependency "rake", "~> 12.0"
|
27
27
|
spec.add_development_dependency "rspec", "~> 3.0"
|
data/ciri-rlp/lib/ciri/rlp.rb
CHANGED
@@ -44,7 +44,7 @@ module Ciri
|
|
44
44
|
if type == Integer
|
45
45
|
item = s.read(1)
|
46
46
|
if item.nil?
|
47
|
-
raise
|
47
|
+
raise InvalidError.new "invalid bool value nil"
|
48
48
|
elsif item == "\x80".b || item.empty?
|
49
49
|
0
|
50
50
|
elsif item.ord < 0x80
|
@@ -60,7 +60,7 @@ module Ciri
|
|
60
60
|
elsif item == Bool::ENCODED_FALSE
|
61
61
|
false
|
62
62
|
else
|
63
|
-
raise
|
63
|
+
raise InvalidError.new "invalid bool value #{item}"
|
64
64
|
end
|
65
65
|
elsif type.is_a?(Class) && type.respond_to?(:rlp_decode)
|
66
66
|
type.rlp_decode(s)
|
@@ -73,10 +73,18 @@ module Ciri
|
|
73
73
|
i += 1
|
74
74
|
end
|
75
75
|
end
|
76
|
+
elsif type == RawString
|
77
|
+
str = decode_stream(s)
|
78
|
+
raise RLP::InvalidError.new "decode #{str.class} from RawString" unless str.is_a?(String)
|
79
|
+
str
|
80
|
+
elsif type == RawList
|
81
|
+
list = decode_stream(s)
|
82
|
+
raise RLP::InvalidError.new "decode #{list.class} from RawList" unless list.is_a?(Array)
|
83
|
+
list
|
76
84
|
elsif type == Raw
|
77
85
|
decode_stream(s)
|
78
86
|
else
|
79
|
-
raise RLP::
|
87
|
+
raise RLP::InvalidError.new "unknown type #{type}"
|
80
88
|
end
|
81
89
|
rescue
|
82
90
|
STDERR.puts "when decoding #{s} into #{type}"
|
@@ -89,7 +97,6 @@ module Ciri
|
|
89
97
|
s = StringIO.new(s) if s.is_a?(String)
|
90
98
|
c = first_char || s.read(1)
|
91
99
|
list = []
|
92
|
-
|
93
100
|
sub_s = case c.ord
|
94
101
|
when 0xc0..0xf7
|
95
102
|
length = c.ord - 0xc0
|
@@ -97,11 +104,13 @@ module Ciri
|
|
97
104
|
when 0xf8..0xff
|
98
105
|
length_binary = s.read(c.ord - 0xf7)
|
99
106
|
length = int_from_binary(length_binary)
|
100
|
-
s
|
107
|
+
check_range_and_read(s, length)
|
101
108
|
else
|
102
|
-
raise
|
109
|
+
raise InvalidError.new("invalid char #{c}")
|
103
110
|
end
|
104
111
|
|
112
|
+
raise InvalidError.new("stream EOF when encode_list") unless sub_s
|
113
|
+
|
105
114
|
decoder.call(list, StringIO.new(sub_s))
|
106
115
|
list
|
107
116
|
end
|
@@ -110,6 +119,7 @@ module Ciri
|
|
110
119
|
|
111
120
|
def decode_stream(s)
|
112
121
|
c = s.read(1)
|
122
|
+
raise InvalidError.new("read none char from stream") unless c
|
113
123
|
case c.ord
|
114
124
|
when 0x00..0x7f
|
115
125
|
c
|
@@ -119,7 +129,7 @@ module Ciri
|
|
119
129
|
when 0xb8..0xbf
|
120
130
|
length_binary = s.read(c.ord - 0xb7)
|
121
131
|
length = int_from_binary(length_binary)
|
122
|
-
s
|
132
|
+
check_range_and_read(s, length)
|
123
133
|
else
|
124
134
|
decode_list(s, c) do |list, s2|
|
125
135
|
until s2.eof?
|
@@ -129,6 +139,12 @@ module Ciri
|
|
129
139
|
end
|
130
140
|
end
|
131
141
|
|
142
|
+
def check_range_and_read(s, length)
|
143
|
+
s.read(length)
|
144
|
+
rescue RangeError
|
145
|
+
raise InvalidError.new("length too long: #{length}")
|
146
|
+
end
|
147
|
+
|
132
148
|
def int_from_binary(input)
|
133
149
|
Ciri::Utils.big_endian_decode(input)
|
134
150
|
end
|
@@ -70,12 +70,18 @@ module Ciri
|
|
70
70
|
if type.size == 1 # array type
|
71
71
|
encode_list(item) {|i| encode_with_type(i, type[0])}
|
72
72
|
else # unknown
|
73
|
-
raise RLP::
|
73
|
+
raise RLP::InvalidError.new "type size should be 1, got #{type}"
|
74
74
|
end
|
75
75
|
elsif type == Raw
|
76
76
|
encode_raw(item)
|
77
|
+
elsif type == RawString
|
78
|
+
raise RLP::InvalidError.new "expect String, got #{item.class}" unless item.is_a?(String)
|
79
|
+
encode_raw(item)
|
80
|
+
elsif type == RawList
|
81
|
+
raise RLP::InvalidError.new "expect Array, got #{item.class}" unless item.is_a?(Array)
|
82
|
+
encode_raw(item)
|
77
83
|
else
|
78
|
-
raise RLP::
|
84
|
+
raise RLP::InvalidError.new "unknown type #{type}"
|
79
85
|
end
|
80
86
|
rescue
|
81
87
|
STDERR.puts "when encoding #{Utils.to_hex item.to_s} into #{type}"
|
@@ -30,6 +30,12 @@ module Ciri
|
|
30
30
|
class Raw
|
31
31
|
end
|
32
32
|
|
33
|
+
class RawString
|
34
|
+
end
|
35
|
+
|
36
|
+
class RawList
|
37
|
+
end
|
38
|
+
|
33
39
|
# Serializable module allow ruby objects serialize/deserialize to or from RLP encoding.
|
34
40
|
# See Ciri::RLP::Serializable::TYPES for supported type.
|
35
41
|
#
|
@@ -69,7 +75,7 @@ module Ciri
|
|
69
75
|
#
|
70
76
|
module Serializable
|
71
77
|
# nil represent RLP raw value(string or array of string)
|
72
|
-
TYPES = [Raw, Integer, Bool].map {|key| [key, true]}.to_h.freeze
|
78
|
+
TYPES = [Raw, RawString, RawList, Integer, Bool].map {|key| [key, true]}.to_h.freeze
|
73
79
|
|
74
80
|
# Schema specific columns types of classes, normally you should not use Serializable::Schema directly
|
75
81
|
#
|
@@ -176,9 +182,13 @@ module Ciri
|
|
176
182
|
end
|
177
183
|
|
178
184
|
def schema(data_schema = nil)
|
179
|
-
|
180
|
-
|
181
|
-
|
185
|
+
if data_schema
|
186
|
+
@data_schema = Schema.new(data_schema).tap do |schema|
|
187
|
+
# define attributes methods
|
188
|
+
define_attributes(schema)
|
189
|
+
end
|
190
|
+
else
|
191
|
+
@data_schema ||= superclass.schema
|
182
192
|
end
|
183
193
|
end
|
184
194
|
|