Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
whqwjb
go-ethereum
提交
9bd6068f
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,发现更多精彩内容 >>
未验证
提交
9bd6068f
编写于
8月 16, 2017
作者:
J
Jeffrey Wilcke
提交者:
Péter Szilágyi
8月 16, 2017
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
core/vm: implement RETURNDATA metropolis opcodes
上级
76069eef
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
253 addition
and
171 deletion
+253
-171
core/vm/gas_table.go
core/vm/gas_table.go
+26
-0
core/vm/instructions.go
core/vm/instructions.go
+23
-0
core/vm/interpreter.go
core/vm/interpreter.go
+10
-4
core/vm/jump_table.go
core/vm/jump_table.go
+34
-16
core/vm/memory_table.go
core/vm/memory_table.go
+4
-0
core/vm/opcodes.go
core/vm/opcodes.go
+156
-151
未找到文件。
core/vm/gas_table.go
浏览文件 @
9bd6068f
...
...
@@ -91,6 +91,32 @@ func gasCalldataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *St
return
gas
,
nil
}
func
gasReturnDataCopy
(
gt
params
.
GasTable
,
evm
*
EVM
,
contract
*
Contract
,
stack
*
Stack
,
mem
*
Memory
,
memorySize
uint64
)
(
uint64
,
error
)
{
gas
,
err
:=
memoryGasCost
(
mem
,
memorySize
)
if
err
!=
nil
{
return
0
,
err
}
var
overflow
bool
if
gas
,
overflow
=
math
.
SafeAdd
(
gas
,
GasFastestStep
);
overflow
{
return
0
,
errGasUintOverflow
}
words
,
overflow
:=
bigUint64
(
stack
.
Back
(
2
))
if
overflow
{
return
0
,
errGasUintOverflow
}
if
words
,
overflow
=
math
.
SafeMul
(
toWordSize
(
words
),
params
.
CopyGas
);
overflow
{
return
0
,
errGasUintOverflow
}
if
gas
,
overflow
=
math
.
SafeAdd
(
gas
,
words
);
overflow
{
return
0
,
errGasUintOverflow
}
return
gas
,
nil
}
func
gasSStore
(
gt
params
.
GasTable
,
evm
*
EVM
,
contract
*
Contract
,
stack
*
Stack
,
mem
*
Memory
,
memorySize
uint64
)
(
uint64
,
error
)
{
var
(
y
,
x
=
stack
.
Back
(
1
),
stack
.
Back
(
0
)
...
...
core/vm/instructions.go
浏览文件 @
9bd6068f
...
...
@@ -31,6 +31,7 @@ import (
var
(
bigZero
=
new
(
big
.
Int
)
errWriteProtection
=
errors
.
New
(
"evm: write protection"
)
errReadOutOfBound
=
errors
.
New
(
"evm: read out of bound"
)
)
func
opAdd
(
pc
*
uint64
,
evm
*
EVM
,
contract
*
Contract
,
memory
*
Memory
,
stack
*
Stack
)
([]
byte
,
error
)
{
...
...
@@ -360,6 +361,28 @@ func opCalldataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, st
return
nil
,
nil
}
func
opReturnDataSize
(
pc
*
uint64
,
evm
*
EVM
,
contract
*
Contract
,
memory
*
Memory
,
stack
*
Stack
)
([]
byte
,
error
)
{
stack
.
push
(
evm
.
interpreter
.
intPool
.
get
()
.
SetUint64
(
uint64
(
len
(
evm
.
interpreter
.
returnData
))))
return
nil
,
nil
}
func
opReturnDataCopy
(
pc
*
uint64
,
evm
*
EVM
,
contract
*
Contract
,
memory
*
Memory
,
stack
*
Stack
)
([]
byte
,
error
)
{
var
(
mOff
=
stack
.
pop
()
cOff
=
stack
.
pop
()
l
=
stack
.
pop
()
)
defer
evm
.
interpreter
.
intPool
.
put
(
mOff
,
cOff
,
l
)
cEnd
:=
new
(
big
.
Int
)
.
Add
(
cOff
,
l
)
if
cEnd
.
BitLen
()
>
64
||
uint64
(
len
(
evm
.
interpreter
.
returnData
))
<
cEnd
.
Uint64
()
{
return
nil
,
errReadOutOfBound
}
memory
.
Set
(
mOff
.
Uint64
(),
l
.
Uint64
(),
evm
.
interpreter
.
returnData
[
cOff
.
Uint64
()
:
cEnd
.
Uint64
()])
return
nil
,
nil
}
func
opExtCodeSize
(
pc
*
uint64
,
evm
*
EVM
,
contract
*
Contract
,
memory
*
Memory
,
stack
*
Stack
)
([]
byte
,
error
)
{
a
:=
stack
.
pop
()
...
...
core/vm/interpreter.go
浏览文件 @
9bd6068f
...
...
@@ -60,6 +60,8 @@ type Interpreter struct {
intPool
*
intPool
readonly
bool
// returnData contains the last call's return data
returnData
[]
byte
}
// NewInterpreter returns a new instance of the Interpreter.
...
...
@@ -113,6 +115,10 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
in
.
evm
.
depth
++
defer
func
()
{
in
.
evm
.
depth
--
}()
// Reset the previous call's return data. It's unimportant to preserve the old buffer
// as every returning call will return new data anyway.
in
.
returnData
=
nil
// Don't bother with the execution if there's no code.
if
len
(
contract
.
Code
)
==
0
{
return
nil
,
nil
...
...
@@ -213,10 +219,10 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
case
!
operation
.
jumps
:
pc
++
}
// if the operation
returned a value make sure that is also set
//
the last return data
.
if
res
!=
nil
{
mem
.
lastReturn
=
ret
// if the operation
clears the return data (e.g. it has returning data)
//
set the last return to the result of the operation
.
if
operation
.
clearsReturndata
{
in
.
returnData
=
res
}
}
return
nil
,
nil
...
...
core/vm/jump_table.go
浏览文件 @
9bd6068f
...
...
@@ -53,6 +53,8 @@ type operation struct {
valid
bool
// reverts determined whether the operation reverts state
reverts
bool
// clearsReturndata determines whether the opertions clears the return data
clearsReturndata
bool
}
var
(
...
...
@@ -73,6 +75,19 @@ func NewMetropolisInstructionSet() [256]operation {
memorySize
:
memoryStaticCall
,
valid
:
true
,
}
instructionSet
[
RETURNDATASIZE
]
=
operation
{
execute
:
opReturnDataSize
,
gasCost
:
constGasFunc
(
GasQuickStep
),
validateStack
:
makeStackFunc
(
0
,
1
),
valid
:
true
,
}
instructionSet
[
RETURNDATACOPY
]
=
operation
{
execute
:
opReturnDataCopy
,
gasCost
:
gasReturnDataCopy
,
validateStack
:
makeStackFunc
(
3
,
0
),
memorySize
:
memoryReturnDataCopy
,
valid
:
true
,
}
return
instructionSet
}
...
...
@@ -861,26 +876,29 @@ func NewFrontierInstructionSet() [256]operation {
writes
:
true
,
},
CREATE
:
{
execute
:
opCreate
,
gasCost
:
gasCreate
,
validateStack
:
makeStackFunc
(
3
,
1
),
memorySize
:
memoryCreate
,
valid
:
true
,
writes
:
true
,
execute
:
opCreate
,
gasCost
:
gasCreate
,
validateStack
:
makeStackFunc
(
3
,
1
),
memorySize
:
memoryCreate
,
valid
:
true
,
writes
:
true
,
clearsReturndata
:
true
,
},
CALL
:
{
execute
:
opCall
,
gasCost
:
gasCall
,
validateStack
:
makeStackFunc
(
7
,
1
),
memorySize
:
memoryCall
,
valid
:
true
,
execute
:
opCall
,
gasCost
:
gasCall
,
validateStack
:
makeStackFunc
(
7
,
1
),
memorySize
:
memoryCall
,
valid
:
true
,
clearsReturndata
:
true
,
},
CALLCODE
:
{
execute
:
opCallCode
,
gasCost
:
gasCallCode
,
validateStack
:
makeStackFunc
(
7
,
1
),
memorySize
:
memoryCall
,
valid
:
true
,
execute
:
opCallCode
,
gasCost
:
gasCallCode
,
validateStack
:
makeStackFunc
(
7
,
1
),
memorySize
:
memoryCall
,
valid
:
true
,
clearsReturndata
:
true
,
},
RETURN
:
{
execute
:
opReturn
,
...
...
core/vm/memory_table.go
浏览文件 @
9bd6068f
...
...
@@ -30,6 +30,10 @@ func memoryCalldataCopy(stack *Stack) *big.Int {
return
calcMemSize
(
stack
.
Back
(
0
),
stack
.
Back
(
2
))
}
func
memoryReturnDataCopy
(
stack
*
Stack
)
*
big
.
Int
{
return
calcMemSize
(
stack
.
Back
(
0
),
stack
.
Back
(
2
))
}
func
memoryCodeCopy
(
stack
*
Stack
)
*
big
.
Int
{
return
calcMemSize
(
stack
.
Back
(
0
),
stack
.
Back
(
2
))
}
...
...
core/vm/opcodes.go
浏览文件 @
9bd6068f
...
...
@@ -82,10 +82,11 @@ const (
GASPRICE
EXTCODESIZE
EXTCODECOPY
RETURNDATASIZE
RETURNDATACOPY
)
const
(
// 0x40 range - block operations
BLOCKHASH
OpCode
=
0x40
+
iota
COINBASE
...
...
@@ -239,27 +240,29 @@ var opCodeToString = map[OpCode]string{
SHA3
:
"SHA3"
,
// 0x30 range - closure state
ADDRESS
:
"ADDRESS"
,
BALANCE
:
"BALANCE"
,
ORIGIN
:
"ORIGIN"
,
CALLER
:
"CALLER"
,
CALLVALUE
:
"CALLVALUE"
,
CALLDATALOAD
:
"CALLDATALOAD"
,
CALLDATASIZE
:
"CALLDATASIZE"
,
CALLDATACOPY
:
"CALLDATACOPY"
,
CODESIZE
:
"CODESIZE"
,
CODECOPY
:
"CODECOPY"
,
GASPRICE
:
"GASPRICE"
,
ADDRESS
:
"ADDRESS"
,
BALANCE
:
"BALANCE"
,
ORIGIN
:
"ORIGIN"
,
CALLER
:
"CALLER"
,
CALLVALUE
:
"CALLVALUE"
,
CALLDATALOAD
:
"CALLDATALOAD"
,
CALLDATASIZE
:
"CALLDATASIZE"
,
CALLDATACOPY
:
"CALLDATACOPY"
,
CODESIZE
:
"CODESIZE"
,
CODECOPY
:
"CODECOPY"
,
GASPRICE
:
"GASPRICE"
,
EXTCODESIZE
:
"EXTCODESIZE"
,
EXTCODECOPY
:
"EXTCODECOPY"
,
RETURNDATASIZE
:
"RETURNDATASIZE"
,
RETURNDATACOPY
:
"RETURNDATACOPY"
,
// 0x40 range - block operations
BLOCKHASH
:
"BLOCKHASH"
,
COINBASE
:
"COINBASE"
,
TIMESTAMP
:
"TIMESTAMP"
,
NUMBER
:
"NUMBER"
,
DIFFICULTY
:
"DIFFICULTY"
,
GASLIMIT
:
"GASLIMIT"
,
EXTCODESIZE
:
"EXTCODESIZE"
,
EXTCODECOPY
:
"EXTCODECOPY"
,
BLOCKHASH
:
"BLOCKHASH"
,
COINBASE
:
"COINBASE"
,
TIMESTAMP
:
"TIMESTAMP"
,
NUMBER
:
"NUMBER"
,
DIFFICULTY
:
"DIFFICULTY"
,
GASLIMIT
:
"GASLIMIT"
,
// 0x50 range - 'storage' and execution
POP
:
"POP"
,
...
...
@@ -374,137 +377,139 @@ func (o OpCode) String() string {
}
var
stringToOp
=
map
[
string
]
OpCode
{
"STOP"
:
STOP
,
"ADD"
:
ADD
,
"MUL"
:
MUL
,
"SUB"
:
SUB
,
"DIV"
:
DIV
,
"SDIV"
:
SDIV
,
"MOD"
:
MOD
,
"SMOD"
:
SMOD
,
"EXP"
:
EXP
,
"NOT"
:
NOT
,
"LT"
:
LT
,
"GT"
:
GT
,
"SLT"
:
SLT
,
"SGT"
:
SGT
,
"EQ"
:
EQ
,
"ISZERO"
:
ISZERO
,
"SIGNEXTEND"
:
SIGNEXTEND
,
"AND"
:
AND
,
"OR"
:
OR
,
"XOR"
:
XOR
,
"BYTE"
:
BYTE
,
"ADDMOD"
:
ADDMOD
,
"MULMOD"
:
MULMOD
,
"SHA3"
:
SHA3
,
"ADDRESS"
:
ADDRESS
,
"BALANCE"
:
BALANCE
,
"ORIGIN"
:
ORIGIN
,
"CALLER"
:
CALLER
,
"CALLVALUE"
:
CALLVALUE
,
"CALLDATALOAD"
:
CALLDATALOAD
,
"CALLDATASIZE"
:
CALLDATASIZE
,
"CALLDATACOPY"
:
CALLDATACOPY
,
"DELEGATECALL"
:
DELEGATECALL
,
"STATICCALL"
:
STATICCALL
,
"CODESIZE"
:
CODESIZE
,
"CODECOPY"
:
CODECOPY
,
"GASPRICE"
:
GASPRICE
,
"BLOCKHASH"
:
BLOCKHASH
,
"COINBASE"
:
COINBASE
,
"TIMESTAMP"
:
TIMESTAMP
,
"NUMBER"
:
NUMBER
,
"DIFFICULTY"
:
DIFFICULTY
,
"GASLIMIT"
:
GASLIMIT
,
"EXTCODESIZE"
:
EXTCODESIZE
,
"EXTCODECOPY"
:
EXTCODECOPY
,
"POP"
:
POP
,
"MLOAD"
:
MLOAD
,
"MSTORE"
:
MSTORE
,
"MSTORE8"
:
MSTORE8
,
"SLOAD"
:
SLOAD
,
"SSTORE"
:
SSTORE
,
"JUMP"
:
JUMP
,
"JUMPI"
:
JUMPI
,
"PC"
:
PC
,
"MSIZE"
:
MSIZE
,
"GAS"
:
GAS
,
"JUMPDEST"
:
JUMPDEST
,
"PUSH1"
:
PUSH1
,
"PUSH2"
:
PUSH2
,
"PUSH3"
:
PUSH3
,
"PUSH4"
:
PUSH4
,
"PUSH5"
:
PUSH5
,
"PUSH6"
:
PUSH6
,
"PUSH7"
:
PUSH7
,
"PUSH8"
:
PUSH8
,
"PUSH9"
:
PUSH9
,
"PUSH10"
:
PUSH10
,
"PUSH11"
:
PUSH11
,
"PUSH12"
:
PUSH12
,
"PUSH13"
:
PUSH13
,
"PUSH14"
:
PUSH14
,
"PUSH15"
:
PUSH15
,
"PUSH16"
:
PUSH16
,
"PUSH17"
:
PUSH17
,
"PUSH18"
:
PUSH18
,
"PUSH19"
:
PUSH19
,
"PUSH20"
:
PUSH20
,
"PUSH21"
:
PUSH21
,
"PUSH22"
:
PUSH22
,
"PUSH23"
:
PUSH23
,
"PUSH24"
:
PUSH24
,
"PUSH25"
:
PUSH25
,
"PUSH26"
:
PUSH26
,
"PUSH27"
:
PUSH27
,
"PUSH28"
:
PUSH28
,
"PUSH29"
:
PUSH29
,
"PUSH30"
:
PUSH30
,
"PUSH31"
:
PUSH31
,
"PUSH32"
:
PUSH32
,
"DUP1"
:
DUP1
,
"DUP2"
:
DUP2
,
"DUP3"
:
DUP3
,
"DUP4"
:
DUP4
,
"DUP5"
:
DUP5
,
"DUP6"
:
DUP6
,
"DUP7"
:
DUP7
,
"DUP8"
:
DUP8
,
"DUP9"
:
DUP9
,
"DUP10"
:
DUP10
,
"DUP11"
:
DUP11
,
"DUP12"
:
DUP12
,
"DUP13"
:
DUP13
,
"DUP14"
:
DUP14
,
"DUP15"
:
DUP15
,
"DUP16"
:
DUP16
,
"SWAP1"
:
SWAP1
,
"SWAP2"
:
SWAP2
,
"SWAP3"
:
SWAP3
,
"SWAP4"
:
SWAP4
,
"SWAP5"
:
SWAP5
,
"SWAP6"
:
SWAP6
,
"SWAP7"
:
SWAP7
,
"SWAP8"
:
SWAP8
,
"SWAP9"
:
SWAP9
,
"SWAP10"
:
SWAP10
,
"SWAP11"
:
SWAP11
,
"SWAP12"
:
SWAP12
,
"SWAP13"
:
SWAP13
,
"SWAP14"
:
SWAP14
,
"SWAP15"
:
SWAP15
,
"SWAP16"
:
SWAP16
,
"LOG0"
:
LOG0
,
"LOG1"
:
LOG1
,
"LOG2"
:
LOG2
,
"LOG3"
:
LOG3
,
"LOG4"
:
LOG4
,
"CREATE"
:
CREATE
,
"CALL"
:
CALL
,
"RETURN"
:
RETURN
,
"CALLCODE"
:
CALLCODE
,
"SELFDESTRUCT"
:
SELFDESTRUCT
,
"STOP"
:
STOP
,
"ADD"
:
ADD
,
"MUL"
:
MUL
,
"SUB"
:
SUB
,
"DIV"
:
DIV
,
"SDIV"
:
SDIV
,
"MOD"
:
MOD
,
"SMOD"
:
SMOD
,
"EXP"
:
EXP
,
"NOT"
:
NOT
,
"LT"
:
LT
,
"GT"
:
GT
,
"SLT"
:
SLT
,
"SGT"
:
SGT
,
"EQ"
:
EQ
,
"ISZERO"
:
ISZERO
,
"SIGNEXTEND"
:
SIGNEXTEND
,
"AND"
:
AND
,
"OR"
:
OR
,
"XOR"
:
XOR
,
"BYTE"
:
BYTE
,
"ADDMOD"
:
ADDMOD
,
"MULMOD"
:
MULMOD
,
"SHA3"
:
SHA3
,
"ADDRESS"
:
ADDRESS
,
"BALANCE"
:
BALANCE
,
"ORIGIN"
:
ORIGIN
,
"CALLER"
:
CALLER
,
"CALLVALUE"
:
CALLVALUE
,
"CALLDATALOAD"
:
CALLDATALOAD
,
"CALLDATASIZE"
:
CALLDATASIZE
,
"CALLDATACOPY"
:
CALLDATACOPY
,
"DELEGATECALL"
:
DELEGATECALL
,
"STATICCALL"
:
STATICCALL
,
"CODESIZE"
:
CODESIZE
,
"CODECOPY"
:
CODECOPY
,
"GASPRICE"
:
GASPRICE
,
"EXTCODESIZE"
:
EXTCODESIZE
,
"EXTCODECOPY"
:
EXTCODECOPY
,
"RETURNDATASIZE"
:
RETURNDATASIZE
,
"RETURNDATACOPY"
:
RETURNDATACOPY
,
"BLOCKHASH"
:
BLOCKHASH
,
"COINBASE"
:
COINBASE
,
"TIMESTAMP"
:
TIMESTAMP
,
"NUMBER"
:
NUMBER
,
"DIFFICULTY"
:
DIFFICULTY
,
"GASLIMIT"
:
GASLIMIT
,
"POP"
:
POP
,
"MLOAD"
:
MLOAD
,
"MSTORE"
:
MSTORE
,
"MSTORE8"
:
MSTORE8
,
"SLOAD"
:
SLOAD
,
"SSTORE"
:
SSTORE
,
"JUMP"
:
JUMP
,
"JUMPI"
:
JUMPI
,
"PC"
:
PC
,
"MSIZE"
:
MSIZE
,
"GAS"
:
GAS
,
"JUMPDEST"
:
JUMPDEST
,
"PUSH1"
:
PUSH1
,
"PUSH2"
:
PUSH2
,
"PUSH3"
:
PUSH3
,
"PUSH4"
:
PUSH4
,
"PUSH5"
:
PUSH5
,
"PUSH6"
:
PUSH6
,
"PUSH7"
:
PUSH7
,
"PUSH8"
:
PUSH8
,
"PUSH9"
:
PUSH9
,
"PUSH10"
:
PUSH10
,
"PUSH11"
:
PUSH11
,
"PUSH12"
:
PUSH12
,
"PUSH13"
:
PUSH13
,
"PUSH14"
:
PUSH14
,
"PUSH15"
:
PUSH15
,
"PUSH16"
:
PUSH16
,
"PUSH17"
:
PUSH17
,
"PUSH18"
:
PUSH18
,
"PUSH19"
:
PUSH19
,
"PUSH20"
:
PUSH20
,
"PUSH21"
:
PUSH21
,
"PUSH22"
:
PUSH22
,
"PUSH23"
:
PUSH23
,
"PUSH24"
:
PUSH24
,
"PUSH25"
:
PUSH25
,
"PUSH26"
:
PUSH26
,
"PUSH27"
:
PUSH27
,
"PUSH28"
:
PUSH28
,
"PUSH29"
:
PUSH29
,
"PUSH30"
:
PUSH30
,
"PUSH31"
:
PUSH31
,
"PUSH32"
:
PUSH32
,
"DUP1"
:
DUP1
,
"DUP2"
:
DUP2
,
"DUP3"
:
DUP3
,
"DUP4"
:
DUP4
,
"DUP5"
:
DUP5
,
"DUP6"
:
DUP6
,
"DUP7"
:
DUP7
,
"DUP8"
:
DUP8
,
"DUP9"
:
DUP9
,
"DUP10"
:
DUP10
,
"DUP11"
:
DUP11
,
"DUP12"
:
DUP12
,
"DUP13"
:
DUP13
,
"DUP14"
:
DUP14
,
"DUP15"
:
DUP15
,
"DUP16"
:
DUP16
,
"SWAP1"
:
SWAP1
,
"SWAP2"
:
SWAP2
,
"SWAP3"
:
SWAP3
,
"SWAP4"
:
SWAP4
,
"SWAP5"
:
SWAP5
,
"SWAP6"
:
SWAP6
,
"SWAP7"
:
SWAP7
,
"SWAP8"
:
SWAP8
,
"SWAP9"
:
SWAP9
,
"SWAP10"
:
SWAP10
,
"SWAP11"
:
SWAP11
,
"SWAP12"
:
SWAP12
,
"SWAP13"
:
SWAP13
,
"SWAP14"
:
SWAP14
,
"SWAP15"
:
SWAP15
,
"SWAP16"
:
SWAP16
,
"LOG0"
:
LOG0
,
"LOG1"
:
LOG1
,
"LOG2"
:
LOG2
,
"LOG3"
:
LOG3
,
"LOG4"
:
LOG4
,
"CREATE"
:
CREATE
,
"CALL"
:
CALL
,
"RETURN"
:
RETURN
,
"CALLCODE"
:
CALLCODE
,
"SELFDESTRUCT"
:
SELFDESTRUCT
,
}
func
StringToOp
(
str
string
)
OpCode
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录