Skip to content

Commit 2ea5d99

Browse files
committed
extracted witness and producer logic
1 parent 1bb2243 commit 2ea5d99

File tree

6 files changed

+317
-297
lines changed

6 files changed

+317
-297
lines changed

modules/oracle/price/api/coingecko.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ const (
1212
pageLimit = 250
1313
)
1414

15+
var _ PriceQuery = &CoinGecko{}
16+
1517
type CoinGecko struct {
1618
baseUrl string
1719
apiKey string

modules/oracle/price/api/coinmarketcap.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type CoinMarketCap struct {
1515
currency string
1616
}
1717

18-
// var _ PriceQuery = &CoinMarketCap{}
18+
var _ PriceQuery = &CoinMarketCap{}
1919

2020
// Source implements PriceQuery
2121
func (c *CoinMarketCap) Source() string {

modules/oracle/price/handle_block_tick.go

Lines changed: 0 additions & 293 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,12 @@ package price
22

33
import (
44
"context"
5-
"encoding/base64"
6-
"encoding/hex"
75
"errors"
8-
"fmt"
9-
"log/slog"
106
"math"
11-
"slices"
127
"strings"
138
"time"
14-
"vsc-node/lib/dids"
15-
"vsc-node/modules/common"
16-
"vsc-node/modules/db/vsc/elections"
179
"vsc-node/modules/oracle/p2p"
1810
"vsc-node/modules/oracle/price/api"
19-
stateEngine "vsc-node/modules/state-processing"
20-
transactionpool "vsc-node/modules/transaction-pool"
21-
22-
blsu "github.com/protolambda/bls12-381-util"
2311
)
2412

2513
type CollectedPricePoint struct {
@@ -81,7 +69,6 @@ func (o *PriceOracle) HandleBlockTick(
8169
}
8270
}
8371

84-
// make transaction
8572
var handler interface {
8673
handle(map[string]api.PricePoint) error
8774
}
@@ -167,284 +154,4 @@ func (o *PriceOracle) collectAveragePricePoints(
167154
}
168155
}
169156

170-
// block producer
171-
type Producer struct {
172-
p2p.OracleP2PSpec
173-
ctx context.Context
174-
logger *slog.Logger
175-
blockTickSignal p2p.BlockTickSignal
176-
signatureChannel *SignatureResponseChannel
177-
}
178-
179-
func (p *Producer) handle(medianPriceMap map[string]api.PricePoint) error {
180-
p.logger.Debug("making block", "median-prices", medianPriceMap)
181-
182-
tx, err := makeTx(medianPriceMap)
183-
if err != nil {
184-
return fmt.Errorf("failed to create tx: %w", err)
185-
}
186-
187-
// broadcast signature request
188-
sigRequestMsg := SignatureRequestMessage{
189-
SigHash: tx.String(),
190-
MedianPrice: medianPriceMap,
191-
}
192-
193-
msg, err := makePriceOracleMessage(signatureRequestCode, &sigRequestMsg)
194-
if err != nil {
195-
return fmt.Errorf("failed to make message: %w", err)
196-
}
197-
198-
if err := p.Broadcast(p2p.MsgPriceOracle, msg); err != nil {
199-
return fmt.Errorf("failed to broadcast signature request: %w", err)
200-
}
201-
p.logger.Debug(
202-
"signature request broadcasted",
203-
"sig-hash", sigRequestMsg.SigHash,
204-
"median-price", sigRequestMsg.MedianPrice,
205-
)
206-
207-
// make bls circuit
208-
txCid := tx.Cid()
209-
210-
witnessDIDs := make([]dids.Member, len(p.blockTickSignal.ElectedMembers))
211-
for i := range p.blockTickSignal.ElectedMembers {
212-
witnessDIDs[i] = dids.BlsDID(p.blockTickSignal.ElectedMembers[i].Key)
213-
}
214-
215-
circuit, err := dids.NewBlsCircuitGenerator(witnessDIDs).Generate(txCid)
216-
if err != nil {
217-
return fmt.Errorf("failed to generate bls circuit: %w", err)
218-
}
219-
220-
// collect and verify signatures with bls circuit
221-
222-
ctx, cancel := context.WithTimeout(p.ctx, 15*time.Second)
223-
defer cancel()
224-
225-
if err := p.collectSignature(ctx, circuit); err != nil {
226-
return fmt.Errorf("failed to collect signatures: %w", err)
227-
}
228-
229-
// make transaction + submit to contract
230-
blsCircuit, err := circuit.Finalize()
231-
if err != nil {
232-
return fmt.Errorf("failed to finalize circuit: %w", err)
233-
}
234-
235-
serializedCircuit, err := blsCircuit.Serialize()
236-
if err != nil {
237-
return fmt.Errorf("failed to finalize circuit: %w", err)
238-
}
239-
240-
sigPackage := stateEngine.TransactionSig{
241-
Type: "vsc-sig",
242-
Sigs: []common.Sig{{
243-
Algo: "bls-agg",
244-
Sig: serializedCircuit.Signature,
245-
Bv: serializedCircuit.BitVector,
246-
Kid: "",
247-
}},
248-
}
249-
250-
sigBytes, err := common.EncodeDagCbor(sigPackage)
251-
if err != nil {
252-
return fmt.Errorf("failed encode DagCbor: %w", err)
253-
}
254-
255-
// submit contract
256-
vscTx := transactionpool.SerializedVSCTransaction{
257-
Tx: txCid.Bytes(),
258-
Sig: sigBytes,
259-
}
260-
261-
if err := p.submitToContract(vscTx); err != nil {
262-
return fmt.Errorf("failed to submit to contract: %w", err)
263-
}
264-
265-
return nil
266-
}
267-
268-
func (p *Producer) collectSignature(
269-
ctx context.Context,
270-
circuit dids.PartialBlsCircuit,
271-
) error {
272-
signatureThreshold := (p.blockTickSignal.TotalElectionWeight * 2) / 3
273-
signedWeight := uint64(0)
274-
275-
p.logger.Debug(
276-
"collecting signatures",
277-
"signature-threshold", signatureThreshold,
278-
)
279-
280-
receiver := p.signatureChannel.Open()
281-
defer p.signatureChannel.Close()
282-
283-
for signedWeight < signatureThreshold {
284-
select {
285-
case <-ctx.Done():
286-
if err := ctx.Err(); err != nil {
287-
return fmt.Errorf("context error: %w", err)
288-
} else {
289-
return errors.New("signature collection timed out")
290-
}
291-
292-
case msg := <-receiver:
293-
memberIndex := slices.IndexFunc(
294-
p.blockTickSignal.ElectedMembers,
295-
func(e elections.ElectionMember) bool {
296-
return e.Account == msg.Signer
297-
},
298-
)
299-
300-
if memberIndex == -1 {
301-
p.logger.Debug(
302-
"invalid witness signature, dropping.",
303-
"signer", msg.Signer,
304-
)
305-
continue
306-
}
307-
308-
member := &p.blockTickSignal.ElectedMembers[memberIndex]
309-
memberDID := dids.BlsDID(member.Key)
310-
311-
added, err := circuit.AddAndVerify(memberDID, msg.Signature)
312-
if err != nil {
313-
p.logger.Error("failed to verify signature", "err", err)
314-
continue
315-
}
316-
317-
if !added {
318-
p.logger.Debug("invalid signature, signature not added to circuit")
319-
continue
320-
}
321-
322-
signedWeight += p.blockTickSignal.WeightMap[memberIndex]
323-
p.logger.Debug(
324-
"received witness signature, appending to witnessSigned",
325-
"signer", member.Account, "signature", msg.Signature,
326-
)
327-
}
328-
}
329-
330-
return nil
331-
}
332-
333-
// TODO: implement this function
334-
func (p *Producer) submitToContract(transactionpool.SerializedVSCTransaction) error {
335-
p.logger.Error("submit to contract not implemented")
336-
return nil
337-
}
338-
339157
// witness
340-
type Witness struct {
341-
p2p.OracleP2PSpec
342-
ctx context.Context
343-
logger *slog.Logger
344-
blockTickSignal p2p.BlockTickSignal
345-
signatureRequestChannel *SignatureRequestChannel
346-
identity common.IdentityConfig
347-
}
348-
349-
func (w *Witness) handle(medianPriceMap map[string]api.PricePoint) error {
350-
w.logger.Debug(
351-
"witness median prices",
352-
"median-prices", medianPriceMap,
353-
)
354-
355-
// receiving median price
356-
receiver := w.signatureRequestChannel.Open()
357-
defer w.signatureRequestChannel.Close()
358-
359-
ctx, cancel := context.WithTimeout(w.ctx, 30*time.Second)
360-
defer cancel()
361-
362-
var signatureRequest SignatureRequestMessage
363-
select {
364-
case <-ctx.Done():
365-
return ctx.Err()
366-
367-
case signatureRequest = <-receiver:
368-
w.logger.Debug("signature request received")
369-
}
370-
371-
w.logger.Debug("signature request received",
372-
"median-price", signatureRequest.MedianPrice,
373-
"signature-hash", signatureRequest.SigHash,
374-
)
375-
376-
// compare broadcasted vs local median price
377-
for sym, pricePoint := range signatureRequest.MedianPrice {
378-
localPricePoint, ok := medianPriceMap[sym]
379-
if !ok {
380-
return fmt.Errorf("failed to verify median price point for ticker %s", sym)
381-
}
382-
383-
priceOk := floatEqual(pricePoint.Price, localPricePoint.Price)
384-
if !priceOk {
385-
return fmt.Errorf("failed to verify median price for ticker %s", sym)
386-
}
387-
388-
volumeOk := floatEqual(pricePoint.Volume, localPricePoint.Volume)
389-
if !volumeOk {
390-
return fmt.Errorf("failed to verify median volume for ticker %s", sym)
391-
}
392-
}
393-
394-
w.logger.Debug("median price verified", "broadcasted-median-price", signatureRequest.MedianPrice)
395-
396-
// make tx + verify incoming sig hash
397-
tx, err := makeTx(signatureRequest.MedianPrice)
398-
if err != nil {
399-
return fmt.Errorf("failed to create transaction: %w", err)
400-
}
401-
402-
txCid := tx.Cid()
403-
sigHashOk := signatureRequest.SigHash == txCid.String()
404-
if !sigHashOk {
405-
return fmt.Errorf("invalid signature hash")
406-
}
407-
408-
w.logger.Debug("signature hash verified", "sig-hash", signatureRequest.SigHash)
409-
410-
// sign data
411-
blsKeyDecoded, err := hex.DecodeString(w.identity.Get().BlsPrivKeySeed)
412-
if err != nil {
413-
return fmt.Errorf("failed to decode BLS private key seed: %w", err)
414-
}
415-
416-
if len(blsKeyDecoded) != 32 {
417-
return errors.New("bls priv seed must be 32 bytes")
418-
}
419-
420-
var blsKeyBuf [32]byte
421-
copy(blsKeyBuf[:], blsKeyDecoded)
422-
423-
blsSecretKey := &blsu.SecretKey{}
424-
if err := blsSecretKey.Deserialize(&blsKeyBuf); err != nil {
425-
return fmt.Errorf("failed to deserialize bls priv key: %w", err)
426-
}
427-
428-
sigBytes := blsu.Sign(blsSecretKey, txCid.Bytes()).Serialize()
429-
430-
// broadcast signature
431-
signatureResponse := &SignatureResponseMessage{
432-
Signer: w.identity.Get().HiveUsername,
433-
Signature: base64.RawURLEncoding.EncodeToString(sigBytes[:]),
434-
}
435-
436-
msg, err := makePriceOracleMessage(signatureResponseCode, signatureResponse)
437-
if err != nil {
438-
return fmt.Errorf("failed to make price oracle message: %w", err)
439-
}
440-
441-
if err := w.Broadcast(p2p.MsgPriceOracle, msg); err != nil {
442-
return fmt.Errorf("failed to broadcast signature response: %w", err)
443-
}
444-
w.logger.Debug(
445-
"signature response broadcasted",
446-
"signature", signatureResponse.Signature,
447-
)
448-
449-
return nil
450-
}

modules/oracle/price/price_oracle.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ import (
1717
var (
1818
_ p2p.MessageHandler = &PriceOracle{}
1919
_ aggregate.Plugin = &PriceOracle{}
20-
21-
_ api.PriceQuery = &api.CoinGecko{}
22-
_ api.PriceQuery = &api.CoinMarketCap{}
2320
)
2421

2522
type PriceOracle struct {

0 commit comments

Comments
 (0)