package application import ( "bytes" "encoding/binary" "math/big" "github.com/iden3/go-iden3-crypto/poseidon" pcrypto "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" "github.com/pkg/errors" "go.uber.org/zap" "golang.org/x/crypto/sha3" "google.golang.org/protobuf/proto" "source.quilibrium.com/quilibrium/monorepo/node/crypto" "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 { TokenOutputs *protobufs.TokenOutputs Tries []*tries.RollingFrecencyCritbitTrie CoinStore store.CoinStore Logger *zap.Logger Difficulty uint32 } 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( frame *protobufs.ClockFrame, tries []*tries.RollingFrecencyCritbitTrie, store store.CoinStore, logger *zap.Logger, ) (*TokenApplication, error) { _, tokenOutputs, err := GetOutputsFromClockFrame(frame) if err != nil { return nil, errors.Wrap(err, "materialize application from frame") } return &TokenApplication{ TokenOutputs: tokenOutputs, Tries: tries, CoinStore: store, Logger: logger, Difficulty: frame.Difficulty, }, 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{} for _, transition := range transitions.Requests { req: switch t := transition.Request.(type) { case *protobufs.TokenRequest_Announce: var primary *protobufs.Ed448Signature payload := []byte{} if t.Announce == nil || t.Announce.PublicKeySignaturesEd448 == nil { if !skipFailures { return nil, nil, nil, errors.Wrap( ErrInvalidStateTransition, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } for i, p := range t.Announce.PublicKeySignaturesEd448 { if p.PublicKey == nil || p.Signature == nil || p.PublicKey.KeyValue == nil { if !skipFailures { return nil, nil, nil, errors.Wrap( ErrInvalidStateTransition, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } if i == 0 { primary = p } else { payload = append(payload, p.PublicKey.KeyValue...) if err := p.Verify(primary.PublicKey.KeyValue); err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( err, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } } } if primary == nil { if !skipFailures { return nil, nil, nil, errors.Wrap( ErrInvalidStateTransition, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } if err := primary.Verify(payload); err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( err, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } if t.Announce.InitialProof != nil && t.Announce.InitialProof.Proofs != nil { payload = []byte("mint") for _, p := range t.Announce.InitialProof.Proofs { payload = append(payload, p...) } if err := t.Announce.InitialProof.Signature.Verify(payload); err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } pk, err := pcrypto.UnmarshalEd448PublicKey( t.Announce.InitialProof.Signature.PublicKey.KeyValue, ) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } peerId, err := peer.IDFromPublicKey(pk) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } addr, err := poseidon.HashBytes( t.Announce.InitialProof.Signature.PublicKey.KeyValue, ) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } if len(t.Announce.InitialProof.Proofs) == 3 && bytes.Equal( t.Announce.InitialProof.Proofs[0], []byte("pre-dusk"), ) && bytes.Equal(t.Announce.InitialProof.Proofs[1], make([]byte, 32)) && currentFrameNumber < 604800 { delete := []*protobufs.TokenOutput{} if !bytes.Equal(t.Announce.InitialProof.Proofs[1], make([]byte, 32)) { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } data := t.Announce.InitialProof.Proofs[2] if len(data) < 28 { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } increment := binary.BigEndian.Uint32(data[:4]) parallelism := binary.BigEndian.Uint32(data[8:12]) inputLen := binary.BigEndian.Uint64(data[12:20]) if len(delete) != 0 { if delete[0].GetDeletedProof().Difficulty-1 != increment { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } } if uint64(len(data[20:])) < inputLen+8 { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } input := make([]byte, inputLen) copy(input[:], data[20:20+inputLen]) outputLen := binary.BigEndian.Uint64(data[20+inputLen : 20+inputLen+8]) if uint64(len(data[20+inputLen+8:])) < outputLen { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } output := make([]byte, outputLen) copy(output[:], data[20+inputLen+8:]) dataProver := crypto.NewKZGInclusionProver(a.Logger) wesoProver := crypto.NewWesolowskiFrameProver(a.Logger) index := binary.BigEndian.Uint32(output[:4]) indexProof := output[4:520] kzgCommitment := output[520:594] kzgProof := output[594:668] ip := sha3.Sum512(indexProof) v, err := dataProver.VerifyRaw( ip[:], kzgCommitment, int(index), kzgProof, nearestApplicablePowerOfTwo(uint64(parallelism)), ) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } if !v { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } wp := []byte{} wp = append(wp, peerId...) wp = append(wp, input...) v = wesoProver.VerifyPreDuskChallengeProof( wp, increment, index, indexProof, ) if !v { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } pomwBasis := big.NewInt(1200000) reward := new(big.Int).Mul(pomwBasis, big.NewInt(int64(parallelism))) if len(delete) != 0 { reward.Add( reward, new(big.Int).SetBytes(delete[0].GetDeletedProof().Amount), ) } if increment != 0 { add := &protobufs.PreCoinProof{ Amount: reward.FillBytes(make([]byte, 32)), Index: index, IndexProof: indexProof, Commitment: kzgCommitment, Proof: append(append([]byte{}, kzgProof...), indexProof...), Parallelism: parallelism, Difficulty: increment, Owner: &protobufs.AccountRef{ Account: &protobufs.AccountRef_ImplicitAccount{ ImplicitAccount: &protobufs.ImplicitAccount{ ImplicitType: 0, Address: addr.FillBytes(make([]byte, 32)), }, }, }, } outputs.Outputs = append(outputs.Outputs, &protobufs.TokenOutput{ Output: &protobufs.TokenOutput_Proof{ Proof: add, }, }) } else { add := &protobufs.Coin{ Amount: reward.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)), }, }, }, } outputs.Outputs = append(outputs.Outputs, &protobufs.TokenOutput{ Output: &protobufs.TokenOutput_Coin{ Coin: add, }, }) } outputs.Outputs = append(outputs.Outputs, delete...) } else { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } } case *protobufs.TokenRequest_Merge: newCoin := &protobufs.Coin{} newTotal := new(big.Int) newIntersection := make([]byte, 1024) payload := []byte("merge") if t.Merge == nil || t.Merge.Coins == nil || t.Merge.Signature == nil { if !skipFailures { return nil, nil, nil, errors.Wrap( ErrInvalidStateTransition, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } for _, c := range t.Merge.Coins { if c.Address == nil { if !skipFailures { return nil, nil, nil, errors.Wrap( ErrInvalidStateTransition, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } payload = append(payload, c.Address...) } if t.Merge.Signature.PublicKey == nil || t.Merge.Signature.Signature == nil { if !skipFailures { return nil, nil, nil, errors.Wrap( ErrInvalidStateTransition, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } if err := t.Merge.Signature.Verify(payload); err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( err, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } addr, err := poseidon.HashBytes(t.Merge.Signature.PublicKey.KeyValue) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( err, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } pk, err := pcrypto.UnmarshalEd448PublicKey( t.Merge.Signature.PublicKey.KeyValue, ) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } peerId, err := peer.IDFromPublicKey(pk) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } altAddr, err := poseidon.HashBytes([]byte(peerId)) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } owner := &protobufs.AccountRef{} deleted := []*protobufs.TokenOutput{} for _, c := range t.Merge.Coins { coin, err := a.CoinStore.GetCoinByAddress(c.Address) if err != nil && !skipFailures { if !skipFailures { return nil, nil, nil, errors.Wrap(err, "apply transitions") } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } if !bytes.Equal( coin.Owner.GetImplicitAccount().Address, addr.FillBytes(make([]byte, 32)), ) && !bytes.Equal( coin.Owner.GetImplicitAccount().Address, altAddr.FillBytes(make([]byte, 32)), ) { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid owner"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } newTotal.Add(newTotal, new(big.Int).SetBytes(coin.Amount)) for i := range coin.Intersection { newIntersection[i] |= coin.Intersection[i] } owner = coin.Owner deleted = append(deleted, &protobufs.TokenOutput{ Output: &protobufs.TokenOutput_DeletedCoin{ DeletedCoin: c, }, }) } newCoin.Amount = newTotal.FillBytes(make([]byte, 32)) newCoin.Intersection = newIntersection newCoin.Owner = owner outputs.Outputs = append(outputs.Outputs, &protobufs.TokenOutput{ Output: &protobufs.TokenOutput_Coin{ Coin: newCoin, }, }) outputs.Outputs = append(outputs.Outputs, deleted...) finalizedTransitions.Requests = append( finalizedTransitions.Requests, transition, ) case *protobufs.TokenRequest_Split: newCoins := []*protobufs.Coin{} newAmounts := []*big.Int{} payload := []byte{} if t.Split.Signature.PublicKey == nil || t.Split.Signature.Signature == nil || t.Split.OfCoin == nil { if !skipFailures { return nil, nil, nil, errors.Wrap( ErrInvalidStateTransition, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } coin, err := a.CoinStore.GetCoinByAddress(t.Split.OfCoin.Address) if err != nil && !skipFailures { if !skipFailures { return nil, nil, nil, errors.Wrap(err, "apply transitions") } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } payload = append(payload, []byte("split")...) payload = append(payload, t.Split.OfCoin.Address...) for _, a := range t.Split.Amounts { payload = append(payload, a...) } if err := t.Split.Signature.Verify(payload); err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( err, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } addr, err := poseidon.HashBytes(t.Split.Signature.PublicKey.KeyValue) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( err, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } pk, err := pcrypto.UnmarshalEd448PublicKey( t.Split.Signature.PublicKey.KeyValue, ) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } peerId, err := peer.IDFromPublicKey(pk) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } altAddr, err := poseidon.HashBytes([]byte(peerId)) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } if !bytes.Equal( coin.Owner.GetImplicitAccount().Address, addr.FillBytes(make([]byte, 32)), ) && !bytes.Equal( coin.Owner.GetImplicitAccount().Address, altAddr.FillBytes(make([]byte, 32)), ) { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid owner"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } amounts := t.Split.Amounts total := new(big.Int) for _, amount := range amounts { amountBI := new(big.Int).SetBytes(amount) newAmounts = append(newAmounts, amountBI) total.Add(total, amountBI) newCoins = append(newCoins, &protobufs.Coin{ Amount: amountBI.FillBytes(make([]byte, 32)), Owner: coin.Owner, Intersection: coin.Intersection, }) } if new(big.Int).SetBytes(coin.Amount).Cmp(total) != 0 { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid split"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } for _, c := range newCoins { outputs.Outputs = append(outputs.Outputs, &protobufs.TokenOutput{ Output: &protobufs.TokenOutput_Coin{ Coin: c, }, }) } outputs.Outputs = append( outputs.Outputs, &protobufs.TokenOutput{ Output: &protobufs.TokenOutput_DeletedCoin{ DeletedCoin: t.Split.OfCoin, }, }, ) finalizedTransitions.Requests = append( finalizedTransitions.Requests, transition, ) case *protobufs.TokenRequest_Transfer: payload := []byte("transfer") coin, err := a.CoinStore.GetCoinByAddress(t.Transfer.OfCoin.Address) if err != nil && !skipFailures { if !skipFailures { return nil, nil, nil, errors.Wrap(err, "apply transitions") } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } payload = append(payload, t.Transfer.OfCoin.Address...) payload = append( payload, t.Transfer.ToAccount.GetImplicitAccount().Address..., ) if err := t.Transfer.Signature.Verify(payload); err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( err, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } addr, err := poseidon.HashBytes(t.Transfer.Signature.PublicKey.KeyValue) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( err, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } pk, err := pcrypto.UnmarshalEd448PublicKey( t.Transfer.Signature.PublicKey.KeyValue, ) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } peerId, err := peer.IDFromPublicKey(pk) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } altAddr, err := poseidon.HashBytes([]byte(peerId)) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } if !bytes.Equal( coin.Owner.GetImplicitAccount().Address, addr.FillBytes(make([]byte, 32)), ) && !bytes.Equal( coin.Owner.GetImplicitAccount().Address, altAddr.FillBytes(make([]byte, 32)), ) { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid owner"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } newIntersection := coin.Intersection for i, b := range p2p.GetBloomFilter( addr.FillBytes(make([]byte, 32)), 1024, 3, ) { newIntersection[i] |= b } outputs.Outputs = append( outputs.Outputs, &protobufs.TokenOutput{ Output: &protobufs.TokenOutput_Coin{ Coin: &protobufs.Coin{ Amount: coin.Amount, Intersection: newIntersection, Owner: t.Transfer.ToAccount, }, }, }, &protobufs.TokenOutput{ Output: &protobufs.TokenOutput_DeletedCoin{ DeletedCoin: t.Transfer.OfCoin, }, }, ) finalizedTransitions.Requests = append( finalizedTransitions.Requests, transition, ) case *protobufs.TokenRequest_Mint: if t.Mint.Signature == nil || t.Mint.Signature.PublicKey == nil || t.Mint.Signature.Signature == nil || t.Mint.Proofs == nil { if !skipFailures { return nil, nil, nil, errors.Wrap( ErrInvalidStateTransition, "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } payload := []byte("mint") for _, p := range t.Mint.Proofs { payload = append(payload, p...) } if err := t.Mint.Signature.Verify(payload); err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } pk, err := pcrypto.UnmarshalEd448PublicKey( t.Mint.Signature.PublicKey.KeyValue, ) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } peerId, err := peer.IDFromPublicKey(pk) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } addr, err := poseidon.HashBytes(t.Mint.Signature.PublicKey.KeyValue) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } if len(t.Mint.Proofs) == 3 && bytes.Equal( t.Mint.Proofs[0], []byte("pre-dusk"), ) && (!bytes.Equal(t.Mint.Proofs[1], make([]byte, 32)) || currentFrameNumber < 604800) { delete := []*protobufs.TokenOutput{} if !bytes.Equal(t.Mint.Proofs[1], make([]byte, 32)) { pre, err := a.CoinStore.GetPreCoinProofByAddress(t.Mint.Proofs[1]) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } delete = append(delete, &protobufs.TokenOutput{ Output: &protobufs.TokenOutput_DeletedProof{ DeletedProof: pre, }, }) } data := t.Mint.Proofs[2] if len(data) < 28 { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } increment := binary.BigEndian.Uint32(data[:4]) parallelism := binary.BigEndian.Uint32(data[8:12]) inputLen := binary.BigEndian.Uint64(data[12:20]) if len(delete) != 0 { if delete[0].GetDeletedProof().Difficulty-1 != increment { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } } if uint64(len(data[20:])) < inputLen+8 { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } input := make([]byte, inputLen) copy(input[:], data[20:20+inputLen]) outputLen := binary.BigEndian.Uint64(data[20+inputLen : 20+inputLen+8]) if uint64(len(data[20+inputLen+8:])) < outputLen { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } output := make([]byte, outputLen) copy(output[:], data[20+inputLen+8:]) dataProver := crypto.NewKZGInclusionProver(a.Logger) wesoProver := crypto.NewWesolowskiFrameProver(a.Logger) index := binary.BigEndian.Uint32(output[:4]) indexProof := output[4:520] kzgCommitment := output[520:594] kzgProof := output[594:668] ip := sha3.Sum512(indexProof) v, err := dataProver.VerifyRaw( ip[:], kzgCommitment, int(index), kzgProof, nearestApplicablePowerOfTwo(uint64(parallelism)), ) if err != nil { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } if !v { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } wp := []byte{} wp = append(wp, peerId...) wp = append(wp, input...) v = wesoProver.VerifyPreDuskChallengeProof( wp, increment, index, indexProof, ) if !v { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } pomwBasis := big.NewInt(1200000) reward := new(big.Int).Mul(pomwBasis, big.NewInt(int64(parallelism))) if len(delete) != 0 { reward.Add( reward, new(big.Int).SetBytes(delete[0].GetDeletedProof().Amount), ) } if increment != 0 { add := &protobufs.PreCoinProof{ Amount: reward.FillBytes(make([]byte, 32)), Index: index, IndexProof: indexProof, Commitment: kzgCommitment, Proof: append(append([]byte{}, kzgProof...), indexProof...), Parallelism: parallelism, Difficulty: increment, Owner: &protobufs.AccountRef{ Account: &protobufs.AccountRef_ImplicitAccount{ ImplicitAccount: &protobufs.ImplicitAccount{ ImplicitType: 0, Address: addr.FillBytes(make([]byte, 32)), }, }, }, } outputs.Outputs = append(outputs.Outputs, &protobufs.TokenOutput{ Output: &protobufs.TokenOutput_Proof{ Proof: add, }, }) } else { add := &protobufs.Coin{ Amount: reward.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)), }, }, }, } outputs.Outputs = append(outputs.Outputs, &protobufs.TokenOutput{ Output: &protobufs.TokenOutput_Coin{ Coin: add, }, }) } outputs.Outputs = append(outputs.Outputs, delete...) } else { ring := -1 addrBytes := addr.FillBytes(make([]byte, 32)) for i, t := range a.Tries { n := t.FindNearest(addrBytes) if n != nil && bytes.Equal(n.External.Key, addrBytes) { ring = i } } if ring == -1 { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } for _, p := range t.Mint.Proofs { if len(p) < 516+len(peerId)+8+32 { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } if !bytes.Equal(p[516:len(peerId)], []byte(peerId)) { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } wesoProver := crypto.NewWesolowskiFrameProver(a.Logger) frameNumber := binary.BigEndian.Uint64( p[516+len(peerId) : 516+len(peerId)+8], ) if frameNumber > currentFrameNumber { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } frames, proofs, err := a.CoinStore.GetPreCoinProofsForOwner( addr.FillBytes(make([]byte, 32)), ) if err == nil { none := true for _, f := range frames { if f == frameNumber { none = false break } } if !none { for _, pr := range proofs { if bytes.Equal(pr.Proof, p) { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } } } } if !wesoProver.VerifyChallengeProof(p[516:], a.Difficulty, p[:516]) { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } scale := len(p2p.GetOnesIndices(p[516+len(peerId)+8 : 32])) if scale == 0 { if !skipFailures { return nil, nil, nil, errors.Wrap( errors.New("invalid data"), "apply transitions", ) } failedTransitions.Requests = append( failedTransitions.Requests, transition, ) break req } ringFactor := big.NewInt(2) ringFactor.Exp(ringFactor, big.NewInt(int64(ring)), nil) storage := big.NewInt(int64(1024 / (256 / scale))) unitFactor := big.NewInt(8000000000) storage.Mul(storage, unitFactor) storage.Quo(storage, ringFactor) outputs.Outputs = append(outputs.Outputs, &protobufs.TokenOutput{ Output: &protobufs.TokenOutput_Proof{ Proof: &protobufs.PreCoinProof{ Amount: storage.FillBytes(make([]byte, 32)), Proof: p, 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)), }, }, }, }, }, }) } } finalizedTransitions.Requests = append( finalizedTransitions.Requests, transition, ) } } a.TokenOutputs = outputs return a, finalizedTransitions, failedTransitions, nil } func nearestApplicablePowerOfTwo(number uint64) uint64 { power := uint64(128) if number > 2048 { power = 65536 } else if number > 1024 { power = 2048 } else if number > 128 { power = 1024 } return power } func (a *TokenApplication) MaterializeStateFromApplication() ( *protobufs.TokenOutputs, error, ) { var err error state := &protobufs.TokenOutputs{} if err != nil { return nil, errors.Wrap(err, "materialize state from application") } return state, nil }