Skip to content

Commit

Permalink
Merge branch 'master' into feat/db
Browse files Browse the repository at this point in the history
  • Loading branch information
darrenvechain committed Dec 18, 2024
2 parents 5b9ebd1 + 5867f22 commit a20f44f
Show file tree
Hide file tree
Showing 14 changed files with 178 additions and 61 deletions.
9 changes: 9 additions & 0 deletions .github/workflows/publish-docker-images.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: Update Docker Hub
# official documentation docker: https://docs.docker.com/build/ci/github-actions/update-dockerhub-desc/
if: ${{ inputs.environment == 'docker-publish' && github.event_name != 'pull_request' }}
uses: peter-evans/dockerhub-description@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
repository: ${{ github.repository }}

- name: Scan for vulnerabilities
uses: crazy-max/ghaction-container-scan@v3
if: ${{ github.event_name == 'pull_request' || github.ref_name == 'master' }}
Expand Down
14 changes: 8 additions & 6 deletions api/doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@

swagger-ui from https://github.com/swagger-api/swagger-ui @v5.11.2
- Created [window-observer.js](./swagger-ui/window-observer.js) to remove `Try it out` functionality for subscription endpoints
- See the latest version [here](https://unpkg.com/browse/[email protected]/)

```bash
curl https://unpkg.com/swagger-ui-dist@5.11.2/swagger-ui.css > swagger-ui/swagger-ui.css
curl https://unpkg.com/swagger-ui-dist@5.11.2/swagger-ui-bundle.js > swagger-ui/swagger-ui-bundle.js
curl https://unpkg.com/swagger-ui-dist@5.11.2/swagger-ui-standalone-preset.js > swagger-ui/swagger-ui-standalone-preset.js
curl https://unpkg.com/swagger-ui-dist@5.18.2/swagger-ui.css > swagger-ui/swagger-ui.css
curl https://unpkg.com/swagger-ui-dist@5.18.2/swagger-ui-bundle.js > swagger-ui/swagger-ui-bundle.js
curl https://unpkg.com/swagger-ui-dist@5.18.2/swagger-ui-standalone-preset.js > swagger-ui/swagger-ui-standalone-preset.js
```

## Stoplight
Spotlight UI from https://github.com/stoplightio/elements @v8.0.3
Spotlight UI from https://github.com/stoplightio/elements @v8.5.2
- Created [window-observer.js](./stoplight-ui/window-observer.js) to remove `Send API Request` functionality for subscription endpoints
- See the latest version [here](https://unpkg.com/browse/@stoplight/[email protected]/)

```bash
curl https://unpkg.com/@stoplight/elements@8.0.3/styles.min.css > stoplight-ui/styles.min.css
curl https://unpkg.com/@stoplight/elements@8.0.3/web-components.min.js > stoplight-ui/web-components.min.js
curl https://unpkg.com/@stoplight/elements@8.5.2/styles.min.css > stoplight-ui/styles.min.css
curl https://unpkg.com/@stoplight/elements@8.5.2/web-components.min.js > stoplight-ui/web-components.min.js
```
2 changes: 1 addition & 1 deletion api/doc/stoplight-ui/styles.min.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion api/doc/stoplight-ui/web-components.min.js

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions api/doc/swagger-ui/swagger-ui-bundle.js

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions api/doc/swagger-ui/swagger-ui-standalone-preset.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion api/doc/swagger-ui/swagger-ui.css

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions chain/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ func newCache(maxSize int) *cache {
return &cache{c}
}

func (c *cache) GetOrLoad(key interface{}, load func() (interface{}, error)) (interface{}, error) {
func (c *cache) GetOrLoad(key interface{}, load func() (interface{}, error)) (interface{}, bool, error) {
if value, ok := c.Get(key); ok {
return value, nil
return value, true, nil
}
value, err := load()
if err != nil {
return nil, err
return nil, false, err
}
c.Add(key, value)
return value, nil
return value, false, nil
}
2 changes: 1 addition & 1 deletion chain/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ package chain
import "github.com/vechain/thor/v2/metrics"

var (
metricCacheHitMiss = metrics.LazyLoadCounterVec("repo_cache_hit_miss_count", []string{"type", "event"})
metricCacheHitMiss = metrics.LazyLoadGaugeVec("repo_cache_hit_miss_count", []string{"type", "event"})
)
58 changes: 42 additions & 16 deletions chain/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ type Repository struct {
summaries *cache
txs *cache
receipts *cache

stats struct {
summaries thor.CacheStats
txs thor.CacheStats
receipts thor.CacheStats
}
}
}

Expand Down Expand Up @@ -283,29 +289,43 @@ func (r *Repository) GetMaxBlockNum() (uint32, error) {
}

// GetBlockSummary get block summary by block id.
func (r *Repository) GetBlockSummary(id thor.Bytes32) (summary *BlockSummary, err error) {
var blk interface{}
result := "hit"
if blk, err = r.caches.summaries.GetOrLoad(id, func() (interface{}, error) {
result = "miss"
func (r *Repository) GetBlockSummary(id thor.Bytes32) (*BlockSummary, error) {
blk, cached, err := r.caches.summaries.GetOrLoad(id, func() (interface{}, error) {
return loadBlockSummary(r.hdrStore, id)
}); err != nil {
return
})
if err != nil {
return nil, err
}

if cached {
if r.caches.stats.summaries.Hit()%2000 == 0 {
_, hit, miss := r.caches.stats.summaries.Stats()
metricCacheHitMiss().SetWithLabel(hit, map[string]string{"type": "block-summary", "event": "hit"})
metricCacheHitMiss().SetWithLabel(miss, map[string]string{"type": "blocks", "event": "miss"})
}
} else {
r.caches.stats.summaries.Miss()
}
metricCacheHitMiss().AddWithLabel(1, map[string]string{"type": "block-summary", "event": result})
return blk.(*BlockSummary), nil
}

func (r *Repository) getTransaction(key []byte) (*tx.Transaction, error) {
result := "hit"
trx, err := r.caches.txs.GetOrLoad(string(key), func() (interface{}, error) {
result = "miss"
trx, cached, err := r.caches.txs.GetOrLoad(string(key), func() (interface{}, error) {
return loadTransaction(r.bodyStore, key)
})
if err != nil {
return nil, err
}
metricCacheHitMiss().AddWithLabel(1, map[string]string{"type": "transaction", "event": result})

if cached {
if r.caches.stats.txs.Hit()%2000 == 0 {
_, hit, miss := r.caches.stats.txs.Stats()
metricCacheHitMiss().SetWithLabel(hit, map[string]string{"type": "transaction", "event": "hit"})
metricCacheHitMiss().SetWithLabel(miss, map[string]string{"type": "transaction", "event": "miss"})
}
} else {
r.caches.stats.txs.Miss()
}
return trx.(*tx.Transaction), nil
}

Expand Down Expand Up @@ -353,15 +373,21 @@ func (r *Repository) GetBlock(id thor.Bytes32) (*block.Block, error) {
}

func (r *Repository) getReceipt(key []byte) (*tx.Receipt, error) {
result := "hit"
receipt, err := r.caches.receipts.GetOrLoad(string(key), func() (interface{}, error) {
result = "miss"
receipt, cached, err := r.caches.receipts.GetOrLoad(string(key), func() (interface{}, error) {
return loadReceipt(r.bodyStore, key)
})
if err != nil {
return nil, err
}
metricCacheHitMiss().AddWithLabel(1, map[string]string{"type": "receipt", "event": result})
if cached {
if r.caches.stats.receipts.Hit()%2000 == 0 {
_, hit, miss := r.caches.stats.receipts.Stats()
metricCacheHitMiss().SetWithLabel(hit, map[string]string{"type": "receipt", "event": "hit"})
metricCacheHitMiss().SetWithLabel(miss, map[string]string{"type": "receipt", "event": "miss"})
}
} else {
r.caches.stats.receipts.Miss()
}
return receipt.(*tx.Receipt), nil
}

Expand Down
2 changes: 1 addition & 1 deletion muxdb/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ import (
"github.com/vechain/thor/v2/metrics"
)

var metricCacheHitMissGaugeVec = metrics.LazyLoadGaugeVec("cache_hit_miss_count", []string{"type", "event"})
var metricCacheHitMiss = metrics.LazyLoadGaugeVec("cache_hit_miss_count", []string{"type", "event"})
64 changes: 38 additions & 26 deletions runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package runtime

import (
"fmt"
"math/big"
"sync/atomic"

Expand Down Expand Up @@ -249,50 +250,54 @@ func (rt *Runtime) newEVM(stateDB *statedb.StateDB, clauseIndex uint32, txCtx *x
},
OnSuicideContract: func(_ *vm.EVM, contractAddr, tokenReceiver common.Address) {
// it's IMPORTANT to process energy before token
amount, err := rt.state.GetEnergy(thor.Address(contractAddr), rt.ctx.Time)
energy, err := rt.state.GetEnergy(thor.Address(contractAddr), rt.ctx.Time)
if err != nil {
panic(err)
}
if amount.Sign() != 0 {
// add remained energy of suiciding contract to receiver.
// no need to clear contract's energy, vm will delete the whole contract later.
bal := stateDB.GetBalance(contractAddr)

if bal.Sign() != 0 || energy.Sign() != 0 {
receiverEnergy, err := rt.state.GetEnergy(thor.Address(tokenReceiver), rt.ctx.Time)
if err != nil {
panic(err)
}
// touch the receiver's energy
// no need to clear contract's energy, vm will delete the whole contract later.
if err := rt.state.SetEnergy(
thor.Address(tokenReceiver),
new(big.Int).Add(receiverEnergy, amount),
new(big.Int).Add(receiverEnergy, energy),
rt.ctx.Time); err != nil {
panic(err)
}

// see ERC20's Transfer event
topics := []common.Hash{
common.Hash(energyTransferEvent.ID()),
common.BytesToHash(contractAddr[:]),
common.BytesToHash(tokenReceiver[:]),
// emit event if there is energy in the account
if energy.Sign() != 0 {
// see ERC20's Transfer event
topics := []common.Hash{
common.Hash(energyTransferEvent.ID()),
common.BytesToHash(contractAddr[:]),
common.BytesToHash(tokenReceiver[:]),
}

data, err := energyTransferEvent.Encode(energy)
if err != nil {
panic(err)
}

stateDB.AddLog(&types.Log{
Address: common.Address(builtin.Energy.Address),
Topics: topics,
Data: data,
})
}

data, err := energyTransferEvent.Encode(amount)
if err != nil {
panic(err)
}

stateDB.AddLog(&types.Log{
Address: common.Address(builtin.Energy.Address),
Topics: topics,
Data: data,
})
}

if amount := stateDB.GetBalance(contractAddr); amount.Sign() != 0 {
stateDB.AddBalance(tokenReceiver, amount)
if bal.Sign() != 0 {
stateDB.AddBalance(tokenReceiver, bal)

stateDB.AddTransfer(&tx.Transfer{
Sender: thor.Address(contractAddr),
Recipient: thor.Address(tokenReceiver),
Amount: amount,
Amount: bal,
})
}
},
Expand Down Expand Up @@ -328,7 +333,14 @@ func (rt *Runtime) PrepareClause(
defer func() {
if e := recover(); e != nil {
// caught state error
err = e.(error)
switch e := e.(type) {
case error:
err = e
case string:
err = errors.New(e)
default:
err = fmt.Errorf("runtime: unknown error: %v", e)
}
}
}()

Expand Down
35 changes: 35 additions & 0 deletions thor/cachestats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2024 The VeChainThor developers

// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying
// file LICENSE or <https://www.gnu.org/licenses/lgpl-3.0.html>
package thor

import "sync/atomic"

// CacheStats is a utility for collecting cache hit/miss.
type CacheStats struct {
hit, miss atomic.Int64
flag atomic.Int32
}

// Hit records a hit.
func (cs *CacheStats) Hit() int64 { return cs.hit.Add(1) }

// Miss records a miss.
func (cs *CacheStats) Miss() int64 { return cs.miss.Add(1) }

// Stats returns the number of hits and misses and whether
// the hit rate was changed comparing to the last call.
func (cs *CacheStats) Stats() (bool, int64, int64) {
hit := cs.hit.Load()
miss := cs.miss.Load()
lookups := hit + miss

hitRate := float64(0)
if lookups > 0 {
hitRate = float64(hit) / float64(lookups)
}
flag := int32(hitRate * 1000)

return cs.flag.Swap(flag) != flag, hit, miss
}
35 changes: 35 additions & 0 deletions thor/cachestats_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2024 The VeChainThor developers

// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying
// file LICENSE or <https://www.gnu.org/licenses/lgpl-3.0.html>

package thor

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestCacheStats(t *testing.T) {
cs := &CacheStats{}
cs.Hit()
cs.Miss()
_, hit, miss := cs.Stats()

assert.Equal(t, int64(1), hit)
assert.Equal(t, int64(1), miss)

changed, _, _ := cs.Stats()
assert.False(t, changed)

cs.Hit()
cs.Miss()
assert.Equal(t, int64(3), cs.Hit())

changed, hit, miss = cs.Stats()

assert.Equal(t, int64(3), hit)
assert.Equal(t, int64(2), miss)
assert.True(t, changed)
}

0 comments on commit a20f44f

Please sign in to comment.