2024-02-13 07:04:56 +00:00
|
|
|
package crypto
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"crypto"
|
|
|
|
"crypto/rand"
|
|
|
|
"encoding/binary"
|
|
|
|
"math/big"
|
|
|
|
|
|
|
|
"github.com/cloudflare/circl/sign/ed448"
|
|
|
|
"github.com/iden3/go-iden3-crypto/poseidon"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"golang.org/x/crypto/sha3"
|
|
|
|
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/vdf"
|
|
|
|
"source.quilibrium.com/quilibrium/monorepo/node/keys"
|
|
|
|
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
|
|
|
|
"source.quilibrium.com/quilibrium/monorepo/node/tries"
|
|
|
|
)
|
|
|
|
|
|
|
|
type WesolowskiFrameProver struct {
|
|
|
|
logger *zap.Logger
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewWesolowskiFrameProver(logger *zap.Logger) *WesolowskiFrameProver {
|
|
|
|
return &WesolowskiFrameProver{
|
|
|
|
logger,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *WesolowskiFrameProver) ProveMasterClockFrame(
|
|
|
|
previousFrame *protobufs.ClockFrame,
|
|
|
|
timestamp int64,
|
|
|
|
difficulty uint32,
|
|
|
|
) (*protobufs.ClockFrame, error) {
|
|
|
|
input := []byte{}
|
|
|
|
input = append(input, previousFrame.Filter...)
|
|
|
|
input = binary.BigEndian.AppendUint64(input, previousFrame.FrameNumber+1)
|
|
|
|
input = binary.BigEndian.AppendUint32(input, difficulty)
|
|
|
|
input = append(input, previousFrame.Output[:]...)
|
|
|
|
|
|
|
|
b := sha3.Sum256(input)
|
|
|
|
v := vdf.New(difficulty, b)
|
|
|
|
v.Execute()
|
|
|
|
o := v.GetOutput()
|
|
|
|
|
|
|
|
previousSelectorBytes := [516]byte{}
|
|
|
|
copy(previousSelectorBytes[:], previousFrame.Output[:516])
|
|
|
|
|
|
|
|
parent, err := poseidon.HashBytes(previousSelectorBytes[:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "prove clock frame")
|
|
|
|
}
|
|
|
|
|
|
|
|
frame := &protobufs.ClockFrame{
|
|
|
|
Filter: previousFrame.Filter,
|
|
|
|
FrameNumber: previousFrame.FrameNumber + 1,
|
|
|
|
Timestamp: timestamp,
|
|
|
|
Difficulty: difficulty,
|
|
|
|
ParentSelector: parent.FillBytes(make([]byte, 32)),
|
|
|
|
Input: previousFrame.Output,
|
|
|
|
AggregateProofs: []*protobufs.InclusionAggregateProof{},
|
|
|
|
Output: o[:],
|
|
|
|
}
|
|
|
|
|
|
|
|
return frame, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *WesolowskiFrameProver) VerifyMasterClockFrame(
|
|
|
|
frame *protobufs.ClockFrame,
|
|
|
|
) error {
|
|
|
|
input := []byte{}
|
|
|
|
input = append(input, frame.Filter...)
|
|
|
|
input = binary.BigEndian.AppendUint64(input, frame.FrameNumber)
|
|
|
|
input = binary.BigEndian.AppendUint32(input, frame.Difficulty)
|
|
|
|
input = append(input, frame.Input...)
|
|
|
|
|
|
|
|
if len(frame.Input) < 516 {
|
|
|
|
return errors.Wrap(
|
|
|
|
errors.New("invalid input"),
|
|
|
|
"verify clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(frame.AggregateProofs) > 0 {
|
|
|
|
return errors.Wrap(
|
|
|
|
errors.New("invalid input"),
|
|
|
|
"verify clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
if frame.PublicKeySignature != nil {
|
|
|
|
return errors.Wrap(
|
|
|
|
errors.New("invalid input"),
|
|
|
|
"verify clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(frame.Input) != 516 {
|
|
|
|
return errors.Wrap(
|
|
|
|
errors.New("invalid input"),
|
|
|
|
"verify clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-02-21 05:02:14 +00:00
|
|
|
if len(frame.Output) != 516 {
|
|
|
|
return errors.Wrap(
|
|
|
|
errors.New("invalid output"),
|
|
|
|
"verify clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-02-13 07:04:56 +00:00
|
|
|
b := sha3.Sum256(input)
|
|
|
|
v := vdf.New(frame.Difficulty, b)
|
|
|
|
proof := [516]byte{}
|
|
|
|
copy(proof[:], frame.Output)
|
|
|
|
|
|
|
|
if !v.Verify(proof) {
|
|
|
|
w.logger.Error("invalid proof",
|
|
|
|
zap.Binary("filter", frame.Filter),
|
|
|
|
zap.Uint64("frame_number", frame.FrameNumber),
|
|
|
|
zap.Uint32("difficulty", frame.Difficulty),
|
|
|
|
zap.Binary("frame_input", frame.Input),
|
|
|
|
zap.Binary("frame_output", frame.Output),
|
|
|
|
)
|
|
|
|
return errors.Wrap(
|
|
|
|
errors.New("invalid proof"),
|
|
|
|
"verify clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
previousSelectorBytes := [516]byte{}
|
|
|
|
copy(previousSelectorBytes[:], frame.Input[:516])
|
|
|
|
|
|
|
|
parent, err := poseidon.HashBytes(previousSelectorBytes[:])
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "verify clock frame")
|
|
|
|
}
|
|
|
|
|
|
|
|
selector := new(big.Int).SetBytes(frame.ParentSelector)
|
|
|
|
if parent.Cmp(selector) != 0 {
|
|
|
|
return errors.Wrap(
|
|
|
|
errors.New("selector did not match input"),
|
|
|
|
"verify clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *WesolowskiFrameProver) CreateMasterGenesisFrame(
|
|
|
|
filter []byte,
|
|
|
|
seed []byte,
|
|
|
|
difficulty uint32,
|
|
|
|
) (
|
|
|
|
*protobufs.ClockFrame,
|
|
|
|
error,
|
|
|
|
) {
|
|
|
|
b := sha3.Sum256(seed)
|
|
|
|
v := vdf.New(difficulty, b)
|
|
|
|
|
|
|
|
v.Execute()
|
|
|
|
o := v.GetOutput()
|
|
|
|
inputMessage := o[:]
|
|
|
|
|
|
|
|
w.logger.Debug("proving genesis frame")
|
|
|
|
input := []byte{}
|
|
|
|
input = append(input, filter...)
|
|
|
|
input = binary.BigEndian.AppendUint64(input, 0)
|
|
|
|
input = binary.BigEndian.AppendUint32(input, difficulty)
|
|
|
|
if bytes.Equal(seed, []byte{0x00}) {
|
|
|
|
value := [516]byte{}
|
|
|
|
input = append(input, value[:]...)
|
|
|
|
} else {
|
|
|
|
input = append(input, seed...)
|
|
|
|
}
|
|
|
|
|
|
|
|
b = sha3.Sum256(input)
|
|
|
|
v = vdf.New(difficulty, b)
|
|
|
|
|
|
|
|
v.Execute()
|
|
|
|
o = v.GetOutput()
|
|
|
|
|
|
|
|
frame := &protobufs.ClockFrame{
|
|
|
|
Filter: filter,
|
|
|
|
FrameNumber: 0,
|
|
|
|
Timestamp: 0,
|
|
|
|
Difficulty: difficulty,
|
|
|
|
Input: inputMessage,
|
|
|
|
Output: o[:],
|
|
|
|
ParentSelector: []byte{
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
},
|
|
|
|
AggregateProofs: []*protobufs.InclusionAggregateProof{},
|
|
|
|
PublicKeySignature: nil,
|
|
|
|
}
|
|
|
|
|
|
|
|
return frame, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *WesolowskiFrameProver) ProveDataClockFrame(
|
|
|
|
previousFrame *protobufs.ClockFrame,
|
|
|
|
commitments [][]byte,
|
|
|
|
aggregateProofs []*protobufs.InclusionAggregateProof,
|
|
|
|
provingKey crypto.Signer,
|
|
|
|
timestamp int64,
|
|
|
|
difficulty uint32,
|
|
|
|
) (*protobufs.ClockFrame, error) {
|
|
|
|
var pubkey []byte
|
|
|
|
pubkeyType := keys.KeyTypeEd448
|
|
|
|
ed448PublicKey, ok := provingKey.Public().(ed448.PublicKey)
|
|
|
|
if ok {
|
|
|
|
pubkey = []byte(ed448PublicKey)
|
|
|
|
} else {
|
|
|
|
return nil, errors.Wrap(
|
|
|
|
errors.New("no valid signature provided"),
|
|
|
|
"prove clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
h, err := poseidon.HashBytes(pubkey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(
|
|
|
|
errors.New("could not hash proving key"),
|
|
|
|
"prove clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
address := h.Bytes()
|
|
|
|
input := []byte{}
|
|
|
|
input = append(input, previousFrame.Filter...)
|
|
|
|
input = binary.BigEndian.AppendUint64(input, previousFrame.FrameNumber+1)
|
|
|
|
input = binary.BigEndian.AppendUint64(input, uint64(timestamp))
|
|
|
|
input = binary.BigEndian.AppendUint32(input, difficulty)
|
|
|
|
input = append(input, address...)
|
|
|
|
input = append(input, previousFrame.Output[:]...)
|
|
|
|
|
|
|
|
commitmentInput := []byte{}
|
|
|
|
for _, commitment := range commitments {
|
|
|
|
commitmentInput = append(commitmentInput, commitment...)
|
|
|
|
}
|
|
|
|
|
|
|
|
input = append(input, commitmentInput...)
|
|
|
|
|
|
|
|
b := sha3.Sum256(input)
|
|
|
|
v := vdf.New(difficulty, b)
|
|
|
|
|
|
|
|
v.Execute()
|
|
|
|
o := v.GetOutput()
|
|
|
|
|
|
|
|
// TODO: make this configurable for signing algorithms that allow
|
|
|
|
// user-supplied hash functions
|
|
|
|
signature, err := provingKey.Sign(
|
|
|
|
rand.Reader,
|
|
|
|
append(append([]byte{}, b[:]...), o[:]...),
|
|
|
|
crypto.Hash(0),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(
|
|
|
|
err,
|
|
|
|
"prove",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
previousSelectorBytes := [516]byte{}
|
|
|
|
copy(previousSelectorBytes[:], previousFrame.Output[:516])
|
|
|
|
|
|
|
|
parent, err := poseidon.HashBytes(previousSelectorBytes[:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "prove clock frame")
|
|
|
|
}
|
|
|
|
|
|
|
|
frame := &protobufs.ClockFrame{
|
|
|
|
Filter: previousFrame.Filter,
|
|
|
|
FrameNumber: previousFrame.FrameNumber + 1,
|
|
|
|
Timestamp: timestamp,
|
|
|
|
Difficulty: difficulty,
|
2024-02-18 04:52:19 +00:00
|
|
|
ParentSelector: parent.FillBytes(make([]byte, 32)),
|
2024-02-13 07:04:56 +00:00
|
|
|
Input: append(
|
|
|
|
append([]byte{}, previousFrame.Output...),
|
|
|
|
commitmentInput...,
|
|
|
|
),
|
|
|
|
AggregateProofs: aggregateProofs,
|
|
|
|
Output: o[:],
|
|
|
|
}
|
|
|
|
|
|
|
|
switch pubkeyType {
|
|
|
|
case keys.KeyTypeEd448:
|
|
|
|
frame.PublicKeySignature = &protobufs.ClockFrame_PublicKeySignatureEd448{
|
|
|
|
PublicKeySignatureEd448: &protobufs.Ed448Signature{
|
|
|
|
Signature: signature,
|
|
|
|
PublicKey: &protobufs.Ed448PublicKey{
|
|
|
|
KeyValue: pubkey,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nil, errors.Wrap(
|
|
|
|
errors.New("unsupported proving key"),
|
|
|
|
"prove clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return frame, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *WesolowskiFrameProver) CreateDataGenesisFrame(
|
|
|
|
filter []byte,
|
|
|
|
origin []byte,
|
|
|
|
difficulty uint32,
|
|
|
|
inclusionProof *InclusionAggregateProof,
|
|
|
|
proverKeys [][]byte,
|
|
|
|
preDusk bool,
|
|
|
|
) (*protobufs.ClockFrame, *tries.RollingFrecencyCritbitTrie, error) {
|
|
|
|
frameProverTrie := &tries.RollingFrecencyCritbitTrie{}
|
|
|
|
for _, s := range proverKeys {
|
|
|
|
addr, err := poseidon.HashBytes(s)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
addrBytes := addr.Bytes()
|
|
|
|
addrBytes = append(make([]byte, 32-len(addrBytes)), addrBytes...)
|
|
|
|
frameProverTrie.Add(addrBytes, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
w.logger.Info("proving genesis frame")
|
|
|
|
input := []byte{}
|
|
|
|
input = append(input, filter...)
|
|
|
|
input = binary.BigEndian.AppendUint64(input, 0)
|
|
|
|
input = binary.BigEndian.AppendUint64(input, 0)
|
|
|
|
input = binary.BigEndian.AppendUint32(input, difficulty)
|
|
|
|
input = append(input, origin...)
|
|
|
|
if !preDusk {
|
|
|
|
input = append(input, inclusionProof.AggregateCommitment...)
|
|
|
|
}
|
|
|
|
|
|
|
|
b := sha3.Sum256(input)
|
|
|
|
v := vdf.New(difficulty, b)
|
|
|
|
|
|
|
|
v.Execute()
|
|
|
|
o := v.GetOutput()
|
|
|
|
|
|
|
|
commitments := []*protobufs.InclusionCommitment{}
|
|
|
|
for i, commit := range inclusionProof.InclusionCommitments {
|
|
|
|
commitments = append(commitments, &protobufs.InclusionCommitment{
|
|
|
|
Filter: filter,
|
|
|
|
FrameNumber: 0,
|
|
|
|
Position: uint32(i),
|
|
|
|
TypeUrl: commit.TypeUrl,
|
|
|
|
Data: commit.Data,
|
|
|
|
Commitment: commit.Commitment,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
frame := &protobufs.ClockFrame{
|
|
|
|
Filter: filter,
|
|
|
|
FrameNumber: 0,
|
|
|
|
Timestamp: 0,
|
|
|
|
Difficulty: difficulty,
|
|
|
|
Input: append(
|
|
|
|
append([]byte{}, origin...),
|
|
|
|
inclusionProof.AggregateCommitment...,
|
|
|
|
),
|
|
|
|
Output: o[:],
|
|
|
|
ParentSelector: []byte{
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
},
|
|
|
|
AggregateProofs: []*protobufs.InclusionAggregateProof{
|
|
|
|
{
|
|
|
|
Filter: filter,
|
|
|
|
FrameNumber: 0,
|
|
|
|
InclusionCommitments: commitments,
|
|
|
|
Proof: inclusionProof.Proof,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
PublicKeySignature: nil,
|
|
|
|
}
|
|
|
|
|
|
|
|
return frame, frameProverTrie, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *WesolowskiFrameProver) VerifyDataClockFrame(
|
|
|
|
frame *protobufs.ClockFrame,
|
|
|
|
) error {
|
|
|
|
var pubkey []byte
|
|
|
|
var signature []byte
|
|
|
|
pubkeyType := keys.KeyTypeEd448
|
|
|
|
ed448PublicKey := frame.GetPublicKeySignatureEd448()
|
|
|
|
if ed448PublicKey != nil {
|
|
|
|
pubkey = ed448PublicKey.PublicKey.KeyValue
|
|
|
|
signature = ed448PublicKey.Signature
|
|
|
|
} else {
|
|
|
|
return errors.Wrap(
|
|
|
|
errors.New("no valid signature provided"),
|
|
|
|
"verify clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
h, err := poseidon.HashBytes(pubkey)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(
|
|
|
|
errors.New("could not hash proving key"),
|
|
|
|
"verify clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
address := h.Bytes()
|
|
|
|
|
|
|
|
input := []byte{}
|
|
|
|
input = append(input, frame.Filter...)
|
|
|
|
input = binary.BigEndian.AppendUint64(input, frame.FrameNumber)
|
|
|
|
input = binary.BigEndian.AppendUint64(input, uint64(frame.Timestamp))
|
|
|
|
input = binary.BigEndian.AppendUint32(input, frame.Difficulty)
|
|
|
|
input = append(input, address...)
|
|
|
|
input = append(input, frame.Input...)
|
|
|
|
|
|
|
|
if len(frame.Input) < 516 {
|
|
|
|
return errors.Wrap(
|
|
|
|
errors.New("invalid input"),
|
|
|
|
"verify clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-02-21 05:02:14 +00:00
|
|
|
if len(frame.Output) != 516 {
|
|
|
|
return errors.Wrap(
|
|
|
|
errors.New("invalid output"),
|
|
|
|
"verify clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-02-13 07:04:56 +00:00
|
|
|
b := sha3.Sum256(input)
|
|
|
|
v := vdf.New(frame.Difficulty, b)
|
|
|
|
proof := [516]byte{}
|
|
|
|
copy(proof[:], frame.Output)
|
|
|
|
|
|
|
|
// TODO: make this configurable for signing algorithms that allow
|
|
|
|
// user-supplied hash functions
|
|
|
|
switch pubkeyType {
|
|
|
|
case keys.KeyTypeEd448:
|
|
|
|
if len(pubkey) != 57 || len(signature) != 114 || !ed448.VerifyAny(
|
|
|
|
pubkey,
|
|
|
|
append(append([]byte{}, b[:]...), frame.Output...),
|
|
|
|
signature,
|
|
|
|
crypto.Hash(0),
|
|
|
|
) {
|
|
|
|
return errors.Wrap(
|
|
|
|
errors.New("invalid signature for issuer"),
|
|
|
|
"verify clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !v.Verify(proof) {
|
|
|
|
return errors.Wrap(
|
|
|
|
errors.New("invalid proof"),
|
|
|
|
"verify clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
previousSelectorBytes := [516]byte{}
|
|
|
|
copy(previousSelectorBytes[:], frame.Input[:516])
|
|
|
|
|
|
|
|
parent, err := poseidon.HashBytes(previousSelectorBytes[:])
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "verify clock frame")
|
|
|
|
}
|
|
|
|
|
|
|
|
selector := new(big.Int).SetBytes(frame.ParentSelector)
|
|
|
|
if parent.Cmp(selector) != 0 {
|
|
|
|
return errors.Wrap(
|
|
|
|
errors.New("selector did not match input"),
|
|
|
|
"verify clock frame",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|