Merge branch 'v2.0.6' into develop-2.1-pre-milestone3

This commit is contained in:
Cassandra Heart 2025-01-09 02:37:18 -06:00
commit 41ab1161b7
No known key found for this signature in database
GPG Key ID: 6352152859385958
14 changed files with 1365 additions and 349 deletions

View File

@ -3,7 +3,10 @@
**/.vscode
github.env
Taskfile.yaml
.git
# Rust
target
vdf/generated
bls48581/generated

View File

@ -150,11 +150,11 @@ var Signatories = []string{
"92cd8ee5362f3ae274a75ab9471024dbc144bff441ed8af7d19750ac512ff51e40e7f7b01e4f96b6345dd58878565948c3eb52c53f250b5080",
"001a4cbfce5d9aeb7e20665b0d236721b228a32f0baee62ffa77f45b82ecaf577e8a38b7ef91fcf7d2d2d2b504f085461398d30b24abb1d700",
"65b835071731c6e785bb2d107c7d85d8a537d79c435c3f42bb2f87027f93f858d7b37c598cef267a5db46e345f7a6f81969b465686657d1e00",
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"b6df0ebab6ea20cc2eb718db5873c07bb50cf239a16bb6306bbe0f24280664f99f732c4049b8eda1226067e70ffb81958834d486942a122100",
"3e087771c36098cb2d371711fd882d309b4caebbd06ded3077a975231344f027ad31c7069e76ba5070451d8eb5abf29bfeb34fcdf9ba906480",
"57be2861faf0fffcbfd122c85c77010dce8f213030905781b85b6f345d912c7b5ace17797d9810899dfb8d13e7c8369595740725ab3dd5bd00",
"61628beef8f6964466fd078d6a2b90a397ab0777a14b9728227fd19f36752f9451b1a8d780740a0b9a8ce3df5f89ca7b9ff17de9274a270980",
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"9ab76d775487c85c8e5aa0c5b3f961772967899a14644651031ae5f98ac197bee3f8880492c4fdba268716fc4b7c38ffcac370b663ac10b600",
"81d63a45f068629f568de812f18be5807bfe828a830097f09cf02330d6acd35e3607401df3fda08b03b68ea6e68afd506b23506b11e87a0f80",
"6e2872f73c4868c4286bef7bfe2f5479a41c42f4e07505efa4883c7950c740252e0eea78eef10c584b19b1dcda01f7767d3135d07c33244100",
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",

View File

@ -43,7 +43,7 @@ func FormatVersion(version []byte) string {
}
func GetPatchNumber() byte {
return 0x00
return 0x01
}
func GetRCNumber() byte {

View File

@ -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,
},

View File

@ -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",

View File

@ -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
}

View File

@ -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"
@ -18,22 +19,473 @@ import (
)
// for tests, these need to be var
const PROOF_FRAME_CUTOFF = 1
const PROOF_FRAME_RING_RESET = 5750
const PROOF_FRAME_RING_RESET_2 = 7650
const PROOF_FRAME_RING_RESET_3 = 13369
const PROOF_FRAME_SENIORITY_REPAIR = 25745
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...)
@ -51,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")
@ -66,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")
}
@ -131,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{
@ -202,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(
@ -350,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,
@ -391,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,
@ -409,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{

View File

@ -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)
@ -189,6 +355,164 @@ func TestHandleProverJoin(t *testing.T) {
assert.NoError(t, err)
err = app.CoinStore.PutCoin(txn, 1, a, e.Coin, stateTree)
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, stateTree)
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, stateTree)
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, stateTree)
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, stateTree)
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, stateTree)
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, stateTree)
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, stateTree)
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, stateTree)
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, stateTree)
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, stateTree)
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, stateTree)
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, stateTree)
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)
@ -209,33 +533,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)
}

View File

@ -1006,8 +1006,7 @@ func (e *TokenExecutionEngine) ProcessFrame(
e.peerSeniority = activeMap
if frame.FrameNumber == application.PROOF_FRAME_RING_RESET ||
frame.FrameNumber == application.PROOF_FRAME_RING_RESET_2 ||
frame.FrameNumber == application.PROOF_FRAME_RING_RESET_3 {
frame.FrameNumber == application.PROOF_FRAME_RING_RESET_2 {
e.logger.Info("performing ring reset")
seniorityMap, err := RebuildPeerSeniority(e.pubSub.GetNetwork())
if err != nil {
@ -1058,7 +1057,7 @@ func (e *TokenExecutionEngine) performSeniorityMapRepair(
)
RebuildPeerSeniority(0)
for f := uint64(53028); f < frame.FrameNumber; f++ {
for f := uint64(application.PROOF_FRAME_RING_RESET_2); f < frame.FrameNumber; f++ {
frame, _, err := e.clockStore.GetDataClockFrame(e.intrinsicFilter, f, false)
if err != nil {
break
@ -1170,8 +1169,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)
}
}
}
}

View File

@ -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

View File

@ -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=

View File

@ -10,6 +10,7 @@ import (
"time"
pcrypto "github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/multiformats/go-multiaddr"
mn "github.com/multiformats/go-multiaddr/net"
"github.com/pkg/errors"
@ -44,22 +45,21 @@ 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
} else if req.Output != nil {
if req.Output != nil {
challenge = binary.BigEndian.AppendUint64(
challenge,
frameNumber,
)
challenge = binary.BigEndian.AppendUint32(challenge, req.Core)
challenge = append(challenge, req.Output...)
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.FrameNumber),
zap.Uint32("difficulty", req.Difficulty),
zap.Int("output_len", len(req.Output)),
)
} else {
return nil, errors.Wrap(
errors.New("invalid request"),

View File

@ -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

View File

@ -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 {
@ -154,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")
})
}
})
}
}