Skip to content
This repository was archived by the owner on Apr 4, 2023. It is now read-only.

Commit 62fcfde

Browse files
Merge pull request #20 from ava-labs/value-units
Work Units Overhaul
2 parents fccfe8f + 15f3c63 commit 62fcfde

32 files changed

+488
-201
lines changed

README.md

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -77,26 +77,21 @@ prefix pat info &{Owner:0xc00011dd70 LastUpdated:1638591044 Expiry:1638591074 Ke
7777
```
7878

7979
# Difficulty Estiamtes
80+
To see what performance you can get, run:
8081
```bash
81-
difficulty: 0 avg solution time: 1.74µs
82-
difficulty: 1 avg solution time: 3.263µs
83-
difficulty: 2 avg solution time: 7.591µs
84-
difficulty: 3 avg solution time: 9.47µs
85-
difficulty: 4 avg solution time: 17.529µs
86-
difficulty: 5 avg solution time: 30.728µs
87-
difficulty: 6 avg solution time: 56.324µs
88-
difficulty: 7 avg solution time: 96.442µs
89-
difficulty: 8 avg solution time: 191.322µs
90-
difficulty: 9 avg solution time: 386.001µs
91-
difficulty: 10 avg solution time: 794.525µs
92-
difficulty: 11 avg solution time: 1.541153ms
93-
difficulty: 12 avg solution time: 3.766751ms
94-
difficulty: 13 avg solution time: 6.686821ms
95-
difficulty: 14 avg solution time: 11.626156ms
96-
difficulty: 15 avg solution time: 24.456795ms
97-
difficulty: 16 avg solution time: 50.756825ms
98-
difficulty: 17 avg solution time: 108.462722ms
99-
difficulty: 18 avg solution time: 218.103048ms
100-
difficulty: 19 avg solution time: 458.385183ms
101-
difficulty: 20 avg solution time: 863.973251ms
82+
go test -bench=. ./pow/...
83+
```
84+
85+
Here are some example results:
86+
```bash
87+
goos: darwin
88+
goarch: amd64
89+
pkg: github.com/ava-labs/quarkvm/pow
90+
cpu: Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
91+
BenchmarkDifficulty1-16 1145233 1047 ns/op
92+
BenchmarkDifficulty10-16 113216 10335 ns/op
93+
BenchmarkDifficulty50-16 23011 52336 ns/op
94+
BenchmarkDifficulty100-16 11540 102029 ns/op
95+
BenchmarkDifficulty500-16 1962 535265 ns/op
96+
BenchmarkDifficulty1000-16 1132 1082758 ns/op
10297
```

chain/base_tx.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,7 @@ func (b *BaseTx) ExecuteBase() error {
5757
}
5858
return nil
5959
}
60+
61+
func (b *BaseTx) Units() uint64 {
62+
return BaseTxUnits
63+
}

chain/block.go

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type StatefulBlock struct {
2424
Prnt ids.ID `serialize:"true" json:"parent"`
2525
Tmstmp int64 `serialize:"true" json:"timestamp"`
2626
Hght uint64 `serialize:"true" json:"height"`
27-
Difficulty uint64 `serialize:"true" json:"difficulty"`
27+
Difficulty uint64 `serialize:"true" json:"difficulty"` // difficulty per unit
2828
Cost uint64 `serialize:"true" json:"cost"`
2929
Txs []*Transaction `serialize:"true" json:"txs"`
3030
}
@@ -59,7 +59,6 @@ func NewBlock(vm VM, parent snowman.Block, tmstp int64, context *Context) *State
5959
}
6060
}
6161

62-
// TODO: check work here? Seems like a DoS vuln?
6362
func ParseBlock(
6463
source []byte,
6564
status choices.Status,
@@ -159,20 +158,18 @@ func (b *StatelessBlock) verify() (*StatelessBlock, *versiondb.Database, error)
159158
}
160159

161160
// Process new transactions
162-
log.Debug("build context", "next difficulty", context.NextDifficulty, "next cost", context.NextCost)
163-
var surplusDifficulty uint64
161+
log.Debug("build context", "height", b.Hght, "difficulty", b.Difficulty, "cost", b.Cost)
162+
surplusWork := uint64(0)
164163
for _, tx := range b.Txs {
165164
if err := tx.Execute(onAcceptDB, b.Tmstmp, context); err != nil {
166-
log.Debug("failed tx verification", "err", err)
167165
return nil, nil, err
168166
}
169-
surplusDifficulty += tx.Difficulty() - context.NextDifficulty
167+
surplusWork += (tx.Difficulty() - b.Difficulty) * tx.Units()
170168
}
171169
// Ensure enough work is performed to compensate for block production speed
172170
requiredSurplus := b.Difficulty * b.Cost
173-
if surplusDifficulty < requiredSurplus {
174-
log.Debug("insufficient block surplus", "found", surplusDifficulty, "required", requiredSurplus)
175-
return nil, nil, ErrInsufficientSurplus
171+
if surplusWork < requiredSurplus {
172+
return nil, nil, fmt.Errorf("%w: required=%d found=%d", ErrInsufficientSurplus, requiredSurplus, surplusWork)
176173
}
177174
return parent, onAcceptDB, nil
178175
}

chain/builder.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ func BuildBlock(vm VM, preferred ids.ID) (snowman.Block, error) {
4343
mempool.Prune(context.RecentBlockIDs) // clean out invalid txs
4444
vdb := versiondb.New(parentDB)
4545
b.Txs = []*Transaction{}
46-
for len(b.Txs) < TargetTransactions && mempool.Len() > 0 {
46+
units := uint64(0)
47+
for units < TargetUnits && mempool.Len() > 0 {
4748
next, diff := mempool.PopMax()
4849
if diff < b.Difficulty {
4950
mempool.Add(next)
@@ -61,6 +62,7 @@ func BuildBlock(vm VM, preferred ids.ID) (snowman.Block, error) {
6162
}
6263
// Wait to add prefix until after verification
6364
b.Txs = append(b.Txs, next)
65+
units += next.Units()
6466
}
6567

6668
// Compute block hash and marshaled representation

chain/chain.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,26 @@ import (
99

1010
// TODO: load from genesis
1111
const (
12-
MaxValueLength = 256
13-
LookbackWindow = 10
14-
BlockTarget = 1
15-
TargetTransactions = 10 * LookbackWindow / BlockTarget // TODO: can be higher on real network
16-
MinDifficulty = 1 // TODO: set much higher on real network
17-
MinBlockCost = 0 // in units of tx surplus
18-
expiryTime = 30 // TODO: set much longer on real network
12+
ExpiryTime = 60 * 60 * 24 * 30 // 30 Days
13+
ValueUnitSize = 256 // 256B
14+
MaxValueSize = 1 << 10 * 128 // 128KB (500 Units)
15+
BaseTxUnits = 10
16+
17+
LookbackWindow = 60 // 60 Seconds
18+
BlockTarget = 1 // 1 Block per Second
19+
TargetUnits = BaseTxUnits * 512 * LookbackWindow / BlockTarget // 512 Units Per Block
20+
21+
MinDifficulty = 10 // ~10ms per unit (100 ms for claim)
22+
MinBlockCost = 1 // Minimum Unit Overhead
1923
)
2024

2125
type Context struct {
2226
RecentBlockIDs ids.Set
2327
RecentTxIDs ids.Set
28+
RecentUnits uint64
29+
30+
Difficulties []uint64
31+
Costs []uint64
2432

2533
NextCost uint64
2634
NextDifficulty uint64

chain/claim_tx.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type ClaimTx struct {
1616
*BaseTx `serialize:"true" json:"baseTx"`
1717
}
1818

19-
func (c *ClaimTx) Execute(db database.Database, blockTime int64) error {
19+
func (c *ClaimTx) Execute(db database.Database, blockTime uint64) error {
2020
// Restrict address prefix to be owned by pk
2121
// [33]byte prefix is reserved for pubkey
2222
if len(c.Prefix) == crypto.SECP256K1RPKLen && !bytes.Equal(c.Sender[:], c.Prefix) {
@@ -37,10 +37,10 @@ func (c *ClaimTx) Execute(db database.Database, blockTime int64) error {
3737
Owner: c.Sender,
3838
Created: blockTime,
3939
LastUpdated: blockTime,
40-
Expiry: blockTime + expiryTime,
41-
Keys: 1,
40+
Expiry: blockTime + ExpiryTime,
41+
Units: 1,
4242
}
43-
if err := PutPrefixInfo(db, c.Prefix, newInfo, -1); err != nil {
43+
if err := PutPrefixInfo(db, c.Prefix, newInfo, 0); err != nil {
4444
return err
4545
}
4646
return nil

chain/claim_tx_test.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,22 @@ func TestClaimTx(t *testing.T) {
5353
},
5454
{ // invalid claim due to expiration
5555
tx: &ClaimTx{BaseTx: &BaseTx{Sender: sender, Prefix: []byte("foo")}},
56-
blockTime: 1,
56+
blockTime: 100,
5757
err: ErrPrefixNotExpired,
5858
},
5959
{ // successful new claim
6060
tx: &ClaimTx{BaseTx: &BaseTx{Sender: sender, Prefix: []byte("foo")}},
61-
blockTime: 100,
61+
blockTime: ExpiryTime * 2,
6262
err: nil,
6363
},
6464
{ // successful new claim by different owner
6565
tx: &ClaimTx{BaseTx: &BaseTx{Sender: sender2, Prefix: []byte("foo")}},
66-
blockTime: 150,
66+
blockTime: ExpiryTime * 4,
6767
err: nil,
6868
},
6969
{ // invalid claim due to expiration by different owner
7070
tx: &ClaimTx{BaseTx: &BaseTx{Sender: sender2, Prefix: []byte("foo")}},
71-
blockTime: 177,
71+
blockTime: ExpiryTime*4 + 3,
7272
err: ErrPrefixNotExpired,
7373
},
7474
}
@@ -79,7 +79,7 @@ func TestClaimTx(t *testing.T) {
7979
t.Fatalf("#%d: ExpireNext errored %v", i, err)
8080
}
8181
}
82-
err := tv.tx.Execute(db, tv.blockTime)
82+
err := tv.tx.Execute(db, uint64(tv.blockTime))
8383
if !errors.Is(err, tv.err) {
8484
t.Fatalf("#%d: tx.Execute err expected %v, got %v", i, tv.err, err)
8585
}
@@ -99,12 +99,16 @@ func TestClaimTx(t *testing.T) {
9999
}
100100

101101
// Cleanup DB after all txs submitted
102-
if err := ExpireNext(db, 0, 1000); err != nil {
102+
if err := ExpireNext(db, 0, ExpiryTime*10); err != nil {
103103
t.Fatal(err)
104104
}
105-
if err := PruneNext(db, 100); err != nil {
105+
pruned, err := PruneNext(db, 100)
106+
if err != nil {
106107
t.Fatal(err)
107108
}
109+
if pruned != 3 {
110+
t.Fatalf("expected to prune 3 but got %d", pruned)
111+
}
108112
_, exists, err := GetPrefixInfo(db, []byte("foo"))
109113
if err != nil {
110114
t.Fatalf("failed to get prefix info %v", err)

chain/codec.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,23 @@ package chain
66
import (
77
"github.com/ava-labs/avalanchego/codec"
88
"github.com/ava-labs/avalanchego/codec/linearcodec"
9+
"github.com/ava-labs/avalanchego/utils/units"
910
"github.com/ava-labs/avalanchego/utils/wrappers"
1011
)
1112

12-
// codecVersion is the current default codec version
13-
const codecVersion = 0
13+
const (
14+
// codecVersion is the current default codec version
15+
codecVersion = 0
16+
17+
// maxSize is 1MB to support large blocks (~9 large key settings)
18+
maxSize = 1 * units.MiB
19+
)
1420

1521
var codecManager codec.Manager
1622

1723
func init() {
1824
c := linearcodec.NewDefault()
19-
codecManager = codec.NewDefaultManager()
25+
codecManager = codec.NewManager(maxSize)
2026
errs := wrappers.Errs{}
2127
errs.Add(
2228
c.RegisterType(&BaseTx{}),

chain/lifeline_tx.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type LifelineTx struct {
1313
*BaseTx `serialize:"true" json:"baseTx"`
1414
}
1515

16-
func (l *LifelineTx) Execute(db database.Database, blockTime int64) error {
16+
func (l *LifelineTx) Execute(db database.Database, blockTime uint64) error {
1717
i, has, err := GetPrefixInfo(db, l.Prefix)
1818
if err != nil {
1919
return err
@@ -22,8 +22,8 @@ func (l *LifelineTx) Execute(db database.Database, blockTime int64) error {
2222
if !has {
2323
return ErrPrefixMissing
2424
}
25-
// If you are "in debt", lifeline only adds but doesn't reset to new
25+
// Lifeline spread across all units
2626
lastExpiry := i.Expiry
27-
i.Expiry += expiryTime / i.Keys
27+
i.Expiry += ExpiryTime / i.Units
2828
return PutPrefixInfo(db, l.Prefix, i, lastExpiry)
2929
}

chain/lifeline_tx_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func TestLifelineTx(t *testing.T) {
2727

2828
tt := []struct {
2929
utx UnsignedTransaction
30-
blockTime int64
30+
blockTime uint64
3131
err error
3232
}{
3333
{ // invalid when prefix info is missing

0 commit comments

Comments
 (0)