mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 10:27:26 +08:00
531 lines
12 KiB
Go
531 lines
12 KiB
Go
package application
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto"
|
|
"encoding/binary"
|
|
"sync"
|
|
|
|
"github.com/iden3/go-iden3-crypto/poseidon"
|
|
"github.com/pkg/errors"
|
|
"go.uber.org/zap"
|
|
"google.golang.org/protobuf/proto"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/config"
|
|
qcrypto "source.quilibrium.com/quilibrium/monorepo/node/crypto"
|
|
qruntime "source.quilibrium.com/quilibrium/monorepo/node/internal/runtime"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/store"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/tries"
|
|
)
|
|
|
|
var ErrInvalidStateTransition = errors.New("invalid state transition")
|
|
|
|
var TOKEN_ADDRESS = []byte{
|
|
// poseidon("q_mainnet_token")
|
|
0x11, 0x55, 0x85, 0x84, 0xaf, 0x70, 0x17, 0xa9,
|
|
0xbf, 0xd1, 0xff, 0x18, 0x64, 0x30, 0x2d, 0x64,
|
|
0x3f, 0xbe, 0x58, 0xc6, 0x2d, 0xcf, 0x90, 0xcb,
|
|
0xcd, 0x8f, 0xde, 0x74, 0xa2, 0x67, 0x94, 0xd9,
|
|
}
|
|
|
|
type TokenApplication struct {
|
|
Beacon []byte
|
|
TokenOutputs *protobufs.TokenOutputs
|
|
Tries []*tries.RollingFrecencyCritbitTrie
|
|
CoinStore store.CoinStore
|
|
ClockStore store.ClockStore
|
|
PubSub p2p.PubSub
|
|
Logger *zap.Logger
|
|
Difficulty uint32
|
|
FrameProver qcrypto.FrameProver
|
|
}
|
|
|
|
func GetOutputsFromClockFrame(
|
|
frame *protobufs.ClockFrame,
|
|
) (
|
|
*protobufs.TokenRequests,
|
|
*protobufs.TokenOutputs,
|
|
error,
|
|
) {
|
|
var associatedProof []byte
|
|
var tokenOutputs *protobufs.TokenOutputs
|
|
if len(frame.AggregateProofs) > 0 {
|
|
for _, proofs := range frame.AggregateProofs {
|
|
for _, inclusion := range proofs.InclusionCommitments {
|
|
if inclusion.TypeUrl == protobufs.IntrinsicExecutionOutputType {
|
|
output := protobufs.IntrinsicExecutionOutput{}
|
|
if err := proto.Unmarshal(inclusion.Data, &output); err != nil {
|
|
return nil, nil, errors.Wrap(err, "get outputs from clock frame")
|
|
}
|
|
|
|
tokenOutputs = &protobufs.TokenOutputs{}
|
|
if err := proto.Unmarshal(output.Output, tokenOutputs); err != nil {
|
|
return nil, nil, errors.Wrap(err, "get outputs from clock frame")
|
|
}
|
|
|
|
associatedProof = output.Proof
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
transition := &protobufs.TokenRequests{}
|
|
if frame.FrameNumber != 0 {
|
|
if err := proto.Unmarshal(associatedProof, transition); err != nil {
|
|
return nil, nil, errors.Wrap(err, "get outputs from clock frame")
|
|
}
|
|
}
|
|
|
|
return transition, tokenOutputs, nil
|
|
}
|
|
|
|
func MaterializeApplicationFromFrame(
|
|
privKey crypto.Signer,
|
|
frame *protobufs.ClockFrame,
|
|
tries []*tries.RollingFrecencyCritbitTrie,
|
|
coinStore store.CoinStore,
|
|
clockStore store.ClockStore,
|
|
pubSub p2p.PubSub,
|
|
logger *zap.Logger,
|
|
frameProver qcrypto.FrameProver,
|
|
) (*TokenApplication, error) {
|
|
_, tokenOutputs, err := GetOutputsFromClockFrame(frame)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "materialize application from frame")
|
|
}
|
|
|
|
genesis := config.GetGenesis()
|
|
|
|
return &TokenApplication{
|
|
Beacon: genesis.Beacon,
|
|
TokenOutputs: tokenOutputs,
|
|
Tries: tries,
|
|
CoinStore: coinStore,
|
|
ClockStore: clockStore,
|
|
Logger: logger,
|
|
PubSub: pubSub,
|
|
Difficulty: frame.Difficulty,
|
|
FrameProver: frameProver,
|
|
}, nil
|
|
}
|
|
|
|
func (a *TokenApplication) ApplyTransitions(
|
|
currentFrameNumber uint64,
|
|
transitions *protobufs.TokenRequests,
|
|
skipFailures bool,
|
|
) (
|
|
*TokenApplication,
|
|
*protobufs.TokenRequests,
|
|
*protobufs.TokenRequests,
|
|
error,
|
|
) {
|
|
finalizedTransitions := &protobufs.TokenRequests{}
|
|
failedTransitions := &protobufs.TokenRequests{}
|
|
outputs := &protobufs.TokenOutputs{}
|
|
lockMap := map[string]struct{}{}
|
|
|
|
frame, _, err := a.ClockStore.GetDataClockFrame(
|
|
p2p.GetBloomFilter(TOKEN_ADDRESS, 256, 3),
|
|
currentFrameNumber-1,
|
|
false,
|
|
)
|
|
if err != nil {
|
|
return nil, nil, nil, errors.Wrap(
|
|
ErrInvalidStateTransition,
|
|
"apply transitions")
|
|
}
|
|
|
|
requests := []*protobufs.TokenRequest{}
|
|
if skipFailures {
|
|
mints := tries.NewMinHeap[*protobufs.TokenRequest]()
|
|
for _, req := range transitions.Requests {
|
|
mints.Push(req)
|
|
}
|
|
|
|
requests = mints.All()
|
|
} else {
|
|
requests = transitions.Requests
|
|
}
|
|
|
|
set := make([]*protobufs.TokenRequest, len(requests))
|
|
fails := make([]*protobufs.TokenRequest, len(requests))
|
|
|
|
wg := sync.WaitGroup{}
|
|
throttle := make(chan struct{}, qruntime.WorkerCount(0, false))
|
|
|
|
for i, transition := range requests {
|
|
switch t := transition.Request.(type) {
|
|
case *protobufs.TokenRequest_Mint:
|
|
if t == nil {
|
|
fails[i] = transition
|
|
continue
|
|
}
|
|
throttle <- struct{}{}
|
|
wg.Add(1)
|
|
go func(i int, transition *protobufs.TokenRequest) {
|
|
defer func() { <-throttle }()
|
|
defer wg.Done()
|
|
if err := t.Mint.Validate(); err != nil {
|
|
fails[i] = transition
|
|
}
|
|
}(i, transition)
|
|
}
|
|
}
|
|
wg.Wait()
|
|
|
|
parallelismMap := map[int]uint64{}
|
|
if len(a.Tries) > 1 {
|
|
for i := range a.Tries[1:] {
|
|
parallelismMap[i] = 0
|
|
}
|
|
}
|
|
|
|
seen := map[string]struct{}{}
|
|
|
|
for i, transition := range requests {
|
|
if fails[i] != nil {
|
|
continue
|
|
}
|
|
switch t := transition.Request.(type) {
|
|
case *protobufs.TokenRequest_Mint:
|
|
if len(t.Mint.Proofs) == 1 {
|
|
addr, err := poseidon.HashBytes(
|
|
t.Mint.Signature.PublicKey.KeyValue,
|
|
)
|
|
if err != nil {
|
|
fails[i] = transition
|
|
continue
|
|
}
|
|
if a.Tries[0].Contains(addr.FillBytes(make([]byte, 32))) &&
|
|
bytes.Equal(t.Mint.Signature.PublicKey.KeyValue, a.Beacon) {
|
|
if _, ok := seen[string(t.Mint.Proofs[0][32:])]; !ok {
|
|
set[i] = transition
|
|
seen[string(t.Mint.Proofs[0][32:])] = struct{}{}
|
|
continue
|
|
}
|
|
}
|
|
fails[i] = transition
|
|
continue
|
|
} else if len(t.Mint.Proofs) >= 3 && currentFrameNumber > PROOF_FRAME_CUTOFF {
|
|
frameNumber := binary.BigEndian.Uint64(t.Mint.Proofs[2])
|
|
if frameNumber < currentFrameNumber-1 {
|
|
fails[i] = transition
|
|
continue
|
|
}
|
|
_, _, err := t.Mint.RingAndParallelism(
|
|
func(addr []byte) int {
|
|
if _, ok := seen[string(addr)]; ok {
|
|
return -1
|
|
}
|
|
|
|
ring := -1
|
|
for i, t := range a.Tries[1:] {
|
|
if t.Contains(addr) {
|
|
ring = i
|
|
seen[string(addr)] = struct{}{}
|
|
}
|
|
}
|
|
|
|
return ring
|
|
},
|
|
)
|
|
if err == nil {
|
|
// fmt.Println(i, "checked ring test")
|
|
set[i] = transition
|
|
} else {
|
|
// fmt.Println(i, "failed ring test", err)
|
|
fails[i] = transition
|
|
}
|
|
}
|
|
default:
|
|
set[i] = transition
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
req:
|
|
switch t := transition.Request.(type) {
|
|
case *protobufs.TokenRequest_Announce:
|
|
success, err := a.handleAnnounce(currentFrameNumber, lockMap, t.Announce)
|
|
if err != nil {
|
|
if !skipFailures {
|
|
return nil, nil, nil, errors.Wrap(
|
|
err,
|
|
"apply transitions",
|
|
)
|
|
}
|
|
failedTransitions.Requests = append(
|
|
failedTransitions.Requests,
|
|
transition,
|
|
)
|
|
break req
|
|
}
|
|
outputsSet[i] = success
|
|
successes[i] = transition
|
|
case *protobufs.TokenRequest_Join:
|
|
success, err := a.handleDataAnnounceProverJoin(
|
|
currentFrameNumber,
|
|
lockMap,
|
|
t.Join,
|
|
)
|
|
if err != nil {
|
|
if !skipFailures {
|
|
return nil, nil, nil, errors.Wrap(
|
|
err,
|
|
"apply transitions",
|
|
)
|
|
}
|
|
fails[i] = transition
|
|
break req
|
|
}
|
|
outputsSet[i] = success
|
|
successes[i] = transition
|
|
case *protobufs.TokenRequest_Leave:
|
|
success, err := a.handleDataAnnounceProverLeave(
|
|
currentFrameNumber,
|
|
lockMap,
|
|
t.Leave,
|
|
)
|
|
if err != nil {
|
|
if !skipFailures {
|
|
return nil, nil, nil, errors.Wrap(
|
|
err,
|
|
"apply transitions",
|
|
)
|
|
}
|
|
fails[i] = transition
|
|
break req
|
|
}
|
|
outputsSet[i] = success
|
|
successes[i] = transition
|
|
case *protobufs.TokenRequest_Resume:
|
|
success, err := a.handleDataAnnounceProverResume(
|
|
currentFrameNumber,
|
|
lockMap,
|
|
t.Resume,
|
|
)
|
|
if err != nil {
|
|
if !skipFailures {
|
|
return nil, nil, nil, errors.Wrap(
|
|
err,
|
|
"apply transitions",
|
|
)
|
|
}
|
|
fails[i] = transition
|
|
break req
|
|
}
|
|
outputsSet[i] = success
|
|
successes[i] = transition
|
|
case *protobufs.TokenRequest_Pause:
|
|
success, err := a.handleDataAnnounceProverPause(
|
|
currentFrameNumber,
|
|
lockMap,
|
|
t.Pause,
|
|
)
|
|
if err != nil {
|
|
if !skipFailures {
|
|
return nil, nil, nil, errors.Wrap(
|
|
err,
|
|
"apply transitions",
|
|
)
|
|
}
|
|
fails[i] = transition
|
|
break req
|
|
}
|
|
outputsSet[i] = success
|
|
successes[i] = transition
|
|
case *protobufs.TokenRequest_Merge:
|
|
success, err := a.handleMerge(currentFrameNumber, lockMap, t.Merge)
|
|
if err != nil {
|
|
if !skipFailures {
|
|
return nil, nil, nil, errors.Wrap(
|
|
err,
|
|
"apply transitions",
|
|
)
|
|
}
|
|
fails[i] = transition
|
|
break req
|
|
}
|
|
outputsSet[i] = success
|
|
successes[i] = transition
|
|
case *protobufs.TokenRequest_Split:
|
|
success, err := a.handleSplit(currentFrameNumber, lockMap, t.Split)
|
|
if err != nil {
|
|
if !skipFailures {
|
|
return nil, nil, nil, errors.Wrap(
|
|
err,
|
|
"apply transitions",
|
|
)
|
|
}
|
|
fails[i] = transition
|
|
break req
|
|
}
|
|
outputsSet[i] = success
|
|
successes[i] = transition
|
|
case *protobufs.TokenRequest_Transfer:
|
|
success, err := a.handleTransfer(currentFrameNumber, lockMap, t.Transfer)
|
|
if err != nil {
|
|
if !skipFailures {
|
|
return nil, nil, nil, errors.Wrap(
|
|
err,
|
|
"apply transitions",
|
|
)
|
|
}
|
|
fails[i] = transition
|
|
break req
|
|
}
|
|
outputsSet[i] = success
|
|
successes[i] = transition
|
|
}
|
|
}
|
|
|
|
for i, transition := range set {
|
|
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 transition == nil {
|
|
continue
|
|
}
|
|
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{}{}
|
|
wg.Add(1)
|
|
go func(i int, transition *protobufs.TokenRequest) {
|
|
defer func() { <-throttle }()
|
|
defer wg.Done()
|
|
success, err := a.handleMint(
|
|
currentFrameNumber,
|
|
t.Mint,
|
|
frame,
|
|
processedMap[i],
|
|
parallelismMap,
|
|
)
|
|
if err != nil {
|
|
fails[i] = transition
|
|
return
|
|
}
|
|
outputsSet[i] = success
|
|
successes[i] = transition
|
|
}(i, transition)
|
|
}
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
finalFails := []*protobufs.TokenRequest{}
|
|
for _, fail := range fails {
|
|
if fail != nil {
|
|
finalFails = append(finalFails, fail)
|
|
}
|
|
}
|
|
if len(finalFails) != 0 && !skipFailures {
|
|
return nil, nil, nil, errors.Wrap(
|
|
ErrInvalidStateTransition,
|
|
"apply transitions",
|
|
)
|
|
}
|
|
finalSuccesses := []*protobufs.TokenRequest{}
|
|
for _, success := range successes {
|
|
if success != nil {
|
|
finalSuccesses = append(finalSuccesses, success)
|
|
}
|
|
}
|
|
|
|
outputs.Outputs = []*protobufs.TokenOutput{}
|
|
for _, out := range outputsSet {
|
|
if out != nil {
|
|
for _, o := range out {
|
|
outputs.Outputs = append(outputs.Outputs, o)
|
|
}
|
|
}
|
|
}
|
|
|
|
a.TokenOutputs = outputs
|
|
|
|
finalizedTransitions.Requests = finalSuccesses
|
|
failedTransitions.Requests = finalFails
|
|
return a, finalizedTransitions, failedTransitions, nil
|
|
}
|
|
|
|
func (a *TokenApplication) MaterializeStateFromApplication() (
|
|
*protobufs.TokenOutputs,
|
|
error,
|
|
) {
|
|
return a.TokenOutputs, nil
|
|
}
|