Skip to content

Commit 62b3cbd

Browse files
committed
vocone: refactor, fixes, persist mempool and finish block missing methods
Signed-off-by: Pau Escrich <p4u@dabax.net>
1 parent 80138ef commit 62b3cbd

5 files changed

Lines changed: 998 additions & 264 deletions

File tree

cmd/voconed/voconed.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"context"
45
"encoding/hex"
56
"fmt"
67
"os"
@@ -226,7 +227,12 @@ func main() {
226227
vc.App.SetBlockTimeTarget(time.Second * time.Duration(config.blockSeconds))
227228
vc.SetBlockSize(config.blockSize)
228229

229-
go vc.Start()
230+
ctx, cancel := context.WithCancel(context.Background())
231+
go func() {
232+
if err := vc.Start(ctx); err != nil {
233+
log.Fatal(err)
234+
}
235+
}()
230236
uAPI, err := vc.EnableAPI("0.0.0.0", config.port, config.path)
231237
if err != nil {
232238
log.Fatal(err)
@@ -264,4 +270,8 @@ func main() {
264270
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
265271
<-c
266272
log.Warnf("received SIGTERM, exiting at %s", time.Now().Format(time.RFC850))
273+
cancel()
274+
if err := vc.Close(); err != nil {
275+
log.Warnw("error closing vocone", "err", err)
276+
}
267277
}

vocone/blockstore.go

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
package vocone
2+
3+
import (
4+
"encoding/binary"
5+
"encoding/json"
6+
"time"
7+
8+
comettmhash "github.com/cometbft/cometbft/crypto/tmhash"
9+
comettypes "github.com/cometbft/cometbft/types"
10+
"go.vocdoni.io/dvote/crypto/ethereum"
11+
"go.vocdoni.io/dvote/log"
12+
"go.vocdoni.io/proto/build/go/models"
13+
"google.golang.org/protobuf/proto"
14+
)
15+
16+
// blockMeta holds persisted metadata for each block.
17+
type blockMeta struct {
18+
Timestamp int64 `json:"t"`
19+
TxCount int32 `json:"n"`
20+
StateRoot []byte `json:"r,omitempty"`
21+
Hash []byte `json:"h,omitempty"`
22+
ProposerAddress []byte `json:"p,omitempty"`
23+
LastBlockHash []byte `json:"l,omitempty"`
24+
DataHash []byte `json:"d,omitempty"`
25+
}
26+
27+
// storeBlockMeta persists block metadata and a hash→height reverse index.
28+
func (vc *Vocone) storeBlockMeta(height int64, timestamp time.Time, txCount int32,
29+
stateRoot, blockHash, proposerAddr, lastBlockHash, dataHash []byte,
30+
) error {
31+
meta := blockMeta{
32+
Timestamp: timestamp.UnixNano(),
33+
TxCount: txCount,
34+
StateRoot: stateRoot,
35+
Hash: blockHash,
36+
ProposerAddress: proposerAddr,
37+
LastBlockHash: lastBlockHash,
38+
DataHash: dataHash,
39+
}
40+
data, err := json.Marshal(&meta)
41+
if err != nil {
42+
return err
43+
}
44+
wTx := vc.blockStore.WriteTx()
45+
defer wTx.Discard()
46+
if err := wTx.Set(metaKey(height), data); err != nil {
47+
return err
48+
}
49+
// Store hash→height reverse index for GetBlockByHash lookups.
50+
if len(blockHash) > 0 {
51+
heightBytes := make([]byte, 8)
52+
binary.BigEndian.PutUint64(heightBytes, uint64(height))
53+
if err := wTx.Set(blockHashKey(blockHash), heightBytes); err != nil {
54+
return err
55+
}
56+
}
57+
return wTx.Commit()
58+
}
59+
60+
// loadBlockMeta reads block metadata from the store.
61+
func (vc *Vocone) loadBlockMeta(height int64) (*blockMeta, error) {
62+
data, err := vc.blockStore.Get(metaKey(height))
63+
if err != nil {
64+
return nil, err
65+
}
66+
var meta blockMeta
67+
if err := json.Unmarshal(data, &meta); err != nil {
68+
return nil, err
69+
}
70+
return &meta, nil
71+
}
72+
73+
// buildBlock constructs a comettypes.Block with all header fields populated
74+
// so that Block.Hash() returns a deterministic, non-nil hash.
75+
func (vc *Vocone) buildBlock(height int64, timestamp time.Time, txs [][]byte, appHash []byte) *comettypes.Block {
76+
blk := &comettypes.Block{
77+
Header: comettypes.Header{
78+
ChainID: vc.App.ChainID(),
79+
Height: height,
80+
Time: timestamp,
81+
ProposerAddress: vc.proposerAddress,
82+
AppHash: appHash,
83+
// ValidatorsHash is required for Header.Hash() to return non-nil.
84+
ValidatorsHash: comettmhash.Sum(vc.proposerAddress),
85+
NextValidatorsHash: comettmhash.Sum(vc.proposerAddress),
86+
ConsensusHash: comettmhash.Sum([]byte("vocone")),
87+
},
88+
// LastCommit must be non-nil for Block.Hash() to return non-nil.
89+
LastCommit: &comettypes.Commit{Height: height - 1},
90+
}
91+
// Set the previous block hash if available.
92+
if height > 0 {
93+
if prevMeta, err := vc.loadBlockMeta(height - 1); err == nil && len(prevMeta.Hash) > 0 {
94+
blk.Header.LastBlockID = comettypes.BlockID{Hash: prevMeta.Hash}
95+
}
96+
}
97+
// Populate transactions.
98+
blk.Data.Txs = make([]comettypes.Tx, len(txs))
99+
for i, tx := range txs {
100+
blk.Data.Txs[i] = tx
101+
}
102+
blk.Header.DataHash = blk.Data.Hash()
103+
return blk
104+
}
105+
106+
// getBlock reconstructs a block from the persistent store.
107+
func (vc *Vocone) getBlock(height int64) *comettypes.Block {
108+
if vc.closed.Load() {
109+
return &comettypes.Block{Header: comettypes.Header{Height: height}}
110+
}
111+
meta, err := vc.loadBlockMeta(height)
112+
if err != nil {
113+
// No metadata — return a minimal block shell.
114+
return &comettypes.Block{
115+
Header: comettypes.Header{
116+
ChainID: vc.App.ChainID(),
117+
Height: height,
118+
},
119+
}
120+
}
121+
122+
blk := &comettypes.Block{
123+
Header: comettypes.Header{
124+
ChainID: vc.App.ChainID(),
125+
Height: height,
126+
Time: time.Unix(0, meta.Timestamp),
127+
ProposerAddress: meta.ProposerAddress,
128+
AppHash: meta.StateRoot,
129+
DataHash: meta.DataHash,
130+
// Required for Hash() to return non-nil.
131+
ValidatorsHash: comettmhash.Sum(meta.ProposerAddress),
132+
NextValidatorsHash: comettmhash.Sum(meta.ProposerAddress),
133+
ConsensusHash: comettmhash.Sum([]byte("vocone")),
134+
},
135+
// LastCommit must be non-nil for Block.Hash() to return non-nil.
136+
LastCommit: &comettypes.Commit{Height: height - 1},
137+
}
138+
if len(meta.LastBlockHash) > 0 {
139+
blk.Header.LastBlockID = comettypes.BlockID{Hash: meta.LastBlockHash}
140+
}
141+
142+
// Read exactly txCount transactions
143+
for i := int32(0); i < meta.TxCount; i++ {
144+
txData, err := vc.blockStore.Get(txKey(height, i))
145+
if err != nil {
146+
log.Warnw("missing tx in block store",
147+
"height", height, "index", i, "err", err)
148+
break
149+
}
150+
blk.Data.Txs = append(blk.Data.Txs, txData)
151+
}
152+
return blk
153+
}
154+
155+
// getBlockByHash looks up a block by its hash using the reverse index.
156+
func (vc *Vocone) getBlockByHash(hash []byte) *comettypes.Block {
157+
heightBytes, err := vc.blockStore.Get(blockHashKey(hash))
158+
if err != nil {
159+
return nil
160+
}
161+
if len(heightBytes) != 8 {
162+
return nil
163+
}
164+
height := int64(binary.BigEndian.Uint64(heightBytes))
165+
return vc.getBlock(height)
166+
}
167+
168+
// getTx retrieves a single transaction from the block store.
169+
func (vc *Vocone) getTx(height uint32, txIndex int32) (*models.SignedTx, error) {
170+
txData, err := vc.blockStore.Get(txKey(int64(height), txIndex))
171+
if err != nil {
172+
return nil, err
173+
}
174+
stx := &models.SignedTx{}
175+
return stx, proto.Unmarshal(txData, stx)
176+
}
177+
178+
// getTxWithHash retrieves a transaction and its hash from the block store.
179+
func (vc *Vocone) getTxWithHash(height uint32, txIndex int32) (*models.SignedTx, []byte, error) {
180+
txData, err := vc.blockStore.Get(txKey(int64(height), txIndex))
181+
if err != nil {
182+
return nil, nil, err
183+
}
184+
stx := &models.SignedTx{}
185+
return stx, ethereum.HashRaw(txData), proto.Unmarshal(txData, stx)
186+
}
187+
188+
// txKey builds the db key for a transaction: "tx/" + height (8 bytes BE) + "/" + txIndex (4 bytes BE).
189+
func txKey(height int64, txIndex int32) []byte {
190+
key := make([]byte, len(prefixTx)+8+1+4)
191+
copy(key, prefixTx)
192+
binary.BigEndian.PutUint64(key[len(prefixTx):], uint64(height))
193+
key[len(prefixTx)+8] = '/'
194+
binary.BigEndian.PutUint32(key[len(prefixTx)+9:], uint32(txIndex))
195+
return key
196+
}
197+
198+
// metaKey builds the db key for block metadata: "meta/" + height (8 bytes BE).
199+
func metaKey(height int64) []byte {
200+
key := make([]byte, len(prefixMeta)+8)
201+
copy(key, prefixMeta)
202+
binary.BigEndian.PutUint64(key[len(prefixMeta):], uint64(height))
203+
return key
204+
}
205+
206+
// blockHashKey builds the db key for the hash→height reverse index: "blockhash/" + hash.
207+
func blockHashKey(hash []byte) []byte {
208+
key := make([]byte, len(prefixBlockHash)+len(hash))
209+
copy(key, prefixBlockHash)
210+
copy(key[len(prefixBlockHash):], hash)
211+
return key
212+
}

0 commit comments

Comments
 (0)