Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
e5fba8fd
Commit
e5fba8fd
authored
Jul 07, 2015
by
Jeffrey Wilcke
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1428 from obscuren/coinbase-fixes
cmd,eth,rpc,tests: default coinbase
parents
916d1554
37c1a8f6
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
99 additions
and
51 deletions
+99
-51
main.go
cmd/geth/main.go
+1
-16
flags.go
cmd/utils/flags.go
+24
-4
backend.go
eth/backend.go
+8
-2
miner.go
miner/miner.go
+5
-0
worker.go
miner/worker.go
+6
-0
miner.go
rpc/api/miner.go
+10
-0
miner_args.go
rpc/api/miner_args.go
+26
-0
miner_js.go
rpc/api/miner_js.go
+7
-0
block_test_util.go
tests/block_test_util.go
+1
-1
state_test_util.go
tests/state_test_util.go
+11
-9
types.go
xeth/types.go
+0
-10
xeth.go
xeth/xeth.go
+0
-9
No files found.
cmd/geth/main.go
View file @
e5fba8fd
...
@@ -461,22 +461,7 @@ func execJSFiles(ctx *cli.Context) {
...
@@ -461,22 +461,7 @@ func execJSFiles(ctx *cli.Context) {
func
unlockAccount
(
ctx
*
cli
.
Context
,
am
*
accounts
.
Manager
,
addr
string
,
i
int
)
(
addrHex
,
auth
string
)
{
func
unlockAccount
(
ctx
*
cli
.
Context
,
am
*
accounts
.
Manager
,
addr
string
,
i
int
)
(
addrHex
,
auth
string
)
{
var
err
error
var
err
error
// Load startup keys. XXX we are going to need a different format
addrHex
=
utils
.
ParamToAddress
(
addr
,
am
)
if
!
((
len
(
addr
)
==
40
)
||
(
len
(
addr
)
==
42
))
{
// with or without 0x
var
index
int
index
,
err
=
strconv
.
Atoi
(
addr
)
if
err
!=
nil
{
utils
.
Fatalf
(
"Invalid account address '%s'"
,
addr
)
}
addrHex
,
err
=
am
.
AddressByIndex
(
index
)
if
err
!=
nil
{
utils
.
Fatalf
(
"%v"
,
err
)
}
}
else
{
addrHex
=
addr
}
// Attempt to unlock the account 3 times
// Attempt to unlock the account 3 times
attempts
:=
3
attempts
:=
3
for
tries
:=
0
;
tries
<
attempts
;
tries
++
{
for
tries
:=
0
;
tries
<
attempts
;
tries
++
{
...
...
cmd/utils/flags.go
View file @
e5fba8fd
...
@@ -9,6 +9,7 @@ import (
...
@@ -9,6 +9,7 @@ import (
"os"
"os"
"path/filepath"
"path/filepath"
"runtime"
"runtime"
"strconv"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/metrics"
...
@@ -122,8 +123,8 @@ var (
...
@@ -122,8 +123,8 @@ var (
}
}
EtherbaseFlag
=
cli
.
StringFlag
{
EtherbaseFlag
=
cli
.
StringFlag
{
Name
:
"etherbase"
,
Name
:
"etherbase"
,
Usage
:
"Public address for block mining rewards. By default the address
of your primary account
is used"
,
Usage
:
"Public address for block mining rewards. By default the address
first created
is used"
,
Value
:
"
primary
"
,
Value
:
"
0
"
,
}
}
GasPriceFlag
=
cli
.
StringFlag
{
GasPriceFlag
=
cli
.
StringFlag
{
Name
:
"gasprice"
,
Name
:
"gasprice"
,
...
@@ -351,6 +352,8 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
...
@@ -351,6 +352,8 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
if
len
(
customName
)
>
0
{
if
len
(
customName
)
>
0
{
clientID
+=
"/"
+
customName
clientID
+=
"/"
+
customName
}
}
am
:=
MakeAccountManager
(
ctx
)
return
&
eth
.
Config
{
return
&
eth
.
Config
{
Name
:
common
.
MakeName
(
clientID
,
version
),
Name
:
common
.
MakeName
(
clientID
,
version
),
DataDir
:
ctx
.
GlobalString
(
DataDirFlag
.
Name
),
DataDir
:
ctx
.
GlobalString
(
DataDirFlag
.
Name
),
...
@@ -361,9 +364,9 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
...
@@ -361,9 +364,9 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
LogFile
:
ctx
.
GlobalString
(
LogFileFlag
.
Name
),
LogFile
:
ctx
.
GlobalString
(
LogFileFlag
.
Name
),
Verbosity
:
ctx
.
GlobalInt
(
VerbosityFlag
.
Name
),
Verbosity
:
ctx
.
GlobalInt
(
VerbosityFlag
.
Name
),
LogJSON
:
ctx
.
GlobalString
(
LogJSONFlag
.
Name
),
LogJSON
:
ctx
.
GlobalString
(
LogJSONFlag
.
Name
),
Etherbase
:
c
tx
.
GlobalString
(
EtherbaseFlag
.
Name
),
Etherbase
:
c
ommon
.
HexToAddress
(
ParamToAddress
(
ctx
.
GlobalString
(
EtherbaseFlag
.
Name
),
am
)
),
MinerThreads
:
ctx
.
GlobalInt
(
MinerThreadsFlag
.
Name
),
MinerThreads
:
ctx
.
GlobalInt
(
MinerThreadsFlag
.
Name
),
AccountManager
:
MakeAccountManager
(
ctx
)
,
AccountManager
:
am
,
VmDebug
:
ctx
.
GlobalBool
(
VMDebugFlag
.
Name
),
VmDebug
:
ctx
.
GlobalBool
(
VMDebugFlag
.
Name
),
MaxPeers
:
ctx
.
GlobalInt
(
MaxPeersFlag
.
Name
),
MaxPeers
:
ctx
.
GlobalInt
(
MaxPeersFlag
.
Name
),
MaxPendingPeers
:
ctx
.
GlobalInt
(
MaxPendingPeersFlag
.
Name
),
MaxPendingPeers
:
ctx
.
GlobalInt
(
MaxPendingPeersFlag
.
Name
),
...
@@ -488,3 +491,20 @@ func StartPProf(ctx *cli.Context) {
...
@@ -488,3 +491,20 @@ func StartPProf(ctx *cli.Context) {
log
.
Println
(
http
.
ListenAndServe
(
address
,
nil
))
log
.
Println
(
http
.
ListenAndServe
(
address
,
nil
))
}()
}()
}
}
func
ParamToAddress
(
addr
string
,
am
*
accounts
.
Manager
)
(
addrHex
string
)
{
if
!
((
len
(
addr
)
==
40
)
||
(
len
(
addr
)
==
42
))
{
// with or without 0x
index
,
err
:=
strconv
.
Atoi
(
addr
)
if
err
!=
nil
{
Fatalf
(
"Invalid account address '%s'"
,
addr
)
}
addrHex
,
err
=
am
.
AddressByIndex
(
index
)
if
err
!=
nil
{
Fatalf
(
"%v"
,
err
)
}
}
else
{
addrHex
=
addr
}
return
}
eth/backend.go
View file @
e5fba8fd
...
@@ -88,7 +88,7 @@ type Config struct {
...
@@ -88,7 +88,7 @@ type Config struct {
Shh
bool
Shh
bool
Dial
bool
Dial
bool
Etherbase
string
Etherbase
common
.
Address
GasPrice
*
big
.
Int
GasPrice
*
big
.
Int
MinerThreads
int
MinerThreads
int
AccountManager
*
accounts
.
Manager
AccountManager
*
accounts
.
Manager
...
@@ -324,7 +324,7 @@ func New(config *Config) (*Ethereum, error) {
...
@@ -324,7 +324,7 @@ func New(config *Config) (*Ethereum, error) {
eventMux
:
&
event
.
TypeMux
{},
eventMux
:
&
event
.
TypeMux
{},
accountManager
:
config
.
AccountManager
,
accountManager
:
config
.
AccountManager
,
DataDir
:
config
.
DataDir
,
DataDir
:
config
.
DataDir
,
etherbase
:
co
mmon
.
HexToAddress
(
config
.
Etherbase
)
,
etherbase
:
co
nfig
.
Etherbase
,
clientVersion
:
config
.
Name
,
// TODO should separate from Name
clientVersion
:
config
.
Name
,
// TODO should separate from Name
netVersionId
:
config
.
NetworkId
,
netVersionId
:
config
.
NetworkId
,
NatSpec
:
config
.
NatSpec
,
NatSpec
:
config
.
NatSpec
,
...
@@ -480,6 +480,12 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) {
...
@@ -480,6 +480,12 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) {
return
return
}
}
// set in js console via admin interface or wrapper from cli flags
func
(
self
*
Ethereum
)
SetEtherbase
(
etherbase
common
.
Address
)
{
self
.
etherbase
=
etherbase
self
.
miner
.
SetEtherbase
(
etherbase
)
}
func
(
s
*
Ethereum
)
StopMining
()
{
s
.
miner
.
Stop
()
}
func
(
s
*
Ethereum
)
StopMining
()
{
s
.
miner
.
Stop
()
}
func
(
s
*
Ethereum
)
IsMining
()
bool
{
return
s
.
miner
.
Mining
()
}
func
(
s
*
Ethereum
)
IsMining
()
bool
{
return
s
.
miner
.
Mining
()
}
func
(
s
*
Ethereum
)
Miner
()
*
miner
.
Miner
{
return
s
.
miner
}
func
(
s
*
Ethereum
)
Miner
()
*
miner
.
Miner
{
return
s
.
miner
}
...
...
miner/miner.go
View file @
e5fba8fd
...
@@ -137,3 +137,8 @@ func (self *Miner) PendingState() *state.StateDB {
...
@@ -137,3 +137,8 @@ func (self *Miner) PendingState() *state.StateDB {
func
(
self
*
Miner
)
PendingBlock
()
*
types
.
Block
{
func
(
self
*
Miner
)
PendingBlock
()
*
types
.
Block
{
return
self
.
worker
.
pendingBlock
()
return
self
.
worker
.
pendingBlock
()
}
}
func
(
self
*
Miner
)
SetEtherbase
(
addr
common
.
Address
)
{
self
.
coinbase
=
addr
self
.
worker
.
setEtherbase
(
addr
)
}
miner/worker.go
View file @
e5fba8fd
...
@@ -124,6 +124,12 @@ func newWorker(coinbase common.Address, eth core.Backend) *worker {
...
@@ -124,6 +124,12 @@ func newWorker(coinbase common.Address, eth core.Backend) *worker {
return
worker
return
worker
}
}
func
(
self
*
worker
)
setEtherbase
(
addr
common
.
Address
)
{
self
.
mu
.
Lock
()
defer
self
.
mu
.
Unlock
()
self
.
coinbase
=
addr
}
func
(
self
*
worker
)
pendingState
()
*
state
.
StateDB
{
func
(
self
*
worker
)
pendingState
()
*
state
.
StateDB
{
self
.
currentMu
.
Lock
()
self
.
currentMu
.
Lock
()
defer
self
.
currentMu
.
Unlock
()
defer
self
.
currentMu
.
Unlock
()
...
...
rpc/api/miner.go
View file @
e5fba8fd
...
@@ -19,6 +19,7 @@ var (
...
@@ -19,6 +19,7 @@ var (
"miner_makeDAG"
:
(
*
minerApi
)
.
MakeDAG
,
"miner_makeDAG"
:
(
*
minerApi
)
.
MakeDAG
,
"miner_setExtra"
:
(
*
minerApi
)
.
SetExtra
,
"miner_setExtra"
:
(
*
minerApi
)
.
SetExtra
,
"miner_setGasPrice"
:
(
*
minerApi
)
.
SetGasPrice
,
"miner_setGasPrice"
:
(
*
minerApi
)
.
SetGasPrice
,
"miner_setEtherbase"
:
(
*
minerApi
)
.
SetEtherbase
,
"miner_startAutoDAG"
:
(
*
minerApi
)
.
StartAutoDAG
,
"miner_startAutoDAG"
:
(
*
minerApi
)
.
StartAutoDAG
,
"miner_start"
:
(
*
minerApi
)
.
StartMiner
,
"miner_start"
:
(
*
minerApi
)
.
StartMiner
,
"miner_stopAutoDAG"
:
(
*
minerApi
)
.
StopAutoDAG
,
"miner_stopAutoDAG"
:
(
*
minerApi
)
.
StopAutoDAG
,
...
@@ -119,6 +120,15 @@ func (self *minerApi) SetGasPrice(req *shared.Request) (interface{}, error) {
...
@@ -119,6 +120,15 @@ func (self *minerApi) SetGasPrice(req *shared.Request) (interface{}, error) {
return
true
,
nil
return
true
,
nil
}
}
func
(
self
*
minerApi
)
SetEtherbase
(
req
*
shared
.
Request
)
(
interface
{},
error
)
{
args
:=
new
(
SetEtherbaseArgs
)
if
err
:=
self
.
codec
.
Decode
(
req
.
Params
,
&
args
);
err
!=
nil
{
return
false
,
err
}
self
.
ethereum
.
SetEtherbase
(
args
.
Etherbase
)
return
nil
,
nil
}
func
(
self
*
minerApi
)
StartAutoDAG
(
req
*
shared
.
Request
)
(
interface
{},
error
)
{
func
(
self
*
minerApi
)
StartAutoDAG
(
req
*
shared
.
Request
)
(
interface
{},
error
)
{
self
.
ethereum
.
StartAutoDAG
()
self
.
ethereum
.
StartAutoDAG
()
return
true
,
nil
return
true
,
nil
...
...
rpc/api/miner_args.go
View file @
e5fba8fd
...
@@ -5,6 +5,7 @@ import (
...
@@ -5,6 +5,7 @@ import (
"math/big"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rpc/shared"
"github.com/ethereum/go-ethereum/rpc/shared"
)
)
...
@@ -76,6 +77,31 @@ func (args *GasPriceArgs) UnmarshalJSON(b []byte) (err error) {
...
@@ -76,6 +77,31 @@ func (args *GasPriceArgs) UnmarshalJSON(b []byte) (err error) {
return
shared
.
NewInvalidTypeError
(
"Price"
,
"not a string"
)
return
shared
.
NewInvalidTypeError
(
"Price"
,
"not a string"
)
}
}
type
SetEtherbaseArgs
struct
{
Etherbase
common
.
Address
}
func
(
args
*
SetEtherbaseArgs
)
UnmarshalJSON
(
b
[]
byte
)
(
err
error
)
{
var
obj
[]
interface
{}
if
err
:=
json
.
Unmarshal
(
b
,
&
obj
);
err
!=
nil
{
return
shared
.
NewDecodeParamError
(
err
.
Error
())
}
if
len
(
obj
)
<
1
{
return
shared
.
NewInsufficientParamsError
(
len
(
obj
),
1
)
}
if
addr
,
ok
:=
obj
[
0
]
.
(
string
);
ok
{
args
.
Etherbase
=
common
.
HexToAddress
(
addr
)
if
(
args
.
Etherbase
==
common
.
Address
{})
{
return
shared
.
NewInvalidTypeError
(
"Etherbase"
,
"not a valid address"
)
}
return
nil
}
return
shared
.
NewInvalidTypeError
(
"Etherbase"
,
"not a string"
)
}
type
MakeDAGArgs
struct
{
type
MakeDAGArgs
struct
{
BlockNumber
int64
BlockNumber
int64
}
}
...
...
rpc/api/miner_js.go
View file @
e5fba8fd
...
@@ -17,6 +17,13 @@ web3._extend({
...
@@ -17,6 +17,13 @@ web3._extend({
params: 1,
params: 1,
inputFormatter: [null]
inputFormatter: [null]
}),
}),
new web3._extend.Method({
name: 'setEtherbase',
call: 'miner_setEtherbase',
params: 1,
inputFormatter: [web3._extend.formatters.formatInputInt],
outputFormatter: web3._extend.formatters.formatOutputBool
}),
new web3._extend.Method({
new web3._extend.Method({
name: 'setExtra',
name: 'setExtra',
call: 'miner_setExtra',
call: 'miner_setExtra',
...
...
tests/block_test_util.go
View file @
e5fba8fd
...
@@ -180,7 +180,7 @@ func (test *BlockTest) makeEthConfig() *eth.Config {
...
@@ -180,7 +180,7 @@ func (test *BlockTest) makeEthConfig() *eth.Config {
return
&
eth
.
Config
{
return
&
eth
.
Config
{
DataDir
:
common
.
DefaultDataDir
(),
DataDir
:
common
.
DefaultDataDir
(),
Verbosity
:
5
,
Verbosity
:
5
,
Etherbase
:
"primary"
,
Etherbase
:
common
.
Address
{}
,
AccountManager
:
accounts
.
NewManager
(
ks
),
AccountManager
:
accounts
.
NewManager
(
ks
),
NewDB
:
func
(
path
string
)
(
common
.
Database
,
error
)
{
return
ethdb
.
NewMemDatabase
()
},
NewDB
:
func
(
path
string
)
(
common
.
Database
,
error
)
{
return
ethdb
.
NewMemDatabase
()
},
}
}
...
...
tests/state_test_util.go
View file @
e5fba8fd
...
@@ -2,6 +2,7 @@ package tests
...
@@ -2,6 +2,7 @@ package tests
import
(
import
(
"bytes"
"bytes"
"encoding/hex"
"fmt"
"fmt"
"io"
"io"
"math/big"
"math/big"
...
@@ -147,13 +148,12 @@ func runStateTest(test VmTest) error {
...
@@ -147,13 +148,12 @@ func runStateTest(test VmTest) error {
func
RunState
(
statedb
*
state
.
StateDB
,
env
,
tx
map
[
string
]
string
)
([]
byte
,
state
.
Logs
,
*
big
.
Int
,
error
)
{
func
RunState
(
statedb
*
state
.
StateDB
,
env
,
tx
map
[
string
]
string
)
([]
byte
,
state
.
Logs
,
*
big
.
Int
,
error
)
{
var
(
var
(
keyPair
,
_
=
crypto
.
NewKeyPairFromSec
([]
byte
(
common
.
Hex2Bytes
(
tx
[
"secretKey"
])))
data
=
common
.
FromHex
(
tx
[
"data"
])
data
=
common
.
FromHex
(
tx
[
"data"
])
gas
=
common
.
Big
(
tx
[
"gasLimit"
])
gas
=
common
.
Big
(
tx
[
"gasLimit"
])
price
=
common
.
Big
(
tx
[
"gasPrice"
])
price
=
common
.
Big
(
tx
[
"gasPrice"
])
value
=
common
.
Big
(
tx
[
"value"
])
value
=
common
.
Big
(
tx
[
"value"
])
nonce
=
common
.
Big
(
tx
[
"nonce"
])
.
Uint64
()
nonce
=
common
.
Big
(
tx
[
"nonce"
])
.
Uint64
()
caddr
=
common
.
HexToAddress
(
env
[
"currentCoinbase"
])
caddr
=
common
.
HexToAddress
(
env
[
"currentCoinbase"
])
)
)
var
to
*
common
.
Address
var
to
*
common
.
Address
...
@@ -168,9 +168,11 @@ func RunState(statedb *state.StateDB, env, tx map[string]string) ([]byte, state.
...
@@ -168,9 +168,11 @@ func RunState(statedb *state.StateDB, env, tx map[string]string) ([]byte, state.
coinbase
:=
statedb
.
GetOrNewStateObject
(
caddr
)
coinbase
:=
statedb
.
GetOrNewStateObject
(
caddr
)
coinbase
.
SetGasLimit
(
common
.
Big
(
env
[
"currentGasLimit"
]))
coinbase
.
SetGasLimit
(
common
.
Big
(
env
[
"currentGasLimit"
]))
message
:=
NewMessage
(
common
.
BytesToAddress
(
keyPair
.
Address
()),
to
,
data
,
value
,
gas
,
price
,
nonce
)
key
,
_
:=
hex
.
DecodeString
(
tx
[
"secretKey"
])
addr
:=
crypto
.
PubkeyToAddress
(
crypto
.
ToECDSA
(
key
)
.
PublicKey
)
message
:=
NewMessage
(
addr
,
to
,
data
,
value
,
gas
,
price
,
nonce
)
vmenv
:=
NewEnvFromMap
(
statedb
,
env
,
tx
)
vmenv
:=
NewEnvFromMap
(
statedb
,
env
,
tx
)
vmenv
.
origin
=
common
.
BytesToAddress
(
keyPair
.
Address
())
vmenv
.
origin
=
addr
ret
,
_
,
err
:=
core
.
ApplyMessage
(
vmenv
,
message
,
coinbase
)
ret
,
_
,
err
:=
core
.
ApplyMessage
(
vmenv
,
message
,
coinbase
)
if
core
.
IsNonceErr
(
err
)
||
core
.
IsInvalidTxErr
(
err
)
||
state
.
IsGasLimitErr
(
err
)
{
if
core
.
IsNonceErr
(
err
)
||
core
.
IsInvalidTxErr
(
err
)
||
state
.
IsGasLimitErr
(
err
)
{
statedb
.
Set
(
snapshot
)
statedb
.
Set
(
snapshot
)
...
...
xeth/types.go
View file @
e5fba8fd
...
@@ -168,16 +168,6 @@ func (self *Transaction) ToString() string {
...
@@ -168,16 +168,6 @@ func (self *Transaction) ToString() string {
return
self
.
ref
.
String
()
return
self
.
ref
.
String
()
}
}
type
Key
struct
{
Address
string
`json:"address"`
PrivateKey
string
`json:"privateKey"`
PublicKey
string
`json:"publicKey"`
}
func
NewKey
(
key
*
crypto
.
KeyPair
)
*
Key
{
return
&
Key
{
common
.
ToHex
(
key
.
Address
()),
common
.
ToHex
(
key
.
PrivateKey
),
common
.
ToHex
(
key
.
PublicKey
)}
}
type
PReceipt
struct
{
type
PReceipt
struct
{
CreatedContract
bool
`json:"createdContract"`
CreatedContract
bool
`json:"createdContract"`
Address
string
`json:"address"`
Address
string
`json:"address"`
...
...
xeth/xeth.go
View file @
e5fba8fd
...
@@ -476,15 +476,6 @@ func (self *XEth) IsContract(address string) bool {
...
@@ -476,15 +476,6 @@ func (self *XEth) IsContract(address string) bool {
return
len
(
self
.
State
()
.
SafeGet
(
address
)
.
Code
())
>
0
return
len
(
self
.
State
()
.
SafeGet
(
address
)
.
Code
())
>
0
}
}
func
(
self
*
XEth
)
SecretToAddress
(
key
string
)
string
{
pair
,
err
:=
crypto
.
NewKeyPairFromSec
(
common
.
FromHex
(
key
))
if
err
!=
nil
{
return
""
}
return
common
.
ToHex
(
pair
.
Address
())
}
func
(
self
*
XEth
)
UninstallFilter
(
id
int
)
bool
{
func
(
self
*
XEth
)
UninstallFilter
(
id
int
)
bool
{
defer
self
.
filterManager
.
UninstallFilter
(
id
)
defer
self
.
filterManager
.
UninstallFilter
(
id
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment