mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 10:27:26 +08:00
v2.0.3-b2
This commit is contained in:
parent
e469fad46a
commit
f3e502a2d1
@ -137,7 +137,7 @@ var unlock *SignedGenesisUnlock
|
||||
func DownloadAndVerifyGenesis(network uint) (*SignedGenesisUnlock, error) {
|
||||
if network != 0 {
|
||||
unlock = &SignedGenesisUnlock{
|
||||
GenesisSeedHex: "726573697374206d7563682c206f626579206c6974746c657c000000000000000000000007",
|
||||
GenesisSeedHex: "726573697374206d7563682c206f626579206c6974746c657c000000000000000000000008",
|
||||
Beacon: []byte{
|
||||
0x58, 0xef, 0xd9, 0x7e, 0xdd, 0x0e, 0xb6, 0x2f,
|
||||
0x51, 0xc7, 0x5d, 0x00, 0x29, 0x12, 0x45, 0x49,
|
||||
|
||||
@ -40,5 +40,5 @@ func GetPatchNumber() byte {
|
||||
}
|
||||
|
||||
func GetRCNumber() byte {
|
||||
return 0x01
|
||||
return 0x02
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ func (e *DataClockConsensusEngine) prove(
|
||||
var validTransactions *protobufs.TokenRequests
|
||||
var invalidTransactions *protobufs.TokenRequests
|
||||
app, validTransactions, invalidTransactions, err = app.ApplyTransitions(
|
||||
previousFrame.FrameNumber,
|
||||
previousFrame.FrameNumber+1,
|
||||
e.stagedTransactions,
|
||||
true,
|
||||
)
|
||||
|
||||
@ -18,6 +18,7 @@ import (
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
mn "github.com/multiformats/go-multiaddr/net"
|
||||
"github.com/pkg/errors"
|
||||
mt "github.com/txaty/go-merkletree"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
@ -98,6 +99,7 @@ type DataClockConsensusEngine struct {
|
||||
statsClient protobufs.NodeStatsClient
|
||||
currentReceivingSyncPeersMx sync.Mutex
|
||||
currentReceivingSyncPeers int
|
||||
announcedJoin int
|
||||
beaconPeerId []byte
|
||||
|
||||
frameChan chan *protobufs.ClockFrame
|
||||
@ -544,6 +546,8 @@ func (e *DataClockConsensusEngine) Start() <-chan error {
|
||||
}
|
||||
}
|
||||
|
||||
var previousTree *mt.MerkleTree
|
||||
|
||||
for e.state < consensus.EngineStateStopping {
|
||||
nextFrame, err := e.dataTimeReel.Head()
|
||||
if err != nil {
|
||||
@ -551,12 +555,17 @@ func (e *DataClockConsensusEngine) Start() <-chan error {
|
||||
}
|
||||
|
||||
if frame.FrameNumber == nextFrame.FrameNumber {
|
||||
time.Sleep(5 * time.Second)
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
if nextFrame.Timestamp < time.Now().UnixMilli()-30000 {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
frame = nextFrame
|
||||
_, tries, err := e.clockStore.GetDataClockFrame(
|
||||
_, triesAtFrame, err := e.clockStore.GetDataClockFrame(
|
||||
e.filter,
|
||||
frame.FrameNumber,
|
||||
false,
|
||||
@ -565,10 +574,40 @@ func (e *DataClockConsensusEngine) Start() <-chan error {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for i, trie := range tries[1:] {
|
||||
modulo := len(clients)
|
||||
|
||||
for i, trie := range triesAtFrame[1:] {
|
||||
if trie.Contains(peerProvingKeyAddress) {
|
||||
e.logger.Info("creating data shard ring proof", zap.Int("ring", i))
|
||||
e.PerformTimeProof(frame, frame.Difficulty, clients)
|
||||
outputs := e.PerformTimeProof(frame, frame.Difficulty, clients)
|
||||
proofTree, payload, output := tries.PackOutputIntoPayloadAndProof(
|
||||
outputs,
|
||||
modulo,
|
||||
frame,
|
||||
previousTree,
|
||||
)
|
||||
previousTree = proofTree
|
||||
|
||||
sig, err := e.pubSub.SignMessage(
|
||||
payload,
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
e.publishMessage(e.txFilter, &protobufs.TokenRequest{
|
||||
Request: &protobufs.TokenRequest_Mint{
|
||||
Mint: &protobufs.MintCoinRequest{
|
||||
Proofs: output,
|
||||
Signature: &protobufs.Ed448Signature{
|
||||
PublicKey: &protobufs.Ed448PublicKey{
|
||||
KeyValue: e.pubSub.GetPublicKey(),
|
||||
},
|
||||
Signature: sig,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -581,10 +620,10 @@ func (e *DataClockConsensusEngine) PerformTimeProof(
|
||||
frame *protobufs.ClockFrame,
|
||||
difficulty uint32,
|
||||
clients []protobufs.DataIPCServiceClient,
|
||||
) []byte {
|
||||
) []mt.DataBlock {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(clients))
|
||||
output := make([][]byte, len(clients))
|
||||
output := make([]mt.DataBlock, len(clients))
|
||||
for i, client := range clients {
|
||||
i := i
|
||||
client := client
|
||||
@ -660,37 +699,18 @@ func (e *DataClockConsensusEngine) PerformTimeProof(
|
||||
continue
|
||||
}
|
||||
|
||||
output[i] = resp.Output
|
||||
output[i] = tries.NewProofLeaf(resp.Output)
|
||||
break
|
||||
}
|
||||
if output[i] == nil {
|
||||
output[i] = tries.NewProofLeaf([]byte{})
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
payload := []byte("mint")
|
||||
for _, out := range output {
|
||||
payload = append(payload, out...)
|
||||
}
|
||||
sig, err := e.pubSub.SignMessage(
|
||||
payload,
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
e.publishMessage(e.txFilter, &protobufs.TokenRequest{
|
||||
Request: &protobufs.TokenRequest_Mint{
|
||||
Mint: &protobufs.MintCoinRequest{
|
||||
Proofs: output,
|
||||
Signature: &protobufs.Ed448Signature{
|
||||
PublicKey: &protobufs.Ed448PublicKey{
|
||||
KeyValue: e.pubSub.GetPublicKey(),
|
||||
},
|
||||
Signature: sig,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
return []byte{}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func (e *DataClockConsensusEngine) Stop(force bool) <-chan error {
|
||||
|
||||
@ -118,20 +118,9 @@ func (e *DataClockConsensusEngine) processFrame(
|
||||
|
||||
return nextFrame
|
||||
} else {
|
||||
_, tries, err := e.clockStore.GetDataClockFrame(
|
||||
e.filter,
|
||||
latestFrame.FrameNumber,
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
e.logger.Error("error while fetching frame", zap.Error(err))
|
||||
return latestFrame
|
||||
}
|
||||
found := false
|
||||
for _, trie := range tries[1:] {
|
||||
found = found || trie.Contains(e.pubSub.GetPeerID())
|
||||
}
|
||||
if !found && dataFrame.Timestamp > time.Now().UnixMilli()-30000 {
|
||||
e.announcedJoin++
|
||||
if e.announcedJoin < 5 && !e.IsInProverTrie(e.pubSub.GetPeerID()) &&
|
||||
dataFrame.Timestamp > time.Now().UnixMilli()-30000 {
|
||||
e.logger.Info("announcing prover join")
|
||||
e.announceProverJoin()
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package application
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/iden3/go-iden3-crypto/poseidon"
|
||||
@ -15,6 +16,7 @@ import (
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/store"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/tries"
|
||||
)
|
||||
|
||||
func (a *TokenApplication) handleMint(
|
||||
@ -147,52 +149,94 @@ func (a *TokenApplication) handleMint(
|
||||
)
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
challenge := []byte{}
|
||||
challenge = append(challenge, peerId...)
|
||||
challenge = binary.BigEndian.AppendUint64(
|
||||
challenge,
|
||||
currentFrameNumber-1,
|
||||
)
|
||||
|
||||
digest := make([]byte, 128)
|
||||
s := sha3.NewShake256()
|
||||
pubkey, _ := pk.Raw()
|
||||
s.Write(pubkey)
|
||||
_, err = s.Read(digest)
|
||||
_, prfs, err := a.CoinStore.GetPreCoinProofsForOwner(
|
||||
altAddr.FillBytes(make([]byte, 32)),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
|
||||
outputs := []*protobufs.TokenOutput{}
|
||||
proofs := []byte{}
|
||||
hits := 0
|
||||
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,
|
||||
false,
|
||||
)
|
||||
|
||||
for i, p := range t.Proofs {
|
||||
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 nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newCommitment, parallelism, newFrame, 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 nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
|
||||
if !verified {
|
||||
a.Logger.Debug(
|
||||
"tree verification failed",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
)
|
||||
}
|
||||
|
||||
if verified && delete != nil && len(t.Proofs) > 3 {
|
||||
hash := sha3.Sum256(previousFrame.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(i),
|
||||
uint32(pick),
|
||||
)
|
||||
individualChallenge = append(individualChallenge, frame.Output...)
|
||||
if len(p) != 516 {
|
||||
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(p)),
|
||||
zap.Int("proof_size", len(leaf)),
|
||||
)
|
||||
continue
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
|
||||
hits++
|
||||
|
||||
wesoProver := crypto.NewWesolowskiFrameProver(a.Logger)
|
||||
|
||||
if !wesoProver.VerifyChallengeProof(
|
||||
individualChallenge,
|
||||
frame.Difficulty,
|
||||
p,
|
||||
) {
|
||||
fmt.Printf("%x\n", individualChallenge)
|
||||
if bytes.Equal(leaf, bytes.Repeat([]byte{0x00}, 516)) ||
|
||||
!wesoProver.VerifyChallengeProof(
|
||||
individualChallenge,
|
||||
frame.Difficulty,
|
||||
leaf,
|
||||
) {
|
||||
a.Logger.Debug(
|
||||
"invalid proof",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
@ -200,71 +244,104 @@ func (a *TokenApplication) handleMint(
|
||||
)
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
|
||||
proofs = append(proofs, p...)
|
||||
}
|
||||
|
||||
if hits == 0 {
|
||||
outputs := []*protobufs.TokenOutput{}
|
||||
|
||||
if delete != nil {
|
||||
outputs = append(
|
||||
outputs,
|
||||
&protobufs.TokenOutput{
|
||||
Output: &protobufs.TokenOutput_DeletedProof{
|
||||
DeletedProof: delete,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
if verified && delete != nil && len(t.Proofs) > 3 {
|
||||
|
||||
ringFactor := big.NewInt(2)
|
||||
ringFactor.Exp(ringFactor, big.NewInt(int64(ring)), nil)
|
||||
|
||||
// const for testnet
|
||||
storage := big.NewInt(int64(256 * parallelism))
|
||||
unitFactor := big.NewInt(8000000000)
|
||||
storage.Mul(storage, unitFactor)
|
||||
storage.Quo(storage, big.NewInt(proverSet))
|
||||
storage.Quo(storage, ringFactor)
|
||||
|
||||
a.Logger.Debug(
|
||||
"no proofs",
|
||||
"issued reward",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
zap.String("reward", storage.String()),
|
||||
)
|
||||
|
||||
outputs = append(
|
||||
outputs,
|
||||
&protobufs.TokenOutput{
|
||||
Output: &protobufs.TokenOutput_Proof{
|
||||
Proof: &protobufs.PreCoinProof{
|
||||
Commitment: binary.BigEndian.AppendUint64(
|
||||
append([]byte{}, newCommitment...),
|
||||
newFrame,
|
||||
),
|
||||
Amount: storage.FillBytes(make([]byte, 32)),
|
||||
Proof: payload,
|
||||
Difficulty: a.Difficulty,
|
||||
Owner: &protobufs.AccountRef{
|
||||
Account: &protobufs.AccountRef_ImplicitAccount{
|
||||
ImplicitAccount: &protobufs.ImplicitAccount{
|
||||
ImplicitType: 0,
|
||||
Address: altAddr.FillBytes(make([]byte, 32)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&protobufs.TokenOutput{
|
||||
Output: &protobufs.TokenOutput_Coin{
|
||||
Coin: &protobufs.Coin{
|
||||
Amount: storage.FillBytes(make([]byte, 32)),
|
||||
Intersection: make([]byte, 1024),
|
||||
Owner: &protobufs.AccountRef{
|
||||
Account: &protobufs.AccountRef_ImplicitAccount{
|
||||
ImplicitAccount: &protobufs.ImplicitAccount{
|
||||
ImplicitType: 0,
|
||||
Address: altAddr.FillBytes(make([]byte, 32)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
} else {
|
||||
outputs = append(
|
||||
outputs,
|
||||
&protobufs.TokenOutput{
|
||||
Output: &protobufs.TokenOutput_Proof{
|
||||
Proof: &protobufs.PreCoinProof{
|
||||
Commitment: binary.BigEndian.AppendUint64(
|
||||
append([]byte{}, newCommitment...),
|
||||
newFrame,
|
||||
),
|
||||
Proof: payload,
|
||||
Difficulty: a.Difficulty,
|
||||
Owner: &protobufs.AccountRef{
|
||||
Account: &protobufs.AccountRef_ImplicitAccount{
|
||||
ImplicitAccount: &protobufs.ImplicitAccount{
|
||||
ImplicitType: 0,
|
||||
Address: altAddr.FillBytes(make([]byte, 32)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint")
|
||||
}
|
||||
|
||||
ringFactor := big.NewInt(2)
|
||||
ringFactor.Exp(ringFactor, big.NewInt(int64(ring)), nil)
|
||||
|
||||
storage := big.NewInt(int64(512 * hits))
|
||||
unitFactor := big.NewInt(8000000000)
|
||||
storage.Mul(storage, unitFactor)
|
||||
storage.Quo(storage, big.NewInt(proverSet))
|
||||
storage.Quo(storage, ringFactor)
|
||||
|
||||
a.Logger.Debug(
|
||||
"issued reward",
|
||||
zap.String("peer_id", base58.Encode([]byte(peerId))),
|
||||
zap.Uint64("frame_number", currentFrameNumber),
|
||||
zap.String("reward", storage.String()),
|
||||
)
|
||||
|
||||
outputs = append(
|
||||
outputs,
|
||||
&protobufs.TokenOutput{
|
||||
Output: &protobufs.TokenOutput_Proof{
|
||||
Proof: &protobufs.PreCoinProof{
|
||||
Amount: storage.FillBytes(make([]byte, 32)),
|
||||
Proof: proofs,
|
||||
Difficulty: a.Difficulty,
|
||||
Owner: &protobufs.AccountRef{
|
||||
Account: &protobufs.AccountRef_ImplicitAccount{
|
||||
ImplicitAccount: &protobufs.ImplicitAccount{
|
||||
ImplicitType: 0,
|
||||
Address: addr.FillBytes(make([]byte, 32)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&protobufs.TokenOutput{
|
||||
Output: &protobufs.TokenOutput_Coin{
|
||||
Coin: &protobufs.Coin{
|
||||
Amount: storage.FillBytes(make([]byte, 32)),
|
||||
Intersection: make([]byte, 1024),
|
||||
Owner: &protobufs.AccountRef{
|
||||
Account: &protobufs.AccountRef_ImplicitAccount{
|
||||
ImplicitAccount: &protobufs.ImplicitAccount{
|
||||
ImplicitType: 0,
|
||||
Address: addr.FillBytes(make([]byte, 32)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
lockMap[string(t.Signature.PublicKey.KeyValue)] = struct{}{}
|
||||
return outputs, nil
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -13,8 +14,10 @@ import (
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/txaty/go-merkletree"
|
||||
"go.uber.org/zap"
|
||||
qcrypto "source.quilibrium.com/quilibrium/monorepo/node/crypto"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/token"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/token/application"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
|
||||
@ -144,39 +147,45 @@ func TestHandleProverJoin(t *testing.T) {
|
||||
assert.Error(t, err)
|
||||
txn, _ = app.ClockStore.NewTransaction()
|
||||
frame2, _ := wprover.ProveDataClockFrame(frame1, [][]byte{}, []*protobufs.InclusionAggregateProof{}, bprivKey, time.Now().UnixMilli(), 10000)
|
||||
selbi, _ = frame1.GetSelector()
|
||||
app.ClockStore.StageDataClockFrame(selbi.FillBytes(make([]byte, 32)), frame1, txn)
|
||||
app.ClockStore.CommitDataClockFrame(frame2.Filter, 1, selbi.FillBytes(make([]byte, 32)), app.Tries, txn, false)
|
||||
selbi, _ = frame2.GetSelector()
|
||||
app.ClockStore.StageDataClockFrame(selbi.FillBytes(make([]byte, 32)), frame2, txn)
|
||||
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,
|
||||
1,
|
||||
2,
|
||||
)
|
||||
individualChallenge := append([]byte{}, challenge...)
|
||||
individualChallenge = binary.BigEndian.AppendUint32(
|
||||
individualChallenge,
|
||||
uint32(0),
|
||||
)
|
||||
individualChallenge = append(individualChallenge, frame1.Output...)
|
||||
individualChallenge = append(individualChallenge, frame2.Output...)
|
||||
fmt.Printf("%x\n", individualChallenge)
|
||||
out, _ := wprover.CalculateChallengeProof(individualChallenge, 10000)
|
||||
|
||||
payload = []byte("mint")
|
||||
payload = append(payload, out...)
|
||||
proofTree, payload, output := tries.PackOutputIntoPayloadAndProof(
|
||||
[]merkletree.DataBlock{tries.NewProofLeaf(out), tries.NewProofLeaf(make([]byte, 516))},
|
||||
2,
|
||||
frame2,
|
||||
nil,
|
||||
)
|
||||
|
||||
sig, _ = privKey.Sign(payload)
|
||||
_, _, _, err = app.ApplyTransitions(2, &protobufs.TokenRequests{
|
||||
app, success, _, err = app.ApplyTransitions(2, &protobufs.TokenRequests{
|
||||
Requests: []*protobufs.TokenRequest{
|
||||
&protobufs.TokenRequest{
|
||||
Request: &protobufs.TokenRequest_Mint{
|
||||
Mint: &protobufs.MintCoinRequest{
|
||||
Proofs: [][]byte{out},
|
||||
Proofs: output,
|
||||
Signature: &protobufs.Ed448Signature{
|
||||
Signature: sig,
|
||||
PublicKey: &protobufs.Ed448PublicKey{
|
||||
KeyValue: pubkey,
|
||||
},
|
||||
Signature: sig,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -185,4 +194,69 @@ func TestHandleProverJoin(t *testing.T) {
|
||||
}, false)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, success.Requests, 1)
|
||||
assert.Len(t, app.TokenOutputs.Outputs, 1)
|
||||
txn, _ = app.CoinStore.NewTransaction()
|
||||
for i, o := range app.TokenOutputs.Outputs {
|
||||
switch e := o.Output.(type) {
|
||||
case *protobufs.TokenOutput_Coin:
|
||||
a, err := token.GetAddressOfCoin(e.Coin, 1, uint64(i))
|
||||
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(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()
|
||||
txn, _ = app.ClockStore.NewTransaction()
|
||||
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, 1, selbi.FillBytes(make([]byte, 32)), app.Tries, txn, false)
|
||||
txn.Commit()
|
||||
|
||||
proofTree, payload, output = tries.PackOutputIntoPayloadAndProof(
|
||||
[]merkletree.DataBlock{tries.NewProofLeaf(out), tries.NewProofLeaf(make([]byte, 516))},
|
||||
2,
|
||||
frame3,
|
||||
proofTree,
|
||||
)
|
||||
|
||||
sig, _ = privKey.Sign(payload)
|
||||
app, success, _, err = app.ApplyTransitions(3, &protobufs.TokenRequests{
|
||||
Requests: []*protobufs.TokenRequest{
|
||||
&protobufs.TokenRequest{
|
||||
Request: &protobufs.TokenRequest_Mint{
|
||||
Mint: &protobufs.MintCoinRequest{
|
||||
Proofs: output,
|
||||
Signature: &protobufs.Ed448Signature{
|
||||
PublicKey: &protobufs.Ed448PublicKey{
|
||||
KeyValue: pubkey,
|
||||
},
|
||||
Signature: sig,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, success.Requests, 1)
|
||||
assert.Len(t, app.TokenOutputs.Outputs, 3)
|
||||
}
|
||||
|
||||
@ -758,7 +758,7 @@ func (e *TokenExecutionEngine) VerifyExecution(
|
||||
}
|
||||
|
||||
a, _, _, err = a.ApplyTransitions(
|
||||
parent.FrameNumber,
|
||||
frame.FrameNumber,
|
||||
transition,
|
||||
false,
|
||||
)
|
||||
|
||||
@ -62,6 +62,7 @@ require (
|
||||
github.com/pion/turn/v2 v2.1.6 // indirect
|
||||
github.com/pion/webrtc/v3 v3.2.40 // indirect
|
||||
github.com/rychipman/easylex v0.0.0-20160129204217-49ee7767142f // indirect
|
||||
github.com/txaty/go-merkletree v0.2.2 // indirect
|
||||
go.opentelemetry.io/otel v1.16.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.16.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.16.0 // indirect
|
||||
|
||||
@ -520,6 +520,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/txaty/go-merkletree v0.2.2 h1:K5bHDFK+Q3KK+gEJeyTOECKuIwl/LVo4CI+cm0/p34g=
|
||||
github.com/txaty/go-merkletree v0.2.2/go.mod h1:w5HPEu7ubNw5LzS+91m+1/GtuZcWHKiPU3vEGi+ThJM=
|
||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
|
||||
176
node/tries/proof_leaf.go
Normal file
176
node/tries/proof_leaf.go
Normal file
@ -0,0 +1,176 @@
|
||||
package tries
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/bits"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
mt "github.com/txaty/go-merkletree"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
|
||||
)
|
||||
|
||||
type ProofLeaf struct {
|
||||
output []byte
|
||||
}
|
||||
|
||||
var _ mt.DataBlock = (*ProofLeaf)(nil)
|
||||
|
||||
func NewProofLeaf(output []byte) *ProofLeaf {
|
||||
return &ProofLeaf{output}
|
||||
}
|
||||
|
||||
func (p *ProofLeaf) Serialize() ([]byte, error) {
|
||||
return p.output, nil
|
||||
}
|
||||
|
||||
func PackOutputIntoPayloadAndProof(
|
||||
outputs []mt.DataBlock,
|
||||
modulo int,
|
||||
frame *protobufs.ClockFrame,
|
||||
previousTree *mt.MerkleTree,
|
||||
) (*mt.MerkleTree, []byte, [][]byte) {
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
payload := []byte("mint")
|
||||
payload = append(payload, tree.Root...)
|
||||
payload = binary.BigEndian.AppendUint32(payload, uint32(modulo))
|
||||
payload = binary.BigEndian.AppendUint64(payload, frame.FrameNumber)
|
||||
|
||||
output := [][]byte{
|
||||
tree.Root,
|
||||
binary.BigEndian.AppendUint32([]byte{}, uint32(modulo)),
|
||||
binary.BigEndian.AppendUint64([]byte{}, frame.FrameNumber),
|
||||
}
|
||||
|
||||
if previousTree != nil {
|
||||
hash := sha3.Sum256(frame.Output)
|
||||
pick := BytesToUnbiasedMod(hash, uint64(modulo))
|
||||
for _, sib := range previousTree.Proofs[int(pick)].Siblings {
|
||||
payload = append(payload, sib...)
|
||||
output = append(output, sib)
|
||||
}
|
||||
payload = binary.BigEndian.AppendUint32(
|
||||
payload,
|
||||
previousTree.Proofs[int(pick)].Path,
|
||||
)
|
||||
output = append(
|
||||
output,
|
||||
binary.BigEndian.AppendUint32(
|
||||
[]byte{},
|
||||
previousTree.Proofs[int(pick)].Path,
|
||||
),
|
||||
)
|
||||
payload = append(payload, previousTree.Leaves[int(pick)]...)
|
||||
output = append(output, previousTree.Leaves[int(pick)])
|
||||
}
|
||||
return tree, payload, output
|
||||
}
|
||||
|
||||
func UnpackAndVerifyOutput(
|
||||
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])
|
||||
|
||||
payload := []byte("mint")
|
||||
payload = append(payload, treeRoot...)
|
||||
payload = binary.BigEndian.AppendUint32(payload, modulo)
|
||||
payload = binary.BigEndian.AppendUint64(payload, frameNumber)
|
||||
|
||||
if len(output) > 3 {
|
||||
numSiblings := bits.Len64(uint64(modulo) - 1)
|
||||
if len(output) != 5+numSiblings {
|
||||
return nil, 0, 0, false, errors.Wrap(
|
||||
fmt.Errorf("invalid number of proof elements"),
|
||||
"unpack and verify output",
|
||||
)
|
||||
}
|
||||
|
||||
siblings := output[3 : 3+numSiblings]
|
||||
for _, sib := range siblings {
|
||||
payload = append(payload, sib...)
|
||||
}
|
||||
|
||||
pathBytes := output[3+numSiblings]
|
||||
path := binary.BigEndian.Uint32(pathBytes)
|
||||
payload = binary.BigEndian.AppendUint32(payload, path)
|
||||
|
||||
leaf := output[len(output)-1]
|
||||
payload = append(payload, leaf...)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
hashValue := binary.BigEndian.Uint64(input[:8])
|
||||
|
||||
maxValid := math.MaxUint64 - (math.MaxUint64 % modulus)
|
||||
|
||||
result := hashValue
|
||||
for result > maxValid {
|
||||
offset := uint64(8)
|
||||
for result > maxValid && offset <= 24 {
|
||||
nextBytes := binary.BigEndian.Uint64(input[offset : offset+8])
|
||||
result = (result * 31) ^ nextBytes
|
||||
offset += 8
|
||||
}
|
||||
|
||||
if result > maxValid {
|
||||
result = (result * 31) ^ (result >> 32)
|
||||
}
|
||||
}
|
||||
|
||||
return result % modulus
|
||||
}
|
||||
173
node/tries/proof_leaf_test.go
Normal file
173
node/tries/proof_leaf_test.go
Normal file
@ -0,0 +1,173 @@
|
||||
package tries_test
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
mt "github.com/txaty/go-merkletree"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/tries"
|
||||
)
|
||||
|
||||
func TestPackAndVerifyOutput(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
numLeaves int
|
||||
modulo int
|
||||
frameNum uint64
|
||||
withPrev bool
|
||||
}{
|
||||
{
|
||||
name: "Basic case without previous tree",
|
||||
numLeaves: 4,
|
||||
modulo: 4,
|
||||
frameNum: 1,
|
||||
withPrev: false,
|
||||
},
|
||||
{
|
||||
name: "With previous tree",
|
||||
numLeaves: 8,
|
||||
modulo: 8,
|
||||
frameNum: 2,
|
||||
withPrev: true,
|
||||
},
|
||||
{
|
||||
name: "Large tree with previous",
|
||||
numLeaves: 16,
|
||||
modulo: 16,
|
||||
frameNum: 3,
|
||||
withPrev: true,
|
||||
},
|
||||
{
|
||||
name: "Non-power-of-2 modulo",
|
||||
numLeaves: 10,
|
||||
modulo: 7,
|
||||
frameNum: 4,
|
||||
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, payload, output := tries.PackOutputIntoPayloadAndProof(
|
||||
outputs,
|
||||
tc.modulo,
|
||||
frame,
|
||||
previousTree,
|
||||
)
|
||||
require.NotNil(t, tree)
|
||||
require.NotEmpty(t, payload)
|
||||
require.NotEmpty(t, output)
|
||||
|
||||
var previousRoot []byte
|
||||
if previousTree != nil {
|
||||
previousRoot = previousTree.Root
|
||||
}
|
||||
|
||||
treeRoot, modulo, frameNumber, verified, err := tries.UnpackAndVerifyOutput(
|
||||
previousRoot,
|
||||
output,
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.True(t, verified, "Output verification failed")
|
||||
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")
|
||||
|
||||
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))
|
||||
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.UnpackAndVerifyOutput(
|
||||
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 {
|
||||
lastIdx := len(corruptedOutput) - 1
|
||||
corruptedLeaf := make([]byte, len(corruptedOutput[lastIdx]))
|
||||
copy(corruptedLeaf, corruptedOutput[lastIdx])
|
||||
corruptedLeaf[0] ^= 0xFF
|
||||
corruptedOutput[lastIdx] = corruptedLeaf
|
||||
}
|
||||
|
||||
_, _, _, verified, err = tries.UnpackAndVerifyOutput(
|
||||
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