Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
duniter
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container registry
Model registry
Monitor
Service Desk
Analyze
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
nodes
typescript
duniter
Commits
809b413e
Commit
809b413e
authored
7 years ago
by
Cédric Moreau
Browse files
Options
Downloads
Patches
Plain Diff
[fix]
#1076
Write the Unit Test
parent
43dce8c9
Branches
Branches containing commit
Tags
Tags containing commit
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
.gitignore
+2
-0
2 additions, 0 deletions
.gitignore
test/fast/fork-resolution-3-3.ts
+373
-0
373 additions, 0 deletions
test/fast/fork-resolution-3-3.ts
with
375 additions
and
0 deletions
.gitignore
+
2
−
0
View file @
809b413e
...
@@ -50,3 +50,5 @@ test/integration/documents-currency.js*
...
@@ -50,3 +50,5 @@ test/integration/documents-currency.js*
test/integration/documents-currency.d.ts
test/integration/documents-currency.d.ts
test/fast/modules/crawler/block_pulling.js*
test/fast/modules/crawler/block_pulling.js*
test/fast/modules/crawler/block_pulling.d.ts
test/fast/modules/crawler/block_pulling.d.ts
test/fast/fork*.js*
test/fast/fork*.d.ts
This diff is collapsed.
Click to expand it.
test/fast/fork-resolution-3-3.ts
0 → 100644
+
373
−
0
View file @
809b413e
import
*
as
assert
from
'
assert
'
describe
(
"
Fork resolution 3-3 algo
"
,
()
=>
{
it
(
'
should switch on a valid fork
'
,
()
=>
{
// B10 -- B11 -- B12 -- B13
// | `- C12 -- C13 -- C14 -- C15 -- C16
// |
// `- (= B13 - ForkWindowSize)
const
bc
=
new
Blockchain
(
Block
.
from
(
"
B10
"
))
bc
.
add
(
Block
.
from
(
"
B11
"
))
bc
.
add
(
Block
.
from
(
"
B12
"
))
bc
.
add
(
Block
.
from
(
"
B13
"
))
assert
.
equal
(
bc
.
current
.
number
,
13
)
const
sbx
=
new
BlockSandbox
([
Block
.
from
(
"
C12
"
,
"
B11
"
),
Block
.
from
(
"
C13
"
),
Block
.
from
(
"
C14
"
),
Block
.
from
(
"
C15
"
),
Block
.
from
(
"
C16
"
)
])
const
switcher
=
new
BlockchainSwitcher
(
bc
,
sbx
)
switcher
.
tryToFork
()
assert
.
equal
(
bc
.
current
.
number
,
16
)
assert
.
equal
(
bc
.
current
.
hash
,
"
C16
"
)
})
it
(
'
should not switch if no fork block 3-3 exist
'
,
async
()
=>
{
// B10 -- B11 -- B12 -- B13
// | `- C12 -- C13 -- C14 -- C15
// |
// `- (= B13 - ForkWindowSize)
const
bc
=
new
Blockchain
(
Block
.
from
(
"
B10
"
))
bc
.
add
(
Block
.
from
(
"
B11
"
))
bc
.
add
(
Block
.
from
(
"
B12
"
))
bc
.
add
(
Block
.
from
(
"
B13
"
))
assert
.
equal
(
bc
.
current
.
number
,
13
)
const
sbx
=
new
BlockSandbox
([
Block
.
from
(
"
C12
"
,
"
B11
"
),
Block
.
from
(
"
C13
"
),
Block
.
from
(
"
C14
"
),
Block
.
from
(
"
C15
"
)
])
const
switcher
=
new
BlockchainSwitcher
(
bc
,
sbx
)
switcher
.
tryToFork
()
assert
.
equal
(
bc
.
current
.
number
,
13
)
assert
.
equal
(
bc
.
current
.
hash
,
"
B13
"
)
})
it
(
'
should eliminate a fork with missing blocks
'
,
async
()
=>
{
// B10 -- B11 -- B12 -- B13
// | `- C12 -- C13 -- C14 -- C15 -- C16
// |
// `- (= B13 - ForkWindowSize)
const
bc
=
new
Blockchain
(
Block
.
from
(
"
B10
"
))
bc
.
add
(
Block
.
from
(
"
B11
"
))
bc
.
add
(
Block
.
from
(
"
B12
"
))
bc
.
add
(
Block
.
from
(
"
B13
"
))
assert
.
equal
(
bc
.
current
.
number
,
13
)
const
sbx
=
new
BlockSandbox
([
Block
.
from
(
"
C14
"
),
Block
.
from
(
"
C15
"
)
])
const
switcher
=
new
BlockchainSwitcher
(
bc
,
sbx
)
switcher
.
tryToFork
()
assert
.
equal
(
bc
.
current
.
number
,
13
)
assert
.
equal
(
bc
.
current
.
hash
,
"
B13
"
)
})
it
(
'
should eliminate a fork out of fork window
'
,
async
()
=>
{
// B10 -- B11 -- B12 -- B13
// + -- C11 -- C12 -- C13 -- C14 -- C15 -- C16
// |
// `- (= B13 - ForkWindowSize)
const
bc
=
new
Blockchain
(
Block
.
from
(
"
B10
"
))
bc
.
add
(
Block
.
from
(
"
B11
"
))
bc
.
add
(
Block
.
from
(
"
B12
"
))
bc
.
add
(
Block
.
from
(
"
B13
"
))
assert
.
equal
(
bc
.
current
.
number
,
13
)
const
sbx
=
new
BlockSandbox
([
Block
.
from
(
"
C10
"
),
Block
.
from
(
"
C11
"
),
Block
.
from
(
"
C12
"
),
Block
.
from
(
"
C13
"
),
Block
.
from
(
"
C14
"
),
Block
.
from
(
"
C15
"
),
Block
.
from
(
"
C16
"
)
])
const
switcher
=
new
BlockchainSwitcher
(
bc
,
sbx
)
switcher
.
tryToFork
()
assert
.
equal
(
bc
.
current
.
number
,
13
)
assert
.
equal
(
bc
.
current
.
hash
,
"
B13
"
)
})
it
(
'
should accept a fork right on the limit of the fork window
'
,
async
()
=>
{
// B10 -- B11 -- B12 -- B13
// |` -- C11 -- C12 -- C13 -- C14 -- C15 -- C16
// |
// `- (= B13 - ForkWindowSize)
const
bc
=
new
Blockchain
(
Block
.
from
(
"
B10
"
))
bc
.
add
(
Block
.
from
(
"
B11
"
))
bc
.
add
(
Block
.
from
(
"
B12
"
))
bc
.
add
(
Block
.
from
(
"
B13
"
))
assert
.
equal
(
bc
.
current
.
number
,
13
)
const
sbx
=
new
BlockSandbox
([
Block
.
from
(
"
C11
"
,
"
B10
"
),
Block
.
from
(
"
C12
"
),
Block
.
from
(
"
C13
"
),
Block
.
from
(
"
C14
"
),
Block
.
from
(
"
C15
"
),
Block
.
from
(
"
C16
"
)
])
const
switcher
=
new
BlockchainSwitcher
(
bc
,
sbx
)
switcher
.
tryToFork
()
assert
.
equal
(
bc
.
current
.
number
,
16
)
assert
.
equal
(
bc
.
current
.
hash
,
"
C16
"
)
})
it
(
'
should eliminate a fork whose 2nd block is invalid
'
,
async
()
=>
{
// B10 -- B11 -- B12 -- B13
// | `- C12 -- C13 -- C14 -- C15 -- C16
// |
// `- (= B13 - ForkWindowSize)
const
bc
=
new
Blockchain
(
Block
.
from
(
"
B10
"
))
bc
.
add
(
Block
.
from
(
"
B11
"
))
bc
.
add
(
Block
.
from
(
"
B12
"
))
bc
.
add
(
Block
.
from
(
"
B13
"
))
assert
.
equal
(
bc
.
current
.
number
,
13
)
const
sbx
=
new
BlockSandbox
([
Block
.
from
(
"
C12
"
,
"
B11
"
),
Block
.
from
(
"
C13
"
),
Block
.
from
(
"
C14
"
,
""
,
()
=>
false
),
Block
.
from
(
"
C15
"
),
Block
.
from
(
"
C16
"
)
])
const
switcher
=
new
BlockchainSwitcher
(
bc
,
sbx
)
switcher
.
tryToFork
()
assert
.
equal
(
bc
.
current
.
number
,
13
)
assert
.
equal
(
bc
.
current
.
hash
,
"
B13
"
)
})
it
(
'
should select the longest fork
'
,
async
()
=>
{
// B10 -- B11 -- B12 -- B13
// | `- C12 -- C13 -- C14 -- C15 -- C16
// | `- D13 -- D14 -- D15 -- D16 -- D17
// |
// `- (= B13 - ForkWindowSize)
const
bc
=
new
Blockchain
(
Block
.
from
(
"
B10
"
))
bc
.
add
(
Block
.
from
(
"
B11
"
))
bc
.
add
(
Block
.
from
(
"
B12
"
))
bc
.
add
(
Block
.
from
(
"
B13
"
))
assert
.
equal
(
bc
.
current
.
number
,
13
)
const
sbx
=
new
BlockSandbox
([
Block
.
from
(
"
C12
"
,
"
B11
"
),
Block
.
from
(
"
C13
"
),
Block
.
from
(
"
C14
"
),
Block
.
from
(
"
C15
"
),
Block
.
from
(
"
C16
"
),
Block
.
from
(
"
D13
"
,
"
C12
"
),
Block
.
from
(
"
D14
"
),
Block
.
from
(
"
D15
"
),
Block
.
from
(
"
D16
"
),
Block
.
from
(
"
D17
"
)
])
const
switcher
=
new
BlockchainSwitcher
(
bc
,
sbx
)
switcher
.
tryToFork
()
assert
.
equal
(
bc
.
current
.
number
,
17
)
assert
.
equal
(
bc
.
current
.
hash
,
"
D17
"
)
})
})
const
avgGenTime
=
5
*
60
const
forkWindowSize
=
3
class
BlockchainSwitcher
{
constructor
(
private
bc
:
Blockchain
,
private
sbx
:
BlockSandbox
)
{}
tryToFork
()
{
const
current
=
this
.
bc
.
current
const
numberStart
=
current
.
number
+
3
const
timeStart
=
current
.
time
+
3
*
avgGenTime
const
suites
:
Block
[][]
=
[]
const
potentials
:
Block
[]
=
this
.
sbx
.
getPotentials
(
numberStart
,
timeStart
)
const
invalids
:
{
[
hash
:
string
]:
Block
}
=
{}
// Phase 1: find chains
for
(
const
candidate
of
potentials
)
{
const
suite
:
Block
[]
=
[]
if
(
!
invalids
[
candidate
.
hash
]
&&
!
BlockchainSwitcher
.
suitesContains
(
suites
,
candidate
))
{
let
previous
:
Block
|
null
=
candidate
,
commonRootFound
=
false
while
(
previous
&&
previous
.
number
>
current
.
number
-
forkWindowSize
)
{
suite
.
push
(
previous
)
const
previousNumber
=
previous
.
number
-
1
const
previousHash
=
previous
.
previousHash
previous
=
this
.
bc
.
getBlock
(
previousNumber
,
previousHash
)
if
(
previous
)
{
// Stop the loop: common block has been found
previous
=
null
suites
.
push
(
suite
)
commonRootFound
=
true
}
else
{
// Have a look in sandboxes
previous
=
this
.
sbx
.
getBlock
(
previousNumber
,
previousHash
)
}
}
// Forget about invalid blocks
if
(
!
commonRootFound
)
{
for
(
const
b
of
suite
)
{
invalids
[
b
.
hash
]
=
b
}
}
}
}
// Phase 2: select the best chain
let
longestChain
:
null
|
Block
[]
=
null
for
(
const
s
of
suites
)
{
s
.
reverse
()
const
reverted
=
this
.
bc
.
revertTo
(
s
[
0
].
number
-
1
)
let
added
=
true
,
i
=
0
,
successfulBlocks
:
Block
[]
=
[]
while
(
added
)
{
try
{
this
.
bc
.
add
(
s
[
i
])
successfulBlocks
.
push
(
s
[
i
])
}
catch
(
e
)
{
added
=
false
}
i
++
}
if
(
successfulBlocks
.
length
)
{
this
.
bc
.
revertTo
(
this
.
bc
.
current
.
number
-
successfulBlocks
.
length
)
}
reverted
.
reverse
()
for
(
const
b
of
reverted
)
{
this
.
bc
.
add
(
b
)
}
if
((
!
longestChain
&&
successfulBlocks
.
length
>
0
)
||
(
longestChain
&&
longestChain
.
length
<
successfulBlocks
.
length
))
{
longestChain
=
successfulBlocks
}
}
// Phase 3: a best exist? apply it if it respects the 3-3 rule
if
(
longestChain
)
{
const
b
=
longestChain
[
longestChain
.
length
-
1
]
if
(
b
.
number
>=
numberStart
&&
b
.
time
>=
timeStart
)
{
this
.
bc
.
revertTo
(
longestChain
[
0
].
number
-
1
)
for
(
const
b
of
longestChain
)
{
this
.
bc
.
add
(
b
)
}
}
}
return
this
.
bc
.
current
}
static
suitesContains
(
suites
:
Block
[][],
block
:
Block
)
{
for
(
const
suite
of
suites
)
{
for
(
const
b
of
suite
)
{
if
(
b
.
number
===
block
.
number
&&
b
.
hash
===
block
.
hash
)
{
return
true
}
}
}
return
false
}
}
class
BlockSandbox
{
constructor
(
private
blocks
:
Block
[]
=
[])
{}
getBlock
(
number
:
number
,
hash
:
string
)
{
for
(
const
b
of
this
.
blocks
)
{
if
(
b
.
number
===
number
&&
b
.
hash
===
hash
)
{
return
b
}
}
return
null
}
getPotentials
(
numberStart
:
number
,
timeStart
:
number
)
{
const
potentials
=
[]
for
(
const
b
of
this
.
blocks
)
{
if
(
b
.
number
>=
numberStart
&&
b
.
time
>=
timeStart
)
{
potentials
.
push
(
b
)
}
}
return
potentials
}
}
class
Blockchain
{
private
blocks
:
Block
[]
=
[]
constructor
(
rootBlock
:
Block
)
{
this
.
blocks
.
push
(
rootBlock
)
}
get
current
()
{
return
this
.
blocks
[
this
.
blocks
.
length
-
1
]
}
add
(
block
:
Block
)
{
if
(
!
block
.
chainsOn
(
this
.
current
))
{
throw
"
Unchainable
"
}
this
.
blocks
.
push
(
block
)
return
block
}
getBlock
(
number
:
number
,
hash
:
string
)
{
for
(
const
b
of
this
.
blocks
)
{
if
(
b
.
number
===
number
&&
b
.
hash
===
hash
)
{
return
b
}
}
return
null
}
revertTo
(
number
:
number
)
{
const
reverted
:
Block
[]
=
[]
if
(
this
.
current
.
number
<
number
)
{
throw
"
Already below this number
"
}
while
(
this
.
current
.
number
>
number
)
{
const
poped
:
Block
=
this
.
blocks
.
pop
()
as
Block
reverted
.
push
(
poped
)
}
return
reverted
}
}
class
Block
{
private
constructor
(
public
chain
:
string
,
public
number
:
number
,
private
thePreviousHash
:
string
,
private
chainsOnHook
:
(
previous
:
Block
)
=>
boolean
=
()
=>
true
)
{
}
get
hash
()
{
return
[
this
.
chain
,
this
.
number
].
join
(
''
)
}
get
time
()
{
return
this
.
number
*
avgGenTime
}
get
previousHash
()
{
return
this
.
thePreviousHash
||
[
this
.
chain
,
this
.
number
-
1
].
join
(
''
)
}
chainsOn
(
previous
:
Block
)
{
return
this
.
number
===
previous
.
number
+
1
&&
(
this
.
chain
===
previous
.
chain
||
this
.
previousHash
===
previous
.
hash
)
&&
this
.
chainsOnHook
(
previous
)
}
static
from
(
hash
:
string
,
previousHash
=
""
,
chainsOnHook
:
undefined
|
((
previous
:
Block
)
=>
boolean
)
=
undefined
)
{
const
match
=
hash
.
match
(
/
([
A-Z
])(\d
+
)
/
)
const
chain
=
match
&&
match
[
1
]
||
""
const
number
=
parseInt
(
match
&&
match
[
2
]
||
"
0
"
)
return
new
Block
(
chain
,
number
,
previousHash
,
chainsOnHook
)
}
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment