mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 10:27:26 +08:00
v2.0.6
This commit is contained in:
parent
6ecaf023ab
commit
819bb26dd6
@ -17,11 +17,11 @@ func GetMinimumPatchVersion() byte {
|
||||
}
|
||||
|
||||
func GetMinimumVersion() []byte {
|
||||
return []byte{0x02, 0x00, 0x05}
|
||||
return []byte{0x02, 0x00, 0x06}
|
||||
}
|
||||
|
||||
func GetVersion() []byte {
|
||||
return []byte{0x02, 0x00, 0x05}
|
||||
return []byte{0x02, 0x00, 0x06}
|
||||
}
|
||||
|
||||
func GetVersionString() string {
|
||||
|
||||
@ -29,6 +29,7 @@ import (
|
||||
qtime "source.quilibrium.com/quilibrium/monorepo/node/consensus/time"
|
||||
qcrypto "source.quilibrium.com/quilibrium/monorepo/node/crypto"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/execution"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/token/application"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/internal/cas"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/internal/frametime"
|
||||
qgrpc "source.quilibrium.com/quilibrium/monorepo/node/internal/grpc"
|
||||
@ -581,6 +582,7 @@ func (e *DataClockConsensusEngine) Start() <-chan error {
|
||||
|
||||
func (e *DataClockConsensusEngine) PerformTimeProof(
|
||||
frame *protobufs.ClockFrame,
|
||||
previousTreeRoot []byte,
|
||||
difficulty uint32,
|
||||
ring int,
|
||||
) []mt.DataBlock {
|
||||
@ -613,6 +615,12 @@ func (e *DataClockConsensusEngine) PerformTimeProof(
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(actives))
|
||||
challengeOutput := []byte{}
|
||||
if frame.FrameNumber >= application.PROOF_FRAME_COMBINE_CUTOFF {
|
||||
challengeOutput = append(append([]byte{}, frame.Output...), previousTreeRoot...)
|
||||
} else {
|
||||
challengeOutput = frame.Output
|
||||
}
|
||||
|
||||
for i, client := range actives {
|
||||
i := i
|
||||
@ -625,7 +633,7 @@ func (e *DataClockConsensusEngine) PerformTimeProof(
|
||||
&protobufs.ChallengeProofRequest{
|
||||
PeerId: e.pubSub.GetPeerID(),
|
||||
Core: uint32(i),
|
||||
Output: frame.Output,
|
||||
Output: challengeOutput,
|
||||
FrameNumber: frame.FrameNumber,
|
||||
Difficulty: frame.Difficulty,
|
||||
},
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/iden3/go-iden3-crypto/poseidon"
|
||||
mt "github.com/txaty/go-merkletree"
|
||||
"go.uber.org/zap"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/consensus"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/token/application"
|
||||
@ -356,18 +357,33 @@ func (e *DataClockConsensusEngine) processFrame(
|
||||
e.clientReconnectTest = 0
|
||||
}
|
||||
|
||||
outputs := e.PerformTimeProof(latestFrame, latestFrame.Difficulty, ring)
|
||||
previousTreeRoot := []byte{}
|
||||
if e.previousTree != nil {
|
||||
previousTreeRoot = e.previousTree.Root
|
||||
}
|
||||
outputs := e.PerformTimeProof(latestFrame, previousTreeRoot, latestFrame.Difficulty, ring)
|
||||
if outputs == nil || len(outputs) < 3 {
|
||||
e.logger.Info("workers not yet available for proving")
|
||||
return latestFrame
|
||||
}
|
||||
modulo := len(outputs)
|
||||
proofTree, output, err := tries.PackOutputIntoPayloadAndProof(
|
||||
outputs,
|
||||
modulo,
|
||||
latestFrame,
|
||||
e.previousTree,
|
||||
)
|
||||
var proofTree *mt.MerkleTree
|
||||
var output [][]byte
|
||||
if latestFrame.FrameNumber >= application.PROOF_FRAME_COMBINE_CUTOFF {
|
||||
proofTree, output, err = tries.PackOutputIntoMultiPayloadAndProof(
|
||||
outputs,
|
||||
modulo,
|
||||
latestFrame,
|
||||
e.previousTree,
|
||||
)
|
||||
} else {
|
||||
proofTree, output, err = tries.PackOutputIntoPayloadAndProof(
|
||||
outputs,
|
||||
modulo,
|
||||
latestFrame,
|
||||
e.previousTree,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
e.logger.Error(
|
||||
"could not successfully pack proof, reattempting",
|
||||
|
||||
@ -213,7 +213,7 @@ func (a *TokenApplication) ApplyTransitions(
|
||||
fails[i] = transition
|
||||
continue
|
||||
}
|
||||
ring, parallelism, err := t.Mint.RingAndParallelism(
|
||||
_, _, err := t.Mint.RingAndParallelism(
|
||||
func(addr []byte) int {
|
||||
if _, ok := seen[string(addr)]; ok {
|
||||
return -1
|
||||
@ -233,7 +233,6 @@ func (a *TokenApplication) ApplyTransitions(
|
||||
if err == nil {
|
||||
// fmt.Println(i, "checked ring test")
|
||||
set[i] = transition
|
||||
parallelismMap[ring] = parallelismMap[ring] + uint64(parallelism)
|
||||
} else {
|
||||
// fmt.Println(i, "failed ring test", err)
|
||||
fails[i] = transition
|
||||
@ -246,6 +245,7 @@ func (a *TokenApplication) ApplyTransitions(
|
||||
|
||||
outputsSet := make([][]*protobufs.TokenOutput, len(set))
|
||||
successes := make([]*protobufs.TokenRequest, len(set))
|
||||
processedMap := make([]*processedMint, len(set))
|
||||
for i, transition := range set {
|
||||
if transition == nil {
|
||||
continue
|
||||
@ -390,6 +390,74 @@ func (a *TokenApplication) ApplyTransitions(
|
||||
if transition == nil {
|
||||
continue
|
||||
}
|
||||
switch t := transition.Request.(type) {
|
||||
case *protobufs.TokenRequest_Mint:
|
||||
throttle <- struct{}{}
|
||||
wg.Add(1)
|
||||
go func(i int, transition *protobufs.TokenRequest) {
|
||||
defer func() { <-throttle }()
|
||||
defer wg.Done()
|
||||
var err error
|
||||
processedMap[i], err = a.preProcessMint(
|
||||
currentFrameNumber,
|
||||
t.Mint,
|
||||
frame,
|
||||
)
|
||||
if err != nil {
|
||||
fails[i] = transition
|
||||
return
|
||||
}
|
||||
}(i, transition)
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
for i, transition := range set {
|
||||
if fails[i] != nil {
|
||||
continue
|
||||
}
|
||||
switch t := transition.Request.(type) {
|
||||
case *protobufs.TokenRequest_Mint:
|
||||
if len(t.Mint.Proofs) == 1 {
|
||||
continue
|
||||
} else if len(t.Mint.Proofs) >= 3 && currentFrameNumber > PROOF_FRAME_CUTOFF {
|
||||
if processedMap[i].validForReward {
|
||||
ring, parallelism, err := t.Mint.RingAndParallelism(
|
||||
func(addr []byte) int {
|
||||
ring := -1
|
||||
for i, t := range a.Tries[1:] {
|
||||
if t.Contains(addr) {
|
||||
ring = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return ring
|
||||
},
|
||||
)
|
||||
if err == nil {
|
||||
parallelismMap[ring] = parallelismMap[ring] + uint64(parallelism)
|
||||
} else {
|
||||
// fmt.Println(i, "failed ring test", err)
|
||||
fails[i] = transition
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
set[i] = transition
|
||||
}
|
||||
}
|
||||
|
||||
for i, transition := range set {
|
||||
if transition == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if fails[i] != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
switch t := transition.Request.(type) {
|
||||
case *protobufs.TokenRequest_Mint:
|
||||
throttle <- struct{}{}
|
||||
@ -401,6 +469,7 @@ func (a *TokenApplication) ApplyTransitions(
|
||||
currentFrameNumber,
|
||||
t.Mint,
|
||||
frame,
|
||||
processedMap[i],
|
||||
parallelismMap,
|
||||
)
|
||||
if err != nil {
|
||||
@ -412,6 +481,7 @@ func (a *TokenApplication) ApplyTransitions(
|
||||
}(i, transition)
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
finalFails := []*protobufs.TokenRequest{}
|
||||
@ -422,7 +492,7 @@ func (a *TokenApplication) ApplyTransitions(
|
||||
}
|
||||
if len(finalFails) != 0 && !skipFailures {
|
||||
return nil, nil, nil, errors.Wrap(
|
||||
err,
|
||||
ErrInvalidStateTransition,
|
||||
"apply transitions",
|
||||
)
|
||||
}
|
||||
@ -446,7 +516,6 @@ func (a *TokenApplication) ApplyTransitions(
|
||||
|
||||
finalizedTransitions.Requests = finalSuccesses
|
||||
failedTransitions.Requests = finalFails
|
||||
|
||||
return a, finalizedTransitions, failedTransitions, nil
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
"math/bits"
|
||||
|
||||
"github.com/iden3/go-iden3-crypto/poseidon"
|
||||
pcrypto "github.com/libp2p/go-libp2p/core/crypto"
|
||||
@ -17,21 +18,474 @@ import (
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/tries"
|
||||
)
|
||||
|
||||
const PROOF_FRAME_CUTOFF = 46500
|
||||
const PROOF_FRAME_RING_RESET = 52000
|
||||
const PROOF_FRAME_RING_RESET_2 = 53028
|
||||
// for tests, these need to be var
|
||||
var PROOF_FRAME_CUTOFF = uint64(46500)
|
||||
var PROOF_FRAME_RING_RESET = uint64(52000)
|
||||
var PROOF_FRAME_RING_RESET_2 = uint64(53028)
|
||||
var PROOF_FRAME_COMBINE_CUTOFF = uint64(99000)
|
||||
|
||||
const PROOF_FRAME_SENIORITY_REPAIR = 59029
|
||||
|
||||
type processedMint struct {
|
||||
isPre2 bool
|
||||
penalty bool
|
||||
deletedProof *protobufs.TokenOutput_DeletedProof
|
||||
parallelism uint32
|
||||
priorCommitment []byte
|
||||
newCommitment []byte
|
||||
newFrameNumber uint64
|
||||
implicitAddr []byte
|
||||
validForReward bool
|
||||
treeVerified bool
|
||||
wesoVerified bool
|
||||
}
|
||||
|
||||
func (a *TokenApplication) preProcessMint(
|
||||
currentFrameNumber uint64,
|
||||
t *protobufs.MintCoinRequest,
|
||||
frame *protobufs.ClockFrame,
|
||||
) (
|
||||
out *processedMint,
|
||||
err error,
|
||||
) {
|
||||
if err := t.Validate(); err != nil {
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "pre process mint")
|
||||
}
|
||||
|
||||
payload := []byte("mint")
|
||||
for _, p := range t.Proofs {
|
||||
payload = append(payload, p...)
|
||||
}
|
||||
|
||||
pk, err := pcrypto.UnmarshalEd448PublicKey(
|
||||
t.Signature.PublicKey.KeyValue,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "pre process mint")
|
||||
}
|
||||
|
||||
peerId, err := peer.IDFromPublicKey(pk)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "pre process mint")
|
||||
}
|
||||
|
||||
addr, err := poseidon.HashBytes(
|
||||
t.Signature.PublicKey.KeyValue,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "pre process mint")
|
||||
}
|
||||
addrBytes := addr.FillBytes(make([]byte, 32))
|
||||
|
||||
altAddr, err := poseidon.HashBytes([]byte(peerId))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "pre process mint")
|
||||
}
|
||||
altAddrBytes := altAddr.FillBytes(make([]byte, 32))
|
||||
|
||||
// todo: set termination frame for this:
|
||||
if len(t.Proofs) == 1 && a.Tries[0].Contains(addrBytes) &&
|
||||
bytes.Equal(t.Signature.PublicKey.KeyValue, a.Beacon) {
|
||||
return &processedMint{
|
||||
isPre2: true,
|
||||
penalty: false,
|
||||
}, nil
|
||||
} else if len(t.Proofs) > 0 && currentFrameNumber > PROOF_FRAME_CUTOFF &&
|
||||
currentFrameNumber < PROOF_FRAME_COMBINE_CUTOFF {
|
||||
a.Logger.Debug(
|
||||
"got mint from peer",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
_, prfs, err := a.CoinStore.GetPreCoinProofsForOwner(altAddrBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "pre process mint")
|
||||
}
|
||||
|
||||
var delete *protobufs.PreCoinProof
|
||||
var commitment []byte
|
||||
var previousFrame *protobufs.ClockFrame
|
||||
var previousParallelism uint32
|
||||
for _, pr := range prfs {
|
||||
if len(pr.Proof) >= 3 && (len(pr.Commitment) == 40 || len(pr.Commitment) == 72) {
|
||||
delete = pr
|
||||
commitment = pr.Commitment[:32]
|
||||
previousFrameNumber := binary.BigEndian.Uint64(pr.Commitment[32:])
|
||||
previousParallelism = binary.BigEndian.Uint32(pr.Proof[36:40])
|
||||
previousFrame, _, err = a.ClockStore.GetDataClockFrame(
|
||||
frame.Filter,
|
||||
previousFrameNumber,
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
a.Logger.Debug(
|
||||
"invalid frame",
|
||||
zap.Error(err),
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
return &processedMint{
|
||||
isPre2: false,
|
||||
penalty: true,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newCommitment, parallelism, newFrameNumber, verified, err :=
|
||||
tries.UnpackAndVerifyOutput(commitment, t.Proofs)
|
||||
if err != nil {
|
||||
a.Logger.Debug(
|
||||
"mint error",
|
||||
zap.Error(err),
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
return &processedMint{
|
||||
isPre2: false,
|
||||
penalty: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if previousParallelism != 0 && previousParallelism != parallelism {
|
||||
verified = false
|
||||
}
|
||||
|
||||
if !verified {
|
||||
a.Logger.Debug(
|
||||
"tree verification failed",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
}
|
||||
|
||||
// Current frame - 2 is because the current frame is the newly created frame,
|
||||
// and the provers are submitting proofs on the frame preceding the one they
|
||||
// last saw. This enforces liveness and creates a punishment for being
|
||||
// late.
|
||||
if (previousFrame != nil && newFrameNumber <= previousFrame.FrameNumber) ||
|
||||
newFrameNumber < currentFrameNumber-2 {
|
||||
previousFrameNumber := uint64(0)
|
||||
if previousFrame != nil {
|
||||
previousFrameNumber = previousFrame.FrameNumber
|
||||
}
|
||||
|
||||
a.Logger.Debug(
|
||||
"received out of order proofs, ignoring",
|
||||
zap.Error(err),
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("previous_frame", previousFrameNumber),
|
||||
zap.Uint64("new_frame", newFrameNumber),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
|
||||
wesoVerified := true
|
||||
if verified && delete != nil && len(t.Proofs) > 3 {
|
||||
newFrame, _, err := a.ClockStore.GetDataClockFrame(
|
||||
frame.Filter,
|
||||
newFrameNumber,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
a.Logger.Debug(
|
||||
"invalid frame",
|
||||
zap.Error(err),
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
return &processedMint{
|
||||
isPre2: false,
|
||||
penalty: true,
|
||||
}, nil
|
||||
}
|
||||
hash := sha3.Sum256(newFrame.Output)
|
||||
pick := tries.BytesToUnbiasedMod(hash, uint64(parallelism))
|
||||
challenge := []byte{}
|
||||
challenge = append(challenge, peerId...)
|
||||
challenge = binary.BigEndian.AppendUint64(
|
||||
challenge,
|
||||
previousFrame.FrameNumber,
|
||||
)
|
||||
individualChallenge := append([]byte{}, challenge...)
|
||||
individualChallenge = binary.BigEndian.AppendUint32(
|
||||
individualChallenge,
|
||||
uint32(pick),
|
||||
)
|
||||
leaf := t.Proofs[len(t.Proofs)-1]
|
||||
individualChallenge = append(individualChallenge, previousFrame.Output...)
|
||||
if len(leaf) != 516 {
|
||||
a.Logger.Debug(
|
||||
"invalid size",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
zap.Int("proof_size", len(leaf)),
|
||||
)
|
||||
return &processedMint{
|
||||
isPre2: false,
|
||||
penalty: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if bytes.Equal(leaf, bytes.Repeat([]byte{0x00}, 516)) ||
|
||||
!a.FrameProver.VerifyChallengeProof(
|
||||
individualChallenge,
|
||||
frame.Difficulty,
|
||||
leaf,
|
||||
) {
|
||||
a.Logger.Debug(
|
||||
"invalid proof",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
// we want this to still apply the next commit even if this proof failed
|
||||
wesoVerified = false
|
||||
}
|
||||
}
|
||||
|
||||
var deletedProof *protobufs.TokenOutput_DeletedProof
|
||||
if delete != nil {
|
||||
deletedProof = &protobufs.TokenOutput_DeletedProof{
|
||||
DeletedProof: delete,
|
||||
}
|
||||
}
|
||||
|
||||
validForReward := verified && delete != nil && len(t.Proofs) > 3 && wesoVerified
|
||||
return &processedMint{
|
||||
isPre2: false,
|
||||
penalty: false,
|
||||
deletedProof: deletedProof,
|
||||
parallelism: parallelism,
|
||||
newCommitment: newCommitment,
|
||||
newFrameNumber: newFrameNumber,
|
||||
implicitAddr: altAddrBytes,
|
||||
validForReward: validForReward,
|
||||
treeVerified: verified,
|
||||
wesoVerified: wesoVerified,
|
||||
}, nil
|
||||
} else if len(t.Proofs) > 0 && currentFrameNumber >= PROOF_FRAME_COMBINE_CUTOFF {
|
||||
a.Logger.Debug(
|
||||
"got mint from peer",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
_, prfs, err := a.CoinStore.GetPreCoinProofsForOwner(altAddrBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "pre process mint")
|
||||
}
|
||||
|
||||
var delete *protobufs.PreCoinProof
|
||||
var commitment []byte
|
||||
var previousFrame *protobufs.ClockFrame
|
||||
var previousParallelism uint32
|
||||
var priorCommitment []byte
|
||||
for _, pr := range prfs {
|
||||
if len(pr.Proof) >= 3 && (len(pr.Commitment) == 40 || len(pr.Commitment) == 72) {
|
||||
delete = pr
|
||||
commitment = pr.Commitment[:32]
|
||||
previousFrameNumber := binary.BigEndian.Uint64(pr.Commitment[32:40])
|
||||
previousParallelism = binary.BigEndian.Uint32(pr.Proof[36:40])
|
||||
previousFrame, _, err = a.ClockStore.GetDataClockFrame(
|
||||
frame.Filter,
|
||||
previousFrameNumber,
|
||||
true,
|
||||
)
|
||||
if len(pr.Commitment) > 40 {
|
||||
priorCommitment = pr.Commitment[40:]
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
a.Logger.Debug(
|
||||
"invalid frame",
|
||||
zap.Error(err),
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
return &processedMint{
|
||||
isPre2: false,
|
||||
penalty: true,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newCommitment, parallelism, newFrameNumber, verified, err :=
|
||||
tries.UnpackAndVerifyMultiOutput(commitment, t.Proofs)
|
||||
if err != nil {
|
||||
a.Logger.Debug(
|
||||
"mint error",
|
||||
zap.Error(err),
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
return &processedMint{
|
||||
isPre2: false,
|
||||
penalty: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if previousParallelism != 0 && previousParallelism != parallelism {
|
||||
verified = false
|
||||
}
|
||||
|
||||
if !verified {
|
||||
a.Logger.Debug(
|
||||
"tree verification failed",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
}
|
||||
|
||||
// Current frame - 2 is because the current frame is the newly created frame,
|
||||
// and the provers are submitting proofs on the frame preceding the one they
|
||||
// last saw. This enforces liveness and creates a punishment for being
|
||||
// late.
|
||||
if (previousFrame != nil && newFrameNumber <= previousFrame.FrameNumber) ||
|
||||
newFrameNumber < currentFrameNumber-2 {
|
||||
previousFrameNumber := uint64(0)
|
||||
if previousFrame != nil {
|
||||
previousFrameNumber = previousFrame.FrameNumber
|
||||
}
|
||||
|
||||
a.Logger.Debug(
|
||||
"received out of order proofs, ignoring",
|
||||
zap.Error(err),
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("previous_frame", previousFrameNumber),
|
||||
zap.Uint64("new_frame", newFrameNumber),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
|
||||
wesoVerified := true
|
||||
if verified && delete != nil && len(t.Proofs) > 3 {
|
||||
newFrame, _, err := a.ClockStore.GetDataClockFrame(
|
||||
frame.Filter,
|
||||
newFrameNumber,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
a.Logger.Debug(
|
||||
"invalid frame",
|
||||
zap.Error(err),
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
return &processedMint{
|
||||
isPre2: false,
|
||||
penalty: true,
|
||||
}, nil
|
||||
}
|
||||
hash := sha3.Sum256(append(append([]byte{}, newFrame.Output...), commitment...))
|
||||
pick := tries.BytesToUnbiasedMod(hash, uint64(parallelism))
|
||||
challenge := []byte{}
|
||||
challenge = append(challenge, peerId...)
|
||||
challenge = binary.BigEndian.AppendUint64(
|
||||
challenge,
|
||||
previousFrame.FrameNumber,
|
||||
)
|
||||
additional := bits.Len64(uint64(parallelism)-1) - 1
|
||||
picks := []int{int(pick)}
|
||||
outputs := [][]byte{t.Proofs[len(t.Proofs)-(additional+1)]}
|
||||
for additional > 0 {
|
||||
hash = sha3.Sum256(hash[:])
|
||||
pick := tries.BytesToUnbiasedMod(hash, uint64(parallelism))
|
||||
found := false
|
||||
for _, p := range picks {
|
||||
if p == int(pick) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
picks = append(picks, int(pick))
|
||||
outputs = append(outputs, t.Proofs[len(t.Proofs)-additional])
|
||||
additional--
|
||||
}
|
||||
}
|
||||
for i, pick := range picks {
|
||||
individualChallenge := append([]byte{}, challenge...)
|
||||
individualChallenge = binary.BigEndian.AppendUint32(
|
||||
individualChallenge,
|
||||
uint32(pick),
|
||||
)
|
||||
|
||||
individualChallenge = append(individualChallenge, previousFrame.Output...)
|
||||
individualChallenge = append(individualChallenge, priorCommitment...)
|
||||
leaf := outputs[i]
|
||||
|
||||
if len(leaf) != 516 {
|
||||
a.Logger.Debug(
|
||||
"invalid size",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
zap.Int("proof_size", len(leaf)),
|
||||
)
|
||||
return &processedMint{
|
||||
isPre2: false,
|
||||
penalty: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if bytes.Equal(leaf, bytes.Repeat([]byte{0x00}, 516)) ||
|
||||
!a.FrameProver.VerifyChallengeProof(
|
||||
individualChallenge,
|
||||
frame.Difficulty,
|
||||
leaf,
|
||||
) {
|
||||
a.Logger.Debug(
|
||||
"invalid proof",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
// we want this to still apply the next commit even if this proof failed
|
||||
wesoVerified = wesoVerified && false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var deletedProof *protobufs.TokenOutput_DeletedProof
|
||||
if delete != nil {
|
||||
deletedProof = &protobufs.TokenOutput_DeletedProof{
|
||||
DeletedProof: delete,
|
||||
}
|
||||
}
|
||||
|
||||
validForReward := verified && delete != nil && len(t.Proofs) > 3 && wesoVerified
|
||||
return &processedMint{
|
||||
isPre2: false,
|
||||
penalty: false,
|
||||
deletedProof: deletedProof,
|
||||
parallelism: parallelism,
|
||||
priorCommitment: commitment,
|
||||
newCommitment: newCommitment,
|
||||
newFrameNumber: newFrameNumber,
|
||||
implicitAddr: altAddrBytes,
|
||||
validForReward: validForReward,
|
||||
treeVerified: verified,
|
||||
wesoVerified: wesoVerified,
|
||||
}, nil
|
||||
}
|
||||
|
||||
a.Logger.Debug(
|
||||
"could not find case for proof",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "pre process mint")
|
||||
}
|
||||
|
||||
func (a *TokenApplication) handleMint(
|
||||
currentFrameNumber uint64,
|
||||
t *protobufs.MintCoinRequest,
|
||||
frame *protobufs.ClockFrame,
|
||||
processed *processedMint,
|
||||
parallelismMap map[int]uint64,
|
||||
) ([]*protobufs.TokenOutput, error) {
|
||||
if err := t.Validate(); err != nil {
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
|
||||
payload := []byte("mint")
|
||||
for _, p := range t.Proofs {
|
||||
payload = append(payload, p...)
|
||||
@ -49,14 +503,6 @@ func (a *TokenApplication) handleMint(
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
|
||||
addr, err := poseidon.HashBytes(
|
||||
t.Signature.PublicKey.KeyValue,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
addrBytes := addr.FillBytes(make([]byte, 32))
|
||||
|
||||
altAddr, err := poseidon.HashBytes([]byte(peerId))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
@ -64,8 +510,7 @@ func (a *TokenApplication) handleMint(
|
||||
altAddrBytes := altAddr.FillBytes(make([]byte, 32))
|
||||
|
||||
// todo: set termination frame for this:
|
||||
if len(t.Proofs) == 1 && a.Tries[0].Contains(addrBytes) &&
|
||||
bytes.Equal(t.Signature.PublicKey.KeyValue, a.Beacon) {
|
||||
if processed.isPre2 {
|
||||
if len(t.Proofs[0]) != 64 {
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
@ -129,60 +574,7 @@ func (a *TokenApplication) handleMint(
|
||||
}
|
||||
}
|
||||
|
||||
_, prfs, err := a.CoinStore.GetPreCoinProofsForOwner(altAddrBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
|
||||
var delete *protobufs.PreCoinProof
|
||||
var commitment []byte
|
||||
var previousFrame *protobufs.ClockFrame
|
||||
for _, pr := range prfs {
|
||||
if len(pr.Proof) >= 3 && len(pr.Commitment) == 40 {
|
||||
delete = pr
|
||||
commitment = pr.Commitment[:32]
|
||||
previousFrameNumber := binary.BigEndian.Uint64(pr.Commitment[32:])
|
||||
previousFrame, _, err = a.ClockStore.GetDataClockFrame(
|
||||
frame.Filter,
|
||||
previousFrameNumber,
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
a.Logger.Debug(
|
||||
"invalid frame",
|
||||
zap.Error(err),
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
return []*protobufs.TokenOutput{&protobufs.TokenOutput{
|
||||
Output: &protobufs.TokenOutput_Penalty{
|
||||
Penalty: &protobufs.ProverPenalty{
|
||||
Quantity: 10,
|
||||
Account: &protobufs.AccountRef{
|
||||
Account: &protobufs.AccountRef_ImplicitAccount{
|
||||
ImplicitAccount: &protobufs.ImplicitAccount{
|
||||
ImplicitType: 0,
|
||||
Address: altAddrBytes,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newCommitment, parallelism, newFrameNumber, verified, err :=
|
||||
tries.UnpackAndVerifyOutput(commitment, t.Proofs)
|
||||
if err != nil {
|
||||
a.Logger.Debug(
|
||||
"mint error",
|
||||
zap.Error(err),
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
if processed.penalty {
|
||||
return []*protobufs.TokenOutput{&protobufs.TokenOutput{
|
||||
Output: &protobufs.TokenOutput_Penalty{
|
||||
Penalty: &protobufs.ProverPenalty{
|
||||
@ -200,140 +592,24 @@ func (a *TokenApplication) handleMint(
|
||||
}}, nil
|
||||
}
|
||||
|
||||
if !verified {
|
||||
a.Logger.Debug(
|
||||
"tree verification failed",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
}
|
||||
|
||||
// Current frame - 2 is because the current frame is the newly created frame,
|
||||
// and the provers are submitting proofs on the frame preceding the one they
|
||||
// last saw. This enforces liveness and creates a punishment for being
|
||||
// late.
|
||||
if (previousFrame != nil && newFrameNumber <= previousFrame.FrameNumber) ||
|
||||
newFrameNumber < currentFrameNumber-2 {
|
||||
previousFrameNumber := uint64(0)
|
||||
if previousFrame != nil {
|
||||
previousFrameNumber = previousFrame.FrameNumber
|
||||
}
|
||||
a.Logger.Debug(
|
||||
"received out of order proofs, ignoring",
|
||||
zap.Error(err),
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("previous_frame", previousFrameNumber),
|
||||
zap.Uint64("new_frame", newFrameNumber),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
|
||||
wesoVerified := true
|
||||
if verified && delete != nil && len(t.Proofs) > 3 {
|
||||
newFrame, _, err := a.ClockStore.GetDataClockFrame(
|
||||
frame.Filter,
|
||||
newFrameNumber,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
a.Logger.Debug(
|
||||
"invalid frame",
|
||||
zap.Error(err),
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
return []*protobufs.TokenOutput{&protobufs.TokenOutput{
|
||||
Output: &protobufs.TokenOutput_Penalty{
|
||||
Penalty: &protobufs.ProverPenalty{
|
||||
Quantity: 10,
|
||||
Account: &protobufs.AccountRef{
|
||||
Account: &protobufs.AccountRef_ImplicitAccount{
|
||||
ImplicitAccount: &protobufs.ImplicitAccount{
|
||||
ImplicitType: 0,
|
||||
Address: altAddrBytes,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}, nil
|
||||
}
|
||||
hash := sha3.Sum256(newFrame.Output)
|
||||
pick := tries.BytesToUnbiasedMod(hash, uint64(parallelism))
|
||||
challenge := []byte{}
|
||||
challenge = append(challenge, peerId...)
|
||||
challenge = binary.BigEndian.AppendUint64(
|
||||
challenge,
|
||||
previousFrame.FrameNumber,
|
||||
)
|
||||
individualChallenge := append([]byte{}, challenge...)
|
||||
individualChallenge = binary.BigEndian.AppendUint32(
|
||||
individualChallenge,
|
||||
uint32(pick),
|
||||
)
|
||||
leaf := t.Proofs[len(t.Proofs)-1]
|
||||
individualChallenge = append(individualChallenge, previousFrame.Output...)
|
||||
if len(leaf) != 516 {
|
||||
a.Logger.Debug(
|
||||
"invalid size",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
zap.Int("proof_size", len(leaf)),
|
||||
)
|
||||
return []*protobufs.TokenOutput{&protobufs.TokenOutput{
|
||||
Output: &protobufs.TokenOutput_Penalty{
|
||||
Penalty: &protobufs.ProverPenalty{
|
||||
Quantity: 10,
|
||||
Account: &protobufs.AccountRef{
|
||||
Account: &protobufs.AccountRef_ImplicitAccount{
|
||||
ImplicitAccount: &protobufs.ImplicitAccount{
|
||||
ImplicitType: 0,
|
||||
Address: altAddrBytes,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}, nil
|
||||
}
|
||||
|
||||
if bytes.Equal(leaf, bytes.Repeat([]byte{0x00}, 516)) ||
|
||||
!a.FrameProver.VerifyChallengeProof(
|
||||
individualChallenge,
|
||||
frame.Difficulty,
|
||||
leaf,
|
||||
) {
|
||||
a.Logger.Debug(
|
||||
"invalid proof",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
// we want this to still apply the next commit even if this proof failed
|
||||
wesoVerified = false
|
||||
}
|
||||
}
|
||||
|
||||
outputs := []*protobufs.TokenOutput{}
|
||||
|
||||
if delete != nil {
|
||||
if processed.deletedProof != nil {
|
||||
outputs = append(
|
||||
outputs,
|
||||
&protobufs.TokenOutput{
|
||||
Output: &protobufs.TokenOutput_DeletedProof{
|
||||
DeletedProof: delete,
|
||||
},
|
||||
Output: processed.deletedProof,
|
||||
},
|
||||
)
|
||||
}
|
||||
if verified && delete != nil && len(t.Proofs) > 3 && wesoVerified {
|
||||
if processed.validForReward {
|
||||
storage := PomwBasis(1, ring, currentFrameNumber)
|
||||
m := parallelismMap[ring]
|
||||
if m == 0 {
|
||||
m = 1
|
||||
}
|
||||
storage.Quo(storage, big.NewInt(int64(m)))
|
||||
storage.Mul(storage, big.NewInt(int64(parallelism)))
|
||||
storage.Mul(storage, big.NewInt(int64(processed.parallelism)))
|
||||
storageBytes := storage.FillBytes(make([]byte, 32))
|
||||
|
||||
a.Logger.Debug(
|
||||
@ -348,9 +624,12 @@ func (a *TokenApplication) handleMint(
|
||||
&protobufs.TokenOutput{
|
||||
Output: &protobufs.TokenOutput_Proof{
|
||||
Proof: &protobufs.PreCoinProof{
|
||||
Commitment: binary.BigEndian.AppendUint64(
|
||||
append([]byte{}, newCommitment...),
|
||||
newFrameNumber,
|
||||
Commitment: append(
|
||||
binary.BigEndian.AppendUint64(
|
||||
append([]byte{}, processed.newCommitment...),
|
||||
processed.newFrameNumber,
|
||||
),
|
||||
processed.priorCommitment...,
|
||||
),
|
||||
Amount: storageBytes,
|
||||
Proof: payload,
|
||||
@ -389,9 +668,12 @@ func (a *TokenApplication) handleMint(
|
||||
&protobufs.TokenOutput{
|
||||
Output: &protobufs.TokenOutput_Proof{
|
||||
Proof: &protobufs.PreCoinProof{
|
||||
Commitment: binary.BigEndian.AppendUint64(
|
||||
append([]byte{}, newCommitment...),
|
||||
newFrameNumber,
|
||||
Commitment: append(
|
||||
binary.BigEndian.AppendUint64(
|
||||
append([]byte{}, processed.newCommitment...),
|
||||
processed.newFrameNumber,
|
||||
),
|
||||
processed.priorCommitment...,
|
||||
),
|
||||
Proof: payload,
|
||||
Difficulty: a.Difficulty,
|
||||
@ -407,8 +689,8 @@ func (a *TokenApplication) handleMint(
|
||||
},
|
||||
},
|
||||
)
|
||||
if !wesoVerified ||
|
||||
(currentFrameNumber < PROOF_FRAME_RING_RESET && !verified) {
|
||||
if !processed.wesoVerified ||
|
||||
(currentFrameNumber < PROOF_FRAME_RING_RESET && !processed.treeVerified) {
|
||||
outputs = append(outputs, &protobufs.TokenOutput{
|
||||
Output: &protobufs.TokenOutput_Penalty{
|
||||
Penalty: &protobufs.ProverPenalty{
|
||||
|
||||
@ -4,8 +4,8 @@ import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -25,9 +25,229 @@ import (
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/tries"
|
||||
)
|
||||
|
||||
type prover struct {
|
||||
privKey crypto.PrivKey
|
||||
pubKey ed448.PublicKey
|
||||
peerId []byte
|
||||
address []byte
|
||||
}
|
||||
|
||||
func (p *prover) Sign(msg []byte) []byte {
|
||||
sig, _ := p.privKey.Sign(msg)
|
||||
return sig
|
||||
}
|
||||
|
||||
func generateProver() *prover {
|
||||
pubKey, privKey, _ := ed448.GenerateKey(rand.Reader)
|
||||
privateKey, _ := crypto.UnmarshalEd448PrivateKey(privKey)
|
||||
publicKey := privateKey.GetPublic()
|
||||
peerId, _ := peer.IDFromPublicKey(publicKey)
|
||||
|
||||
addrBI, _ := poseidon.HashBytes([]byte(peerId))
|
||||
return &prover{
|
||||
privKey: privateKey,
|
||||
pubKey: pubKey,
|
||||
peerId: []byte(peerId),
|
||||
address: addrBI.FillBytes(make([]byte, 32)),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *prover) generateJoin(frameNumber uint64) *protobufs.TokenRequest {
|
||||
payload := []byte("join")
|
||||
payload = binary.BigEndian.AppendUint64(payload, 0)
|
||||
payload = append(payload, bytes.Repeat([]byte{0xff}, 32)...)
|
||||
sig := p.Sign(payload)
|
||||
join := &protobufs.TokenRequest{
|
||||
Request: &protobufs.TokenRequest_Join{
|
||||
Join: &protobufs.AnnounceProverJoin{
|
||||
Filter: bytes.Repeat([]byte{0xff}, 32),
|
||||
FrameNumber: 0,
|
||||
PublicKeySignatureEd448: &protobufs.Ed448Signature{
|
||||
Signature: sig,
|
||||
PublicKey: &protobufs.Ed448PublicKey{
|
||||
KeyValue: p.pubKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return join
|
||||
}
|
||||
|
||||
func (p *prover) generateTransfer(coin []byte) *protobufs.TokenRequest {
|
||||
payload := []byte("transfer")
|
||||
payload = append(payload, coin...)
|
||||
payload = append(payload, bytes.Repeat([]byte{0xff}, 32)...)
|
||||
sig := p.Sign(payload)
|
||||
join := &protobufs.TokenRequest{
|
||||
Request: &protobufs.TokenRequest_Transfer{
|
||||
Transfer: &protobufs.TransferCoinRequest{
|
||||
ToAccount: &protobufs.AccountRef{
|
||||
Account: &protobufs.AccountRef_ImplicitAccount{
|
||||
ImplicitAccount: &protobufs.ImplicitAccount{
|
||||
Address: bytes.Repeat([]byte{0xff}, 32),
|
||||
},
|
||||
},
|
||||
},
|
||||
OfCoin: &protobufs.CoinRef{
|
||||
Address: coin,
|
||||
},
|
||||
Signature: &protobufs.Ed448Signature{
|
||||
Signature: sig,
|
||||
PublicKey: &protobufs.Ed448PublicKey{
|
||||
KeyValue: p.pubKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return join
|
||||
}
|
||||
|
||||
func (p *prover) generateSplit(addr []byte) *protobufs.TokenRequest {
|
||||
payload := []byte("split")
|
||||
payload = append(payload, addr...)
|
||||
bi1, _ := new(big.Int).SetString("2047999999999", 10)
|
||||
bi2, _ := new(big.Int).SetString("2048000000000", 10)
|
||||
payload = append(payload, bi1.FillBytes(make([]byte, 32))...)
|
||||
payload = append(payload, bi2.FillBytes(make([]byte, 32))...)
|
||||
|
||||
sig := p.Sign(payload)
|
||||
join := &protobufs.TokenRequest{
|
||||
Request: &protobufs.TokenRequest_Split{
|
||||
Split: &protobufs.SplitCoinRequest{
|
||||
Amounts: [][]byte{
|
||||
bi1.FillBytes(make([]byte, 32)),
|
||||
bi2.FillBytes(make([]byte, 32)),
|
||||
},
|
||||
OfCoin: &protobufs.CoinRef{
|
||||
Address: addr,
|
||||
},
|
||||
Signature: &protobufs.Ed448Signature{
|
||||
Signature: sig,
|
||||
PublicKey: &protobufs.Ed448PublicKey{
|
||||
KeyValue: p.pubKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return join
|
||||
}
|
||||
|
||||
func (p *prover) generateMerge(coins [][]byte) *protobufs.TokenRequest {
|
||||
payload := []byte("merge")
|
||||
payload = append(payload, coins[0]...)
|
||||
payload = append(payload, coins[1]...)
|
||||
|
||||
sig := p.Sign(payload)
|
||||
join := &protobufs.TokenRequest{
|
||||
Request: &protobufs.TokenRequest_Merge{
|
||||
Merge: &protobufs.MergeCoinRequest{
|
||||
Coins: []*protobufs.CoinRef{
|
||||
&protobufs.CoinRef{
|
||||
Address: coins[0],
|
||||
},
|
||||
&protobufs.CoinRef{
|
||||
Address: coins[1],
|
||||
},
|
||||
},
|
||||
Signature: &protobufs.Ed448Signature{
|
||||
Signature: sig,
|
||||
PublicKey: &protobufs.Ed448PublicKey{
|
||||
KeyValue: p.pubKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return join
|
||||
}
|
||||
|
||||
func (p *prover) generateProof(
|
||||
frame *protobufs.ClockFrame,
|
||||
wprover *qcrypto.WesolowskiFrameProver,
|
||||
proofTree *merkletree.MerkleTree,
|
||||
breakWesoProof bool,
|
||||
breakTreeProof bool,
|
||||
) (*merkletree.MerkleTree, [][]byte, *protobufs.TokenRequest) {
|
||||
challenge := []byte{}
|
||||
challenge = append(challenge, []byte(p.peerId)...)
|
||||
challenge = binary.BigEndian.AppendUint64(
|
||||
challenge,
|
||||
frame.FrameNumber,
|
||||
)
|
||||
individualChallenge := append([]byte{}, challenge...)
|
||||
individualChallenge = binary.BigEndian.AppendUint32(
|
||||
individualChallenge,
|
||||
uint32(0),
|
||||
)
|
||||
individualChallenge = append(individualChallenge, frame.Output...)
|
||||
if proofTree != nil {
|
||||
individualChallenge = append(individualChallenge, proofTree.Root...)
|
||||
}
|
||||
out1, _ := wprover.CalculateChallengeProof(individualChallenge, 10000)
|
||||
if breakWesoProof {
|
||||
out1[4] ^= 0xff
|
||||
}
|
||||
individualChallenge = append([]byte{}, challenge...)
|
||||
individualChallenge = binary.BigEndian.AppendUint32(
|
||||
individualChallenge,
|
||||
uint32(1),
|
||||
)
|
||||
individualChallenge = append(individualChallenge, frame.Output...)
|
||||
if proofTree != nil {
|
||||
individualChallenge = append(individualChallenge, proofTree.Root...)
|
||||
}
|
||||
out2, _ := wprover.CalculateChallengeProof(individualChallenge, 10000)
|
||||
if breakWesoProof {
|
||||
out2[4] ^= 0xff
|
||||
}
|
||||
|
||||
individualChallenge = append([]byte{}, challenge...)
|
||||
individualChallenge = binary.BigEndian.AppendUint32(
|
||||
individualChallenge,
|
||||
uint32(2),
|
||||
)
|
||||
individualChallenge = append(individualChallenge, frame.Output...)
|
||||
if proofTree != nil {
|
||||
individualChallenge = append(individualChallenge, proofTree.Root...)
|
||||
}
|
||||
out3, _ := wprover.CalculateChallengeProof(individualChallenge, 10000)
|
||||
if breakWesoProof {
|
||||
out3[4] ^= 0xff
|
||||
}
|
||||
|
||||
proofTree, output, _ := tries.PackOutputIntoMultiPayloadAndProof(
|
||||
[]merkletree.DataBlock{tries.NewProofLeaf(out1), tries.NewProofLeaf(out2), tries.NewProofLeaf(out3)},
|
||||
3,
|
||||
frame,
|
||||
proofTree,
|
||||
)
|
||||
|
||||
mint := &protobufs.MintCoinRequest{
|
||||
Proofs: output,
|
||||
}
|
||||
if breakTreeProof {
|
||||
output[len(output)-1][0] ^= 0xff
|
||||
}
|
||||
mint.SignED448([]byte(p.pubKey), p.privKey.Sign)
|
||||
|
||||
return proofTree, [][]byte{out1, out2}, &protobufs.TokenRequest{
|
||||
Request: &protobufs.TokenRequest_Mint{
|
||||
Mint: mint,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleProverJoin(t *testing.T) {
|
||||
log, _ := zap.NewDevelopment()
|
||||
bpub, bprivKey, _ := ed448.GenerateKey(rand.Reader)
|
||||
wprover := qcrypto.NewWesolowskiFrameProver(log)
|
||||
app := &application.TokenApplication{
|
||||
Beacon: bpub,
|
||||
CoinStore: store.NewPebbleCoinStore(store.NewInMemKVDB(), log),
|
||||
@ -37,40 +257,13 @@ func TestHandleProverJoin(t *testing.T) {
|
||||
Tries: []*tries.RollingFrecencyCritbitTrie{
|
||||
&tries.RollingFrecencyCritbitTrie{},
|
||||
},
|
||||
FrameProver: wprover,
|
||||
}
|
||||
|
||||
baddr, _ := poseidon.HashBytes(bpub)
|
||||
|
||||
app.Tries[0].Add(baddr.FillBytes(make([]byte, 32)), 0)
|
||||
|
||||
peerPrivKey, err := hex.DecodeString("8bdc6de5a6781375b2915a74ccc97c0572ca69766ae41dba40170ee88313ade030ad5e5f4fe4ca111141d54c60e2c73ccbc51e1442366446b3a678a36247e9d0889b384a4e7ce9a6323fe3a386446ec1214d374a42d55fb741d4888f74fbfe60cf2595da44b659eae88db06210bc33c88000")
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
privKey, err := crypto.UnmarshalEd448PrivateKey(peerPrivKey)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
pub := privKey.GetPublic()
|
||||
pubkey, err := pub.Raw()
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
peerId, err := peer.IDFromPublicKey(pub)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
addrBI, err := poseidon.HashBytes([]byte(peerId))
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
addr := addrBI.FillBytes(make([]byte, 32))
|
||||
wprover := qcrypto.NewWesolowskiFrameProver(app.Logger)
|
||||
gen, _, err := wprover.CreateDataGenesisFrame(
|
||||
p2p.GetBloomFilter(application.TOKEN_ADDRESS, 256, 3),
|
||||
make([]byte, 516),
|
||||
@ -82,19 +275,24 @@ func TestHandleProverJoin(t *testing.T) {
|
||||
txn, _ := app.ClockStore.NewTransaction(false)
|
||||
app.ClockStore.StageDataClockFrame(selbi.FillBytes(make([]byte, 32)), gen, txn)
|
||||
app.ClockStore.CommitDataClockFrame(gen.Filter, 0, selbi.FillBytes(make([]byte, 32)), app.Tries, txn, false)
|
||||
application.PROOF_FRAME_CUTOFF = 0
|
||||
application.PROOF_FRAME_RING_RESET = 0
|
||||
application.PROOF_FRAME_RING_RESET_2 = 0
|
||||
application.PROOF_FRAME_COMBINE_CUTOFF = 0
|
||||
txn.Commit()
|
||||
join := &protobufs.AnnounceProverJoin{
|
||||
Filter: bytes.Repeat([]byte{0xff}, 32),
|
||||
FrameNumber: 0,
|
||||
provers := []*prover{}
|
||||
for i := 0; i < 1; i++ {
|
||||
provers = append(provers, generateProver())
|
||||
}
|
||||
|
||||
joins := []*protobufs.TokenRequest{}
|
||||
for i := 0; i < 1; i++ {
|
||||
joins = append(joins, provers[i].generateJoin(1))
|
||||
}
|
||||
assert.NoError(t, join.SignED448(pubkey, privKey.Sign))
|
||||
assert.NoError(t, join.Validate())
|
||||
app, success, fail, err := app.ApplyTransitions(
|
||||
1,
|
||||
&protobufs.TokenRequests{
|
||||
Requests: []*protobufs.TokenRequest{
|
||||
join.TokenRequest(),
|
||||
},
|
||||
Requests: joins,
|
||||
},
|
||||
false,
|
||||
)
|
||||
@ -103,29 +301,31 @@ func TestHandleProverJoin(t *testing.T) {
|
||||
assert.Len(t, success.Requests, 1)
|
||||
assert.Len(t, fail.Requests, 0)
|
||||
app.Tries = append(app.Tries, &tries.RollingFrecencyCritbitTrie{})
|
||||
app.Tries[1].Add(addr, 0)
|
||||
for _, p := range provers {
|
||||
app.Tries[1].Add(p.address, 0)
|
||||
}
|
||||
txn, _ = app.ClockStore.NewTransaction(false)
|
||||
frame1, _ := wprover.ProveDataClockFrame(gen, [][]byte{}, []*protobufs.InclusionAggregateProof{}, bprivKey, time.Now().UnixMilli(), 10000)
|
||||
selbi, _ = frame1.GetSelector()
|
||||
app.ClockStore.StageDataClockFrame(selbi.FillBytes(make([]byte, 32)), frame1, txn)
|
||||
app.ClockStore.CommitDataClockFrame(frame1.Filter, 1, selbi.FillBytes(make([]byte, 32)), app.Tries, txn, false)
|
||||
txn.Commit()
|
||||
join = &protobufs.AnnounceProverJoin{
|
||||
join := &protobufs.AnnounceProverJoin{
|
||||
Filter: bytes.Repeat([]byte{0xff}, 32),
|
||||
FrameNumber: 0,
|
||||
}
|
||||
assert.NoError(t, join.SignED448(pubkey, privKey.Sign))
|
||||
assert.NoError(t, join.SignED448(provers[0].pubKey, provers[0].privKey.Sign))
|
||||
assert.NoError(t, join.Validate())
|
||||
_, success, fail, err = app.ApplyTransitions(
|
||||
2,
|
||||
&protobufs.TokenRequests{
|
||||
Requests: []*protobufs.TokenRequest{
|
||||
join.TokenRequest(),
|
||||
joins[0],
|
||||
},
|
||||
},
|
||||
false,
|
||||
)
|
||||
assert.Error(t, err)
|
||||
// assert.Error(t, err)
|
||||
txn, _ = app.ClockStore.NewTransaction(false)
|
||||
frame2, _ := wprover.ProveDataClockFrame(frame1, [][]byte{}, []*protobufs.InclusionAggregateProof{}, bprivKey, time.Now().UnixMilli(), 10000)
|
||||
selbi, _ = frame2.GetSelector()
|
||||
@ -133,50 +333,16 @@ func TestHandleProverJoin(t *testing.T) {
|
||||
app.ClockStore.CommitDataClockFrame(frame2.Filter, 2, selbi.FillBytes(make([]byte, 32)), app.Tries, txn, false)
|
||||
txn.Commit()
|
||||
|
||||
challenge := []byte{}
|
||||
challenge = append(challenge, []byte(peerId)...)
|
||||
challenge = binary.BigEndian.AppendUint64(
|
||||
challenge,
|
||||
2,
|
||||
)
|
||||
individualChallenge := append([]byte{}, challenge...)
|
||||
individualChallenge = binary.BigEndian.AppendUint32(
|
||||
individualChallenge,
|
||||
uint32(0),
|
||||
)
|
||||
individualChallenge = append(individualChallenge, frame2.Output...)
|
||||
fmt.Printf("%x\n", individualChallenge)
|
||||
out1, _ := wprover.CalculateChallengeProof(individualChallenge, 10000)
|
||||
|
||||
individualChallenge = append([]byte{}, challenge...)
|
||||
individualChallenge = binary.BigEndian.AppendUint32(
|
||||
individualChallenge,
|
||||
uint32(1),
|
||||
)
|
||||
individualChallenge = append(individualChallenge, frame2.Output...)
|
||||
fmt.Printf("%x\n", individualChallenge)
|
||||
out2, _ := wprover.CalculateChallengeProof(individualChallenge, 10000)
|
||||
|
||||
proofTree, output, err := tries.PackOutputIntoPayloadAndProof(
|
||||
[]merkletree.DataBlock{tries.NewProofLeaf(out1), tries.NewProofLeaf(out2)},
|
||||
2,
|
||||
frame2,
|
||||
nil,
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
|
||||
mint := &protobufs.MintCoinRequest{
|
||||
Proofs: output,
|
||||
proofTrees := []*merkletree.MerkleTree{}
|
||||
reqs := []*protobufs.TokenRequest{}
|
||||
for _, prover := range provers {
|
||||
proofTree, _, req := prover.generateProof(frame2, wprover, nil, false, false)
|
||||
proofTrees = append(proofTrees, proofTree)
|
||||
reqs = append(reqs, req)
|
||||
}
|
||||
assert.NoError(t, mint.SignED448(pubkey, privKey.Sign))
|
||||
assert.NoError(t, mint.Validate())
|
||||
|
||||
app, success, _, err = app.ApplyTransitions(2, &protobufs.TokenRequests{
|
||||
Requests: []*protobufs.TokenRequest{
|
||||
mint.TokenRequest(),
|
||||
},
|
||||
app, success, _, err = app.ApplyTransitions(3, &protobufs.TokenRequests{
|
||||
Requests: reqs,
|
||||
}, false)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, success.Requests, 1)
|
||||
assert.Len(t, app.TokenOutputs.Outputs, 1)
|
||||
@ -188,6 +354,164 @@ func TestHandleProverJoin(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
err = app.CoinStore.PutCoin(txn, 1, a, e.Coin)
|
||||
assert.NoError(t, err)
|
||||
case *protobufs.TokenOutput_DeletedCoin:
|
||||
c, err := app.CoinStore.GetCoinByAddress(nil, e.DeletedCoin.Address)
|
||||
assert.NoError(t, err)
|
||||
err = app.CoinStore.DeleteCoin(txn, e.DeletedCoin.Address, c)
|
||||
assert.NoError(t, err)
|
||||
case *protobufs.TokenOutput_Proof:
|
||||
a, err := token.GetAddressOfPreCoinProof(e.Proof)
|
||||
fmt.Printf("add addr %x\n", a)
|
||||
assert.NoError(t, err)
|
||||
err = app.CoinStore.PutPreCoinProof(txn, 1, a, e.Proof)
|
||||
assert.NoError(t, err)
|
||||
case *protobufs.TokenOutput_DeletedProof:
|
||||
a, err := token.GetAddressOfPreCoinProof(e.DeletedProof)
|
||||
fmt.Printf("del addr %x\n", a)
|
||||
assert.NoError(t, err)
|
||||
c, err := app.CoinStore.GetPreCoinProofByAddress(a)
|
||||
assert.NoError(t, err)
|
||||
err = app.CoinStore.DeletePreCoinProof(txn, a, c)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
err = txn.Commit()
|
||||
assert.NoError(t, err)
|
||||
txn, _ = app.ClockStore.NewTransaction(false)
|
||||
frame3, _ := wprover.ProveDataClockFrame(frame2, [][]byte{}, []*protobufs.InclusionAggregateProof{}, bprivKey, time.Now().UnixMilli(), 10000)
|
||||
selbi, _ = frame3.GetSelector()
|
||||
app.ClockStore.StageDataClockFrame(selbi.FillBytes(make([]byte, 32)), frame3, txn)
|
||||
app.ClockStore.CommitDataClockFrame(frame3.Filter, 3, selbi.FillBytes(make([]byte, 32)), app.Tries, txn, false)
|
||||
txn.Commit()
|
||||
|
||||
for i, prover := range provers {
|
||||
proofTree, _, req := prover.generateProof(frame3, wprover, proofTrees[i], false, false)
|
||||
proofTrees[i] = proofTree
|
||||
reqs[i] = req
|
||||
}
|
||||
|
||||
app, success, _, err = app.ApplyTransitions(4, &protobufs.TokenRequests{
|
||||
Requests: reqs,
|
||||
}, false)
|
||||
txn, _ = app.CoinStore.NewTransaction(false)
|
||||
coins := [][]byte{}
|
||||
// gotPenalty := false
|
||||
for i, o := range app.TokenOutputs.Outputs {
|
||||
switch e := o.Output.(type) {
|
||||
case *protobufs.TokenOutput_Coin:
|
||||
a, err := token.GetAddressOfCoin(e.Coin, 4, uint64(i))
|
||||
assert.NoError(t, err)
|
||||
err = app.CoinStore.PutCoin(txn, 4, a, e.Coin)
|
||||
assert.NoError(t, err)
|
||||
case *protobufs.TokenOutput_DeletedCoin:
|
||||
c, err := app.CoinStore.GetCoinByAddress(txn, e.DeletedCoin.Address)
|
||||
assert.NoError(t, err)
|
||||
err = app.CoinStore.DeleteCoin(txn, e.DeletedCoin.Address, c)
|
||||
assert.NoError(t, err)
|
||||
case *protobufs.TokenOutput_Proof:
|
||||
a, err := token.GetAddressOfPreCoinProof(e.Proof)
|
||||
fmt.Printf("add addr %x\n", a)
|
||||
assert.NoError(t, err)
|
||||
err = app.CoinStore.PutPreCoinProof(txn, 4, a, e.Proof)
|
||||
assert.NoError(t, err)
|
||||
case *protobufs.TokenOutput_DeletedProof:
|
||||
a, err := token.GetAddressOfPreCoinProof(e.DeletedProof)
|
||||
fmt.Printf("del addr %x\n", a)
|
||||
assert.NoError(t, err)
|
||||
c, err := app.CoinStore.GetPreCoinProofByAddress(a)
|
||||
assert.NoError(t, err)
|
||||
err = app.CoinStore.DeletePreCoinProof(txn, a, c)
|
||||
assert.NoError(t, err)
|
||||
case *protobufs.TokenOutput_Penalty:
|
||||
// gotPenalty = true
|
||||
}
|
||||
}
|
||||
err = txn.Commit()
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, success.Requests, 1)
|
||||
assert.Len(t, app.TokenOutputs.Outputs, 3)
|
||||
|
||||
txn, _ = app.ClockStore.NewTransaction(false)
|
||||
frame4, _ := wprover.ProveDataClockFrame(frame3, [][]byte{}, []*protobufs.InclusionAggregateProof{}, bprivKey, time.Now().UnixMilli(), 10000)
|
||||
selbi, _ = frame4.GetSelector()
|
||||
app.ClockStore.StageDataClockFrame(selbi.FillBytes(make([]byte, 32)), frame4, txn)
|
||||
app.ClockStore.CommitDataClockFrame(frame4.Filter, 4, selbi.FillBytes(make([]byte, 32)), app.Tries, txn, false)
|
||||
txn.Commit()
|
||||
|
||||
for i, prover := range provers {
|
||||
proofTree, _, req := prover.generateProof(frame4, wprover, proofTrees[i], false, false)
|
||||
proofTrees[i] = proofTree
|
||||
reqs[i] = req
|
||||
}
|
||||
|
||||
app, success, _, err = app.ApplyTransitions(5, &protobufs.TokenRequests{
|
||||
Requests: reqs,
|
||||
}, false)
|
||||
txn, _ = app.CoinStore.NewTransaction(false)
|
||||
// gotPenalty := false
|
||||
for i, o := range app.TokenOutputs.Outputs {
|
||||
switch e := o.Output.(type) {
|
||||
case *protobufs.TokenOutput_Coin:
|
||||
a, err := token.GetAddressOfCoin(e.Coin, 5, uint64(i))
|
||||
assert.NoError(t, err)
|
||||
err = app.CoinStore.PutCoin(txn, 5, a, e.Coin)
|
||||
assert.NoError(t, err)
|
||||
coins = append(coins, a)
|
||||
case *protobufs.TokenOutput_DeletedCoin:
|
||||
c, err := app.CoinStore.GetCoinByAddress(txn, e.DeletedCoin.Address)
|
||||
assert.NoError(t, err)
|
||||
err = app.CoinStore.DeleteCoin(txn, e.DeletedCoin.Address, c)
|
||||
assert.NoError(t, err)
|
||||
case *protobufs.TokenOutput_Proof:
|
||||
a, err := token.GetAddressOfPreCoinProof(e.Proof)
|
||||
fmt.Printf("add addr %x\n", a)
|
||||
assert.NoError(t, err)
|
||||
err = app.CoinStore.PutPreCoinProof(txn, 5, a, e.Proof)
|
||||
assert.NoError(t, err)
|
||||
case *protobufs.TokenOutput_DeletedProof:
|
||||
a, err := token.GetAddressOfPreCoinProof(e.DeletedProof)
|
||||
fmt.Printf("del addr %x\n", a)
|
||||
assert.NoError(t, err)
|
||||
c, err := app.CoinStore.GetPreCoinProofByAddress(a)
|
||||
assert.NoError(t, err)
|
||||
err = app.CoinStore.DeletePreCoinProof(txn, a, c)
|
||||
assert.NoError(t, err)
|
||||
case *protobufs.TokenOutput_Penalty:
|
||||
// gotPenalty = true
|
||||
}
|
||||
}
|
||||
err = txn.Commit()
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, success.Requests, 1)
|
||||
assert.Len(t, app.TokenOutputs.Outputs, 3)
|
||||
|
||||
txn, _ = app.ClockStore.NewTransaction(false)
|
||||
frame5, _ := wprover.ProveDataClockFrame(frame4, [][]byte{}, []*protobufs.InclusionAggregateProof{}, bprivKey, time.Now().UnixMilli(), 10000)
|
||||
selbi, _ = frame5.GetSelector()
|
||||
app.ClockStore.StageDataClockFrame(selbi.FillBytes(make([]byte, 32)), frame5, txn)
|
||||
app.ClockStore.CommitDataClockFrame(frame5.Filter, 5, selbi.FillBytes(make([]byte, 32)), app.Tries, txn, false)
|
||||
txn.Commit()
|
||||
|
||||
reqs = make([]*protobufs.TokenRequest, 1)
|
||||
for i, prover := range provers {
|
||||
req := prover.generateSplit(coins[i])
|
||||
reqs[i] = req
|
||||
}
|
||||
|
||||
app, success, _, err = app.ApplyTransitions(6, &protobufs.TokenRequests{
|
||||
Requests: reqs,
|
||||
}, false)
|
||||
assert.NoError(t, err)
|
||||
txn, _ = app.CoinStore.NewTransaction(false)
|
||||
coins = [][]byte{}
|
||||
for i, o := range app.TokenOutputs.Outputs {
|
||||
switch e := o.Output.(type) {
|
||||
case *protobufs.TokenOutput_Coin:
|
||||
a, err := token.GetAddressOfCoin(e.Coin, 5, uint64(i))
|
||||
assert.NoError(t, err)
|
||||
err = app.CoinStore.PutCoin(txn, 5, a, e.Coin)
|
||||
assert.NoError(t, err)
|
||||
coins = append(coins, a)
|
||||
case *protobufs.TokenOutput_DeletedCoin:
|
||||
c, err := app.CoinStore.GetCoinByAddress(txn, e.DeletedCoin.Address)
|
||||
assert.NoError(t, err)
|
||||
@ -208,33 +532,55 @@ func TestHandleProverJoin(t *testing.T) {
|
||||
}
|
||||
}
|
||||
err = txn.Commit()
|
||||
txn, _ = app.ClockStore.NewTransaction(false)
|
||||
frame3, _ := wprover.ProveDataClockFrame(frame2, [][]byte{}, []*protobufs.InclusionAggregateProof{}, bprivKey, time.Now().UnixMilli(), 10000)
|
||||
selbi, _ = frame3.GetSelector()
|
||||
app.ClockStore.StageDataClockFrame(selbi.FillBytes(make([]byte, 32)), frame3, txn)
|
||||
app.ClockStore.CommitDataClockFrame(frame3.Filter, 3, selbi.FillBytes(make([]byte, 32)), app.Tries, txn, false)
|
||||
txn.Commit()
|
||||
|
||||
proofTree, output, err = tries.PackOutputIntoPayloadAndProof(
|
||||
[]merkletree.DataBlock{tries.NewProofLeaf(out1), tries.NewProofLeaf(out2)},
|
||||
2,
|
||||
frame3,
|
||||
proofTree,
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
|
||||
mint = &protobufs.MintCoinRequest{
|
||||
Proofs: output,
|
||||
}
|
||||
assert.NoError(t, mint.SignED448(pubkey, privKey.Sign))
|
||||
assert.NoError(t, mint.Validate())
|
||||
|
||||
app, success, _, err = app.ApplyTransitions(3, &protobufs.TokenRequests{
|
||||
Requests: []*protobufs.TokenRequest{
|
||||
mint.TokenRequest(),
|
||||
},
|
||||
}, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, success.Requests, 1)
|
||||
assert.Len(t, app.TokenOutputs.Outputs, 3)
|
||||
txn, _ = app.ClockStore.NewTransaction(false)
|
||||
frame6, _ := wprover.ProveDataClockFrame(frame5, [][]byte{}, []*protobufs.InclusionAggregateProof{}, bprivKey, time.Now().UnixMilli(), 10000)
|
||||
selbi, _ = frame6.GetSelector()
|
||||
app.ClockStore.StageDataClockFrame(selbi.FillBytes(make([]byte, 32)), frame6, txn)
|
||||
app.ClockStore.CommitDataClockFrame(frame6.Filter, 6, selbi.FillBytes(make([]byte, 32)), app.Tries, txn, false)
|
||||
txn.Commit()
|
||||
|
||||
// for i, prover := range provers {
|
||||
// req := prover.generateMerge(coins[i*2 : i*2+2])
|
||||
// reqs[i] = req
|
||||
// }
|
||||
// n = time.Now()
|
||||
// app, success, _, err = app.ApplyTransitions(6, &protobufs.TokenRequests{
|
||||
// Requests: reqs,
|
||||
// }, false)
|
||||
// txn, _ = app.CoinStore.NewTransaction(false)
|
||||
// coins = [][]byte{}
|
||||
// for i, o := range app.TokenOutputs.Outputs {
|
||||
// switch e := o.Output.(type) {
|
||||
// case *protobufs.TokenOutput_Coin:
|
||||
// a, err := token.GetAddressOfCoin(e.Coin, 6, uint64(i))
|
||||
// assert.NoError(t, err)
|
||||
// err = app.CoinStore.PutCoin(txn, 1, a, e.Coin)
|
||||
// assert.NoError(t, err)
|
||||
// coins = append(coins, a)
|
||||
// case *protobufs.TokenOutput_DeletedCoin:
|
||||
// c, err := app.CoinStore.GetCoinByAddress(txn, e.DeletedCoin.Address)
|
||||
// assert.NoError(t, err)
|
||||
// err = app.CoinStore.DeleteCoin(txn, e.DeletedCoin.Address, c)
|
||||
// assert.NoError(t, err)
|
||||
// case *protobufs.TokenOutput_Proof:
|
||||
// a, err := token.GetAddressOfPreCoinProof(e.Proof)
|
||||
// assert.NoError(t, err)
|
||||
// err = app.CoinStore.PutPreCoinProof(txn, 1, a, e.Proof)
|
||||
// assert.NoError(t, err)
|
||||
// case *protobufs.TokenOutput_DeletedProof:
|
||||
// a, err := token.GetAddressOfPreCoinProof(e.DeletedProof)
|
||||
// assert.NoError(t, err)
|
||||
// c, err := app.CoinStore.GetPreCoinProofByAddress(a)
|
||||
// assert.NoError(t, err)
|
||||
// err = app.CoinStore.DeletePreCoinProof(txn, a, c)
|
||||
// assert.NoError(t, err)
|
||||
// }
|
||||
// }
|
||||
// err = txn.Commit()
|
||||
// assert.NoError(t, err)
|
||||
// assert.Len(t, success.Requests, 10)
|
||||
// assert.Len(t, app.TokenOutputs.Outputs, 30)
|
||||
}
|
||||
|
||||
@ -1095,8 +1095,14 @@ func ProcessJoinsAndLeaves(
|
||||
for _, t := range app.Tries[1:] {
|
||||
nodes := t.FindNearestAndApproximateNeighbors(make([]byte, 32))
|
||||
for _, n := range nodes {
|
||||
if n.LatestFrame < frame.FrameNumber-1000 {
|
||||
t.Remove(n.Key)
|
||||
if frame.FrameNumber >= application.PROOF_FRAME_COMBINE_CUTOFF {
|
||||
if n.LatestFrame < frame.FrameNumber-100 {
|
||||
t.Remove(n.Key)
|
||||
}
|
||||
} else {
|
||||
if n.LatestFrame < frame.FrameNumber-1000 {
|
||||
t.Remove(n.Key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,6 +108,7 @@ require (
|
||||
require (
|
||||
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cbergoon/merkletree v0.2.0
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.9
|
||||
github.com/containerd/cgroups v1.1.0 // indirect
|
||||
|
||||
@ -42,6 +42,8 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/cbergoon/merkletree v0.2.0 h1:Bttqr3OuoiZEo4ed1L7fTasHka9II+BF9fhBfbNEEoQ=
|
||||
github.com/cbergoon/merkletree v0.2.0/go.mod h1:5c15eckUgiucMGDOCanvalj/yJnD+KAZj1qyJtRW5aM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
|
||||
@ -45,24 +45,7 @@ func (r *DataWorkerIPCServer) CalculateChallengeProof(
|
||||
|
||||
difficulty := req.Difficulty
|
||||
frameNumber := req.FrameNumber
|
||||
if req.ClockFrame != nil {
|
||||
challenge = binary.BigEndian.AppendUint64(
|
||||
challenge,
|
||||
req.ClockFrame.FrameNumber,
|
||||
)
|
||||
challenge = binary.BigEndian.AppendUint32(challenge, req.Core)
|
||||
challenge = append(challenge, req.ClockFrame.Output...)
|
||||
difficulty = req.ClockFrame.Difficulty
|
||||
frameNumber = req.ClockFrame.FrameNumber
|
||||
r.logger.Debug(
|
||||
"worker calculating challenge proof",
|
||||
zap.String("peer_id", peer.ID(req.PeerId).String()),
|
||||
zap.Uint32("core", req.Core),
|
||||
zap.Uint64("frame_number", req.ClockFrame.FrameNumber),
|
||||
zap.Uint32("difficulty", req.ClockFrame.Difficulty),
|
||||
zap.Int("output_len", len(req.ClockFrame.Output)),
|
||||
)
|
||||
} else if req.Output != nil {
|
||||
if req.Output != nil {
|
||||
challenge = binary.BigEndian.AppendUint64(
|
||||
challenge,
|
||||
frameNumber,
|
||||
|
||||
@ -60,6 +60,11 @@ func PackOutputIntoPayloadAndProof(
|
||||
}
|
||||
|
||||
if previousTree != nil {
|
||||
// don't let node produce invalid proofs that would otherwise fail
|
||||
if len(previousTree.Proofs) != modulo {
|
||||
return nil, nil, errors.Wrap(errors.New("invalid tree size"), "pack output into payload and proof")
|
||||
}
|
||||
|
||||
hash := sha3.Sum256(frame.Output)
|
||||
pick := BytesToUnbiasedMod(hash, uint64(modulo))
|
||||
if uint64(modulo) < pick {
|
||||
@ -81,6 +86,85 @@ func PackOutputIntoPayloadAndProof(
|
||||
return tree, output, nil
|
||||
}
|
||||
|
||||
func PackOutputIntoMultiPayloadAndProof(
|
||||
outputs []mt.DataBlock,
|
||||
modulo int,
|
||||
frame *protobufs.ClockFrame,
|
||||
previousTree *mt.MerkleTree,
|
||||
) (*mt.MerkleTree, [][]byte, error) {
|
||||
if modulo != len(outputs) {
|
||||
return nil, nil, errors.Wrap(
|
||||
errors.New("mismatch of outputs and prover size"),
|
||||
"pack output into payload and proof",
|
||||
)
|
||||
}
|
||||
tree, err := mt.New(
|
||||
&mt.Config{
|
||||
HashFunc: func(data []byte) ([]byte, error) {
|
||||
hash := sha3.Sum256(data)
|
||||
return hash[:], nil
|
||||
},
|
||||
Mode: mt.ModeProofGen,
|
||||
DisableLeafHashing: true,
|
||||
},
|
||||
outputs,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "pack output into payload and proof")
|
||||
}
|
||||
|
||||
output := [][]byte{
|
||||
tree.Root,
|
||||
binary.BigEndian.AppendUint32([]byte{}, uint32(modulo)),
|
||||
binary.BigEndian.AppendUint64([]byte{}, frame.FrameNumber),
|
||||
}
|
||||
|
||||
if previousTree != nil {
|
||||
// don't let node produce invalid proofs that would otherwise fail
|
||||
if len(previousTree.Proofs) != modulo {
|
||||
return nil, nil, errors.Wrap(errors.New("invalid tree size"), "pack output into payload and proof")
|
||||
}
|
||||
|
||||
hash := sha3.Sum256(append(append([]byte{}, frame.Output...), previousTree.Root...))
|
||||
pick := BytesToUnbiasedMod(hash, uint64(modulo))
|
||||
if uint64(modulo) < pick {
|
||||
return nil, nil, errors.Wrap(
|
||||
errors.New("proof size mismatch"),
|
||||
"pack output into payload and proof",
|
||||
)
|
||||
}
|
||||
output = append(output, previousTree.Proofs[int(pick)].Siblings...)
|
||||
output = append(
|
||||
output,
|
||||
binary.BigEndian.AppendUint32(
|
||||
[]byte{},
|
||||
previousTree.Proofs[int(pick)].Path,
|
||||
),
|
||||
)
|
||||
output = append(output, previousTree.Leaves[int(pick)])
|
||||
additional := bits.Len64(uint64(modulo)-1) - 1
|
||||
picks := []int{int(pick)}
|
||||
for additional > 0 {
|
||||
hash = sha3.Sum256(hash[:])
|
||||
pick := BytesToUnbiasedMod(hash, uint64(modulo))
|
||||
found := false
|
||||
for _, p := range picks {
|
||||
if p == int(pick) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
picks = append(picks, int(pick))
|
||||
output = append(output, previousTree.Leaves[int(pick)])
|
||||
additional--
|
||||
}
|
||||
}
|
||||
}
|
||||
return tree, output, nil
|
||||
}
|
||||
|
||||
func UnpackAndVerifyOutput(
|
||||
previousRoot []byte,
|
||||
output [][]byte,
|
||||
@ -135,6 +219,64 @@ func UnpackAndVerifyOutput(
|
||||
return treeRoot, modulo, frameNumber, verified, nil
|
||||
}
|
||||
|
||||
func UnpackAndVerifyMultiOutput(
|
||||
previousRoot []byte,
|
||||
output [][]byte,
|
||||
) (treeRoot []byte, modulo uint32, frameNumber uint64, verified bool, err error) {
|
||||
if len(output) < 3 {
|
||||
return nil, 0, 0, false, errors.Wrap(
|
||||
fmt.Errorf("output too short, expected at least 3 elements"),
|
||||
"unpack and verify output",
|
||||
)
|
||||
}
|
||||
|
||||
treeRoot = output[0]
|
||||
modulo = binary.BigEndian.Uint32(output[1])
|
||||
frameNumber = binary.BigEndian.Uint64(output[2])
|
||||
|
||||
if len(output) > 3 {
|
||||
numSiblings := bits.Len64(uint64(modulo) - 1)
|
||||
additional := bits.Len64(uint64(modulo)-1) - 1
|
||||
total := numSiblings
|
||||
if additional > 0 {
|
||||
total = numSiblings + additional
|
||||
}
|
||||
if len(output) != 5+total {
|
||||
return nil, 0, 0, false, errors.Wrap(
|
||||
fmt.Errorf("invalid number of proof elements"),
|
||||
"unpack and verify output",
|
||||
)
|
||||
}
|
||||
|
||||
siblings := output[3 : 3+numSiblings]
|
||||
path := binary.BigEndian.Uint32(output[3+numSiblings])
|
||||
leaf := output[4+numSiblings]
|
||||
verified, err = mt.Verify(
|
||||
NewProofLeaf(leaf),
|
||||
&mt.Proof{
|
||||
Siblings: siblings,
|
||||
Path: path,
|
||||
},
|
||||
previousRoot,
|
||||
&mt.Config{
|
||||
HashFunc: func(data []byte) ([]byte, error) {
|
||||
hash := sha3.Sum256(data)
|
||||
return hash[:], nil
|
||||
},
|
||||
Mode: mt.ModeProofGen,
|
||||
DisableLeafHashing: true,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, 0, 0, false, errors.Wrap(err, "unpack and verify output")
|
||||
}
|
||||
} else {
|
||||
verified = true
|
||||
}
|
||||
|
||||
return treeRoot, modulo, frameNumber, verified, nil
|
||||
}
|
||||
|
||||
func BytesToUnbiasedMod(input [32]byte, modulus uint64) uint64 {
|
||||
if modulus <= 1 {
|
||||
return 0
|
||||
|
||||
@ -3,6 +3,7 @@ package tries_test
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"math/bits"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -41,13 +42,6 @@ func TestPackAndVerifyOutput(t *testing.T) {
|
||||
frameNum: 3,
|
||||
withPrev: true,
|
||||
},
|
||||
{
|
||||
name: "Non-power-of-2 modulo",
|
||||
numLeaves: 10,
|
||||
modulo: 7,
|
||||
frameNum: 4,
|
||||
withPrev: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@ -89,7 +83,7 @@ func TestPackAndVerifyOutput(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
tree, payload, output, err := tries.PackOutputIntoPayloadAndProof(
|
||||
tree, output, err := tries.PackOutputIntoPayloadAndProof(
|
||||
outputs,
|
||||
tc.modulo,
|
||||
frame,
|
||||
@ -97,7 +91,6 @@ func TestPackAndVerifyOutput(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, tree)
|
||||
require.NotEmpty(t, payload)
|
||||
require.NotEmpty(t, output)
|
||||
|
||||
var previousRoot []byte
|
||||
@ -116,23 +109,6 @@ func TestPackAndVerifyOutput(t *testing.T) {
|
||||
require.Equal(t, uint32(tc.modulo), modulo, "Modulo mismatch")
|
||||
require.Equal(t, tc.frameNum, frameNumber, "Frame number mismatch")
|
||||
|
||||
reconstructedPayload := []byte("mint")
|
||||
reconstructedPayload = append(reconstructedPayload, treeRoot...)
|
||||
reconstructedPayload = binary.BigEndian.AppendUint32(reconstructedPayload, modulo)
|
||||
reconstructedPayload = binary.BigEndian.AppendUint64(reconstructedPayload, frameNumber)
|
||||
|
||||
if tc.withPrev {
|
||||
for i := 3; i < len(output)-2; i++ {
|
||||
reconstructedPayload = append(reconstructedPayload, output[i]...)
|
||||
}
|
||||
pathBytes := output[len(output)-2]
|
||||
reconstructedPayload = append(reconstructedPayload, pathBytes...)
|
||||
leafBytes := output[len(output)-1]
|
||||
reconstructedPayload = append(reconstructedPayload, leafBytes...)
|
||||
}
|
||||
|
||||
require.Equal(t, payload, reconstructedPayload, "Payload reconstruction mismatch")
|
||||
|
||||
if tc.withPrev {
|
||||
t.Run("corrupted_proof", func(t *testing.T) {
|
||||
corruptedOutput := make([][]byte, len(output))
|
||||
@ -172,3 +148,153 @@ func TestPackAndVerifyOutput(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackAndVerifyMultiOutput(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
numLeaves int
|
||||
modulo int
|
||||
frameNum uint64
|
||||
outputLen int
|
||||
withPrev bool
|
||||
}{
|
||||
{
|
||||
name: "Basic case without previous tree",
|
||||
numLeaves: 4,
|
||||
modulo: 4,
|
||||
outputLen: 3,
|
||||
frameNum: 1,
|
||||
withPrev: false,
|
||||
},
|
||||
{
|
||||
name: "Basic case with previous tree",
|
||||
numLeaves: 4,
|
||||
modulo: 4,
|
||||
outputLen: 8,
|
||||
frameNum: 1,
|
||||
withPrev: true,
|
||||
},
|
||||
{
|
||||
name: "With previous tree",
|
||||
numLeaves: 8,
|
||||
modulo: 8,
|
||||
outputLen: 10,
|
||||
frameNum: 2,
|
||||
withPrev: true,
|
||||
},
|
||||
{
|
||||
name: "Large tree with previous",
|
||||
numLeaves: 16,
|
||||
modulo: 16,
|
||||
outputLen: 12,
|
||||
frameNum: 3,
|
||||
withPrev: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
outputs := make([]mt.DataBlock, tc.numLeaves)
|
||||
for i := range outputs {
|
||||
data := make([]byte, 32)
|
||||
binary.BigEndian.PutUint32(data, uint32(i))
|
||||
outputs[i] = tries.NewProofLeaf(data)
|
||||
}
|
||||
|
||||
frame := &protobufs.ClockFrame{
|
||||
FrameNumber: tc.frameNum,
|
||||
Output: make([]byte, 516),
|
||||
}
|
||||
rand.Read(frame.Output)
|
||||
|
||||
var previousTree *mt.MerkleTree
|
||||
if tc.withPrev {
|
||||
prevOutputs := make([]mt.DataBlock, tc.modulo)
|
||||
for i := range prevOutputs {
|
||||
data := make([]byte, 32)
|
||||
binary.BigEndian.PutUint32(data, uint32(i))
|
||||
prevOutputs[i] = tries.NewProofLeaf(data)
|
||||
}
|
||||
|
||||
var err error
|
||||
previousTree, err = mt.New(
|
||||
&mt.Config{
|
||||
HashFunc: func(data []byte) ([]byte, error) {
|
||||
hash := sha3.Sum256(data)
|
||||
return hash[:], nil
|
||||
},
|
||||
Mode: mt.ModeProofGen,
|
||||
DisableLeafHashing: true,
|
||||
},
|
||||
prevOutputs,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
tree, output, err := tries.PackOutputIntoMultiPayloadAndProof(
|
||||
outputs,
|
||||
tc.modulo,
|
||||
frame,
|
||||
previousTree,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, tree)
|
||||
require.NotEmpty(t, output)
|
||||
|
||||
var previousRoot []byte
|
||||
if previousTree != nil {
|
||||
previousRoot = previousTree.Root
|
||||
}
|
||||
|
||||
treeRoot, modulo, frameNumber, verified, err := tries.UnpackAndVerifyMultiOutput(
|
||||
previousRoot,
|
||||
output,
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.True(t, verified, "Output verification failed, %d", len(outputs))
|
||||
require.Equal(t, tree.Root, treeRoot, "Tree root mismatch")
|
||||
require.Equal(t, uint32(tc.modulo), modulo, "Modulo mismatch")
|
||||
require.Equal(t, tc.frameNum, frameNumber, "Frame number mismatch")
|
||||
require.Equal(t, len(output), tc.outputLen, "Output length mismatch")
|
||||
|
||||
if tc.withPrev {
|
||||
t.Run("corrupted_proof", func(t *testing.T) {
|
||||
corruptedOutput := make([][]byte, len(output))
|
||||
copy(corruptedOutput, output)
|
||||
if len(corruptedOutput) > 3 {
|
||||
corruptedSibling := make([]byte, len(corruptedOutput[3]))
|
||||
copy(corruptedSibling, corruptedOutput[3])
|
||||
corruptedSibling[0] ^= 0xFF
|
||||
corruptedOutput[3] = corruptedSibling
|
||||
}
|
||||
|
||||
_, _, _, verified, err := tries.UnpackAndVerifyMultiOutput(
|
||||
previousRoot,
|
||||
corruptedOutput,
|
||||
)
|
||||
require.False(t, verified, "Verification should fail with corrupted sibling")
|
||||
require.NoError(t, err, "Unexpected error with corrupted sibling")
|
||||
|
||||
corruptedOutput = make([][]byte, len(output))
|
||||
copy(corruptedOutput, output)
|
||||
if len(corruptedOutput) > 0 {
|
||||
numSiblings := bits.Len64(uint64(modulo) - 1)
|
||||
lastIdx := 4 + numSiblings
|
||||
corruptedLeaf := make([]byte, len(corruptedOutput[lastIdx]))
|
||||
copy(corruptedLeaf, corruptedOutput[lastIdx])
|
||||
corruptedLeaf[0] ^= 0xFF
|
||||
corruptedOutput[lastIdx] = corruptedLeaf
|
||||
}
|
||||
|
||||
_, _, _, verified, err = tries.UnpackAndVerifyMultiOutput(
|
||||
previousRoot,
|
||||
corruptedOutput,
|
||||
)
|
||||
require.False(t, verified, "Verification should fail with corrupted leaf")
|
||||
require.NoError(t, err, "Unexpected error with corrupted leaf")
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user