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
9571a512
Commit
9571a512
authored
Jan 11, 2014
by
obscuren
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
gofmt no coding standards
parent
8bbf879c
Changes
14
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
973 additions
and
938 deletions
+973
-938
README.md
README.md
+5
-0
block_manager.go
block_manager.go
+60
-60
dagger.go
dagger.go
+101
-98
dagger_test.go
dagger_test.go
+9
-9
dev_console.go
dev_console.go
+94
-92
ethereum.go
ethereum.go
+66
-66
peer.go
peer.go
+59
-59
rlp.go
rlp.go
+198
-190
rlp_test.go
rlp_test.go
+37
-37
server.go
server.go
+80
-81
test_runner.go
test_runner.go
+15
-15
test_runner_test.go
test_runner_test.go
+14
-14
testing.go
testing.go
+1
-0
vm.go
vm.go
+234
-217
No files found.
README.md
View file @
9571a512
...
@@ -21,3 +21,8 @@ Command line options
...
@@ -21,3 +21,8 @@ Command line options
-c launch the developer console
-c launch the developer console
-m start mining fake blocks and broadcast fake messages to the net
-m start mining fake blocks and broadcast fake messages to the net
Contribution
============
See CONTRIB.md
block_manager.go
View file @
9571a512
package
main
package
main
import
(
import
(
"fmt"
"fmt"
"github.com/ethereum/ethutil-go"
"github.com/ethereum/ethutil-go"
)
)
type
BlockChain
struct
{
type
BlockChain
struct
{
lastBlock
*
ethutil
.
Block
lastBlock
*
ethutil
.
Block
genesisBlock
*
ethutil
.
Block
genesisBlock
*
ethutil
.
Block
}
}
func
NewBlockChain
()
*
BlockChain
{
func
NewBlockChain
()
*
BlockChain
{
bc
:=
&
BlockChain
{}
bc
:=
&
BlockChain
{}
bc
.
genesisBlock
=
ethutil
.
NewBlock
(
ethutil
.
Encode
(
ethutil
.
Genesis
)
)
bc
.
genesisBlock
=
ethutil
.
NewBlock
(
ethutil
.
Encode
(
ethutil
.
Genesis
)
)
return
bc
return
bc
}
}
type
BlockManager
struct
{
type
BlockManager
struct
{
vm
*
Vm
vm
*
Vm
blockChain
*
BlockChain
blockChain
*
BlockChain
}
}
func
NewBlockManager
()
*
BlockManager
{
func
NewBlockManager
()
*
BlockManager
{
bm
:=
&
BlockManager
{
vm
:
NewVm
()}
bm
:=
&
BlockManager
{
vm
:
NewVm
()}
return
bm
return
bm
}
}
// Process a block.
// Process a block.
func
(
bm
*
BlockManager
)
ProcessBlock
(
block
*
ethutil
.
Block
)
error
{
func
(
bm
*
BlockManager
)
ProcessBlock
(
block
*
ethutil
.
Block
)
error
{
// TODO Validation (Or move to other part of the application)
// TODO Validation (Or move to other part of the application)
if
err
:=
bm
.
ValidateBlock
(
block
);
err
!=
nil
{
if
err
:=
bm
.
ValidateBlock
(
block
);
err
!=
nil
{
return
err
return
err
}
}
// Get the tx count. Used to create enough channels to 'join' the go routines
// Get the tx count. Used to create enough channels to 'join' the go routines
txCount
:=
len
(
block
.
Transactions
())
txCount
:=
len
(
block
.
Transactions
())
// Locking channel. When it has been fully buffered this method will return
// Locking channel. When it has been fully buffered this method will return
lockChan
:=
make
(
chan
bool
,
txCount
)
lockChan
:=
make
(
chan
bool
,
txCount
)
// Process each transaction/contract
// Process each transaction/contract
for
_
,
tx
:=
range
block
.
Transactions
()
{
for
_
,
tx
:=
range
block
.
Transactions
()
{
// If there's no recipient, it's a contract
// If there's no recipient, it's a contract
if
tx
.
IsContract
()
{
if
tx
.
IsContract
()
{
go
bm
.
ProcessContract
(
tx
,
block
,
lockChan
)
go
bm
.
ProcessContract
(
tx
,
block
,
lockChan
)
}
else
{
}
else
{
// "finish" tx which isn't a contract
// "finish" tx which isn't a contract
lockChan
<-
true
lockChan
<-
true
}
}
}
}
// Wait for all Tx to finish processing
// Wait for all Tx to finish processing
for
i
:=
0
;
i
<
txCount
;
i
++
{
for
i
:=
0
;
i
<
txCount
;
i
++
{
<-
lockChan
<-
lockChan
}
}
return
nil
return
nil
}
}
func
(
bm
*
BlockManager
)
ValidateBlock
(
block
*
ethutil
.
Block
)
error
{
func
(
bm
*
BlockManager
)
ValidateBlock
(
block
*
ethutil
.
Block
)
error
{
return
nil
return
nil
}
}
func
(
bm
*
BlockManager
)
ProcessContract
(
tx
*
ethutil
.
Transaction
,
block
*
ethutil
.
Block
,
lockChan
chan
bool
)
{
func
(
bm
*
BlockManager
)
ProcessContract
(
tx
*
ethutil
.
Transaction
,
block
*
ethutil
.
Block
,
lockChan
chan
bool
)
{
// Recovering function in case the VM had any errors
// Recovering function in case the VM had any errors
defer
func
()
{
defer
func
()
{
if
r
:=
recover
();
r
!=
nil
{
if
r
:=
recover
();
r
!=
nil
{
fmt
.
Println
(
"Recovered from VM execution with err ="
,
r
)
fmt
.
Println
(
"Recovered from VM execution with err ="
,
r
)
// Let the channel know where done even though it failed (so the execution may resume normally)
// Let the channel know where done even though it failed (so the execution may resume normally)
lockChan
<-
true
lockChan
<-
true
}
}
}()
}()
// Process contract
// Process contract
bm
.
vm
.
ProcContract
(
tx
,
block
,
func
(
opType
OpType
)
bool
{
bm
.
vm
.
ProcContract
(
tx
,
block
,
func
(
opType
OpType
)
bool
{
// TODO turn on once big ints are in place
// TODO turn on once big ints are in place
//if !block.PayFee(tx.Hash(), StepFee.Uint64()) {
//if !block.PayFee(tx.Hash(), StepFee.Uint64()) {
// return false
// return false
//}
//}
return
true
// Continue
return
true
// Continue
})
})
// Broadcast we're done
// Broadcast we're done
lockChan
<-
true
lockChan
<-
true
}
}
dagger.go
View file @
9571a512
package
main
package
main
import
(
import
(
"math/big
"
"github.com/ethereum/ethutil-go
"
"math/rand
"
"github.com/obscuren/sha3
"
"time
"
"hash
"
"github.com/obscuren/sha3
"
"math/big
"
"hash
"
"math/rand
"
"github.com/ethereum/ethutil-go
"
"time
"
)
)
type
Dagger
struct
{
type
Dagger
struct
{
hash
*
big
.
Int
hash
*
big
.
Int
xn
*
big
.
Int
xn
*
big
.
Int
}
}
var
Found
bool
var
Found
bool
func
(
dag
*
Dagger
)
Find
(
obj
*
big
.
Int
,
resChan
chan
int64
)
{
func
(
dag
*
Dagger
)
Find
(
obj
*
big
.
Int
,
resChan
chan
int64
)
{
r
:=
rand
.
New
(
rand
.
NewSource
(
time
.
Now
()
.
UnixNano
()))
r
:=
rand
.
New
(
rand
.
NewSource
(
time
.
Now
()
.
UnixNano
()))
for
i
:=
0
;
i
<
1000
;
i
++
{
for
i
:=
0
;
i
<
1000
;
i
++
{
rnd
:=
r
.
Int63
()
rnd
:=
r
.
Int63
()
if
dag
.
Eval
(
big
.
NewInt
(
rnd
))
.
Cmp
(
obj
)
<
0
{
if
dag
.
Eval
(
big
.
NewInt
(
rnd
))
.
Cmp
(
obj
)
<
0
{
// Post back result on the channel
// Post back result on the channel
resChan
<-
rnd
resChan
<-
rnd
// Notify other threads we've found a valid nonce
// Notify other threads we've found a valid nonce
Found
=
true
Found
=
true
}
}
// Break out if found
// Break out if found
if
Found
{
break
}
if
Found
{
}
break
}
}
resChan
<-
0
resChan
<-
0
}
}
func
(
dag
*
Dagger
)
Search
(
hash
,
diff
*
big
.
Int
)
*
big
.
Int
{
func
(
dag
*
Dagger
)
Search
(
hash
,
diff
*
big
.
Int
)
*
big
.
Int
{
// TODO fix multi threading. Somehow it results in the wrong nonce
// TODO fix multi threading. Somehow it results in the wrong nonce
amountOfRoutines
:=
1
amountOfRoutines
:=
1
dag
.
hash
=
hash
dag
.
hash
=
hash
obj
:=
ethutil
.
BigPow
(
2
,
256
)
obj
:=
ethutil
.
BigPow
(
2
,
256
)
obj
=
obj
.
Div
(
obj
,
diff
)
obj
=
obj
.
Div
(
obj
,
diff
)
Found
=
false
Found
=
false
resChan
:=
make
(
chan
int64
,
3
)
resChan
:=
make
(
chan
int64
,
3
)
var
res
int64
var
res
int64
for
k
:=
0
;
k
<
amountOfRoutines
;
k
++
{
for
k
:=
0
;
k
<
amountOfRoutines
;
k
++
{
go
dag
.
Find
(
obj
,
resChan
)
go
dag
.
Find
(
obj
,
resChan
)
}
}
// Wait for each go routine to finish
// Wait for each go routine to finish
for
k
:=
0
;
k
<
amountOfRoutines
;
k
++
{
for
k
:=
0
;
k
<
amountOfRoutines
;
k
++
{
// Get the result from the channel. 0 = quit
// Get the result from the channel. 0 = quit
if
r
:=
<-
resChan
;
r
!=
0
{
if
r
:=
<-
resChan
;
r
!=
0
{
res
=
r
res
=
r
}
}
}
}
return
big
.
NewInt
(
res
)
return
big
.
NewInt
(
res
)
}
}
func
DaggerVerify
(
hash
,
diff
,
nonce
*
big
.
Int
)
bool
{
func
DaggerVerify
(
hash
,
diff
,
nonce
*
big
.
Int
)
bool
{
dagger
:=
&
Dagger
{}
dagger
:=
&
Dagger
{}
dagger
.
hash
=
hash
dagger
.
hash
=
hash
obj
:=
ethutil
.
BigPow
(
2
,
256
)
obj
:=
ethutil
.
BigPow
(
2
,
256
)
obj
=
obj
.
Div
(
obj
,
diff
)
obj
=
obj
.
Div
(
obj
,
diff
)
return
dagger
.
Eval
(
nonce
)
.
Cmp
(
obj
)
<
0
return
dagger
.
Eval
(
nonce
)
.
Cmp
(
obj
)
<
0
}
}
func
(
dag
*
Dagger
)
Node
(
L
uint64
,
i
uint64
)
*
big
.
Int
{
func
(
dag
*
Dagger
)
Node
(
L
uint64
,
i
uint64
)
*
big
.
Int
{
if
L
==
i
{
if
L
==
i
{
return
dag
.
hash
return
dag
.
hash
}
}
var
m
*
big
.
Int
var
m
*
big
.
Int
if
L
==
9
{
if
L
==
9
{
m
=
big
.
NewInt
(
16
)
m
=
big
.
NewInt
(
16
)
}
else
{
}
else
{
m
=
big
.
NewInt
(
3
)
m
=
big
.
NewInt
(
3
)
}
}
sha
:=
sha3
.
NewKeccak256
()
sha
:=
sha3
.
NewKeccak256
()
sha
.
Reset
()
sha
.
Reset
()
d
:=
sha3
.
NewKeccak256
()
d
:=
sha3
.
NewKeccak256
()
b
:=
new
(
big
.
Int
)
b
:=
new
(
big
.
Int
)
ret
:=
new
(
big
.
Int
)
ret
:=
new
(
big
.
Int
)
for
k
:=
0
;
k
<
int
(
m
.
Uint64
());
k
++
{
for
k
:=
0
;
k
<
int
(
m
.
Uint64
());
k
++
{
d
.
Reset
()
d
.
Reset
()
d
.
Write
(
dag
.
hash
.
Bytes
())
d
.
Write
(
dag
.
hash
.
Bytes
())
d
.
Write
(
dag
.
xn
.
Bytes
())
d
.
Write
(
dag
.
xn
.
Bytes
())
d
.
Write
(
big
.
NewInt
(
int64
(
L
))
.
Bytes
())
d
.
Write
(
big
.
NewInt
(
int64
(
L
))
.
Bytes
())
d
.
Write
(
big
.
NewInt
(
int64
(
i
))
.
Bytes
())
d
.
Write
(
big
.
NewInt
(
int64
(
i
))
.
Bytes
())
d
.
Write
(
big
.
NewInt
(
int64
(
k
))
.
Bytes
())
d
.
Write
(
big
.
NewInt
(
int64
(
k
))
.
Bytes
())
b
.
SetBytes
(
Sum
(
d
))
b
.
SetBytes
(
Sum
(
d
))
pk
:=
b
.
Uint64
()
&
((
1
<<
((
L
-
1
)
*
3
))
-
1
)
pk
:=
b
.
Uint64
()
&
((
1
<<
((
L
-
1
)
*
3
))
-
1
)
sha
.
Write
(
dag
.
Node
(
L
-
1
,
pk
)
.
Bytes
())
sha
.
Write
(
dag
.
Node
(
L
-
1
,
pk
)
.
Bytes
())
}
}
ret
.
SetBytes
(
Sum
(
sha
))
ret
.
SetBytes
(
Sum
(
sha
))
return
ret
return
ret
}
}
func
Sum
(
sha
hash
.
Hash
)
[]
byte
{
func
Sum
(
sha
hash
.
Hash
)
[]
byte
{
in
:=
make
([]
byte
,
32
)
in
:=
make
([]
byte
,
32
)
return
sha
.
Sum
(
in
)
return
sha
.
Sum
(
in
)
}
}
func
(
dag
*
Dagger
)
Eval
(
N
*
big
.
Int
)
*
big
.
Int
{
func
(
dag
*
Dagger
)
Eval
(
N
*
big
.
Int
)
*
big
.
Int
{
pow
:=
ethutil
.
BigPow
(
2
,
26
)
pow
:=
ethutil
.
BigPow
(
2
,
26
)
dag
.
xn
=
N
.
Div
(
N
,
pow
)
dag
.
xn
=
N
.
Div
(
N
,
pow
)
sha
:=
sha3
.
NewKeccak256
()
sha
:=
sha3
.
NewKeccak256
()
sha
.
Reset
()
sha
.
Reset
()
ret
:=
new
(
big
.
Int
)
ret
:=
new
(
big
.
Int
)
for
k
:=
0
;
k
<
4
;
k
++
{
for
k
:=
0
;
k
<
4
;
k
++
{
d
:=
sha3
.
NewKeccak256
()
d
:=
sha3
.
NewKeccak256
()
b
:=
new
(
big
.
Int
)
b
:=
new
(
big
.
Int
)
d
.
Reset
()
d
.
Reset
()
d
.
Write
(
dag
.
hash
.
Bytes
())
d
.
Write
(
dag
.
hash
.
Bytes
())
d
.
Write
(
dag
.
xn
.
Bytes
())
d
.
Write
(
dag
.
xn
.
Bytes
())
d
.
Write
(
N
.
Bytes
())
d
.
Write
(
N
.
Bytes
())
d
.
Write
(
big
.
NewInt
(
int64
(
k
))
.
Bytes
())
d
.
Write
(
big
.
NewInt
(
int64
(
k
))
.
Bytes
())
b
.
SetBytes
(
Sum
(
d
))
b
.
SetBytes
(
Sum
(
d
))
pk
:=
(
b
.
Uint64
()
&
0x1ffffff
)
pk
:=
(
b
.
Uint64
()
&
0x1ffffff
)
sha
.
Write
(
dag
.
Node
(
9
,
pk
)
.
Bytes
())
sha
.
Write
(
dag
.
Node
(
9
,
pk
)
.
Bytes
())
}
}
return
ret
.
SetBytes
(
Sum
(
sha
))
return
ret
.
SetBytes
(
Sum
(
sha
))
}
}
dagger_test.go
View file @
9571a512
package
main
package
main
import
(
import
(
"testin
g"
"math/bi
g"
"math/bi
g"
"testin
g"
)
)
func
BenchmarkDaggerSearch
(
b
*
testing
.
B
)
{
func
BenchmarkDaggerSearch
(
b
*
testing
.
B
)
{
hash
:=
big
.
NewInt
(
0
)
hash
:=
big
.
NewInt
(
0
)
diff
:=
BigPow
(
2
,
36
)
diff
:=
BigPow
(
2
,
36
)
o
:=
big
.
NewInt
(
0
)
// nonce doesn't matter. We're only testing against speed, not validity
o
:=
big
.
NewInt
(
0
)
// nonce doesn't matter. We're only testing against speed, not validity
// Reset timer so the big generation isn't included in the benchmark
// Reset timer so the big generation isn't included in the benchmark
b
.
ResetTimer
()
b
.
ResetTimer
()
// Validate
// Validate
DaggerVerify
(
hash
,
diff
,
o
)
DaggerVerify
(
hash
,
diff
,
o
)
}
}
dev_console.go
View file @
9571a512
package
main
package
main
import
(
import
(
"fmt
"
"bufio
"
"bufio
"
"encoding/hex
"
"string
s"
"error
s"
"os
"
"fmt
"
"errors
"
"github.com/ethereum/ethdb-go
"
"encoding/hex
"
"github.com/ethereum/ethutil-go
"
"github.com/ethereum/ethdb-go
"
"os
"
"github.com/ethereum/ethutil-go
"
"strings
"
)
)
type
Console
struct
{
type
Console
struct
{
db
*
ethdb
.
MemDatabase
db
*
ethdb
.
MemDatabase
trie
*
ethutil
.
Trie
trie
*
ethutil
.
Trie
}
}
func
NewConsole
()
*
Console
{
func
NewConsole
()
*
Console
{
db
,
_
:=
ethdb
.
NewMemDatabase
()
db
,
_
:=
ethdb
.
NewMemDatabase
()
trie
:=
ethutil
.
NewTrie
(
db
,
""
)
trie
:=
ethutil
.
NewTrie
(
db
,
""
)
return
&
Console
{
db
:
db
,
trie
:
trie
}
return
&
Console
{
db
:
db
,
trie
:
trie
}
}
}
func
(
i
*
Console
)
ValidateInput
(
action
string
,
argumentLength
int
)
error
{
func
(
i
*
Console
)
ValidateInput
(
action
string
,
argumentLength
int
)
error
{
err
:=
false
err
:=
false
var
expArgCount
int
var
expArgCount
int
switch
{
switch
{
case
action
==
"update"
&&
argumentLength
!=
2
:
case
action
==
"update"
&&
argumentLength
!=
2
:
err
=
true
err
=
true
expArgCount
=
2
expArgCount
=
2
case
action
==
"get"
&&
argumentLength
!=
1
:
case
action
==
"get"
&&
argumentLength
!=
1
:
err
=
true
err
=
true
expArgCount
=
1
expArgCount
=
1
case
action
==
"dag"
&&
argumentLength
!=
2
:
case
action
==
"dag"
&&
argumentLength
!=
2
:
err
=
true
err
=
true
expArgCount
=
2
expArgCount
=
2
}
}
if
err
{
if
err
{
return
errors
.
New
(
fmt
.
Sprintf
(
"'%s' requires %d args, got %d"
,
action
,
expArgCount
,
argumentLength
))
return
errors
.
New
(
fmt
.
Sprintf
(
"'%s' requires %d args, got %d"
,
action
,
expArgCount
,
argumentLength
))
}
else
{
}
else
{
return
nil
return
nil
}
}
}
}
func
(
i
*
Console
)
ParseInput
(
input
string
)
bool
{
func
(
i
*
Console
)
ParseInput
(
input
string
)
bool
{
scanner
:=
bufio
.
NewScanner
(
strings
.
NewReader
(
input
))
scanner
:=
bufio
.
NewScanner
(
strings
.
NewReader
(
input
))
scanner
.
Split
(
bufio
.
ScanWords
)
scanner
.
Split
(
bufio
.
ScanWords
)
count
:=
0
count
:=
0
var
tokens
[]
string
var
tokens
[]
string
for
scanner
.
Scan
()
{
for
scanner
.
Scan
()
{
count
++
count
++
tokens
=
append
(
tokens
,
scanner
.
Text
())
tokens
=
append
(
tokens
,
scanner
.
Text
())
}
}
if
err
:=
scanner
.
Err
();
err
!=
nil
{
if
err
:=
scanner
.
Err
();
err
!=
nil
{
fmt
.
Fprintln
(
os
.
Stderr
,
"reading input:"
,
err
)
fmt
.
Fprintln
(
os
.
Stderr
,
"reading input:"
,
err
)
}
}
if
len
(
tokens
)
==
0
{
return
true
}
if
len
(
tokens
)
==
0
{
return
true
}
err
:=
i
.
ValidateInput
(
tokens
[
0
],
count
-
1
)
err
:=
i
.
ValidateInput
(
tokens
[
0
],
count
-
1
)
if
err
!=
nil
{
if
err
!=
nil
{
fmt
.
Println
(
err
)
fmt
.
Println
(
err
)
}
else
{
}
else
{
switch
tokens
[
0
]
{
switch
tokens
[
0
]
{
case
"update"
:
case
"update"
:
i
.
trie
.
Update
(
tokens
[
1
],
tokens
[
2
])
i
.
trie
.
Update
(
tokens
[
1
],
tokens
[
2
])
fmt
.
Println
(
hex
.
EncodeToString
([]
byte
(
i
.
trie
.
Root
)))
fmt
.
Println
(
hex
.
EncodeToString
([]
byte
(
i
.
trie
.
Root
)))
case
"get"
:
case
"get"
:
fmt
.
Println
(
i
.
trie
.
Get
(
tokens
[
1
]))
fmt
.
Println
(
i
.
trie
.
Get
(
tokens
[
1
]))
case
"root"
:
case
"root"
:
fmt
.
Println
(
hex
.
EncodeToString
([]
byte
(
i
.
trie
.
Root
)))
fmt
.
Println
(
hex
.
EncodeToString
([]
byte
(
i
.
trie
.
Root
)))
case
"rawroot"
:
case
"rawroot"
:
fmt
.
Println
(
i
.
trie
.
Root
)
fmt
.
Println
(
i
.
trie
.
Root
)
case
"print"
:
case
"print"
:
i
.
db
.
Print
()
i
.
db
.
Print
()
case
"dag"
:
case
"dag"
:
fmt
.
Println
(
DaggerVerify
(
ethutil
.
Big
(
tokens
[
1
]),
// hash
fmt
.
Println
(
DaggerVerify
(
ethutil
.
Big
(
tokens
[
1
]),
// hash
ethutil
.
BigPow
(
2
,
36
),
// diff
ethutil
.
BigPow
(
2
,
36
),
// diff
ethutil
.
Big
(
tokens
[
2
])))
// nonce
ethutil
.
Big
(
tokens
[
2
])))
// nonce
case
"exit"
,
"quit"
,
"q"
:
case
"exit"
,
"quit"
,
"q"
:
return
false
return
false
case
"help"
:
case
"help"
:
fmt
.
Printf
(
"COMMANDS:
\n
"
+
fmt
.
Printf
(
"COMMANDS:
\n
"
+
"
\0
33[1m= DB =
\0
33[0m
\n
"
+
"
\0
33[1m= DB =
\0
33[0m
\n
"
+
"update KEY VALUE - Updates/Creates a new value for the given key
\n
"
+
"update KEY VALUE - Updates/Creates a new value for the given key
\n
"
+
"get KEY - Retrieves the given key
\n
"
+
"get KEY - Retrieves the given key
\n
"
+
"root - Prints the hex encoded merkle root
\n
"
+
"root - Prints the hex encoded merkle root
\n
"
+
"rawroot - Prints the raw merkle root
\n
"
+
"rawroot - Prints the raw merkle root
\n
"
+
"
\0
33[1m= Dagger =
\0
33[0m
\n
"
+
"
\0
33[1m= Dagger =
\0
33[0m
\n
"
+
"dag HASH NONCE - Verifies a nonce with the given hash with dagger
\n
"
)
"dag HASH NONCE - Verifies a nonce with the given hash with dagger
\n
"
)
default
:
default
:
fmt
.
Println
(
"Unknown command:"
,
tokens
[
0
])
fmt
.
Println
(
"Unknown command:"
,
tokens
[
0
])
}
}
}
}
return
true
return
true
}
}
func
(
i
*
Console
)
Start
()
{
func
(
i
*
Console
)
Start
()
{
fmt
.
Printf
(
"Eth Console. Type (help) for help
\n
"
)
fmt
.
Printf
(
"Eth Console. Type (help) for help
\n
"
)
reader
:=
bufio
.
NewReader
(
os
.
Stdin
)
reader
:=
bufio
.
NewReader
(
os
.
Stdin
)
for
{
for
{
fmt
.
Printf
(
"eth >>> "
)
fmt
.
Printf
(
"eth >>> "
)
str
,
_
,
err
:=
reader
.
ReadLine
()
str
,
_
,
err
:=
reader
.
ReadLine
()
if
err
!=
nil
{
if
err
!=
nil
{
fmt
.
Println
(
"Error reading input"
,
err
)
fmt
.
Println
(
"Error reading input"
,
err
)
}
else
{
}
else
{
if
!
i
.
ParseInput
(
string
(
str
))
{
if
!
i
.
ParseInput
(
string
(
str
))
{
return
return
}
}
}
}
}
}
}
}
ethereum.go
View file @
9571a512
package
main
package
main
import
(
import
(
"fmt
"
"flag
"
"os
"
"fmt
"
"os/signal
"
"github.com/ethereum/ethutil-go
"
"fla
g"
"lo
g"
"runtime
"
"os
"
"log
"
"os/signal
"
"github.com/ethereum/ethutil-go
"
"runtime
"
)
)
const
Debug
=
true
const
Debug
=
true
var
StartConsole
bool
var
StartConsole
bool
var
StartMining
bool
var
StartMining
bool
func
Init
()
{
func
Init
()
{
flag
.
BoolVar
(
&
StartConsole
,
"c"
,
false
,
"debug and testing console"
)
flag
.
BoolVar
(
&
StartConsole
,
"c"
,
false
,
"debug and testing console"
)
flag
.
BoolVar
(
&
StartMining
,
"m"
,
false
,
"start dagger mining"
)
flag
.
BoolVar
(
&
StartMining
,
"m"
,
false
,
"start dagger mining"
)
flag
.
Parse
()
flag
.
Parse
()
}
}
// Register interrupt handlers so we can stop the server
// Register interrupt handlers so we can stop the server
func
RegisterInterupts
(
s
*
Server
)
{
func
RegisterInterupts
(
s
*
Server
)
{
// Buffered chan of one is enough
// Buffered chan of one is enough
c
:=
make
(
chan
os
.
Signal
,
1
)
c
:=
make
(
chan
os
.
Signal
,
1
)
// Notify about interrupts for now
// Notify about interrupts for now
signal
.
Notify
(
c
,
os
.
Interrupt
)
signal
.
Notify
(
c
,
os
.
Interrupt
)
go
func
()
{
go
func
()
{
for
sig
:=
range
c
{
for
sig
:=
range
c
{
fmt
.
Printf
(
"Shutting down (%v) ...
\n
"
,
sig
)
fmt
.
Printf
(
"Shutting down (%v) ...
\n
"
,
sig
)
s
.
Stop
()
s
.
Stop
()
}
}
}()
}()
}
}
func
main
()
{
func
main
()
{
runtime
.
GOMAXPROCS
(
runtime
.
NumCPU
())
runtime
.
GOMAXPROCS
(
runtime
.
NumCPU
())
ethutil
.
InitFees
()
ethutil
.
InitFees
()
Init
()
Init
()
if
StartConsole
{
if
StartConsole
{
console
:=
NewConsole
()
console
:=
NewConsole
()
console
.
Start
()
console
.
Start
()
}
else
{
}
else
{
log
.
Println
(
"Starting Ethereum"
)
log
.
Println
(
"Starting Ethereum"
)
server
,
err
:=
NewServer
()
server
,
err
:=
NewServer
()
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Println
(
err
)
log
.
Println
(
err
)
return
return
}
}
RegisterInterupts
(
server
)
RegisterInterupts
(
server
)
if
StartMining
{
if
StartMining
{
log
.
Println
(
"Mining started"
)
log
.
Println
(
"Mining started"
)
dagger
:=
&
Dagger
{}
dagger
:=
&
Dagger
{}
go
func
()
{
go
func
()
{
for
{
for
{
res
:=
dagger
.
Search
(
ethutil
.
Big
(
"0"
),
ethutil
.
BigPow
(
2
,
36
))
res
:=
dagger
.
Search
(
ethutil
.
Big
(
"0"
),
ethutil
.
BigPow
(
2
,
36
))
server
.
Broadcast
(
"block"
,
Encode
(
res
.
String
()))
server
.
Broadcast
(
"block"
,
Encode
(
res
.
String
()))
}
}
}()
}()
}
}
server
.
Start
()
server
.
Start
()
err
=
server
.
ConnectToPeer
(
"localhost:12345"
)
err
=
server
.
ConnectToPeer
(
"localhost:12345"
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Println
(
err
)
log
.
Println
(
err
)
server
.
Stop
()
server
.
Stop
()
return
return
}
}
// Wait for shutdown
// Wait for shutdown
server
.
WaitForShutdown
()
server
.
WaitForShutdown
()
}
}
}
}
peer.go
View file @
9571a512
package
main
package
main
import
(
import
(
"net
"
"github.com/ethereum/ethwire-go
"
"log"
"log"
"github.com/ethereum/ethwire-go
"
"net
"
)
)
type
Peer
struct
{
type
Peer
struct
{
// Server interface
// Server interface
server
*
Server
server
*
Server
// Net connection
// Net connection
conn
net
.
Conn
conn
net
.
Conn
// Output queue which is used to communicate and handle messages
// Output queue which is used to communicate and handle messages
outputQueue
chan
ethwire
.
InOutMsg
outputQueue
chan
ethwire
.
InOutMsg
// Quit channel
// Quit channel
quit
chan
bool
quit
chan
bool
}
}
func
NewPeer
(
conn
net
.
Conn
,
server
*
Server
)
*
Peer
{
func
NewPeer
(
conn
net
.
Conn
,
server
*
Server
)
*
Peer
{
return
&
Peer
{
return
&
Peer
{
outputQueue
:
make
(
chan
ethwire
.
InOutMsg
,
1
),
// Buffered chan of 1 is enough
outputQueue
:
make
(
chan
ethwire
.
InOutMsg
,
1
),
// Buffered chan of 1 is enough
quit
:
make
(
chan
bool
),
quit
:
make
(
chan
bool
),
server
:
server
,
server
:
server
,
conn
:
conn
,
conn
:
conn
,
}
}
}
}
// Outputs any RLP encoded data to the peer
// Outputs any RLP encoded data to the peer
func
(
p
*
Peer
)
QueueMessage
(
msgType
string
,
data
[]
byte
)
{
func
(
p
*
Peer
)
QueueMessage
(
msgType
string
,
data
[]
byte
)
{
p
.
outputQueue
<-
ethwire
.
InOutMsg
{
MsgType
:
msgType
,
Data
:
data
}
p
.
outputQueue
<-
ethwire
.
InOutMsg
{
MsgType
:
msgType
,
Data
:
data
}
}
}
// Outbound message handler. Outbound messages are handled here
// Outbound message handler. Outbound messages are handled here
func
(
p
*
Peer
)
HandleOutbound
()
{
func
(
p
*
Peer
)
HandleOutbound
()
{
out
:
out
:
for
{
for
{
select
{
select
{
// Main message queue. All outbound messages are processed through here
// Main message queue. All outbound messages are processed through here
case
msg
:=
<-
p
.
outputQueue
:
case
msg
:=
<-
p
.
outputQueue
:
// TODO Message checking and handle accordingly
// TODO Message checking and handle accordingly
err
:=
ethwire
.
WriteMessage
(
p
.
conn
,
msg
)
err
:=
ethwire
.
WriteMessage
(
p
.
conn
,
msg
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Println
(
err
)
log
.
Println
(
err
)
// Stop the client if there was an error writing to it
// Stop the client if there was an error writing to it
p
.
Stop
()
p
.
Stop
()
}
}
// Break out of the for loop if a quit message is posted
// Break out of the for loop if a quit message is posted
case
<-
p
.
quit
:
case
<-
p
.
quit
:
break
out
break
out
}
}
}
}
}
}
// Inbound handler. Inbound messages are received here and passed to the appropriate methods
// Inbound handler. Inbound messages are received here and passed to the appropriate methods
func
(
p
*
Peer
)
HandleInbound
()
{
func
(
p
*
Peer
)
HandleInbound
()
{
defer
p
.
Stop
()
defer
p
.
Stop
()
out
:
out
:
for
{
for
{
// Wait for a message from the peer
// Wait for a message from the peer
msg
,
err
:=
ethwire
.
ReadMessage
(
p
.
conn
)
msg
,
err
:=
ethwire
.
ReadMessage
(
p
.
conn
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Println
(
err
)
log
.
Println
(
err
)
break
out
break
out
}
}
// TODO
// TODO
data
,
_
:=
Decode
(
msg
.
Data
,
0
)
data
,
_
:=
Decode
(
msg
.
Data
,
0
)
log
.
Printf
(
"%s, %s
\n
"
,
msg
.
MsgType
,
data
)
log
.
Printf
(
"%s, %s
\n
"
,
msg
.
MsgType
,
data
)
}
}
// Notify the out handler we're quiting
// Notify the out handler we're quiting
p
.
quit
<-
true
p
.
quit
<-
true
}
}
func
(
p
*
Peer
)
Start
()
{
func
(
p
*
Peer
)
Start
()
{
// Run the outbound handler in a new goroutine
// Run the outbound handler in a new goroutine
go
p
.
HandleOutbound
()
go
p
.
HandleOutbound
()
// Run the inbound handler in a new goroutine
// Run the inbound handler in a new goroutine
go
p
.
HandleInbound
()
go
p
.
HandleInbound
()
}
}
func
(
p
*
Peer
)
Stop
()
{
func
(
p
*
Peer
)
Stop
()
{
p
.
conn
.
Close
()
p
.
conn
.
Close
()
p
.
quit
<-
true
p
.
quit
<-
true
}
}
rlp.go
View file @
9571a512
This diff is collapsed.
Click to expand it.
rlp_test.go
View file @
9571a512
package
main
package
main
import
(
import
(
"testing
"
"fmt
"
"fmt
"
"testing
"
)
)
func
TestEncode
(
t
*
testing
.
T
)
{
func
TestEncode
(
t
*
testing
.
T
)
{
strRes
:=
"Cdog"
strRes
:=
"Cdog"
bytes
:=
Encode
(
"dog"
)
bytes
:=
Encode
(
"dog"
)
str
:=
string
(
bytes
)
str
:=
string
(
bytes
)
if
str
!=
strRes
{
if
str
!=
strRes
{
t
.
Error
(
fmt
.
Sprintf
(
"Expected %q, got %q"
,
strRes
,
str
))
t
.
Error
(
fmt
.
Sprintf
(
"Expected %q, got %q"
,
strRes
,
str
))
}
}
//dec,_ := Decode(bytes, 0)
//dec,_ := Decode(bytes, 0)
sliceRes
:=
"
\x83
CdogCgodCcat"
sliceRes
:=
"
\x83
CdogCgodCcat"
strs
:=
[]
string
{
"dog"
,
"god"
,
"cat"
}
strs
:=
[]
string
{
"dog"
,
"god"
,
"cat"
}
bytes
=
Encode
(
strs
)
bytes
=
Encode
(
strs
)
slice
:=
string
(
bytes
)
slice
:=
string
(
bytes
)
if
slice
!=
sliceRes
{
if
slice
!=
sliceRes
{
t
.
Error
(
fmt
.
Sprintf
(
"Expected %q, got %q"
,
sliceRes
,
slice
))
t
.
Error
(
fmt
.
Sprintf
(
"Expected %q, got %q"
,
sliceRes
,
slice
))
}
}
//dec,_ = Decode(bytes, 0)
//dec,_ = Decode(bytes, 0)
}
}
func
TestMultiEncode
(
t
*
testing
.
T
)
{
func
TestMultiEncode
(
t
*
testing
.
T
)
{
inter
:=
[]
interface
{}{
inter
:=
[]
interface
{}{
[]
interface
{}{
[]
interface
{}{
"1"
,
"2"
,
"3"
,
"1"
,
"2"
,
"3"
,
},
},
[]
string
{
[]
string
{
"string"
,
"string"
,
"string2"
,
"string2"
,
"
\x86
A0J1234567890A
\x00
B20A0
\x82
F395843F657986"
,
"
\x86
A0J1234567890A
\x00
B20A0
\x82
F395843F657986"
,
"
\x86
A0J1234567890A
\x00
B20A0
\x8c
F395843F657986I335612448F524099H16716881A0H13114947G2039362G1507139H16719697G1048387E65360"
,
"
\x86
A0J1234567890A
\x00
B20A0
\x8c
F395843F657986I335612448F524099H16716881A0H13114947G2039362G1507139H16719697G1048387E65360"
,
},
},
"test"
,
"test"
,
}
}
bytes
:=
Encode
(
inter
)
bytes
:=
Encode
(
inter
)
Decode
(
bytes
,
0
)
Decode
(
bytes
,
0
)
}
}
func
BenchmarkEncodeDecode
(
b
*
testing
.
B
)
{
func
BenchmarkEncodeDecode
(
b
*
testing
.
B
)
{
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
bytes
:=
Encode
([]
string
{
"dog"
,
"god"
,
"cat"
})
bytes
:=
Encode
([]
string
{
"dog"
,
"god"
,
"cat"
})
Decode
(
bytes
,
0
)
Decode
(
bytes
,
0
)
}
}
}
}
server.go
View file @
9571a512
package
main
package
main
import
(
import
(
"container/list"
"container/list"
"net
"
"github.com/ethereum/ethdb-go
"
"log
"
"github.com/ethereum/ethutil-go
"
_
"time
"
"log
"
"github.com/ethereum/ethdb-go
"
"net
"
"github.com/ethereum/ethutil-go
"
_
"time
"
)
)
type
Server
struct
{
type
Server
struct
{
// Channel for shutting down the server
// Channel for shutting down the server
shutdownChan
chan
bool
shutdownChan
chan
bool
// DB interface
// DB interface
db
*
ethdb
.
LDBDatabase
db
*
ethdb
.
LDBDatabase
// Block manager for processing new blocks and managing the block chain
// Block manager for processing new blocks and managing the block chain
blockManager
*
BlockManager
blockManager
*
BlockManager
// Peers (NYI)
// Peers (NYI)
peers
*
list
.
List
peers
*
list
.
List
}
}
func
NewServer
()
(
*
Server
,
error
)
{
func
NewServer
()
(
*
Server
,
error
)
{
db
,
err
:=
ethdb
.
NewLDBDatabase
()
db
,
err
:=
ethdb
.
NewLDBDatabase
()
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
ethutil
.
SetConfig
(
db
)
ethutil
.
SetConfig
(
db
)
server
:=
&
Server
{
server
:=
&
Server
{
shutdownChan
:
make
(
chan
bool
),
shutdownChan
:
make
(
chan
bool
),
blockManager
:
NewBlockManager
(),
blockManager
:
NewBlockManager
(),
db
:
db
,
db
:
db
,
peers
:
list
.
New
(),
peers
:
list
.
New
(),
}
}
return
server
,
nil
return
server
,
nil
}
}
func
(
s
*
Server
)
AddPeer
(
conn
net
.
Conn
)
{
func
(
s
*
Server
)
AddPeer
(
conn
net
.
Conn
)
{
peer
:=
NewPeer
(
conn
,
s
)
peer
:=
NewPeer
(
conn
,
s
)
s
.
peers
.
PushBack
(
peer
)
s
.
peers
.
PushBack
(
peer
)
peer
.
Start
()
peer
.
Start
()
log
.
Println
(
"Peer connected ::"
,
conn
.
RemoteAddr
())
log
.
Println
(
"Peer connected ::"
,
conn
.
RemoteAddr
())
}
}
func
(
s
*
Server
)
ConnectToPeer
(
addr
string
)
error
{
func
(
s
*
Server
)
ConnectToPeer
(
addr
string
)
error
{
conn
,
err
:=
net
.
Dial
(
"tcp"
,
addr
)
conn
,
err
:=
net
.
Dial
(
"tcp"
,
addr
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
peer
:=
NewPeer
(
conn
,
s
)
peer
:=
NewPeer
(
conn
,
s
)
s
.
peers
.
PushBack
(
peer
)
s
.
peers
.
PushBack
(
peer
)
peer
.
Start
()
peer
.
Start
()
log
.
Println
(
"Connected to peer ::"
,
conn
.
RemoteAddr
())
log
.
Println
(
"Connected to peer ::"
,
conn
.
RemoteAddr
())
return
nil
return
nil
}
}
func
(
s
*
Server
)
Broadcast
(
msgType
string
,
data
[]
byte
)
{
func
(
s
*
Server
)
Broadcast
(
msgType
string
,
data
[]
byte
)
{
for
e
:=
s
.
peers
.
Front
();
e
!=
nil
;
e
=
e
.
Next
()
{
for
e
:=
s
.
peers
.
Front
();
e
!=
nil
;
e
=
e
.
Next
()
{
if
peer
,
ok
:=
e
.
Value
.
(
*
Peer
);
ok
{
if
peer
,
ok
:=
e
.
Value
.
(
*
Peer
);
ok
{
peer
.
QueueMessage
(
msgType
,
data
)
peer
.
QueueMessage
(
msgType
,
data
)
}
}
}
}
}
}
// Start the server
// Start the server
func
(
s
*
Server
)
Start
()
{
func
(
s
*
Server
)
Start
()
{
// For now this function just blocks the main thread
// For now this function just blocks the main thread
ln
,
err
:=
net
.
Listen
(
"tcp"
,
":12345"
)
ln
,
err
:=
net
.
Listen
(
"tcp"
,
":12345"
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Fatal
(
err
)
log
.
Fatal
(
err
)
}
}
go
func
()
{
go
func
()
{
for
{
for
{
conn
,
err
:=
ln
.
Accept
()
conn
,
err
:=
ln
.
Accept
()
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Println
(
err
)
log
.
Println
(
err
)
continue
continue
}
}
go
s
.
AddPeer
(
conn
)
go
s
.
AddPeer
(
conn
)
}
}
}()
}()
// TMP
// TMP
//go func() {
//go func() {
// for {
// for {
// s.Broadcast("block", Encode("blockdata"))
// s.Broadcast("block", Encode("blockdata"))
//
//
// time.Sleep(100 * time.Millisecond)
// time.Sleep(100 * time.Millisecond)
// }
// }
// }()
// }()
}
}
func
(
s
*
Server
)
Stop
()
{
func
(
s
*
Server
)
Stop
()
{
// Close the database
// Close the database
defer
s
.
db
.
Close
()
defer
s
.
db
.
Close
()
// Loop thru the peers and close them (if we had them)
// Loop thru the peers and close them (if we had them)
for
e
:=
s
.
peers
.
Front
();
e
!=
nil
;
e
=
e
.
Next
()
{
for
e
:=
s
.
peers
.
Front
();
e
!=
nil
;
e
=
e
.
Next
()
{
if
peer
,
ok
:=
e
.
Value
.
(
*
Peer
);
ok
{
if
peer
,
ok
:=
e
.
Value
.
(
*
Peer
);
ok
{
peer
.
Stop
()
peer
.
Stop
()
}
}
}
}
s
.
shutdownChan
<-
true
s
.
shutdownChan
<-
true
}
}
// This function will wait for a shutdown and resumes main thread execution
// This function will wait for a shutdown and resumes main thread execution
func
(
s
*
Server
)
WaitForShutdown
()
{
func
(
s
*
Server
)
WaitForShutdown
()
{
<-
s
.
shutdownChan
<-
s
.
shutdownChan
}
}
test_runner.go
View file @
9571a512
package
main
package
main
import
(
import
(
"fmt
"
"encoding/json
"
"testing
"
"fmt
"
"encoding/json
"
"testing
"
)
)
type
TestSource
struct
{
type
TestSource
struct
{
Inputs
map
[
string
]
string
Inputs
map
[
string
]
string
Expectation
string
Expectation
string
}
}
func
NewTestSource
(
source
string
)
*
TestSource
{
func
NewTestSource
(
source
string
)
*
TestSource
{
s
:=
&
TestSource
{}
s
:=
&
TestSource
{}
err
:=
json
.
Unmarshal
([]
byte
(
source
),
s
)
err
:=
json
.
Unmarshal
([]
byte
(
source
),
s
)
if
err
!=
nil
{
if
err
!=
nil
{
fmt
.
Println
(
err
)
fmt
.
Println
(
err
)
}
}
return
s
return
s
}
}
type
TestRunner
struct
{
type
TestRunner
struct
{
source
*
TestSource
source
*
TestSource
}
}
func
NewTestRunner
(
t
*
testing
.
T
)
*
TestRunner
{
func
NewTestRunner
(
t
*
testing
.
T
)
*
TestRunner
{
return
&
TestRunner
{}
return
&
TestRunner
{}
}
}
func
(
runner
*
TestRunner
)
RunFromString
(
input
string
,
Cb
func
(
*
TestSource
))
{
func
(
runner
*
TestRunner
)
RunFromString
(
input
string
,
Cb
func
(
*
TestSource
))
{
source
:=
NewTestSource
(
input
)
source
:=
NewTestSource
(
input
)
Cb
(
source
)
Cb
(
source
)
}
}
test_runner_test.go
View file @
9571a512
package
main
package
main
import
(
import
(
_
"fmt
"
"encoding/hex
"
"testing
"
_
"fmt
"
"encoding/hex
"
"testing
"
)
)
var
testsource
=
`{"Inputs":{
var
testsource
=
`{"Inputs":{
...
@@ -15,17 +15,17 @@ var testsource = `{"Inputs":{
...
@@ -15,17 +15,17 @@ var testsource = `{"Inputs":{
}`
}`
func
TestTestRunner
(
t
*
testing
.
T
)
{
func
TestTestRunner
(
t
*
testing
.
T
)
{
db
,
_
:=
NewMemDatabase
()
db
,
_
:=
NewMemDatabase
()
trie
:=
NewTrie
(
db
,
""
)
trie
:=
NewTrie
(
db
,
""
)
runner
:=
NewTestRunner
(
t
)
runner
:=
NewTestRunner
(
t
)
runner
.
RunFromString
(
testsource
,
func
(
source
*
TestSource
)
{
runner
.
RunFromString
(
testsource
,
func
(
source
*
TestSource
)
{
for
key
,
value
:=
range
source
.
Inputs
{
for
key
,
value
:=
range
source
.
Inputs
{
trie
.
Update
(
key
,
value
)
trie
.
Update
(
key
,
value
)
}
}
if
hex
.
EncodeToString
([]
byte
(
trie
.
root
))
!=
source
.
Expectation
{
if
hex
.
EncodeToString
([]
byte
(
trie
.
root
))
!=
source
.
Expectation
{
t
.
Error
(
"trie root did not match"
)
t
.
Error
(
"trie root did not match"
)
}
}
})
})
}
}
testing.go
View file @
9571a512
package
main
package
main
/*
/*
import (
import (
...
...
vm.go
View file @
9571a512
This diff is collapsed.
Click to expand it.
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