ceremonyclient/node/execution/intrinsics/token/token_execution_engine.go

1244 lines
29 KiB
Go

package token
import (
"bytes"
"crypto"
_ "embed"
"encoding/binary"
"encoding/hex"
"encoding/json"
"strconv"
"strings"
"sync"
gotime "time"
"github.com/iden3/go-iden3-crypto/poseidon"
pcrypto "github.com/libp2p/go-libp2p/core/crypto"
"github.com/mr-tron/base58"
"github.com/pkg/errors"
"github.com/shopspring/decimal"
"go.uber.org/zap"
"golang.org/x/crypto/sha3"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/vdf"
"source.quilibrium.com/quilibrium/monorepo/node/config"
"source.quilibrium.com/quilibrium/monorepo/node/consensus/data"
"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/keys"
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
"source.quilibrium.com/quilibrium/monorepo/node/store"
)
type TokenExecutionEngine struct {
logger *zap.Logger
clock *data.DataClockConsensusEngine
clockStore store.ClockStore
coinStore store.CoinStore
keyStore store.KeyStore
keyManager keys.KeyManager
engineConfig *config.EngineConfig
pubSub p2p.PubSub
peerIdHash []byte
provingKey crypto.Signer
proverPublicKey []byte
provingKeyAddress []byte
inclusionProver qcrypto.InclusionProver
participantMx sync.Mutex
peerChannels map[string]*p2p.PublicP2PChannel
activeClockFrame *protobufs.ClockFrame
alreadyPublishedShare bool
seenMessageMap map[string]bool
seenMessageMx sync.Mutex
intrinsicFilter []byte
frameProver qcrypto.FrameProver
peerSeniority map[string]uint64
}
func NewTokenExecutionEngine(
logger *zap.Logger,
engineConfig *config.EngineConfig,
keyManager keys.KeyManager,
pubSub p2p.PubSub,
frameProver qcrypto.FrameProver,
inclusionProver qcrypto.InclusionProver,
clockStore store.ClockStore,
dataProofStore store.DataProofStore,
coinStore store.CoinStore,
masterTimeReel *time.MasterTimeReel,
peerInfoManager p2p.PeerInfoManager,
keyStore store.KeyStore,
report *protobufs.SelfTestReport,
) *TokenExecutionEngine {
if logger == nil {
panic(errors.New("logger is nil"))
}
var genesis *config.SignedGenesisUnlock
var err error
for {
genesis, err = config.DownloadAndVerifyGenesis()
if err != nil {
gotime.Sleep(10 * gotime.Minute)
}
if genesis != nil {
break
}
}
seed, err := hex.DecodeString(genesis.GenesisSeedHex)
if err != nil {
panic(err)
}
intrinsicFilter := p2p.GetBloomFilter(application.TOKEN_ADDRESS, 256, 3)
_, _, err = clockStore.GetDataClockFrame(intrinsicFilter, 0, false)
var origin []byte
var inclusionProof *qcrypto.InclusionAggregateProof
var proverKeys [][]byte
var peerSeniority map[string]uint64
genesisCreated := false
if err != nil && errors.Is(err, store.ErrNotFound) {
origin, inclusionProof, proverKeys, peerSeniority = CreateGenesisState(
logger,
engineConfig,
nil,
inclusionProver,
coinStore,
)
genesisCreated = true
} else if err != nil {
panic(err)
}
dataTimeReel := time.NewDataTimeReel(
intrinsicFilter,
logger,
clockStore,
engineConfig,
frameProver,
origin,
inclusionProof,
proverKeys,
)
clock := data.NewDataClockConsensusEngine(
engineConfig,
logger,
keyManager,
clockStore,
coinStore,
keyStore,
pubSub,
frameProver,
inclusionProver,
masterTimeReel,
dataTimeReel,
peerInfoManager,
report,
intrinsicFilter,
seed,
peerSeniority,
)
e := &TokenExecutionEngine{
logger: logger,
clock: clock,
engineConfig: engineConfig,
keyManager: keyManager,
clockStore: clockStore,
coinStore: coinStore,
keyStore: keyStore,
pubSub: pubSub,
inclusionProver: inclusionProver,
frameProver: frameProver,
participantMx: sync.Mutex{},
peerChannels: map[string]*p2p.PublicP2PChannel{},
alreadyPublishedShare: false,
seenMessageMx: sync.Mutex{},
seenMessageMap: map[string]bool{},
intrinsicFilter: intrinsicFilter,
peerSeniority: peerSeniority,
}
peerId := e.pubSub.GetPeerID()
addr, err := poseidon.HashBytes(peerId)
if err != nil {
panic(err)
}
addrBytes := addr.FillBytes(make([]byte, 32))
e.peerIdHash = addrBytes
provingKey, _, publicKeyBytes, provingKeyAddress := e.clock.GetProvingKey(
engineConfig,
)
e.provingKey = provingKey
e.proverPublicKey = publicKeyBytes
e.provingKeyAddress = provingKeyAddress
if genesisCreated {
go func() {
if len(e.engineConfig.MultisigProverEnrollmentPaths) != 0 && genesisCreated {
keys := [][]byte{}
ksigs := [][]byte{}
for _, conf := range e.engineConfig.MultisigProverEnrollmentPaths {
extraConf, err := config.LoadConfig(conf, "", false)
if err != nil {
panic(err)
}
peerPrivKey, err := hex.DecodeString(extraConf.P2P.PeerPrivKey)
if err != nil {
panic(errors.Wrap(err, "error unmarshaling peerkey"))
}
privKey, err := pcrypto.UnmarshalEd448PrivateKey(peerPrivKey)
if err != nil {
panic(errors.Wrap(err, "error unmarshaling peerkey"))
}
pub := privKey.GetPublic()
pubBytes, err := pub.Raw()
if err != nil {
panic(errors.Wrap(err, "error unmarshaling peerkey"))
}
keys = append(keys, pubBytes)
sig, err := privKey.Sign(e.pubSub.GetPublicKey())
if err != nil {
panic(errors.Wrap(err, "error unmarshaling peerkey"))
}
ksigs = append(ksigs, sig)
}
keyjoin := []byte{}
for _, k := range keys {
keyjoin = append(keyjoin, k...)
}
mainsig, err := e.pubSub.SignMessage(keyjoin)
if err != nil {
panic(err)
}
announce := &protobufs.TokenRequest_Announce{
Announce: &protobufs.AnnounceProverRequest{
PublicKeySignaturesEd448: []*protobufs.Ed448Signature{},
},
}
announce.Announce.PublicKeySignaturesEd448 = append(
announce.Announce.PublicKeySignaturesEd448,
&protobufs.Ed448Signature{
PublicKey: &protobufs.Ed448PublicKey{
KeyValue: e.pubSub.GetPublicKey(),
},
Signature: mainsig,
},
)
for i := range keys {
announce.Announce.PublicKeySignaturesEd448 = append(
announce.Announce.PublicKeySignaturesEd448,
&protobufs.Ed448Signature{
PublicKey: &protobufs.Ed448PublicKey{
KeyValue: keys[i],
},
Signature: ksigs[i],
},
)
}
inc, _, _, err := dataProofStore.GetLatestDataTimeProof(
e.pubSub.GetPeerID(),
)
_, parallelism, input, output, err := dataProofStore.GetDataTimeProof(
e.pubSub.GetPeerID(),
inc,
)
if err == nil {
proof := []byte{}
proof = binary.BigEndian.AppendUint32(proof, inc)
proof = binary.BigEndian.AppendUint32(proof, parallelism)
proof = binary.BigEndian.AppendUint64(proof, uint64(len(input)))
proof = append(proof, input...)
proof = binary.BigEndian.AppendUint64(proof, uint64(len(output)))
proof = append(proof, output...)
announce.Announce.InitialProof.Proofs = [][]byte{
[]byte("pre-dusk"),
make([]byte, 32),
proof,
}
payload := []byte("mint")
for _, p := range announce.Announce.InitialProof.Proofs {
payload = append(payload, p...)
}
sig, err := e.pubSub.SignMessage(payload)
if err != nil {
panic(err)
}
announce.Announce.InitialProof.Signature = &protobufs.Ed448Signature{
PublicKey: &protobufs.Ed448PublicKey{
KeyValue: e.pubSub.GetPublicKey(),
},
Signature: sig,
}
}
req := &protobufs.TokenRequest{
Request: announce,
}
err = e.publishMessage(intrinsicFilter, req)
if err != nil {
panic(err)
}
}
}()
}
inc, _, _, err := dataProofStore.GetLatestDataTimeProof(pubSub.GetPeerID())
if err != nil {
go func() {
addrBI, err := poseidon.HashBytes(pubSub.GetPeerID())
if err != nil {
panic(err)
}
addr := addrBI.FillBytes(make([]byte, 32))
for {
_, proofs, err := coinStore.GetPreCoinProofsForOwner(addr)
if err == nil {
for _, proof := range proofs {
if proof.IndexProof != nil && len(proof.IndexProof) != 0 {
if proof.Index < inc {
_, par, input, output, err := dataProofStore.GetDataTimeProof(
pubSub.GetPeerID(),
proof.Index-1,
)
if err == nil {
p := []byte{}
p = binary.BigEndian.AppendUint32(p, proof.Index-1)
p = binary.BigEndian.AppendUint32(p, par)
p = binary.BigEndian.AppendUint64(
p,
uint64(len(input)),
)
p = append(p, input...)
p = binary.BigEndian.AppendUint64(p, uint64(len(output)))
p = append(p, output...)
proofs := [][]byte{
[]byte("pre-dusk"),
make([]byte, 32),
p,
}
payload := []byte("mint")
for _, i := range proofs {
payload = append(payload, i...)
}
sig, err := e.pubSub.SignMessage(payload)
if err != nil {
panic(err)
}
e.publishMessage(e.intrinsicFilter, &protobufs.TokenRequest{
Request: &protobufs.TokenRequest_Mint{
Mint: &protobufs.MintCoinRequest{
Proofs: proofs,
Signature: &protobufs.Ed448Signature{
PublicKey: &protobufs.Ed448PublicKey{
KeyValue: e.pubSub.GetPublicKey(),
},
Signature: sig,
},
},
},
})
}
}
}
}
}
gotime.Sleep(10 * gotime.Second)
}
}()
}
return e
}
var _ execution.ExecutionEngine = (*TokenExecutionEngine)(nil)
// GetName implements ExecutionEngine
func (*TokenExecutionEngine) GetName() string {
return "Token"
}
// GetSupportedApplications implements ExecutionEngine
func (
*TokenExecutionEngine,
) GetSupportedApplications() []*protobufs.Application {
return []*protobufs.Application{
{
Address: application.TOKEN_ADDRESS,
ExecutionContext: protobufs.ExecutionContext_EXECUTION_CONTEXT_INTRINSIC,
},
}
}
var BridgeAddress = "1ac3290d57e064bdb5a57e874b59290226a9f9730d69f1d963600883789d6ee2"
type BridgedPeerJson struct {
Amount string `json:"amount"`
Identifier string `json:"identifier"`
Variant string `json:"variant"`
}
type FirstRetroJson struct {
PeerId string `json:"peerId"`
Reward string `json:"reward"`
}
type SecondRetroJson struct {
PeerId string `json:"peerId"`
Reward string `json:"reward"`
JanPresence bool `json:"janPresence"`
FebPresence bool `json:"febPresence"`
MarPresence bool `json:"marPresence"`
AprPresence bool `json:"aprPresence"`
MayPresence bool `json:"mayPresence"`
}
type ThirdRetroJson struct {
PeerId string `json:"peerId"`
Reward string `json:"reward"`
}
type FourthRetroJson struct {
PeerId string `json:"peerId"`
Reward string `json:"reward"`
}
//go:embed bridged.json
var bridgedPeersJsonBinary []byte
//go:embed ceremony_vouchers.json
var ceremonyVouchersJsonBinary []byte
//go:embed first_retro.json
var firstRetroJsonBinary []byte
//go:embed second_retro.json
var secondRetroJsonBinary []byte
//go:embed third_retro.json
var thirdRetroJsonBinary []byte
//go:embed fourth_retro.json
var fourthRetroJsonBinary []byte
// Creates a genesis state for the intrinsic
func CreateGenesisState(
logger *zap.Logger,
engineConfig *config.EngineConfig,
testProverKeys [][]byte,
inclusionProver qcrypto.InclusionProver,
coinStore store.CoinStore,
) (
[]byte,
*qcrypto.InclusionAggregateProof,
[][]byte,
map[string]uint64,
) {
genesis, err := config.DownloadAndVerifyGenesis()
if err != nil {
panic(err)
}
if err != nil {
panic(errors.New("genesis seed is nil"))
}
seed, err := hex.DecodeString(engineConfig.GenesisSeed)
if err != nil {
panic(err)
}
logger.Info("creating genesis frame from message:")
for _, l := range strings.Split(string(seed), "\n") {
logger.Info(l)
}
difficulty := engineConfig.Difficulty
if difficulty != 100000 {
difficulty = 100000
}
b := sha3.Sum256(seed)
v := vdf.New(difficulty, b)
v.Execute()
o := v.GetOutput()
inputMessage := o[:]
logger.Info("encoding all prior state")
bridged := []*BridgedPeerJson{}
vouchers := []string{}
firstRetro := []*FirstRetroJson{}
secondRetro := []*SecondRetroJson{}
thirdRetro := []*ThirdRetroJson{}
fourthRetro := []*FourthRetroJson{}
err = json.Unmarshal(bridgedPeersJsonBinary, &bridged)
if err != nil {
panic(err)
}
err = json.Unmarshal(ceremonyVouchersJsonBinary, &vouchers)
if err != nil {
panic(err)
}
err = json.Unmarshal(firstRetroJsonBinary, &firstRetro)
if err != nil {
panic(err)
}
err = json.Unmarshal(secondRetroJsonBinary, &secondRetro)
if err != nil {
panic(err)
}
err = json.Unmarshal(thirdRetroJsonBinary, &thirdRetro)
if err != nil {
panic(err)
}
err = json.Unmarshal(fourthRetroJsonBinary, &fourthRetro)
if err != nil {
panic(err)
}
bridgedAddrs := map[string]struct{}{}
bridgeTotal := decimal.Zero
for _, b := range bridged {
amt, err := decimal.NewFromString(b.Amount)
if err != nil {
panic(err)
}
bridgeTotal = bridgeTotal.Add(amt)
bridgedAddrs[b.Identifier] = struct{}{}
}
voucherTotals := map[string]decimal.Decimal{}
peerIdTotals := map[string]decimal.Decimal{}
peerSeniority := map[string]uint64{}
for _, f := range firstRetro {
if _, ok := bridgedAddrs[f.PeerId]; !ok {
peerIdTotals[f.PeerId], err = decimal.NewFromString(f.Reward)
if err != nil {
panic(err)
}
}
// these don't have decimals so we can shortcut
max := 157208
actual, err := strconv.Atoi(f.Reward)
if err != nil {
panic(err)
}
peerSeniority[f.PeerId] = uint64(10 * 6 * 60 * 24 * 92 / (max / actual))
}
for _, v := range vouchers {
if _, ok := bridgedAddrs[v]; !ok {
voucherTotals[v] = decimal.NewFromInt(50)
}
}
for _, f := range secondRetro {
if _, ok := bridgedAddrs[f.PeerId]; !ok {
existing, ok := peerIdTotals[f.PeerId]
amount, err := decimal.NewFromString(f.Reward)
if err != nil {
panic(err)
}
if !ok {
peerIdTotals[f.PeerId] = amount
} else {
peerIdTotals[f.PeerId] = existing.Add(amount)
}
}
if _, ok := peerSeniority[f.PeerId]; !ok {
peerSeniority[f.PeerId] = 0
}
if f.JanPresence {
peerSeniority[f.PeerId] = peerSeniority[f.PeerId] + (10 * 6 * 60 * 24 * 31)
}
if f.FebPresence {
peerSeniority[f.PeerId] = peerSeniority[f.PeerId] + (10 * 6 * 60 * 24 * 29)
}
if f.MarPresence {
peerSeniority[f.PeerId] = peerSeniority[f.PeerId] + (10 * 6 * 60 * 24 * 31)
}
if f.AprPresence {
peerSeniority[f.PeerId] = peerSeniority[f.PeerId] + (10 * 6 * 60 * 24 * 30)
}
if f.MayPresence {
peerSeniority[f.PeerId] = peerSeniority[f.PeerId] + (10 * 6 * 60 * 24 * 31)
}
}
for _, f := range thirdRetro {
existing, ok := peerIdTotals[f.PeerId]
amount, err := decimal.NewFromString(f.Reward)
if err != nil {
panic(err)
}
if !ok {
peerIdTotals[f.PeerId] = amount
} else {
peerIdTotals[f.PeerId] = existing.Add(amount)
}
if _, ok := peerSeniority[f.PeerId]; !ok {
peerSeniority[f.PeerId] = 0
}
peerSeniority[f.PeerId] = peerSeniority[f.PeerId] + (10 * 6 * 60 * 24 * 30)
}
for _, f := range fourthRetro {
existing, ok := peerIdTotals[f.PeerId]
amount, err := decimal.NewFromString(f.Reward)
if err != nil {
panic(err)
}
if !ok {
peerIdTotals[f.PeerId] = amount
} else {
peerIdTotals[f.PeerId] = existing.Add(amount)
}
if _, ok := peerSeniority[f.PeerId]; !ok {
peerSeniority[f.PeerId] = 0
}
peerSeniority[f.PeerId] = peerSeniority[f.PeerId] + (10 * 6 * 60 * 24 * 31)
}
genesisState := &protobufs.TokenOutputs{
Outputs: []*protobufs.TokenOutput{},
}
factor, _ := decimal.NewFromString("8000000000")
bridgeAddressHex, err := hex.DecodeString(BridgeAddress)
if err != nil {
panic(err)
}
genesisState.Outputs = append(genesisState.Outputs, &protobufs.TokenOutput{
Output: &protobufs.TokenOutput_Coin{
Coin: &protobufs.Coin{
Amount: bridgeTotal.Mul(factor).BigInt().FillBytes(
make([]byte, 32),
),
Intersection: make([]byte, 1024),
Owner: &protobufs.AccountRef{
Account: &protobufs.AccountRef_ImplicitAccount{
ImplicitAccount: &protobufs.ImplicitAccount{
Address: bridgeAddressHex,
},
},
},
},
},
})
for peerId, total := range peerIdTotals {
peerBytes, err := base58.Decode(peerId)
if err != nil {
panic(err)
}
addr, err := poseidon.HashBytes(peerBytes)
if err != nil {
panic(err)
}
genesisState.Outputs = append(genesisState.Outputs, &protobufs.TokenOutput{
Output: &protobufs.TokenOutput_Coin{
Coin: &protobufs.Coin{
Amount: total.Mul(factor).BigInt().FillBytes(
make([]byte, 32),
),
Intersection: make([]byte, 1024),
Owner: &protobufs.AccountRef{
Account: &protobufs.AccountRef_ImplicitAccount{
ImplicitAccount: &protobufs.ImplicitAccount{
Address: addr.FillBytes(make([]byte, 32)),
},
},
},
},
},
})
}
for voucher, total := range voucherTotals {
keyBytes, err := hex.DecodeString(voucher[2:])
if err != nil {
panic(err)
}
addr, err := poseidon.HashBytes(keyBytes)
if err != nil {
panic(err)
}
genesisState.Outputs = append(genesisState.Outputs, &protobufs.TokenOutput{
Output: &protobufs.TokenOutput_Coin{
Coin: &protobufs.Coin{
Amount: total.Mul(factor).BigInt().FillBytes(
make([]byte, 32),
),
Intersection: make([]byte, 1024),
Owner: &protobufs.AccountRef{
Account: &protobufs.AccountRef_ImplicitAccount{
ImplicitAccount: &protobufs.ImplicitAccount{
Address: addr.FillBytes(make([]byte, 32)),
},
},
},
},
},
})
}
for _, output := range genesisState.Outputs {
txn, err := coinStore.NewTransaction()
if err != nil {
panic(err)
}
address, err := GetAddressOfCoin(output.GetCoin(), 0)
if err != nil {
panic(err)
}
err = coinStore.PutCoin(
txn,
0,
address,
output.GetCoin(),
)
if err != nil {
panic(err)
}
if err := txn.Commit(); err != nil {
panic(err)
}
}
logger.Info("encoded transcript")
outputBytes, err := proto.Marshal(genesisState)
if err != nil {
panic(err)
}
intrinsicFilter := p2p.GetBloomFilter(application.TOKEN_ADDRESS, 256, 3)
executionOutput := &protobufs.IntrinsicExecutionOutput{
Address: intrinsicFilter,
Output: outputBytes,
Proof: seed,
}
data, err := proto.Marshal(executionOutput)
if err != nil {
panic(err)
}
logger.Debug("encoded execution output")
digest := sha3.NewShake256()
_, err = digest.Write(data)
if err != nil {
panic(err)
}
expand := make([]byte, 1024)
_, err = digest.Read(expand)
if err != nil {
panic(err)
}
commitment, err := inclusionProver.CommitRaw(
expand,
16,
)
if err != nil {
panic(err)
}
logger.Debug("creating kzg proof")
proof, err := inclusionProver.ProveRaw(
expand,
int(expand[0]%16),
16,
)
if err != nil {
panic(err)
}
logger.Info("finalizing execution proof")
return inputMessage, &qcrypto.InclusionAggregateProof{
InclusionCommitments: []*qcrypto.InclusionCommitment{
&qcrypto.InclusionCommitment{
TypeUrl: protobufs.IntrinsicExecutionOutputType,
Data: data,
Commitment: commitment,
},
},
AggregateCommitment: commitment,
Proof: proof,
}, [][]byte{genesis.Beacon}, peerSeniority
}
func GetAddressOfCoin(
coin *protobufs.Coin,
frameNumber uint64,
) ([]byte, error) {
eval := []byte{}
eval = append(eval, application.TOKEN_ADDRESS...)
eval = binary.BigEndian.AppendUint64(eval, frameNumber)
eval = append(eval, coin.Amount...)
eval = append(eval, coin.Intersection...)
eval = binary.BigEndian.AppendUint32(eval, 0)
eval = append(eval, coin.Owner.GetImplicitAccount().Address...)
addressBI, err := poseidon.HashBytes(eval)
if err != nil {
return nil, err
}
return addressBI.FillBytes(make([]byte, 32)), nil
}
func GetAddressOfPreCoinProof(
proof *protobufs.PreCoinProof,
) ([]byte, error) {
eval := []byte{}
eval = append(eval, application.TOKEN_ADDRESS...)
eval = append(eval, proof.Amount...)
eval = binary.BigEndian.AppendUint32(eval, proof.Index)
eval = append(eval, proof.IndexProof...)
eval = append(eval, proof.Commitment...)
eval = append(eval, proof.Proof...)
eval = binary.BigEndian.AppendUint32(eval, proof.Parallelism)
eval = binary.BigEndian.AppendUint32(eval, proof.Difficulty)
eval = binary.BigEndian.AppendUint32(eval, 0)
eval = append(eval, proof.Owner.GetImplicitAccount().Address...)
addressBI, err := poseidon.HashBytes(eval)
if err != nil {
return nil, err
}
return addressBI.FillBytes(make([]byte, 32)), nil
}
// Start implements ExecutionEngine
func (e *TokenExecutionEngine) Start() <-chan error {
errChan := make(chan error)
go func() {
err := <-e.clock.Start()
if err != nil {
panic(err)
}
err = <-e.clock.RegisterExecutor(e, 0)
if err != nil {
panic(err)
}
go e.RunWorker()
errChan <- nil
}()
return errChan
}
// Stop implements ExecutionEngine
func (e *TokenExecutionEngine) Stop(force bool) <-chan error {
errChan := make(chan error)
go func() {
errChan <- <-e.clock.Stop(force)
}()
return errChan
}
// ProcessMessage implements ExecutionEngine
func (e *TokenExecutionEngine) ProcessMessage(
address []byte,
message *protobufs.Message,
) ([]*protobufs.Message, error) {
if bytes.Equal(address, e.GetSupportedApplications()[0].Address) {
e.logger.Debug("processing execution message")
any := &anypb.Any{}
if err := proto.Unmarshal(message.Payload, any); err != nil {
return nil, errors.Wrap(err, "process message")
}
switch any.TypeUrl {
case protobufs.ClockFrameType:
frame := &protobufs.ClockFrame{}
if err := any.UnmarshalTo(frame); err != nil {
return nil, errors.Wrap(err, "process message")
}
if frame.FrameNumber < e.clock.GetFrame().FrameNumber {
return nil, nil
}
if err := e.frameProver.VerifyDataClockFrame(frame); err != nil {
return nil, errors.Wrap(err, "process message")
}
if err := e.VerifyExecution(frame); err != nil {
return nil, errors.Wrap(err, "process message")
}
case protobufs.TokenRequestType:
hash := sha3.Sum256(any.Value)
if any.TypeUrl == protobufs.TokenRequestType {
e.seenMessageMx.Lock()
ref := string(hash[:])
if _, ok := e.seenMessageMap[ref]; !ok {
e.seenMessageMap[ref] = true
} else {
return nil, errors.Wrap(
errors.New("message already received"),
"process message",
)
}
e.seenMessageMx.Unlock()
}
if e.clock.IsInProverTrie(e.proverPublicKey) {
payload, err := proto.Marshal(any)
if err != nil {
return nil, errors.Wrap(err, "process message")
}
h, err := poseidon.HashBytes(payload)
if err != nil {
return nil, errors.Wrap(err, "process message")
}
msg := &protobufs.Message{
Hash: h.Bytes(),
Address: e.provingKeyAddress,
Payload: payload,
}
return []*protobufs.Message{
msg,
}, nil
}
}
}
return nil, nil
}
func (e *TokenExecutionEngine) RunWorker() {
frameChan := e.clock.GetFrameChannel()
for {
select {
case frame := <-frameChan:
e.activeClockFrame = frame
e.logger.Info(
"evaluating next frame",
zap.Uint64(
"frame_number",
frame.FrameNumber,
),
)
app, err := application.MaterializeApplicationFromFrame(
frame,
e.clock.GetFrameProverTries(),
e.coinStore,
e.logger,
)
if err != nil {
e.logger.Error(
"error while materializing application from frame",
zap.Error(err),
)
panic(err)
}
for _, output := range app.TokenOutputs.Outputs {
switch o := output.Output.(type) {
case *protobufs.TokenOutput_Coin:
txn, err := e.coinStore.NewTransaction()
if err != nil {
panic(err)
}
address, err := GetAddressOfCoin(o.Coin, frame.FrameNumber)
if err != nil {
panic(err)
}
err = e.coinStore.PutCoin(
txn,
frame.FrameNumber,
address,
o.Coin,
)
if err != nil {
panic(err)
}
if err := txn.Commit(); err != nil {
panic(err)
}
case *protobufs.TokenOutput_DeletedCoin:
txn, err := e.coinStore.NewTransaction()
if err != nil {
panic(err)
}
coin, err := e.coinStore.GetCoinByAddress(o.DeletedCoin.Address)
if err != nil {
panic(err)
}
err = e.coinStore.DeleteCoin(
txn,
o.DeletedCoin.Address,
coin,
)
if err != nil {
panic(err)
}
if err := txn.Commit(); err != nil {
panic(err)
}
case *protobufs.TokenOutput_Proof:
txn, err := e.coinStore.NewTransaction()
if err != nil {
panic(err)
}
address, err := GetAddressOfPreCoinProof(o.Proof)
if err != nil {
panic(err)
}
err = e.coinStore.PutPreCoinProof(
txn,
frame.FrameNumber,
address,
o.Proof,
)
if err != nil {
panic(err)
}
if err := txn.Commit(); err != nil {
panic(err)
}
case *protobufs.TokenOutput_DeletedProof:
txn, err := e.coinStore.NewTransaction()
if err != nil {
panic(err)
}
address, err := GetAddressOfPreCoinProof(o.DeletedProof)
if err != nil {
panic(err)
}
err = e.coinStore.DeletePreCoinProof(
txn,
address,
o.DeletedProof,
)
if err != nil {
panic(err)
}
if err := txn.Commit(); err != nil {
panic(err)
}
}
}
}
}
}
func (e *TokenExecutionEngine) publishMessage(
filter []byte,
message proto.Message,
) error {
any := &anypb.Any{}
if err := any.MarshalFrom(message); err != nil {
return errors.Wrap(err, "publish message")
}
any.TypeUrl = strings.Replace(
any.TypeUrl,
"type.googleapis.com",
"types.quilibrium.com",
1,
)
payload, err := proto.Marshal(any)
if err != nil {
return errors.Wrap(err, "publish message")
}
h, err := poseidon.HashBytes(payload)
if err != nil {
return errors.Wrap(err, "publish message")
}
msg := &protobufs.Message{
Hash: h.Bytes(),
Address: application.TOKEN_ADDRESS,
Payload: payload,
}
data, err := proto.Marshal(msg)
if err != nil {
return errors.Wrap(err, "publish message")
}
return e.pubSub.PublishToBitmask(filter, data)
}
func (e *TokenExecutionEngine) VerifyExecution(
frame *protobufs.ClockFrame,
) error {
if e.clock.GetFrame().FrameNumber != frame.FrameNumber-1 {
return nil
}
if len(frame.AggregateProofs) > 0 {
for _, proofs := range frame.AggregateProofs {
for _, inclusion := range proofs.InclusionCommitments {
if inclusion.TypeUrl == protobufs.IntrinsicExecutionOutputType {
transition, _, err := application.GetOutputsFromClockFrame(frame)
if err != nil {
return errors.Wrap(err, "verify execution")
}
parent, err := e.clockStore.GetStagedDataClockFrame(
append(
p2p.GetBloomFilter(application.TOKEN_ADDRESS, 256, 3),
),
frame.FrameNumber-1,
frame.ParentSelector,
false,
)
if err != nil && !errors.Is(err, store.ErrNotFound) {
return errors.Wrap(err, "verify execution")
}
if parent == nil {
return errors.Wrap(
errors.New("missing parent frame"),
"verify execution",
)
}
a, err := application.MaterializeApplicationFromFrame(
parent,
e.clock.GetFrameProverTries(),
e.coinStore,
e.logger,
)
if err != nil {
return errors.Wrap(err, "verify execution")
}
a, _, _, err = a.ApplyTransitions(frame.FrameNumber, transition, false)
if err != nil {
return errors.Wrap(err, "verify execution")
}
a2, err := application.MaterializeApplicationFromFrame(
frame,
e.clock.GetFrameProverTries(),
e.coinStore,
e.logger,
)
if err != nil {
return errors.Wrap(err, "verify execution")
}
if len(a.TokenOutputs.Outputs) != len(a2.TokenOutputs.Outputs) {
return errors.Wrap(
application.ErrInvalidStateTransition,
"verify execution",
)
}
for i := range a.TokenOutputs.Outputs {
o1 := a.TokenOutputs.Outputs[i]
o2 := a2.TokenOutputs.Outputs[i]
b1, err := proto.Marshal(o1)
if err != nil {
return errors.Wrap(
application.ErrInvalidStateTransition,
"verify execution",
)
}
b2, err := proto.Marshal(o2)
if err != nil {
return errors.Wrap(
application.ErrInvalidStateTransition,
"verify execution",
)
}
if !bytes.Equal(b1, b2) {
return errors.Wrap(
application.ErrInvalidStateTransition,
"verify execution",
)
}
}
return nil
}
}
}
}
return nil
}
func (e *TokenExecutionEngine) GetPeerInfo() *protobufs.PeerInfoResponse {
return e.clock.GetPeerInfo()
}
func (e *TokenExecutionEngine) GetFrame() *protobufs.ClockFrame {
return e.clock.GetFrame()
}