Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
YottaChain
YTBP
提交
133442b6
Y
YTBP
项目概览
YottaChain
/
YTBP
通知
0
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Y
YTBP
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
133442b6
编写于
8月 01, 2017
作者:
B
Bart Wyatt
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add naive scheduler to block production
add basic tests for scheduler
上级
867a5380
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
734 addition
and
32 deletion
+734
-32
libraries/chain/CMakeLists.txt
libraries/chain/CMakeLists.txt
+1
-0
libraries/chain/block_schedule.cpp
libraries/chain/block_schedule.cpp
+281
-0
libraries/chain/chain_controller.cpp
libraries/chain/chain_controller.cpp
+58
-32
libraries/chain/include/eos/chain/block_schedule.hpp
libraries/chain/include/eos/chain/block_schedule.hpp
+64
-0
tests/common/expect.hpp
tests/common/expect.hpp
+73
-0
tests/tests/block_schedule_tests.cpp
tests/tests/block_schedule_tests.cpp
+257
-0
未找到文件。
libraries/chain/CMakeLists.txt
浏览文件 @
133442b6
...
...
@@ -4,6 +4,7 @@ file(GLOB HEADERS "include/eos/chain/*.hpp")
add_library
(
eos_chain
chain_controller.cpp
wasm_interface.cpp
block_schedule.cpp
fork_database.cpp
...
...
libraries/chain/block_schedule.cpp
0 → 100644
浏览文件 @
133442b6
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <eos/chain/block_schedule.hpp>
namespace
eos
{
namespace
chain
{
static
uint
next_power_of_two
(
uint
input
)
{
if
(
input
==
0
)
{
return
0
;
}
uint
result
=
input
;
result
--
;
result
|=
result
>>
1
;
result
|=
result
>>
2
;
result
|=
result
>>
4
;
result
|=
result
>>
8
;
result
|=
result
>>
16
;
result
++
;
return
result
;
};
typedef
std
::
hash
<
decltype
(
AccountName
::
value
)
>
account_hash
;
static
account_hash
account_hasher
;
struct
schedule_entry
{
schedule_entry
(
uint
_cycle
,
uint
_thread
,
SignedTransaction
const
*
_transaction
)
:
cycle
(
_cycle
)
,
thread
(
_thread
)
,
transaction
(
_transaction
)
{}
uint
cycle
;
uint
thread
;
SignedTransaction
const
*
transaction
;
friend
bool
operator
<
(
schedule_entry
const
&
l
,
schedule_entry
const
&
r
)
{
if
(
l
.
cycle
<
r
.
cycle
)
{
return
true
;
}
else
if
(
l
.
cycle
==
r
.
cycle
)
{
return
l
.
thread
<
r
.
thread
;
}
return
false
;
}
};
static
block_schedule
from_entries
(
vector
<
schedule_entry
>
&
entries
)
{
// sort in reverse to save allocations, this should put the highest thread index
// for the highest cycle index first meaning the naive resize in the loop below
// is usually the largest and only resize
auto
reverse
=
[](
schedule_entry
const
&
l
,
schedule_entry
const
&
r
)
{
return
!
(
l
<
r
);
};
std
::
sort
(
entries
.
begin
(),
entries
.
end
(),
reverse
);
block_schedule
result
;
for
(
auto
const
&
entry
:
entries
)
{
if
(
result
.
cycles
.
size
()
<=
entry
.
cycle
)
{
result
.
cycles
.
resize
(
entry
.
cycle
+
1
);
}
auto
&
cycle
=
result
.
cycles
.
at
(
entry
.
cycle
);
if
(
cycle
.
size
()
<=
entry
.
thread
)
{
cycle
.
resize
(
entry
.
thread
+
1
);
}
// because we are traversing the schedule in reverse to save
// allocations, we cannot emplace_back as that would reverse
// the transactions in a thread
auto
&
thread
=
cycle
.
at
(
entry
.
thread
);
thread
.
user_input
.
emplace
(
thread
.
user_input
.
begin
(),
entry
.
transaction
);
}
return
result
;
}
template
<
typename
CONTAINER
>
auto
initialize_pointer_vector
(
CONTAINER
const
&
c
)
{
vector
<
SignedTransaction
const
*>
result
;
result
.
reserve
(
c
.
size
());
for
(
auto
const
&
t
:
c
)
{
result
.
emplace_back
(
&
t
);
}
return
result
;
}
struct
block_size_skipper
{
size_t
current_size
;
size_t
const
max_size
;
bool
should_skip
(
SignedTransaction
const
*
t
)
const
{
size_t
transaction_size
=
fc
::
raw
::
pack_size
(
*
t
);
// postpone transaction if it would make block too big
if
(
transaction_size
+
current_size
>
max_size
)
{
return
true
;
}
else
{
return
false
;
}
}
void
apply
(
SignedTransaction
const
*
t
)
{
size_t
transaction_size
=
fc
::
raw
::
pack_size
(
*
t
);
current_size
+=
transaction_size
;
}
};
auto
make_skipper
(
const
global_property_object
&
properties
)
{
static
const
size_t
max_block_header_size
=
fc
::
raw
::
pack_size
(
signed_block_header
()
)
+
4
;
auto
maximum_block_size
=
properties
.
configuration
.
maxBlockSize
;
return
block_size_skipper
{
max_block_header_size
,
(
size_t
)
maximum_block_size
};
}
block_schedule
block_schedule
::
by_threading_conflicts
(
deque
<
SignedTransaction
>
const
&
transactions
,
const
global_property_object
&
properties
)
{
static
uint
const
MAX_TXS_PER_THREAD
=
4
;
auto
skipper
=
make_skipper
(
properties
);
uint
HASH_SIZE
=
std
::
max
<
uint
>
(
4096
,
next_power_of_two
(
transactions
.
size
()
/
8
));
vector
<
optional
<
uint
>>
assigned_threads
(
HASH_SIZE
);
vector
<
schedule_entry
>
schedule
;
schedule
.
reserve
(
transactions
.
size
());
auto
current
=
initialize_pointer_vector
(
transactions
);
vector
<
SignedTransaction
const
*>
postponed
;
postponed
.
reserve
(
transactions
.
size
());
vector
<
uint
>
txs_per_thread
;
txs_per_thread
.
reserve
(
HASH_SIZE
);
int
cycle
=
0
;
bool
scheduled
=
true
;
while
(
scheduled
)
{
scheduled
=
false
;
uint
next_thread
=
0
;
for
(
auto
t
:
current
)
{
// skip ?
if
(
skipper
.
should_skip
(
t
))
{
continue
;
}
auto
assigned_to
=
optional
<
uint
>
();
bool
postpone
=
false
;
for
(
const
auto
&
a
:
t
->
scope
)
{
uint
hash_index
=
account_hasher
(
a
)
%
HASH_SIZE
;
if
(
assigned_to
&&
assigned_threads
[
hash_index
]
&&
assigned_to
!=
assigned_threads
[
hash_index
])
{
postpone
=
true
;
postponed
.
push_back
(
t
);
break
;
}
if
(
assigned_threads
[
hash_index
])
{
assigned_to
=
assigned_threads
[
hash_index
];
}
}
if
(
!
postpone
)
{
if
(
!
assigned_to
)
{
assigned_to
=
next_thread
++
;
txs_per_thread
.
resize
(
next_thread
);
}
if
(
txs_per_thread
[
*
assigned_to
]
<
MAX_TXS_PER_THREAD
)
{
for
(
const
auto
&
a
:
t
->
scope
)
{
uint
hash_index
=
account_hasher
(
a
)
%
HASH_SIZE
;
assigned_threads
[
hash_index
]
=
assigned_to
;
}
txs_per_thread
[
*
assigned_to
]
++
;
schedule
.
emplace_back
(
cycle
,
*
assigned_to
,
t
);
scheduled
=
true
;
skipper
.
apply
(
t
);
}
else
{
postponed
.
push_back
(
t
);
}
}
}
current
.
resize
(
0
);
txs_per_thread
.
resize
(
0
);
assigned_threads
.
resize
(
0
);
assigned_threads
.
resize
(
HASH_SIZE
);
std
::
swap
(
current
,
postponed
);
++
cycle
;
}
return
from_entries
(
schedule
);
}
block_schedule
block_schedule
::
by_cycling_conflicts
(
deque
<
SignedTransaction
>
const
&
transactions
,
const
global_property_object
&
properties
)
{
auto
skipper
=
make_skipper
(
properties
);
uint
HASH_SIZE
=
std
::
max
<
uint
>
(
4096
,
next_power_of_two
(
transactions
.
size
()
/
8
));
vector
<
schedule_entry
>
schedule
;
schedule
.
reserve
(
transactions
.
size
());
auto
current
=
initialize_pointer_vector
(
transactions
);
vector
<
SignedTransaction
const
*>
postponed
;
postponed
.
reserve
(
transactions
.
size
());
int
cycle
=
0
;
vector
<
bool
>
used
(
HASH_SIZE
);
bool
scheduled
=
true
;
while
(
scheduled
)
{
scheduled
=
false
;
int
thread
=
0
;
for
(
auto
t
:
current
)
{
// skip ?
if
(
skipper
.
should_skip
(
t
))
{
continue
;
}
bool
u
=
false
;
for
(
const
auto
&
a
:
t
->
scope
)
{
uint
hash_index
=
account_hasher
(
a
)
%
HASH_SIZE
;
if
(
used
[
hash_index
])
{
u
=
true
;
postponed
.
push_back
(
t
);
break
;
}
}
if
(
!
u
)
{
for
(
const
auto
&
a
:
t
->
scope
)
{
uint
hash_index
=
account_hasher
(
a
)
%
HASH_SIZE
;
used
[
hash_index
]
=
true
;
}
schedule
.
emplace_back
(
cycle
,
thread
++
,
t
);
scheduled
=
true
;
skipper
.
apply
(
t
);
}
}
current
.
resize
(
0
);
used
.
resize
(
0
);
used
.
resize
(
HASH_SIZE
);
std
::
swap
(
current
,
postponed
);
++
cycle
;
}
return
from_entries
(
schedule
);
}
}
/* namespace chain */
}
/* namespace eos */
libraries/chain/chain_controller.cpp
浏览文件 @
133442b6
...
...
@@ -26,6 +26,7 @@
#include <eos/chain/exceptions.hpp>
#include <eos/chain/block_summary_object.hpp>
#include <eos/chain/block_schedule.hpp>
#include <eos/chain/global_property_object.hpp>
#include <eos/chain/key_value_object.hpp>
#include <eos/chain/action_objects.hpp>
...
...
@@ -292,11 +293,7 @@ signed_block chain_controller::_generate_block(
if
(
!
(
skip
&
skip_producer_signature
)
)
FC_ASSERT
(
producer_obj
.
signing_key
==
block_signing_private_key
.
get_public_key
()
);
static
const
size_t
max_block_header_size
=
fc
::
raw
::
pack_size
(
signed_block_header
()
)
+
4
;
auto
maximum_block_size
=
get_global_properties
().
configuration
.
maxBlockSize
;
size_t
total_block_size
=
max_block_header_size
;
signed_block
pending_block
;
auto
schedule
=
block_schedule
::
by_threading_conflicts
(
_pending_transactions
,
get_global_properties
());
//
// The following code throws away existing pending_tx_session and
...
...
@@ -312,45 +309,74 @@ signed_block chain_controller::_generate_block(
_pending_tx_session
.
reset
();
_pending_tx_session
=
_db
.
start_undo_session
(
true
);
uint64_t
postponed_tx_count
=
0
;
// pop pending state (reset to head block state)
for
(
const
SignedTransaction
&
tx
:
_pending_transactions
)
{
size_t
new_total_size
=
total_block_size
+
fc
::
raw
::
pack_size
(
tx
);
// postpone transaction if it would make block too big
if
(
new_total_size
>=
maximum_block_size
)
{
postponed_tx_count
++
;
continue
;
}
auto
process_one
=
[
this
](
SignedTransaction
const
*
tx
,
database
&
db
)
->
optional
<
ProcessedTransaction
>
{
try
{
auto
temp_session
=
_db
.
start_undo_session
(
true
);
_apply_transaction
(
tx
);
temp_session
.
squash
();
total_block_size
+=
fc
::
raw
::
pack_size
(
tx
);
if
(
pending_block
.
cycles
.
empty
())
{
pending_block
.
cycles
.
resize
(
1
);
pending_block
.
cycles
.
back
().
resize
(
1
);
}
pending_block
.
cycles
.
back
().
back
().
user_input
.
emplace_back
(
tx
);
#warning TODO: Populate generated blocks with generated transactions
auto
temp_session
=
db
.
start_undo_session
(
true
);
auto
ptx
=
_apply_transaction
(
*
tx
);
temp_session
.
squash
();
return
optional
<
ProcessedTransaction
>
(
ptx
);
}
catch
(
const
fc
::
exception
&
e
)
{
// Do nothing, transaction will not be re-applied
wlog
(
"Transaction was not processed while generating block due to ${e}"
,
(
"e"
,
e
)
);
wlog
(
"The transaction was ${t}"
,
(
"t"
,
tx
)
);
// Do nothing, transaction will not be re-applied
wlog
(
"Transaction was not processed while generating block due to ${e}"
,
(
"e"
,
e
)
);
wlog
(
"The transaction was ${t}"
,
(
"t"
,
*
tx
)
);
}
return
optional
<
ProcessedTransaction
>
();
};
signed_block
pending_block
;
pending_block
.
cycles
.
reserve
(
schedule
.
cycles
.
size
());
size_t
invalid_transaction_count
=
0
;
size_t
valid_transaction_count
=
0
;
for
(
const
auto
&
c
:
schedule
.
cycles
)
{
cycle
block_cycle
;
block_cycle
.
reserve
(
c
.
size
());
for
(
const
auto
&
t
:
c
)
{
thread
block_thread
;
block_thread
.
generated_input
.
reserve
(
t
.
generated_input
.
size
());
for
(
const
auto
&
trx
:
t
.
generated_input
)
{
#warning TODO: Process generated transaction
}
block_thread
.
user_input
.
reserve
(
t
.
user_input
.
size
());
for
(
const
auto
&
trx
:
t
.
user_input
)
{
auto
processed
=
process_one
(
trx
,
_db
);
if
(
processed
)
{
block_thread
.
user_input
.
emplace_back
(
*
processed
);
valid_transaction_count
++
;
}
else
{
invalid_transaction_count
++
;
}
}
if
(
!
(
block_thread
.
generated_input
.
empty
()
&&
block_thread
.
user_input
.
empty
()))
{
block_cycle
.
emplace_back
(
std
::
move
(
block_thread
));
}
}
if
(
!
block_cycle
.
empty
())
{
pending_block
.
cycles
.
emplace_back
(
std
::
move
(
block_cycle
));
}
}
size_t
postponed_tx_count
=
_pending_transactions
.
size
()
-
valid_transaction_count
-
invalid_transaction_count
;
if
(
postponed_tx_count
>
0
)
{
wlog
(
"Postponed ${n} transactions due to block size limit"
,
(
"n"
,
postponed_tx_count
)
);
}
if
(
invalid_transaction_count
>
0
)
{
wlog
(
"Postponed ${n} transactions errors when processing"
,
(
"n"
,
invalid_transaction_count
)
);
}
_pending_tx_session
.
reset
();
// We have temporarily broken the invariant that
...
...
libraries/chain/include/eos/chain/block_schedule.hpp
0 → 100644
浏览文件 @
133442b6
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <eos/chain/chain_controller.hpp>
#include <eos/chain/transaction.hpp>
namespace
eos
{
namespace
chain
{
struct
thread_schedule
{
vector
<
generated_transaction_id_type
>
generated_input
;
vector
<
SignedTransaction
const
*>
user_input
;
};
using
cycle_schedule
=
vector
<
thread_schedule
>
;
/**
* @class block_schedule
* @brief represents a proposed order of execution for a generated block
*/
struct
block_schedule
{
vector
<
cycle_schedule
>
cycles
;
// Algorithms
/**
* A greedy scheduler that attempts to make short threads to resolve scope contention before
* falling back on cycles
* @return the block scheduler
*/
static
block_schedule
by_threading_conflicts
(
deque
<
SignedTransaction
>
const
&
transactions
,
const
global_property_object
&
properties
);
/**
* A greedy scheduler that attempts uses future cycles to resolve scope contention
* @return the block scheduler
*/
static
block_schedule
by_cycling_conflicts
(
deque
<
SignedTransaction
>
const
&
transactions
,
const
global_property_object
&
properties
);
};
}
}
// eos::chain
FC_REFLECT
(
eos
::
chain
::
thread_schedule
,
(
generated_input
)(
user_input
)
)
FC_REFLECT
(
eos
::
chain
::
block_schedule
,
(
cycles
))
tests/common/expect.hpp
0 → 100644
浏览文件 @
133442b6
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
namespace
eos
{
namespace
test
{
template
<
typename
PRED
,
typename
EVAL
,
typename
T
>
struct
expector
{
expector
(
EVAL
_eval
,
T
const
&
_expected
,
char
const
*
const
_msg
)
:
expected
(
_expected
)
,
eval
(
_eval
)
,
msg
(
_msg
)
{}
template
<
typename
INPUT
>
void
operator
()
(
INPUT
const
&
input
)
{
BOOST_TEST_INFO
(
msg
);
auto
actual
=
eval
(
input
);
BOOST_CHECK_EQUAL
(
PRED
()(
actual
,
expected
),
true
);
}
T
expected
;
EVAL
eval
;
char
const
*
const
msg
;
};
template
<
typename
PRED
,
typename
EVAL
,
typename
T
>
auto
expect
(
EVAL
&&
eval
,
T
expected
,
char
const
*
const
msg
)
{
return
expector
<
PRED
,
EVAL
,
T
>
(
eval
,
expected
,
msg
);
}
template
<
typename
EVAL
,
typename
T
>
auto
expect
(
EVAL
&&
eval
,
T
expected
,
char
const
*
const
msg
)
{
return
expector
<
std
::
equal_to
<
T
>
,
EVAL
,
T
>
(
eval
,
expected
,
msg
);
}
template
<
typename
EVAL
>
auto
expect
(
EVAL
&&
eval
,
char
const
*
const
msg
)
{
return
expector
<
std
::
equal_to
<
bool
>
,
EVAL
,
bool
>
(
eval
,
true
,
msg
);
}
#define _NUM_ARGS(A,B,C,N, ...) N
#define NUM_ARGS(...) _NUM_ARGS(__VA_ARGS__, 3, 2, 1)
#define EXPECT(...) EXPECT_(NUM_ARGS(__VA_ARGS__),__VA_ARGS__)
#define EXPECT_(N, ...) EXPECT__(N, __VA_ARGS__)
#define EXPECT__(N, ...) EXPECT_##N(__VA_ARGS__)
#define EXPECT_3(P, E, T) eos::test::expect<P>(E,T,"EXPECT<" #P ">(" #E "," #T ")")
#define EXPECT_2(E, T) eos::test::expect(E,T,"EXPECT(" #E "," #T ")")
#define EXPECT_1(E) eos::test::expect(E,"EXPECT(" #E ")")
}}
\ No newline at end of file
tests/tests/block_schedule_tests.cpp
0 → 100644
浏览文件 @
133442b6
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <boost/test/unit_test.hpp>
#include <eos/chain/block_schedule.hpp>
#include "../common/expect.hpp"
using
namespace
eos
;
using
namespace
chain
;
/*
* Policy based Fixtures for chain properties
*/
class
common_fixture
{
public:
struct
test_transaction
{
test_transaction
(
std
::
initializer_list
<
AccountName
>
const
&
_scopes
)
:
scopes
(
_scopes
)
{
}
std
::
initializer_list
<
AccountName
>
const
&
scopes
;
};
protected:
auto
create_pending
(
std
::
initializer_list
<
test_transaction
>
const
&
transactions
)
{
std
::
deque
<
SignedTransaction
>
result
;
for
(
auto
const
&
t
:
transactions
)
{
SignedTransaction
st
;
st
.
scope
.
reserve
(
t
.
scopes
.
size
());
st
.
scope
.
insert
(
st
.
scope
.
end
(),
t
.
scopes
.
begin
(),
t
.
scopes
.
end
());
result
.
emplace_back
(
st
);
}
return
result
;
}
};
template
<
typename
PROPERTIES_POLICY
>
class
compose_fixture
:
public
common_fixture
{
public:
template
<
typename
SCHED_FN
,
typename
...
VALIDATORS
>
void
schedule_and_validate
(
SCHED_FN
sched_fn
,
std
::
initializer_list
<
test_transaction
>
const
&
transactions
,
VALIDATORS
...
validators
)
{
try
{
auto
pending
=
create_pending
(
transactions
);
auto
schedule
=
sched_fn
(
pending
,
properties_policy
.
properties
);
validate
(
schedule
,
validators
...);
}
FC_LOG_AND_RETHROW
()
}
private:
template
<
typename
VALIDATOR
>
void
validate
(
block_schedule
const
&
schedule
,
VALIDATOR
validator
)
{
validator
(
schedule
);
}
template
<
typename
VALIDATOR
,
typename
...
VALIDATORS
>
void
validate
(
block_schedule
const
&
schedule
,
VALIDATOR
validator
,
VALIDATORS
...
others
)
{
validate
(
schedule
,
validator
);
validate
(
schedule
,
others
...);
}
PROPERTIES_POLICY
properties_policy
;
};
static
void
null_global_property_object_constructor
(
global_property_object
const
&
)
{}
static
chainbase
::
allocator
<
global_property_object
>
null_global_property_object_allocator
(
nullptr
);
struct
base_properties
{
base_properties
()
:
properties
(
null_global_property_object_constructor
,
null_global_property_object_allocator
)
{
}
global_property_object
properties
;
};
struct
default_properties
:
public
base_properties
{
default_properties
()
{
properties
.
configuration
.
maxBlockSize
=
256
*
1024
;
}
};
struct
small_block_properties
:
public
base_properties
{
small_block_properties
()
{
properties
.
configuration
.
maxBlockSize
=
512
;
}
};
typedef
compose_fixture
<
default_properties
>
default_fixture
;
/*
* Evaluators for expect
*/
static
uint
transaction_count
(
block_schedule
const
&
schedule
)
{
uint
result
=
0
;
for
(
auto
const
&
c
:
schedule
.
cycles
)
{
for
(
auto
const
&
t
:
c
)
{
result
+=
t
.
generated_input
.
size
();
result
+=
t
.
user_input
.
size
();
}
}
return
result
;
}
static
uint
cycle_count
(
block_schedule
const
&
schedule
)
{
return
schedule
.
cycles
.
size
();
}
static
bool
schedule_is_valid
(
block_schedule
const
&
schedule
)
{
for
(
auto
const
&
c
:
schedule
.
cycles
)
{
std
::
vector
<
bool
>
scope_in_use
;
for
(
auto
const
&
t
:
c
)
{
std
::
set
<
size_t
>
thread_bits
;
size_t
max_bit
=
0
;
for
(
auto
const
&
gi
:
t
.
generated_input
)
{
#warning TODO: Process generated transaction
}
for
(
auto
const
&
ui
:
t
.
user_input
)
{
for
(
auto
const
&
s
:
ui
->
scope
)
{
size_t
bit
=
boost
::
numeric_cast
<
size_t
>
((
uint64_t
)
s
);
thread_bits
.
emplace
(
bit
);
max_bit
=
std
::
max
(
max_bit
,
bit
);
}
}
if
(
scope_in_use
.
size
()
<=
max_bit
)
{
scope_in_use
.
resize
(
max_bit
+
1
);
}
for
(
auto
b
:
thread_bits
)
{
if
(
scope_in_use
.
at
(
b
))
{
return
false
;
};
scope_in_use
.
at
(
b
)
=
true
;
}
}
}
return
true
;
}
/*
* Test Cases
*/
BOOST_AUTO_TEST_SUITE
(
block_schedule_tests
)
BOOST_FIXTURE_TEST_CASE
(
no_conflicts
,
default_fixture
)
{
// ensure the scheduler can handle basic non-conflicted transactions
// in a single cycle
schedule_and_validate
(
block_schedule
::
by_threading_conflicts
,
{
{
0x1ULL
,
0x2ULL
},
{
0x3ULL
,
0x4ULL
},
{
0x5ULL
,
0x6ULL
},
{
0x7ULL
,
0x8ULL
},
{
0x9ULL
,
0xAULL
}
},
EXPECT
(
schedule_is_valid
),
EXPECT
(
transaction_count
,
5
),
EXPECT
(
cycle_count
,
1
)
);
}
BOOST_FIXTURE_TEST_CASE
(
some_conflicts
,
default_fixture
)
{
// ensure the scheduler can handle conflicted transactions
// using multiple cycles
schedule_and_validate
(
block_schedule
::
by_threading_conflicts
,
{
{
0x1ULL
,
0x2ULL
},
{
0x3ULL
,
0x2ULL
},
{
0x5ULL
,
0x1ULL
},
{
0x7ULL
,
0x1ULL
},
{
0x1ULL
,
0x7ULL
}
},
EXPECT
(
schedule_is_valid
),
EXPECT
(
transaction_count
,
5
),
EXPECT
(
std
::
greater
<
uint
>
,
cycle_count
,
1
)
);
}
BOOST_FIXTURE_TEST_CASE
(
basic_cycle
,
default_fixture
)
{
// ensure the scheduler can handle a basic scope cycle
schedule_and_validate
(
block_schedule
::
by_threading_conflicts
,
{
{
0x1ULL
,
0x2ULL
},
{
0x2ULL
,
0x3ULL
},
{
0x3ULL
,
0x1ULL
},
},
EXPECT
(
schedule_is_valid
),
EXPECT
(
transaction_count
,
3
)
);
}
BOOST_FIXTURE_TEST_CASE
(
small_block
,
compose_fixture
<
small_block_properties
>
)
{
// ensure the scheduler can handle basic block size restrictions
schedule_and_validate
(
block_schedule
::
by_threading_conflicts
,
{
{
0x1ULL
,
0x2ULL
},
{
0x3ULL
,
0x4ULL
},
{
0x5ULL
,
0x6ULL
},
{
0x7ULL
,
0x8ULL
},
{
0x9ULL
,
0xAULL
},
{
0xBULL
,
0xCULL
},
{
0xDULL
,
0xEULL
},
{
0x11ULL
,
0x12ULL
},
{
0x13ULL
,
0x14ULL
},
{
0x15ULL
,
0x16ULL
},
{
0x17ULL
,
0x18ULL
},
{
0x19ULL
,
0x1AULL
},
{
0x1BULL
,
0x1CULL
},
{
0x1DULL
,
0x1EULL
},
{
0x21ULL
,
0x22ULL
},
{
0x23ULL
,
0x24ULL
},
{
0x25ULL
,
0x26ULL
},
{
0x27ULL
,
0x28ULL
},
{
0x29ULL
,
0x2AULL
},
{
0x2BULL
,
0x2CULL
},
{
0x2DULL
,
0x2EULL
},
},
EXPECT
(
schedule_is_valid
),
EXPECT
(
std
::
less
<
uint
>
,
transaction_count
,
21
)
);
}
BOOST_AUTO_TEST_SUITE_END
()
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录