wip: misc updates
AddClaim / AddSupport is working with minimal testing done so far. RemoveClaim / RemoveSupport is implemented, but not tested yet. Some known issues: Currently, we update the BestClaim for each node in a lazy fashion. Each node could add/remove claims/supports without recalculating hash until its being obeserved externally. However, due to the "Takeover Delay" bidding rule, as the block number increases, the bestClaim might changes implicitly. The Trie can't detect this passively, and would need some mechanism for this.
This commit is contained in:
parent
8eae587539
commit
84c64c1018
8 changed files with 454 additions and 124 deletions
34
README.md
34
README.md
|
@ -15,12 +15,40 @@ coming soon
|
|||
This project requires [Go v1.10](https://golang.org/doc/install) or higher.
|
||||
|
||||
``` bash
|
||||
go get -v github.com/lbryio/claimtrie
|
||||
go get -u -v github.com/lbryio/claimtrie
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
Refer to [triesh](https://github.com/lbryio/claimtrie/blob/master/cmd/claimtrie)
|
||||
Refer to [claimtrie](https://github.com/lbryio/claimtrie/blob/master/cmd/claimtrie) for an interactive CLI tool.
|
||||
|
||||
``` bash
|
||||
NAME:
|
||||
claimtrie - A CLI tool for ClaimTrie
|
||||
|
||||
USAGE:
|
||||
main [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
0.0.1
|
||||
|
||||
COMMANDS:
|
||||
add-claim, ac Claim a name with specified amount. (outPoint is generated randomly, if unspecified)
|
||||
add-support, as Add support to a specified Claim. (outPoint is generated randomly, if unspecified)
|
||||
spend-claim, sc Spend a specified Claim.
|
||||
spend-support, ss Spend a specified Support.
|
||||
show, s Show the Key-Value pairs of the Stage or specified commit. (links nodes are showed if -a is also specified)
|
||||
merkle, m Show the Merkle Hash of the Stage.
|
||||
commit, c Commit the current Stage to commit database.
|
||||
reset, r Reset the Stage to a specified commit.
|
||||
log, l List the commits in the coommit database.
|
||||
shell, sh Enter interactive mode
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
--help, -h show help
|
||||
--version, -v print the version
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
|
@ -44,4 +72,4 @@ Our PGP key is [here](https://keybase.io/lbry/key.asc) if you need it.
|
|||
|
||||
## Contact
|
||||
|
||||
The primary contact for this project is [@lyoshenka](https://github.com/lyoshenka) (grin@lbry.io)
|
||||
The primary contact for this project is [@roylee17](https://github.com/roylee) (roylee@lbry.io)
|
88
claim.go
88
claim.go
|
@ -1,19 +1,15 @@
|
|||
package claimtrie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// NewClaim ...
|
||||
func NewClaim(op wire.OutPoint, amt Amount, accepted Height) *Claim {
|
||||
return &Claim{
|
||||
// newClaim ...
|
||||
func newClaim(op wire.OutPoint, amt Amount, accepted Height) *claim {
|
||||
return &claim{
|
||||
op: op,
|
||||
id: NewClaimID(op),
|
||||
amt: amt,
|
||||
|
@ -21,41 +17,35 @@ func NewClaim(op wire.OutPoint, amt Amount, accepted Height) *Claim {
|
|||
}
|
||||
}
|
||||
|
||||
// Claim ...
|
||||
type Claim struct {
|
||||
type claim struct {
|
||||
op wire.OutPoint
|
||||
id ClaimID
|
||||
amt Amount
|
||||
effAmt Amount
|
||||
accepted Height
|
||||
}
|
||||
|
||||
// ActivateAt ...
|
||||
func (c *Claim) ActivateAt(best *Claim, curr, tookover Height) Height {
|
||||
if best == nil || best == c {
|
||||
return c.accepted
|
||||
}
|
||||
return calActiveHeight(c.accepted, curr, tookover)
|
||||
activeAt Height
|
||||
}
|
||||
|
||||
// MarshalJSON customizes the representation of JSON.
|
||||
func (c *Claim) MarshalJSON() ([]byte, error) {
|
||||
func (c *claim) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
OutPoint string
|
||||
ClaimID string
|
||||
Amount Amount
|
||||
EffectiveAmount Amount
|
||||
Accepted Height
|
||||
ActiveAt Height
|
||||
}{
|
||||
OutPoint: c.op.String(),
|
||||
ClaimID: c.id.String(),
|
||||
Amount: c.amt,
|
||||
EffectiveAmount: c.effAmt,
|
||||
Accepted: c.accepted,
|
||||
ActiveAt: c.activeAt,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Claim) String() string {
|
||||
func (c *claim) String() string {
|
||||
b, err := json.MarshalIndent(c, "", " ")
|
||||
if err != nil {
|
||||
fmt.Printf("can't marshal, err :%s", err)
|
||||
|
@ -63,9 +53,8 @@ func (c *Claim) String() string {
|
|||
return string(b)
|
||||
}
|
||||
|
||||
// NewSupport ...
|
||||
func NewSupport(op wire.OutPoint, amt Amount, accepted Height, supported ClaimID) *Support {
|
||||
return &Support{
|
||||
func newSupport(op wire.OutPoint, amt Amount, accepted Height, supported ClaimID) *support {
|
||||
return &support{
|
||||
op: op,
|
||||
amt: amt,
|
||||
accepted: accepted,
|
||||
|
@ -73,78 +62,37 @@ func NewSupport(op wire.OutPoint, amt Amount, accepted Height, supported ClaimID
|
|||
}
|
||||
}
|
||||
|
||||
// Support ...
|
||||
type Support struct {
|
||||
type support struct {
|
||||
op wire.OutPoint
|
||||
amt Amount
|
||||
accepted Height
|
||||
activeAt Height
|
||||
|
||||
supportedID ClaimID
|
||||
supportedClaim *Claim
|
||||
}
|
||||
|
||||
// ActivateAt ...
|
||||
func (s *Support) ActivateAt(best *Claim, curr, tookover Height) Height {
|
||||
if best == nil || best == s.supportedClaim {
|
||||
return s.accepted
|
||||
}
|
||||
return calActiveHeight(s.accepted, curr, tookover)
|
||||
supportedClaim *claim
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (s *Support) MarshalJSON() ([]byte, error) {
|
||||
func (s *support) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
OutPoint string
|
||||
SupportedClaimID string
|
||||
Amount Amount
|
||||
Accepted Height
|
||||
ActiveAt Height
|
||||
}{
|
||||
OutPoint: s.op.String(),
|
||||
SupportedClaimID: s.supportedID.String(),
|
||||
Amount: s.amt,
|
||||
Accepted: s.accepted,
|
||||
ActiveAt: s.activeAt,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Support) String() string {
|
||||
func (s *support) String() string {
|
||||
b, err := json.MarshalIndent(s, "", " ")
|
||||
if err != nil {
|
||||
fmt.Printf("can't marshal, err :%s", err)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// NewClaimID ...
|
||||
func NewClaimID(p wire.OutPoint) ClaimID {
|
||||
w := bytes.NewBuffer(p.Hash[:])
|
||||
if err := binary.Write(w, binary.BigEndian, p.Index); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var id ClaimID
|
||||
copy(id[:], btcutil.Hash160(w.Bytes()))
|
||||
return id
|
||||
}
|
||||
|
||||
// NewClaimIDFromString ...
|
||||
func NewClaimIDFromString(s string) (ClaimID, error) {
|
||||
b, err := hex.DecodeString(s)
|
||||
var id ClaimID
|
||||
copy(id[:], b)
|
||||
return id, err
|
||||
}
|
||||
|
||||
// ClaimID ...
|
||||
type ClaimID [20]byte
|
||||
|
||||
func (id ClaimID) String() string {
|
||||
return hex.EncodeToString(id[:])
|
||||
}
|
||||
|
||||
func calActiveHeight(accepted, curr, tookover Height) Height {
|
||||
factor := Height(32)
|
||||
delay := (curr - tookover) / factor
|
||||
if delay > 4032 {
|
||||
delay = 4032
|
||||
}
|
||||
return accepted + delay
|
||||
}
|
||||
|
|
45
claimid.go
Normal file
45
claimid.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package claimtrie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// NewClaimID ...
|
||||
func NewClaimID(op wire.OutPoint) ClaimID {
|
||||
w := bytes.NewBuffer(op.Hash[:])
|
||||
if err := binary.Write(w, binary.BigEndian, op.Index); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var id ClaimID
|
||||
copy(id[:], btcutil.Hash160(w.Bytes()))
|
||||
return id
|
||||
}
|
||||
|
||||
// NewClaimIDFromString ...
|
||||
func NewClaimIDFromString(s string) (ClaimID, error) {
|
||||
b, err := hex.DecodeString(s)
|
||||
var id ClaimID
|
||||
copy(id[:], b)
|
||||
return id, err
|
||||
}
|
||||
|
||||
// ClaimID ...
|
||||
type ClaimID [20]byte
|
||||
|
||||
func (id ClaimID) String() string {
|
||||
return hex.EncodeToString(id[:])
|
||||
}
|
||||
|
||||
func calActiveHeight(accepted, curr, tookover Height) Height {
|
||||
factor := Height(32)
|
||||
delay := (curr - tookover) / factor
|
||||
if delay > 4032 {
|
||||
delay = 4032
|
||||
}
|
||||
return accepted + delay
|
||||
}
|
14
claimtrie.go
14
claimtrie.go
|
@ -60,14 +60,14 @@ func updateStageNode(stg *merkletrie.Stage, name string, modifier func(n *node)
|
|||
// AddClaim adds a Claim to the Stage of ClaimTrie.
|
||||
func (ct *ClaimTrie) AddClaim(name string, op wire.OutPoint, amt Amount, accepted Height) error {
|
||||
return updateStageNode(ct.stg, name, func(n *node) error {
|
||||
return n.addClaim(NewClaim(op, amt, accepted))
|
||||
return n.addClaim(newClaim(op, amt, accepted))
|
||||
})
|
||||
}
|
||||
|
||||
// AddSupport adds a Support to the Stage of ClaimTrie.
|
||||
func (ct *ClaimTrie) AddSupport(name string, op wire.OutPoint, amt Amount, accepted Height, supported ClaimID) error {
|
||||
return updateStageNode(ct.stg, name, func(n *node) error {
|
||||
return n.addSupport(NewSupport(op, amt, accepted, supported))
|
||||
return n.addSupport(newSupport(op, amt, accepted, supported))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,9 @@ func (ct *ClaimTrie) SpendSupport(name string, op wire.OutPoint) error {
|
|||
func (ct *ClaimTrie) Traverse(visit merkletrie.Visit, update, valueOnly bool) error {
|
||||
// wrapper function to make sure the node is updated before it's observed externally.
|
||||
fn := func(prefix merkletrie.Key, v merkletrie.Value) error {
|
||||
v.(*node).updateBestClaim(ct.bestBlock)
|
||||
if v != nil {
|
||||
v.(*node).updateBestClaim(ct.bestBlock)
|
||||
}
|
||||
return visit(prefix, v)
|
||||
}
|
||||
return ct.stg.Traverse(fn, update, valueOnly)
|
||||
|
@ -97,6 +99,11 @@ func (ct *ClaimTrie) Traverse(visit merkletrie.Visit, update, valueOnly bool) er
|
|||
|
||||
// MerkleHash returns the Merkle Hash of the Stage.
|
||||
func (ct *ClaimTrie) MerkleHash() chainhash.Hash {
|
||||
visit := func(prefix merkletrie.Key, v merkletrie.Value) error {
|
||||
v.(*node).updateBestClaim(ct.bestBlock)
|
||||
return nil
|
||||
}
|
||||
ct.Traverse(visit, true, true)
|
||||
return ct.stg.MerkleHash()
|
||||
}
|
||||
|
||||
|
@ -133,6 +140,7 @@ func (ct *ClaimTrie) Reset(h Height) error {
|
|||
if meta.Height <= h {
|
||||
ct.head = commit
|
||||
ct.bestBlock = h
|
||||
ct.stg = merkletrie.NewStage(commit.MerkleTrie)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,39 +1,39 @@
|
|||
package claimtrie
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
|
||||
"github.com/lbryio/merkletrie"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
func TestClaimTrie_AddClaim(t *testing.T) {
|
||||
type fields struct {
|
||||
stg *merkletrie.Stage
|
||||
}
|
||||
type args struct {
|
||||
name string
|
||||
outPoint wire.OutPoint
|
||||
value Amount
|
||||
height Height
|
||||
}
|
||||
func TestClaimTrie_Commit(t *testing.T) {
|
||||
ct := New()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
name string
|
||||
curr Height
|
||||
amt Amount
|
||||
want chainhash.Hash
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
{name: "0-0", curr: 5, amt: 11},
|
||||
{name: "0-0", curr: 6, amt: 10},
|
||||
{name: "0-0", curr: 7, amt: 14},
|
||||
{name: "0-0", curr: 8, amt: 18},
|
||||
{name: "0-0", curr: 100, amt: 0},
|
||||
{name: "0-0", curr: 101, amt: 30},
|
||||
{name: "0-0", curr: 102, amt: 00},
|
||||
{name: "0-0", curr: 103, amt: 00},
|
||||
{name: "0-0", curr: 104, amt: 00},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ct := &ClaimTrie{
|
||||
stg: tt.fields.stg,
|
||||
}
|
||||
if err := ct.AddClaim(tt.args.name, tt.args.outPoint, tt.args.value, tt.args.height); (err != nil) != tt.wantErr {
|
||||
t.Errorf("ClaimTrie.AddClaim() error = %v, wantErr %v", err, tt.wantErr)
|
||||
if tt.amt != 0 {
|
||||
ct.AddClaim("HELLO", *newOutPoint(0), tt.amt, tt.curr)
|
||||
}
|
||||
ct.Commit(tt.curr)
|
||||
fmt.Printf("ct.Merkle[%2d]: %s, amt: %d\n", ct.BestBlock(), ct.MerkleHash(), tt.amt)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
211
cmd/claimtrie/README.md
Normal file
211
cmd/claimtrie/README.md
Normal file
|
@ -0,0 +1,211 @@
|
|||
# ClaimTrie
|
||||
|
||||
coming soon
|
||||
|
||||
## Installation
|
||||
|
||||
coming soon
|
||||
|
||||
## Usage
|
||||
|
||||
``` bash
|
||||
NAME:
|
||||
claimtrie - A CLI tool for ClaimTrie
|
||||
|
||||
USAGE:
|
||||
main [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
0.0.1
|
||||
|
||||
COMMANDS:
|
||||
add-claim, ac Claim a name with specified amount. (outPoint is generated randomly, if unspecified)
|
||||
add-support, as Add support to a specified Claim. (outPoint is generated randomly, if unspecified)
|
||||
spend-claim, sc Spend a specified Claim.
|
||||
spend-support, ss Spend a specified Support.
|
||||
show, s Show the Key-Value pairs of the Stage or specified commit. (links nodes are showed if -a is also specified)
|
||||
merkle, m Show the Merkle Hash of the Stage.
|
||||
commit, c Commit the current Stage to commit database.
|
||||
reset, r Reset the Stage to a specified commit.
|
||||
log, l List the commits in the coommit database.
|
||||
shell, sh Enter interactive mode
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
--help, -h show help
|
||||
--version, -v print the version
|
||||
```
|
||||
|
||||
## Running from Source
|
||||
|
||||
This project requires [Go v1.10](https://golang.org/doc/install) or higher.
|
||||
|
||||
``` bash
|
||||
go get -u -v github.com/lbryio/claimtrie/cmd/claimtrie
|
||||
go run ${GOPATH}/src/github.com/lbryio/claimtrie/cmd/claimtrie/main.go sh
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
Adding claims.
|
||||
|
||||
``` bash
|
||||
claimtrie > add-claim
|
||||
|
||||
claimtrie > show
|
||||
<BestBlock: 0>
|
||||
Hello : {
|
||||
"Hash": "91185db0db792a6f6ad60e01e99e27f5263b8c3225137ff3b33bd5d3ebe197bd",
|
||||
"Tookover": 0,
|
||||
"BestClaim": {
|
||||
"OutPoint": "5fed5c7a39d47b4432b55d172b312df87142995e83ef28bf070859e94c916f30:48",
|
||||
"ClaimID": "911771619d4e6656bc0f08e1dfab5827756ed39d",
|
||||
"Amount": 44,
|
||||
"EffectiveAmount": 44,
|
||||
"Accepted": 0,
|
||||
"ActiveAt": 0
|
||||
},
|
||||
"Claims": [
|
||||
{
|
||||
"OutPoint": "5fed5c7a39d47b4432b55d172b312df87142995e83ef28bf070859e94c916f30:48",
|
||||
"ClaimID": "911771619d4e6656bc0f08e1dfab5827756ed39d",
|
||||
"Amount": 44,
|
||||
"EffectiveAmount": 44,
|
||||
"Accepted": 0,
|
||||
"ActiveAt": 0
|
||||
},
|
||||
{
|
||||
"OutPoint": "db993d544e0cbea83cff2465d3a8615cfc0750d39aa904a60e8fafab7c315a50:80",
|
||||
"ClaimID": "013df2835b1b8256ed019cc71df2dfb61fdce63c",
|
||||
"Amount": 10,
|
||||
"EffectiveAmount": 10,
|
||||
"Accepted": 0,
|
||||
"ActiveAt": 0
|
||||
}
|
||||
],
|
||||
"Supports": []
|
||||
}
|
||||
claimtrie > commit
|
||||
```
|
||||
|
||||
Commit another claim.
|
||||
|
||||
```bash
|
||||
claimtrie > add-claim --amount 100
|
||||
claimtrie > commit
|
||||
```
|
||||
|
||||
Show logs
|
||||
|
||||
``` bash
|
||||
claimtrie > log
|
||||
|
||||
height: 2, commit 9e2a2cf0e7f2a60e195ce46b261d6a953a3cbb68ef6b3274543ec8fdbf8a171b
|
||||
height: 1, commit ce548249c28d61920d69ac759b82f53b5da52fa611f055c4f44c2d94703667a1
|
||||
height: 0, commit 0000000000000000000000000000000000000000000000000000000000000001
|
||||
```
|
||||
|
||||
Show current status.
|
||||
|
||||
```bash
|
||||
claimtrie > show
|
||||
<BestBlock: 2>
|
||||
Hello : {
|
||||
"Hash": "82629d2e9fb1eb8cc78e9d6712f217d5322f1cd9a3cdd15bf3923ee2d9376e94",
|
||||
"Tookover": 1,
|
||||
"BestClaim": {
|
||||
"OutPoint": "0f2fb103891bdf34344d34a64403537653d344558ebd3138b45e770585950d6e:110",
|
||||
"ClaimID": "128f9a84dddc87afdb747e04c6ce22726d2a90e7",
|
||||
"Amount": 100,
|
||||
"EffectiveAmount": 100,
|
||||
"Accepted": 1,
|
||||
"ActiveAt": 1
|
||||
},
|
||||
"Claims": [
|
||||
{
|
||||
"OutPoint": "5fed5c7a39d47b4432b55d172b312df87142995e83ef28bf070859e94c916f30:48",
|
||||
"ClaimID": "911771619d4e6656bc0f08e1dfab5827756ed39d",
|
||||
"Amount": 44,
|
||||
"EffectiveAmount": 44,
|
||||
"Accepted": 0,
|
||||
"ActiveAt": 0
|
||||
},
|
||||
{
|
||||
"OutPoint": "0f2fb103891bdf34344d34a64403537653d344558ebd3138b45e770585950d6e:110",
|
||||
"ClaimID": "128f9a84dddc87afdb747e04c6ce22726d2a90e7",
|
||||
"Amount": 100,
|
||||
"EffectiveAmount": 100,
|
||||
"Accepted": 1,
|
||||
"ActiveAt": 1
|
||||
},
|
||||
{
|
||||
"OutPoint": "db993d544e0cbea83cff2465d3a8615cfc0750d39aa904a60e8fafab7c315a50:80",
|
||||
"ClaimID": "013df2835b1b8256ed019cc71df2dfb61fdce63c",
|
||||
"Amount": 10,
|
||||
"EffectiveAmount": 10,
|
||||
"Accepted": 0,
|
||||
"ActiveAt": 0
|
||||
}
|
||||
],
|
||||
"Supports": []
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Reset the history to height 1.
|
||||
|
||||
``` bash
|
||||
claimtrie > reset --height 1
|
||||
|
||||
claimtrie > show
|
||||
<BestBlock: 1>
|
||||
Hello : {
|
||||
"Hash": "91185db0db792a6f6ad60e01e99e27f5263b8c3225137ff3b33bd5d3ebe197bd",
|
||||
"Tookover": 0,
|
||||
"BestClaim": {
|
||||
"OutPoint": "5fed5c7a39d47b4432b55d172b312df87142995e83ef28bf070859e94c916f30:48",
|
||||
"ClaimID": "911771619d4e6656bc0f08e1dfab5827756ed39d",
|
||||
"Amount": 44,
|
||||
"EffectiveAmount": 44,
|
||||
"Accepted": 0,
|
||||
"ActiveAt": 0
|
||||
},
|
||||
"Claims": [
|
||||
{
|
||||
"OutPoint": "5fed5c7a39d47b4432b55d172b312df87142995e83ef28bf070859e94c916f30:48",
|
||||
"ClaimID": "911771619d4e6656bc0f08e1dfab5827756ed39d",
|
||||
"Amount": 44,
|
||||
"EffectiveAmount": 44,
|
||||
"Accepted": 0,
|
||||
"ActiveAt": 0
|
||||
},
|
||||
{
|
||||
"OutPoint": "db993d544e0cbea83cff2465d3a8615cfc0750d39aa904a60e8fafab7c315a50:80",
|
||||
"ClaimID": "013df2835b1b8256ed019cc71df2dfb61fdce63c",
|
||||
"Amount": 10,
|
||||
"EffectiveAmount": 10,
|
||||
"Accepted": 0,
|
||||
"ActiveAt": 0
|
||||
}
|
||||
],
|
||||
"Supports": []
|
||||
}
|
||||
claimtrie >
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
coming soon
|
||||
|
||||
## License
|
||||
|
||||
This project is MIT licensed.
|
||||
|
||||
## Security
|
||||
|
||||
We take security seriously. Please contact security@lbry.io regarding any security issues.
|
||||
Our PGP key is [here](https://keybase.io/lbry/key.asc) if you need it.
|
||||
|
||||
## Contact
|
||||
|
||||
The primary contact for this project is [@roylee17](https://github.com/roylee) (roylee@lbry.io)
|
55
node.go
55
node.go
|
@ -12,23 +12,24 @@ import (
|
|||
|
||||
type node struct {
|
||||
tookover Height
|
||||
bestClaim *Claim
|
||||
bestClaim *claim
|
||||
|
||||
claims map[string]*Claim
|
||||
supports map[string]*Support
|
||||
claims map[string]*claim
|
||||
supports map[string]*support
|
||||
}
|
||||
|
||||
func newNode() *node {
|
||||
return &node{
|
||||
claims: map[string]*Claim{},
|
||||
supports: map[string]*Support{},
|
||||
claims: map[string]*claim{},
|
||||
supports: map[string]*support{},
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) addClaim(c *Claim) error {
|
||||
func (n *node) addClaim(c *claim) error {
|
||||
if _, ok := n.claims[c.op.String()]; ok {
|
||||
return ErrDuplicate
|
||||
}
|
||||
c.activeAt = calActiveHeight(c.accepted, c.accepted, n.tookover)
|
||||
n.claims[c.op.String()] = c
|
||||
return nil
|
||||
}
|
||||
|
@ -51,12 +52,13 @@ func (n *node) removeClaim(op wire.OutPoint) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (n *node) addSupport(s *Support) error {
|
||||
func (n *node) addSupport(s *support) error {
|
||||
if _, ok := n.supports[s.op.String()]; ok {
|
||||
return ErrDuplicate
|
||||
}
|
||||
for _, v := range n.claims {
|
||||
if v.id == s.supportedID {
|
||||
s.activeAt = calActiveHeight(s.accepted, s.accepted, n.tookover)
|
||||
s.supportedClaim = v
|
||||
n.supports[s.op.String()] = s
|
||||
return nil
|
||||
|
@ -80,20 +82,20 @@ func (n *node) Hash() chainhash.Hash {
|
|||
|
||||
// MarshalJSON customizes JSON marshaling of the Node.
|
||||
func (n *node) MarshalJSON() ([]byte, error) {
|
||||
c := make([]*Claim, 0, len(n.claims))
|
||||
c := make([]*claim, 0, len(n.claims))
|
||||
for _, v := range n.claims {
|
||||
c = append(c, v)
|
||||
}
|
||||
s := make([]*Support, 0, len(n.supports))
|
||||
s := make([]*support, 0, len(n.supports))
|
||||
for _, v := range n.supports {
|
||||
s = append(s, v)
|
||||
}
|
||||
return json.Marshal(&struct {
|
||||
Hash string
|
||||
Tookover Height
|
||||
BestClaim *Claim
|
||||
Claims []*Claim
|
||||
Supports []*Support
|
||||
BestClaim *claim
|
||||
Claims []*claim
|
||||
Supports []*support
|
||||
}{
|
||||
Hash: n.Hash().String(),
|
||||
Tookover: n.tookover,
|
||||
|
@ -117,17 +119,18 @@ func (n *node) updateEffectiveAmounts(curr Height) {
|
|||
v.effAmt = v.amt
|
||||
}
|
||||
for _, v := range n.supports {
|
||||
if v.ActivateAt(n.bestClaim, curr, n.tookover) <= curr {
|
||||
if v.supportedClaim == n.bestClaim || v.activeAt <= curr {
|
||||
v.supportedClaim.effAmt += v.amt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) updateBestClaim(curr Height) {
|
||||
findCandiadte := func() *Claim {
|
||||
findCandiadte := func() *claim {
|
||||
candidate := n.bestClaim
|
||||
for _, v := range n.claims {
|
||||
if v.ActivateAt(n.bestClaim, curr, n.tookover) > curr {
|
||||
if v.activeAt > curr {
|
||||
// Accepted claim, but noy activated yet.
|
||||
continue
|
||||
}
|
||||
if candidate == nil || v.effAmt > candidate.effAmt {
|
||||
|
@ -136,15 +139,25 @@ func (n *node) updateBestClaim(curr Height) {
|
|||
}
|
||||
return candidate
|
||||
}
|
||||
takeover := func(candidate *claim) {
|
||||
n.bestClaim = candidate
|
||||
n.tookover = curr
|
||||
for _, v := range n.claims {
|
||||
v.activeAt = calActiveHeight(v.accepted, curr, curr)
|
||||
}
|
||||
}
|
||||
for {
|
||||
n.updateEffectiveAmounts(curr)
|
||||
candidate := findCandiadte()
|
||||
if n.bestClaim == nil || n.bestClaim == candidate {
|
||||
n.bestClaim = candidate
|
||||
if n.bestClaim == nil {
|
||||
takeover(candidate)
|
||||
return
|
||||
|
||||
}
|
||||
if n.bestClaim == candidate {
|
||||
return
|
||||
}
|
||||
n.tookover = curr
|
||||
n.bestClaim = candidate
|
||||
takeover(candidate)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,11 +168,11 @@ func (n *node) clone() *node {
|
|||
*clone = *n
|
||||
|
||||
// deep copy of reference and pointer fields.
|
||||
clone.claims = map[string]*Claim{}
|
||||
clone.claims = map[string]*claim{}
|
||||
for k, v := range n.claims {
|
||||
clone.claims[k] = v
|
||||
}
|
||||
clone.supports = map[string]*Support{}
|
||||
clone.supports = map[string]*support{}
|
||||
for k, v := range n.supports {
|
||||
clone.supports[k] = v
|
||||
}
|
||||
|
|
85
node_test.go
85
node_test.go
|
@ -1,6 +1,7 @@
|
|||
package claimtrie
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
|
@ -13,6 +14,14 @@ func newHash(s string) *chainhash.Hash {
|
|||
return h
|
||||
}
|
||||
|
||||
func newOutPoint(idx int) *wire.OutPoint {
|
||||
var h chainhash.Hash
|
||||
if _, err := rand.Read(h[:]); err != nil {
|
||||
return nil
|
||||
}
|
||||
return wire.NewOutPoint(&h, uint32(idx))
|
||||
}
|
||||
|
||||
func Test_calNodeHash(t *testing.T) {
|
||||
type args struct {
|
||||
op wire.OutPoint
|
||||
|
@ -24,22 +33,22 @@ func Test_calNodeHash(t *testing.T) {
|
|||
want chainhash.Hash
|
||||
}{
|
||||
{
|
||||
name: "test1",
|
||||
name: "0-1",
|
||||
args: args{op: wire.OutPoint{Hash: *newHash("c73232a755bf015f22eaa611b283ff38100f2a23fb6222e86eca363452ba0c51"), Index: 0}, h: 0},
|
||||
want: *newHash("48a312fc5141ad648cb5dca99eaf221f7b1bc4d2fc559e1cde4664a46d8688a4"),
|
||||
},
|
||||
{
|
||||
name: "test2",
|
||||
name: "0-2",
|
||||
args: args{op: wire.OutPoint{Hash: *newHash("71c7b8d35b9a3d7ad9a1272b68972979bbd18589f1efe6f27b0bf260a6ba78fa"), Index: 1}, h: 1},
|
||||
want: *newHash("9132cc5ff95ae67bee79281438e7d00c25c9ec8b526174eb267c1b63a55be67c"),
|
||||
},
|
||||
{
|
||||
name: "test3",
|
||||
name: "0-3",
|
||||
args: args{op: wire.OutPoint{Hash: *newHash("c4fc0e2ad56562a636a0a237a96a5f250ef53495c2cb5edd531f087a8de83722"), Index: 0x12345678}, h: 0x87654321},
|
||||
want: *newHash("023c73b8c9179ffcd75bd0f2ed9784aab2a62647585f4b38e4af1d59cf0665d2"),
|
||||
},
|
||||
{
|
||||
name: "test4",
|
||||
name: "0-4",
|
||||
args: args{op: wire.OutPoint{Hash: *newHash("baf52472bd7da19fe1e35116cfb3bd180d8770ffbe3ae9243df1fb58a14b0975"), Index: 0x11223344}, h: 0x88776655},
|
||||
want: *newHash("6a2d40f37cb2afea3b38dea24e1532e18cade5d1dc9c2f8bd635aca2bc4ac980"),
|
||||
},
|
||||
|
@ -52,3 +61,71 @@ func Test_calNodeHash(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
func Test_BestClaim(t *testing.T) {
|
||||
cA := newClaim(*newOutPoint(1), 0, 0)
|
||||
cB := newClaim(*newOutPoint(2), 0, 0)
|
||||
cC := newClaim(*newOutPoint(3), 0, 0)
|
||||
cD := newClaim(*newOutPoint(4), 0, 0)
|
||||
|
||||
s1 := newSupport(*newOutPoint(91), 0, 0, cA.id)
|
||||
|
||||
var n *node
|
||||
type operation int
|
||||
const (
|
||||
opReset = 1 << iota
|
||||
opAddClaim
|
||||
opRemoveClaim
|
||||
opAddSupport
|
||||
opRemoveSupport
|
||||
opCheck
|
||||
)
|
||||
tests := []struct {
|
||||
name string
|
||||
op operation
|
||||
claim *claim
|
||||
support *support
|
||||
amount Amount
|
||||
curr Height
|
||||
want *claim
|
||||
}{
|
||||
{name: "0-0", op: opReset},
|
||||
{name: "0-1", op: opAddClaim, claim: cA, amount: 10, curr: 13, want: cA}, // A(10) is controlling
|
||||
{name: "0-2", op: opAddClaim, claim: cB, amount: 20, curr: 1001, want: cA}, // A(10) is controlling, B(20) is accepted. Act(B) = 1001 + (1001-13)/32 = 1031
|
||||
{name: "0-3", op: opAddSupport, claim: cA, support: s1, amount: 14, curr: 1010, want: cA}, // A(10+14) is controlling, B(20) is accepted.
|
||||
{name: "0-4", op: opAddClaim, claim: cC, amount: 50, curr: 1020, want: cA}, // A(10+14) is controlling, B(20) is accepted, C(50) is accepted. Act(C) = 1020 + (1020-13)/32 = 1051
|
||||
{name: "0-5", op: opCheck, curr: 1031, want: cA}, // A(10+14) is controlling, B(20) is active, C(50) is accepted.
|
||||
{name: "0-6", op: opAddClaim, claim: cD, amount: 300, curr: 1040, want: cA}, // A(10+14) is controlling, B(20) is active, C(50) is accepted, D(300) is accepted. Act(C) = 1040 + (1040-13)/32 = 1072
|
||||
{name: "0-7", op: opCheck, curr: 1051, want: cD}, // A(10+14) is active, B(20) is active, C(50) is active, D(300) is controlling.
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var err error
|
||||
switch tt.op {
|
||||
case opReset:
|
||||
n = newNode()
|
||||
case opAddClaim:
|
||||
tt.claim.amt = tt.amount
|
||||
tt.claim.accepted = tt.curr
|
||||
err = n.addClaim(tt.claim)
|
||||
case opRemoveClaim:
|
||||
err = n.removeClaim(tt.claim.op)
|
||||
case opAddSupport:
|
||||
tt.support.accepted = tt.curr
|
||||
tt.support.amt = tt.amount
|
||||
tt.support.supportedID = tt.claim.id
|
||||
err = n.addSupport(tt.support)
|
||||
case opRemoveSupport:
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("BestClaim() failed, err: %s", err)
|
||||
}
|
||||
n.updateBestClaim(tt.curr)
|
||||
got := n.bestClaim
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("BestClaim() = %d, want %d", got.op.Index, tt.want.op.Index)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue