Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
whqwjb
go-ethereum
提交
e40b447f
G
go-ethereum
项目概览
whqwjb
/
go-ethereum
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
G
go-ethereum
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
e40b447f
编写于
9月 18, 2015
作者:
J
Jeffrey Wilcke
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #1814 from Gustav-Simonsson/common_tests
tests: update common test wrappers and test files
上级
b94b9b01
47ca6904
变更
8
展开全部
隐藏空白更改
内联
并排
Showing
8 changed file
with
13127 addition
and
439 deletion
+13127
-439
cmd/geth/blocktestcmd.go
cmd/geth/blocktestcmd.go
+9
-7
tests/block_test_util.go
tests/block_test_util.go
+73
-36
tests/files/BasicTests/difficulty.json
tests/files/BasicTests/difficulty.json
+12521
-0
tests/files/BlockchainTests/bcUncleTest.json
tests/files/BlockchainTests/bcUncleTest.json
+4
-4
tests/files/BlockchainTests/bcValidBlockTest.json
tests/files/BlockchainTests/bcValidBlockTest.json
+247
-1
tests/files/VMTests/vmArithmeticTest.json
tests/files/VMTests/vmArithmeticTest.json
+238
-388
tests/files/VMTests/vmIOandFlowOperationsTest.json
tests/files/VMTests/vmIOandFlowOperationsTest.json
+35
-0
tests/state_test_util.go
tests/state_test_util.go
+0
-3
未找到文件。
cmd/geth/blocktestcmd.go
浏览文件 @
e40b447f
...
...
@@ -111,25 +111,27 @@ func runOneBlockTest(ctx *cli.Context, test *tests.BlockTest) (*eth.Ethereum, er
if
err
!=
nil
{
return
nil
,
err
}
// if err := ethereum.Start(); err != nil {
// return nil, err
// }
// import the genesis block
ethereum
.
ResetWithGenesisBlock
(
test
.
Genesis
)
// import pre accounts
statedb
,
err
:
=
test
.
InsertPreState
(
ethereum
)
_
,
err
=
test
.
InsertPreState
(
ethereum
)
if
err
!=
nil
{
return
ethereum
,
fmt
.
Errorf
(
"InsertPreState: %v"
,
err
)
}
if
err
:=
test
.
TryBlocksInsert
(
ethereum
.
ChainManager
());
err
!=
nil
{
cm
:=
ethereum
.
ChainManager
()
validBlocks
,
err
:=
test
.
TryBlocksInsert
(
cm
)
if
err
!=
nil
{
return
ethereum
,
fmt
.
Errorf
(
"Block Test load error: %v"
,
err
)
}
if
err
:=
test
.
ValidatePostState
(
statedb
);
err
!=
nil
{
newDB
:=
cm
.
State
()
if
err
:=
test
.
ValidatePostState
(
newDB
);
err
!=
nil
{
return
ethereum
,
fmt
.
Errorf
(
"post state validation failed: %v"
,
err
)
}
return
ethereum
,
nil
return
ethereum
,
test
.
ValidateImportedHeaders
(
cm
,
validBlocks
)
}
tests/block_test_util.go
浏览文件 @
e40b447f
...
...
@@ -44,8 +44,10 @@ import (
type
BlockTest
struct
{
Genesis
*
types
.
Block
Json
*
btJSON
preAccounts
map
[
string
]
btAccount
Json
*
btJSON
preAccounts
map
[
string
]
btAccount
postAccounts
map
[
string
]
btAccount
lastblockhash
string
}
type
btJSON
struct
{
...
...
@@ -53,6 +55,7 @@ type btJSON struct {
GenesisBlockHeader
btHeader
Pre
map
[
string
]
btAccount
PostState
map
[
string
]
btAccount
Lastblockhash
string
}
type
btBlock
struct
{
...
...
@@ -76,6 +79,7 @@ type btHeader struct {
MixHash
string
Nonce
string
Number
string
Hash
string
ParentHash
string
ReceiptTrie
string
SeedHash
string
...
...
@@ -147,7 +151,6 @@ func runBlockTests(bt map[string]*BlockTest, skipTests []string) error {
glog
.
Infoln
(
"Skipping block test"
,
name
)
continue
}
// test the block
if
err
:=
runBlockTest
(
test
);
err
!=
nil
{
return
fmt
.
Errorf
(
"%s: %v"
,
name
,
err
)
...
...
@@ -173,20 +176,29 @@ func runBlockTest(test *BlockTest) error {
}
// import pre accounts
statedb
,
err
:
=
test
.
InsertPreState
(
ethereum
)
_
,
err
=
test
.
InsertPreState
(
ethereum
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"InsertPreState: %v"
,
err
)
}
err
=
test
.
TryBlocksInsert
(
ethereum
.
ChainManager
())
cm
:=
ethereum
.
ChainManager
()
validBlocks
,
err
:=
test
.
TryBlocksInsert
(
cm
)
if
err
!=
nil
{
return
err
}
if
err
=
test
.
ValidatePostState
(
statedb
);
err
!=
nil
{
lastblockhash
:=
common
.
HexToHash
(
test
.
lastblockhash
)
cmlast
:=
cm
.
LastBlockHash
()
if
lastblockhash
!=
cmlast
{
return
fmt
.
Errorf
(
"lastblockhash validation mismatch: want: %x, have: %x"
,
lastblockhash
,
cmlast
)
}
newDB
:=
cm
.
State
()
if
err
=
test
.
ValidatePostState
(
newDB
);
err
!=
nil
{
return
fmt
.
Errorf
(
"post state validation failed: %v"
,
err
)
}
return
nil
return
test
.
ValidateImportedHeaders
(
cm
,
validBlocks
)
}
func
(
test
*
BlockTest
)
makeEthConfig
()
*
eth
.
Config
{
...
...
@@ -264,7 +276,8 @@ func (t *BlockTest) InsertPreState(ethereum *eth.Ethereum) (*state.StateDB, erro
expected we are expected to ignore it and continue processing and then validate the
post state.
*/
func
(
t
*
BlockTest
)
TryBlocksInsert
(
chainManager
*
core
.
ChainManager
)
error
{
func
(
t
*
BlockTest
)
TryBlocksInsert
(
chainManager
*
core
.
ChainManager
)
([]
btBlock
,
error
)
{
validBlocks
:=
make
([]
btBlock
,
0
)
// insert the test blocks, which will execute all transactions
for
_
,
b
:=
range
t
.
Json
.
Blocks
{
cb
,
err
:=
mustConvertBlock
(
b
)
...
...
@@ -272,7 +285,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error {
if
b
.
BlockHeader
==
nil
{
continue
// OK - block is supposed to be invalid, continue with next block
}
else
{
return
fmt
.
Errorf
(
"Block RLP decoding failed when expected to succeed: %v"
,
err
)
return
nil
,
fmt
.
Errorf
(
"Block RLP decoding failed when expected to succeed: %v"
,
err
)
}
}
// RLP decoding worked, try to insert into chain:
...
...
@@ -281,100 +294,103 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error {
if
b
.
BlockHeader
==
nil
{
continue
// OK - block is supposed to be invalid, continue with next block
}
else
{
return
fmt
.
Errorf
(
"Block insertion into chain failed: %v"
,
err
)
return
nil
,
fmt
.
Errorf
(
"Block insertion into chain failed: %v"
,
err
)
}
}
if
b
.
BlockHeader
==
nil
{
return
fmt
.
Errorf
(
"Block insertion should have failed"
)
return
nil
,
fmt
.
Errorf
(
"Block insertion should have failed"
)
}
err
=
t
.
validateBlockHeader
(
b
.
BlockHeader
,
cb
.
Header
())
if
err
!=
nil
{
return
fmt
.
Errorf
(
"Block header validation failed: %v"
,
err
)
// validate RLP decoding by checking all values against test file JSON
if
err
=
validateHeader
(
b
.
BlockHeader
,
cb
.
Header
());
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"Deserialised block header validation failed: %v"
,
err
)
}
validBlocks
=
append
(
validBlocks
,
b
)
}
return
nil
return
validBlocks
,
nil
}
func
(
s
*
BlockTest
)
validateBlock
Header
(
h
*
btHeader
,
h2
*
types
.
Header
)
error
{
func
validate
Header
(
h
*
btHeader
,
h2
*
types
.
Header
)
error
{
expectedBloom
:=
mustConvertBytes
(
h
.
Bloom
)
if
!
bytes
.
Equal
(
expectedBloom
,
h2
.
Bloom
.
Bytes
())
{
return
fmt
.
Errorf
(
"Bloom:
expected: %v, decoded: %v
"
,
expectedBloom
,
h2
.
Bloom
.
Bytes
())
return
fmt
.
Errorf
(
"Bloom:
want: %x have: %x
"
,
expectedBloom
,
h2
.
Bloom
.
Bytes
())
}
expectedCoinbase
:=
mustConvertBytes
(
h
.
Coinbase
)
if
!
bytes
.
Equal
(
expectedCoinbase
,
h2
.
Coinbase
.
Bytes
())
{
return
fmt
.
Errorf
(
"Coinbase:
expected: %v, decoded: %v
"
,
expectedCoinbase
,
h2
.
Coinbase
.
Bytes
())
return
fmt
.
Errorf
(
"Coinbase:
want: %x have: %x
"
,
expectedCoinbase
,
h2
.
Coinbase
.
Bytes
())
}
expectedMixHashBytes
:=
mustConvertBytes
(
h
.
MixHash
)
if
!
bytes
.
Equal
(
expectedMixHashBytes
,
h2
.
MixDigest
.
Bytes
())
{
return
fmt
.
Errorf
(
"MixHash:
expected: %v, decoded: %v
"
,
expectedMixHashBytes
,
h2
.
MixDigest
.
Bytes
())
return
fmt
.
Errorf
(
"MixHash:
want: %x have: %x
"
,
expectedMixHashBytes
,
h2
.
MixDigest
.
Bytes
())
}
expectedNonce
:=
mustConvertBytes
(
h
.
Nonce
)
if
!
bytes
.
Equal
(
expectedNonce
,
h2
.
Nonce
[
:
])
{
return
fmt
.
Errorf
(
"Nonce:
expected: %v, decoded: %v
"
,
expectedNonce
,
h2
.
Nonce
)
return
fmt
.
Errorf
(
"Nonce:
want: %x have: %x
"
,
expectedNonce
,
h2
.
Nonce
)
}
expectedNumber
:=
mustConvertBigInt
(
h
.
Number
,
16
)
if
expectedNumber
.
Cmp
(
h2
.
Number
)
!=
0
{
return
fmt
.
Errorf
(
"Number:
expected: %v, decoded
: %v"
,
expectedNumber
,
h2
.
Number
)
return
fmt
.
Errorf
(
"Number:
want: %v have
: %v"
,
expectedNumber
,
h2
.
Number
)
}
expectedParentHash
:=
mustConvertBytes
(
h
.
ParentHash
)
if
!
bytes
.
Equal
(
expectedParentHash
,
h2
.
ParentHash
.
Bytes
())
{
return
fmt
.
Errorf
(
"Parent hash:
expected: %v, decoded: %v
"
,
expectedParentHash
,
h2
.
ParentHash
.
Bytes
())
return
fmt
.
Errorf
(
"Parent hash:
want: %x have: %x
"
,
expectedParentHash
,
h2
.
ParentHash
.
Bytes
())
}
expectedReceiptHash
:=
mustConvertBytes
(
h
.
ReceiptTrie
)
if
!
bytes
.
Equal
(
expectedReceiptHash
,
h2
.
ReceiptHash
.
Bytes
())
{
return
fmt
.
Errorf
(
"Receipt hash:
expected: %v, decoded: %v
"
,
expectedReceiptHash
,
h2
.
ReceiptHash
.
Bytes
())
return
fmt
.
Errorf
(
"Receipt hash:
want: %x have: %x
"
,
expectedReceiptHash
,
h2
.
ReceiptHash
.
Bytes
())
}
expectedTxHash
:=
mustConvertBytes
(
h
.
TransactionsTrie
)
if
!
bytes
.
Equal
(
expectedTxHash
,
h2
.
TxHash
.
Bytes
())
{
return
fmt
.
Errorf
(
"Tx hash:
expected: %v, decoded: %v
"
,
expectedTxHash
,
h2
.
TxHash
.
Bytes
())
return
fmt
.
Errorf
(
"Tx hash:
want: %x have: %x
"
,
expectedTxHash
,
h2
.
TxHash
.
Bytes
())
}
expectedStateHash
:=
mustConvertBytes
(
h
.
StateRoot
)
if
!
bytes
.
Equal
(
expectedStateHash
,
h2
.
Root
.
Bytes
())
{
return
fmt
.
Errorf
(
"State hash:
expected: %v, decoded: %v
"
,
expectedStateHash
,
h2
.
Root
.
Bytes
())
return
fmt
.
Errorf
(
"State hash:
want: %x have: %x
"
,
expectedStateHash
,
h2
.
Root
.
Bytes
())
}
expectedUncleHash
:=
mustConvertBytes
(
h
.
UncleHash
)
if
!
bytes
.
Equal
(
expectedUncleHash
,
h2
.
UncleHash
.
Bytes
())
{
return
fmt
.
Errorf
(
"Uncle hash:
expected: %v, decoded: %v
"
,
expectedUncleHash
,
h2
.
UncleHash
.
Bytes
())
return
fmt
.
Errorf
(
"Uncle hash:
want: %x have: %x
"
,
expectedUncleHash
,
h2
.
UncleHash
.
Bytes
())
}
expectedExtraData
:=
mustConvertBytes
(
h
.
ExtraData
)
if
!
bytes
.
Equal
(
expectedExtraData
,
h2
.
Extra
)
{
return
fmt
.
Errorf
(
"Extra data:
expected: %v, decoded: %v
"
,
expectedExtraData
,
h2
.
Extra
)
return
fmt
.
Errorf
(
"Extra data:
want: %x have: %x
"
,
expectedExtraData
,
h2
.
Extra
)
}
expectedDifficulty
:=
mustConvertBigInt
(
h
.
Difficulty
,
16
)
if
expectedDifficulty
.
Cmp
(
h2
.
Difficulty
)
!=
0
{
return
fmt
.
Errorf
(
"Difficulty:
expected: %v, decoded
: %v"
,
expectedDifficulty
,
h2
.
Difficulty
)
return
fmt
.
Errorf
(
"Difficulty:
want: %v have
: %v"
,
expectedDifficulty
,
h2
.
Difficulty
)
}
expectedGasLimit
:=
mustConvertBigInt
(
h
.
GasLimit
,
16
)
if
expectedGasLimit
.
Cmp
(
h2
.
GasLimit
)
!=
0
{
return
fmt
.
Errorf
(
"GasLimit:
expected: %v, decoded
: %v"
,
expectedGasLimit
,
h2
.
GasLimit
)
return
fmt
.
Errorf
(
"GasLimit:
want: %v have
: %v"
,
expectedGasLimit
,
h2
.
GasLimit
)
}
expectedGasUsed
:=
mustConvertBigInt
(
h
.
GasUsed
,
16
)
if
expectedGasUsed
.
Cmp
(
h2
.
GasUsed
)
!=
0
{
return
fmt
.
Errorf
(
"GasUsed:
expected: %v, decoded
: %v"
,
expectedGasUsed
,
h2
.
GasUsed
)
return
fmt
.
Errorf
(
"GasUsed:
want: %v have
: %v"
,
expectedGasUsed
,
h2
.
GasUsed
)
}
expectedTimestamp
:=
mustConvertBigInt
(
h
.
Timestamp
,
16
)
if
expectedTimestamp
.
Cmp
(
h2
.
Time
)
!=
0
{
return
fmt
.
Errorf
(
"Timestamp:
expected: %v, decoded
: %v"
,
expectedTimestamp
,
h2
.
Time
)
return
fmt
.
Errorf
(
"Timestamp:
want: %v have
: %v"
,
expectedTimestamp
,
h2
.
Time
)
}
return
nil
}
func
(
t
*
BlockTest
)
ValidatePostState
(
statedb
*
state
.
StateDB
)
error
{
for
addrString
,
acct
:=
range
t
.
preAccounts
{
// validate post state accounts in test file against what we have in state db
for
addrString
,
acct
:=
range
t
.
postAccounts
{
// XXX: is is worth it checking for errors here?
addr
,
err
:=
hex
.
DecodeString
(
addrString
)
if
err
!=
nil
{
...
...
@@ -398,13 +414,34 @@ func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error {
balance2
:=
statedb
.
GetBalance
(
common
.
BytesToAddress
(
addr
))
nonce2
:=
statedb
.
GetNonce
(
common
.
BytesToAddress
(
addr
))
if
!
bytes
.
Equal
(
code2
,
code
)
{
return
fmt
.
Errorf
(
"account code mismatch
, addr, found, expected: "
,
addrString
,
hex
.
EncodeToString
(
code2
),
hex
.
EncodeToString
(
code
))
return
fmt
.
Errorf
(
"account code mismatch
for addr: %s want: %s have: %s"
,
addrString
,
hex
.
EncodeToString
(
code
),
hex
.
EncodeToString
(
code2
))
}
if
balance2
.
Cmp
(
balance
)
!=
0
{
return
fmt
.
Errorf
(
"account balance mismatch
, addr, found, expected: "
,
addrString
,
balance2
,
balance
)
return
fmt
.
Errorf
(
"account balance mismatch
for addr: %s, want: %d, have: %d"
,
addrString
,
balance
,
balance2
)
}
if
nonce2
!=
nonce
{
return
fmt
.
Errorf
(
"account nonce mismatch, addr, found, expected: "
,
addrString
,
nonce2
,
nonce
)
return
fmt
.
Errorf
(
"account nonce mismatch for addr: %s want: %d have: %d"
,
addrString
,
nonce
,
nonce2
)
}
}
return
nil
}
func
(
test
*
BlockTest
)
ValidateImportedHeaders
(
cm
*
core
.
ChainManager
,
validBlocks
[]
btBlock
)
error
{
// to get constant lookup when verifying block headers by hash (some tests have many blocks)
bmap
:=
make
(
map
[
string
]
btBlock
,
len
(
test
.
Json
.
Blocks
))
for
_
,
b
:=
range
validBlocks
{
bmap
[
b
.
BlockHeader
.
Hash
]
=
b
}
// iterate over blocks backwards from HEAD and validate imported
// headers vs test file. some tests have reorgs, and we import
// block-by-block, so we can only validate imported headers after
// all blocks have been processed by ChainManager, as they may not
// be part of the longest chain until last block is imported.
for
b
:=
cm
.
CurrentBlock
();
b
!=
nil
&&
b
.
NumberU64
()
!=
0
;
b
=
cm
.
GetBlock
(
b
.
Header
()
.
ParentHash
)
{
bHash
:=
common
.
Bytes2Hex
(
b
.
Hash
()
.
Bytes
())
// hex without 0x prefix
if
err
:=
validateHeader
(
bmap
[
bHash
]
.
BlockHeader
,
b
.
Header
());
err
!=
nil
{
return
fmt
.
Errorf
(
"Imported block header validation failed: %v"
,
err
)
}
}
return
nil
...
...
@@ -432,7 +469,7 @@ func convertBlockTest(in *btJSON) (out *BlockTest, err error) {
err
=
fmt
.
Errorf
(
"%v
\n
%s"
,
recovered
,
buf
)
}
}()
out
=
&
BlockTest
{
preAccounts
:
in
.
Pre
,
Json
:
in
}
out
=
&
BlockTest
{
preAccounts
:
in
.
Pre
,
postAccounts
:
in
.
PostState
,
Json
:
in
,
lastblockhash
:
in
.
Lastblockhash
}
out
.
Genesis
=
mustConvertGenesis
(
in
.
GenesisBlockHeader
)
return
out
,
err
}
...
...
tests/files/BasicTests/difficulty.json
浏览文件 @
e40b447f
此差异已折叠。
点击以展开。
tests/files/BlockchainTests/bcUncleTest.json
浏览文件 @
e40b447f
...
...
@@ -4826,23 +4826,23 @@
"lastblockhash"
:
"eed1b4da708283370856fc76352d68f36d9766b7f366da372e2992ced9a1f663"
,
"postState"
:
{
"095e7baea6a6c7c4c2dfeb977efac326af552d87"
:
{
"balance"
:
"0x1
4
"
,
"balance"
:
"0x1
e
"
,
"code"
:
"0x"
,
"nonce"
:
"0x00"
,
"storage"
:
{
}
},
"8888f1f195afa192cfee860698584c030f4c9db1"
:
{
"balance"
:
"0x
8ac7230489e8a410
"
,
"balance"
:
"0x
d255d112e1049618
"
,
"code"
:
"0x"
,
"nonce"
:
"0x00"
,
"storage"
:
{
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b"
:
{
"balance"
:
"0x09184e71
fbdc
"
,
"balance"
:
"0x09184e71
a9ca
"
,
"code"
:
"0x"
,
"nonce"
:
"0x0
2
"
,
"nonce"
:
"0x0
3
"
,
"storage"
:
{
}
}
...
...
tests/files/BlockchainTests/bcValidBlockTest.json
浏览文件 @
e40b447f
此差异已折叠。
点击以展开。
tests/files/VMTests/vmArithmeticTest.json
浏览文件 @
e40b447f
此差异已折叠。
点击以展开。
tests/files/VMTests/vmIOandFlowOperationsTest.json
浏览文件 @
e40b447f
...
...
@@ -4845,6 +4845,41 @@
}
}
},
"sstore_underflow"
:
{
"env"
:
{
"currentCoinbase"
:
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
,
"currentDifficulty"
:
"0x0100"
,
"currentGasLimit"
:
"0x0f4240"
,
"currentNumber"
:
"0x00"
,
"currentTimestamp"
:
"0x01"
},
"exec"
:
{
"address"
:
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"
,
"caller"
:
"cd1722f3947def4cf144679da39c4c32bdc35681"
,
"code"
:
"0x600155"
,
"data"
:
"0x"
,
"gas"
:
"0x0186a0"
,
"gasPrice"
:
"0x5af3107a4000"
,
"origin"
:
"cd1722f3947def4cf144679da39c4c32bdc35681"
,
"value"
:
"0x0de0b6b3a7640000"
},
"expect"
:
{
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"
:
{
"storage"
:
{
"0x01"
:
"0x00"
}
}
},
"pre"
:
{
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"
:
{
"balance"
:
"0x152d02c7e14af6800000"
,
"code"
:
"0x600155"
,
"nonce"
:
"0x00"
,
"storage"
:
{
}
}
}
},
"stack_loop"
:
{
"callcreates"
:
[
],
...
...
tests/state_test_util.go
浏览文件 @
e40b447f
...
...
@@ -181,9 +181,6 @@ func runStateTest(test VmTest) error {
// check post state
for
addr
,
account
:=
range
test
.
Post
{
obj
:=
statedb
.
GetStateObject
(
common
.
HexToAddress
(
addr
))
if
obj
==
nil
{
continue
}
if
obj
.
Balance
()
.
Cmp
(
common
.
Big
(
account
.
Balance
))
!=
0
{
return
fmt
.
Errorf
(
"(%x) balance failed. Expected %v, got %v => %v
\n
"
,
obj
.
Address
()
.
Bytes
()[
:
4
],
account
.
Balance
,
obj
.
Balance
(),
new
(
big
.
Int
)
.
Sub
(
common
.
Big
(
account
.
Balance
),
obj
.
Balance
()))
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录