package global import ( "encoding/binary" "fmt" "math/big" "slices" "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" "golang.org/x/crypto/sha3" hgcrdt "source.quilibrium.com/quilibrium/monorepo/hypergraph" "source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/global/compat" "source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/token" hgstate "source.quilibrium.com/quilibrium/monorepo/node/execution/state/hypergraph" "source.quilibrium.com/quilibrium/monorepo/types/crypto" "source.quilibrium.com/quilibrium/monorepo/types/execution/intrinsics" "source.quilibrium.com/quilibrium/monorepo/types/execution/state" "source.quilibrium.com/quilibrium/monorepo/types/hypergraph" "source.quilibrium.com/quilibrium/monorepo/types/keys" "source.quilibrium.com/quilibrium/monorepo/types/schema" "source.quilibrium.com/quilibrium/monorepo/types/store" "source.quilibrium.com/quilibrium/monorepo/types/tries" qcrypto "source.quilibrium.com/quilibrium/monorepo/types/tries" ) type BLS48581SignatureWithProofOfPossession struct { // The BLS48-581 public key of the signer PublicKey []byte // The BLS48-581 signature Signature []byte // The Proof of Possession of public key signature PopSignature []byte } type SeniorityMerge struct { // The key type, used to distinguish old Ed448 keys vs BLS48-581 keys KeyType crypto.KeyType // The public key of the merge source PublicKey []byte // The signature of the public key Signature []byte // Private fields signer crypto.Signer } func NewSeniorityMerge( keyType crypto.KeyType, signer crypto.Signer, ) *SeniorityMerge { return &SeniorityMerge{ KeyType: keyType, PublicKey: signer.Public().([]byte), signer: signer, } } type ProverJoin struct { // The filters representing the join request (can be multiple) Filters [][]byte // The frame number when this request is made FrameNumber uint64 // The public key signature with proof of possession for BLS48581 PublicKeySignatureBLS48581 BLS48581SignatureWithProofOfPossession // Any optional merge targets for seniority MergeTargets []*SeniorityMerge // The optional delegated address for rewards to accrue, when omitted, uses // the prover address DelegateAddress []byte // The proof element assuring availability and commitment of the workers Proof []byte // Private fields keyManager keys.KeyManager hypergraph hypergraph.Hypergraph rdfMultiprover *schema.RDFMultiprover frameProver crypto.FrameProver frameStore store.ClockStore } func NewProverJoin( filters [][]byte, frameNumber uint64, mergeTargets []*SeniorityMerge, delegateAddress []byte, keyManager keys.KeyManager, hypergraph hypergraph.Hypergraph, rdfMultiprover *schema.RDFMultiprover, frameProver crypto.FrameProver, frameStore store.ClockStore, ) (*ProverJoin, error) { return &ProverJoin{ Filters: filters, // buildutils:allow-slice-alias slice is static FrameNumber: frameNumber, MergeTargets: mergeTargets, // buildutils:allow-slice-alias slice is static DelegateAddress: delegateAddress, // buildutils:allow-slice-alias slice is static keyManager: keyManager, hypergraph: hypergraph, rdfMultiprover: rdfMultiprover, frameProver: frameProver, frameStore: frameStore, }, nil } // GetCost implements intrinsics.IntrinsicOperation. func (p *ProverJoin) GetCost() (*big.Int, error) { return big.NewInt(0), nil } // Materialize implements intrinsics.IntrinsicOperation. func (p *ProverJoin) Materialize( frameNumber uint64, state state.State, ) (state.State, error) { hg := state.(*hgstate.HypergraphState) publicKey := p.PublicKeySignatureBLS48581.PublicKey proverAddressBI, err := poseidon.HashBytes(publicKey) if err != nil || proverAddressBI == nil { return nil, errors.Wrap(errors.New("invalid address"), "materialize") } proverAddress := proverAddressBI.FillBytes(make([]byte, 32)) // Full address for the prover entry proverFullAddress := [64]byte{} copy(proverFullAddress[:32], intrinsics.GLOBAL_INTRINSIC_ADDRESS[:]) copy(proverFullAddress[32:], proverAddress) // Check if prover already exists vertex, err := hg.Get( proverFullAddress[:32], proverFullAddress[32:], hgstate.VertexAddsDiscriminator, ) proverExists := err == nil var proverTree *tries.VectorCommitmentTree if proverExists { var ok bool proverTree, ok = vertex.(*tries.VectorCommitmentTree) if !ok || proverTree == nil { return nil, errors.Wrap( errors.New("invalid object returned for vertex"), "materialize", ) } } if !proverExists { // Create new prover entry proverTree = &qcrypto.VectorCommitmentTree{} // Store the public key err = p.rdfMultiprover.Set( GLOBAL_RDF_SCHEMA, intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], "prover:Prover", "PublicKey", publicKey, proverTree, ) if err != nil { return nil, errors.Wrap(err, "materialize") } // Store status (0 = joining since we have allocations joining) err = p.rdfMultiprover.Set( GLOBAL_RDF_SCHEMA, intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], "prover:Prover", "Status", []byte{0}, proverTree, ) if err != nil { return nil, errors.Wrap(err, "materialize") } // Store available storage (initially 0) availableStorageBytes := make([]byte, 8) binary.BigEndian.PutUint64(availableStorageBytes, 0) err = p.rdfMultiprover.Set( GLOBAL_RDF_SCHEMA, intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], "prover:Prover", "AvailableStorage", availableStorageBytes, proverTree, ) if err != nil { return nil, errors.Wrap(err, "materialize") } // Calculate seniority from MergeTargets var seniority uint64 = 0 if len(p.MergeTargets) > 0 { // Convert Ed448 public keys to peer IDs var peerIds []string for _, target := range p.MergeTargets { if target.KeyType == crypto.KeyTypeEd448 { pk, err := pcrypto.UnmarshalEd448PublicKey(target.PublicKey) if err != nil { return nil, errors.Wrap(err, "materialize") } peerId, err := peer.IDFromPublicKey(pk) if err != nil { return nil, errors.Wrap(err, "materialize") } peerIds = append(peerIds, peerId.String()) } } // Get aggregated seniority if len(peerIds) > 0 { seniorityBig := compat.GetAggregatedSeniority(peerIds) if seniorityBig.IsUint64() { seniority = seniorityBig.Uint64() } } } // Store seniority seniorityBytes := make([]byte, 8) binary.BigEndian.PutUint64(seniorityBytes, seniority) err = p.rdfMultiprover.Set( GLOBAL_RDF_SCHEMA, intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], "prover:Prover", "Seniority", seniorityBytes, proverTree, ) if err != nil { return nil, errors.Wrap(err, "materialize") } // Create prover vertex proverVertex := hg.NewVertexAddMaterializedState( intrinsics.GLOBAL_INTRINSIC_ADDRESS, [32]byte(proverAddress), frameNumber, nil, proverTree, ) err = hg.Set( intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], proverAddress, hgstate.VertexAddsDiscriminator, frameNumber, proverVertex, ) if err != nil { return nil, errors.Wrap(err, "materialize") } // Create ProverReward entry in QUIL token address with zero balance rewardTree := &qcrypto.VectorCommitmentTree{} delegateAddress := proverAddress if len(p.DelegateAddress) == 32 { delegateAddress = p.DelegateAddress } derivedRewardAddress, err := poseidon.HashBytes( slices.Concat(token.QUIL_TOKEN_ADDRESS[:], proverAddress), ) if err != nil { return nil, errors.Wrap(err, "materialize") } err = p.rdfMultiprover.Set( GLOBAL_RDF_SCHEMA, intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], "reward:ProverReward", "DelegateAddress", delegateAddress, rewardTree, ) if err != nil { return nil, errors.Wrap(err, "materialize") } // Set zero balance zeroBalance := make([]byte, 32) err = p.rdfMultiprover.Set( GLOBAL_RDF_SCHEMA, intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], "reward:ProverReward", "Balance", zeroBalance, rewardTree, ) if err != nil { return nil, errors.Wrap(err, "materialize") } // Create reward vertex in QUIL token address rewardVertex := hg.NewVertexAddMaterializedState( [32]byte(intrinsics.GLOBAL_INTRINSIC_ADDRESS[:]), [32]byte(derivedRewardAddress.FillBytes(make([]byte, 32))), frameNumber, nil, rewardTree, ) err = hg.Set( intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], derivedRewardAddress.FillBytes(make([]byte, 32)), hgstate.VertexAddsDiscriminator, frameNumber, rewardVertex, ) if err != nil { return nil, errors.Wrap(err, "materialize") } } // Create hyperedge for this prover hyperedgeAddress := [32]byte(proverAddress) hyperedge := hgcrdt.NewHyperedge( intrinsics.GLOBAL_INTRINSIC_ADDRESS, hyperedgeAddress, ) // Get existing hyperedge if it exists existingHyperedge, err := hg.Get( intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], hyperedgeAddress[:], hgstate.HyperedgeAddsDiscriminator, ) if err == nil && existingHyperedge != nil { // Use existing hyperedge var ok bool hyperedge, ok = existingHyperedge.(hypergraph.Hyperedge) if !ok { return nil, errors.Wrap( errors.New("invalid object returned for hyperedge"), "materialize", ) } } // Create ProverAllocation entries for each filter for _, filter := range p.Filters { // Calculate allocation address: poseidon.Hash(publicKey || filter) allocationAddressBI, err := poseidon.HashBytes( slices.Concat([]byte("PROVER_ALLOCATION"), publicKey, filter), ) if err != nil { return nil, errors.Wrap(err, "materialize") } allocationAddress := allocationAddressBI.FillBytes(make([]byte, 32)) // Create allocation tree allocationTree := &qcrypto.VectorCommitmentTree{} // Store prover reference err = p.rdfMultiprover.Set( GLOBAL_RDF_SCHEMA, intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], "allocation:ProverAllocation", "Prover", proverAddress, allocationTree, ) if err != nil { return nil, errors.Wrap(err, "materialize") } // Store allocation status (0 = joining) err = p.rdfMultiprover.Set( GLOBAL_RDF_SCHEMA, intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], "allocation:ProverAllocation", "Status", []byte{0}, allocationTree, ) if err != nil { return nil, errors.Wrap(err, "materialize") } // Store confirmation filter err = p.rdfMultiprover.Set( GLOBAL_RDF_SCHEMA, intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], "allocation:ProverAllocation", "ConfirmationFilter", filter, allocationTree, ) if err != nil { return nil, errors.Wrap(err, "materialize") } // Store join frame number frameNumberBytes := make([]byte, 8) binary.BigEndian.PutUint64(frameNumberBytes, p.FrameNumber) err = p.rdfMultiprover.Set( GLOBAL_RDF_SCHEMA, intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], "allocation:ProverAllocation", "JoinFrameNumber", frameNumberBytes, allocationTree, ) if err != nil { return nil, errors.Wrap(err, "materialize") } // Get a copy of the original allocation tree for change tracking var prior *tries.VectorCommitmentTree originalAllocationVertex, err := hg.Get( intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], allocationAddress, hgstate.VertexAddsDiscriminator, ) if err == nil && originalAllocationVertex != nil { prior = originalAllocationVertex.(*tries.VectorCommitmentTree) } // Create allocation vertex allocationVertex := hg.NewVertexAddMaterializedState( intrinsics.GLOBAL_INTRINSIC_ADDRESS, [32]byte(allocationAddress), frameNumber, prior, allocationTree, ) err = hg.Set( intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], allocationAddress, hgstate.VertexAddsDiscriminator, frameNumber, allocationVertex, ) if err != nil { return nil, errors.Wrap(err, "materialize") } // Add allocation vertex to hyperedge hyperedge.AddExtrinsic(allocationVertex.GetVertex()) } for _, mt := range p.MergeTargets { spentMergeBI, err := poseidon.HashBytes(slices.Concat( []byte("PROVER_JOIN_MERGE"), mt.PublicKey, )) if err != nil { return nil, errors.Wrap(err, "materialize") } // confirm this has not already been used spentAddress := [64]byte{} copy(spentAddress[:32], intrinsics.GLOBAL_INTRINSIC_ADDRESS[:]) copy(spentAddress[32:], spentMergeBI.FillBytes(make([]byte, 32))) spentMergeVertex := hg.NewVertexAddMaterializedState( intrinsics.GLOBAL_INTRINSIC_ADDRESS, [32]byte(spentMergeBI.FillBytes(make([]byte, 32))), frameNumber, nil, &tries.VectorCommitmentTree{}, ) err = hg.Set( intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], spentMergeBI.FillBytes(make([]byte, 32)), hgstate.VertexAddsDiscriminator, frameNumber, spentMergeVertex, ) if err != nil { return nil, errors.Wrap(err, "materialize") } } var priorHyperedge *tries.VectorCommitmentTree previousHyperedge, err := hg.Get( intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], hyperedgeAddress[:], hgstate.HyperedgeAddsDiscriminator, ) if err == nil && previousHyperedge != nil { // Use existing hyperedge var ok bool prior, ok := previousHyperedge.(hypergraph.Hyperedge) if !ok { return nil, errors.Wrap( errors.New("invalid object returned for hyperedge"), "materialize", ) } priorHyperedge = prior.GetExtrinsicTree() } // Update hyperedge hyperedgeState := hg.NewHyperedgeAddMaterializedState( frameNumber, priorHyperedge, hyperedge, ) err = hg.Set( intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], hyperedgeAddress[:], hgstate.HyperedgeAddsDiscriminator, frameNumber, hyperedgeState, ) if err != nil { return nil, errors.Wrap(err, "materialize") } return state, nil } // Prove implements intrinsics.IntrinsicOperation. func (p *ProverJoin) Prove(frameNumber uint64) error { // Get the q-prover-key prover, err := p.keyManager.GetSigningKey("q-prover-key") if err != nil { return errors.Wrap(err, "prove") } for _, mt := range p.MergeTargets { if mt.signer != nil { mt.Signature, err = mt.signer.SignWithDomain( p.PublicKeySignatureBLS48581.PublicKey, []byte("PROVER_JOIN_MERGE"), ) if err != nil { return errors.Wrap(err, "prove") } } } joinClone := p.ToProtobuf() joinClone.PublicKeySignatureBls48581 = nil joinMessage, err := joinClone.ToCanonicalBytes() if err != nil { return errors.Wrap(err, "prove") } // Create the domain for the first signature // Poseidon hash of GLOBAL_INTRINSIC_ADDRESS concatenated with "PROVER_JOIN" joinDomainPreimage := slices.Concat( intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], []byte("PROVER_JOIN"), ) joinDomain, err := poseidon.HashBytes(joinDomainPreimage) if err != nil { return errors.Wrap(err, "prove") } // Create first signature over the join message with the join domain signature, err := prover.SignWithDomain( joinMessage, joinDomain.FillBytes(make([]byte, 32)), ) if err != nil { return errors.Wrap(err, "prove") } // Create the domain for the proof of possession popDomain := []byte("BLS48_POP_SK") // Create the proof of possession signature over the public key with the POP // domain popSignature, err := prover.SignWithDomain( prover.Public().([]byte), popDomain, ) if err != nil { return errors.Wrap(err, "prove") } // Create the BLS48581SignatureWithProofOfPossession p.PublicKeySignatureBLS48581 = BLS48581SignatureWithProofOfPossession{ Signature: signature, PublicKey: prover.Public().([]byte), PopSignature: popSignature, } return nil } func (p *ProverJoin) GetReadAddresses(frameNumber uint64) ([][]byte, error) { return nil, nil } func (p *ProverJoin) GetWriteAddresses(frameNumber uint64) ([][]byte, error) { publicKey := p.PublicKeySignatureBLS48581.PublicKey proverAddressBI, err := poseidon.HashBytes(publicKey) if err != nil || proverAddressBI == nil { return nil, errors.Wrap( errors.New("invalid address"), "get write addresses", ) } proverAddress := proverAddressBI.FillBytes(make([]byte, 32)) proverFullAddress := [64]byte{} copy(proverFullAddress[:32], intrinsics.GLOBAL_INTRINSIC_ADDRESS[:]) copy(proverFullAddress[32:], proverAddress) addresses := map[string]struct{}{} addresses[string(proverFullAddress[:])] = struct{}{} derivedRewardAddress, err := poseidon.HashBytes( slices.Concat(token.QUIL_TOKEN_ADDRESS[:], proverAddress), ) if err != nil { return nil, errors.Wrap(err, "get write addresses") } addresses[string(slices.Concat( intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], derivedRewardAddress.FillBytes(make([]byte, 32)), ))] = struct{}{} for _, filter := range p.Filters { allocationAddressBI, err := poseidon.HashBytes( slices.Concat([]byte("PROVER_ALLOCATION"), publicKey, filter), ) if err != nil { return nil, errors.Wrap(err, "get write addresses") } allocationAddress := allocationAddressBI.FillBytes(make([]byte, 32)) addresses[string(slices.Concat( intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], allocationAddress, ))] = struct{}{} } for _, mt := range p.MergeTargets { spentMergeBI, err := poseidon.HashBytes(slices.Concat( []byte("PROVER_JOIN_MERGE"), mt.PublicKey, )) if err != nil { return nil, errors.Wrap(err, "get write addresses") } addresses[string(slices.Concat( intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], spentMergeBI.FillBytes(make([]byte, 32)), ))] = struct{}{} } result := [][]byte{} for key := range addresses { result = append(result, []byte(key)) } return result, nil } // Verify implements intrinsics.IntrinsicOperation. func (p *ProverJoin) Verify(frameNumber uint64) (valid bool, err error) { defer func() { if r := recover(); r != nil { valid = false err = fmt.Errorf("panic from: %v", r) } }() // First check if prover can join (not in tree or in left state) addressBI, err := poseidon.HashBytes(p.PublicKeySignatureBLS48581.PublicKey) if err != nil { return false, errors.Wrap(err, "verify") } address := addressBI.FillBytes(make([]byte, 32)) for _, filter := range p.Filters { if len(filter) < 32 { return false, errors.Wrap(errors.New("invalid filter size"), "verify") } } if len(p.Proof)%516 != 0 || len(p.Proof)/516 != len(p.Filters) { return false, errors.Wrap(errors.New("proof size mismatch"), "verify") } // Disallow too old of a request if p.FrameNumber+10 < frameNumber { return false, errors.Wrap(errors.New("outdated request"), "verify") } frame, err := p.frameStore.GetGlobalClockFrame(p.FrameNumber) if err != nil { frames, err := p.frameStore.RangeGlobalClockFrameCandidates( p.FrameNumber, p.FrameNumber, ) if err != nil { return false, errors.Wrap(errors.Wrap( err, fmt.Sprintf("frame number: %d", p.FrameNumber), ), "verify") } if !frames.First() || !frames.Valid() { return false, errors.Wrap(errors.Wrap( errors.New("not found"), fmt.Sprintf("frame number: %d", p.FrameNumber), ), "verify") } frame, err = frames.Value() frames.Close() if err != nil { return false, errors.Wrap(errors.Wrap( err, fmt.Sprintf("frame number: %d", p.FrameNumber), ), "verify") } } // Prepare challenge for verification challenge := sha3.Sum256(frame.Header.Output) ids := [][]byte{} for idx, filter := range p.Filters { ids = append(ids, slices.Concat( address, filter, binary.BigEndian.AppendUint32(nil, uint32(idx)), )) } solutions := [][516]byte{} for i := range p.Filters { solutions = append(solutions, [516]byte(p.Proof[i*516:(i+1)*516])) } valid, err = p.frameProver.VerifyMultiProof( challenge, frame.Header.Difficulty, ids, solutions, ) if err != nil || !valid { return false, errors.Wrap(errors.New("invalid multi proof"), "verify") } for _, mt := range p.MergeTargets { valid, err := p.keyManager.ValidateSignature( mt.KeyType, mt.PublicKey, p.PublicKeySignatureBLS48581.PublicKey, mt.Signature, []byte("PROVER_JOIN_MERGE"), ) if err != nil || !valid { return false, errors.Wrap(err, "verify") } spentMergeBI, err := poseidon.HashBytes(slices.Concat( []byte("PROVER_JOIN_MERGE"), mt.PublicKey, )) if err != nil { return false, errors.Wrap(err, "verify") } // confirm this has not already been used spentAddress := [64]byte{} copy(spentAddress[:32], intrinsics.GLOBAL_INTRINSIC_ADDRESS[:]) copy(spentAddress[32:], spentMergeBI.FillBytes(make([]byte, 32))) v, err := p.hypergraph.GetVertex(spentAddress) if err == nil && v != nil { return false, errors.Wrap( errors.New("merge target already used"), "verify", ) } } // Get the existing prover vertex data proverAddress := [64]byte{} copy(proverAddress[:32], intrinsics.GLOBAL_INTRINSIC_ADDRESS[:]) copy(proverAddress[32:], address) proverVertexData, err := p.hypergraph.GetVertexData(proverAddress) if err == nil && proverVertexData != nil { tree := proverVertexData kickedFrame, err := p.rdfMultiprover.Get( GLOBAL_RDF_SCHEMA, "allocation:ProverAllocation", "KickFrameNumber", tree, ) if err == nil && len(kickedFrame) == 8 { kickedFrame := binary.BigEndian.Uint64(kickedFrame) if kickedFrame != 0 { // Prover has been kicked for malicious behavior return false, errors.Wrap( errors.New("prover has been previously kicked"), "verify", ) } } } for _, f := range p.Filters { allocationAddressBI, err := poseidon.HashBytes( slices.Concat( []byte("PROVER_ALLOCATION"), p.PublicKeySignatureBLS48581.PublicKey, f, ), ) if err != nil { return false, errors.Wrap(err, "verify") } allocationAddress := allocationAddressBI.FillBytes(make([]byte, 32)) // Create composite address: GLOBAL_INTRINSIC_ADDRESS + prover address fullAddress := [64]byte{} copy(fullAddress[:32], intrinsics.GLOBAL_INTRINSIC_ADDRESS[:]) copy(fullAddress[32:], allocationAddress) // Get the existing prover allocation vertex data vertexData, err := p.hypergraph.GetVertexData(fullAddress) if err == nil && vertexData != nil { // Prover exists, check if they're in left state (4) tree := vertexData // Check if prover is in left state (4) statusData, err := p.rdfMultiprover.Get( GLOBAL_RDF_SCHEMA, "allocation:ProverAllocation", "Status", tree, ) if err == nil && len(statusData) > 0 { status := statusData[0] if status != 4 { // Prover is in some other state - cannot join return false, errors.Wrap( errors.New("prover already exists in non-left state"), "verify", ) } } } } // If we get here, either prover doesn't exist or is in left state - both are // valid joinClone := p.ToProtobuf() joinClone.PublicKeySignatureBls48581 = nil joinMessage, err := joinClone.ToCanonicalBytes() if err != nil { return false, errors.Wrap(err, "verify") } // Create the domain for the first signature // Poseidon hash of GLOBAL_INTRINSIC_ADDRESS concatenated with "PROVER_JOIN" joinDomainPreimage := slices.Concat( intrinsics.GLOBAL_INTRINSIC_ADDRESS[:], []byte("PROVER_JOIN"), ) joinDomain, err := poseidon.HashBytes(joinDomainPreimage) if err != nil { return false, errors.Wrap(err, "verify") } // Create the domain for the proof of possession popDomain := []byte("BLS48_POP_SK") // Verify the signature valid, err = p.keyManager.ValidateSignature( crypto.KeyTypeBLS48581G1, p.PublicKeySignatureBLS48581.PublicKey, joinMessage, p.PublicKeySignatureBLS48581.Signature, joinDomain.FillBytes(make([]byte, 32)), ) if err != nil || !valid { return false, errors.Wrap(errors.New("invalid signature"), "verify") } // Verify the proof of possession valid, err = p.keyManager.ValidateSignature( crypto.KeyTypeBLS48581G1, p.PublicKeySignatureBLS48581.PublicKey, p.PublicKeySignatureBLS48581.PublicKey, p.PublicKeySignatureBLS48581.PopSignature, popDomain, ) if err != nil || !valid { return false, errors.Wrap(errors.New("invalid pop signature"), "verify") } // Verify any merge signatures for _, mt := range p.MergeTargets { valid, err := p.keyManager.ValidateSignature( mt.KeyType, mt.PublicKey, p.PublicKeySignatureBLS48581.PublicKey, mt.Signature, []byte("PROVER_JOIN_MERGE"), ) if err != nil || !valid { return false, errors.Wrap(errors.New("invalid merge signature"), "verify") } } return true, nil } var _ intrinsics.IntrinsicOperation = (*ProverJoin)(nil)