github.com/ConsenSys/Quorum@v20.10.0+incompatible/core/state_transition_pmh.go (about)

     1  package core
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/ethereum/go-ethereum/common"
     7  	"github.com/ethereum/go-ethereum/core/state"
     8  	"github.com/ethereum/go-ethereum/core/types"
     9  	"github.com/ethereum/go-ethereum/log"
    10  	"github.com/ethereum/go-ethereum/private/engine"
    11  )
    12  
    13  type pmcStateTransitionAPI interface {
    14  	SetTxPrivacyMetadata(pm *types.PrivacyMetadata)
    15  	IsPrivacyEnhancementsEnabled() bool
    16  	RevertToSnapshot(int)
    17  	GetStatePrivacyMetadata(addr common.Address) (*state.PrivacyMetadata, error)
    18  	CalculateMerkleRoot() (common.Hash, error)
    19  	AffectedContracts() []common.Address
    20  }
    21  
    22  func newPMH(st pmcStateTransitionAPI) *privateMessageHandler {
    23  	return &privateMessageHandler{stAPI: st}
    24  }
    25  
    26  type privateMessageHandler struct {
    27  	stAPI pmcStateTransitionAPI
    28  
    29  	hasPrivatePayload bool
    30  
    31  	snapshot                int
    32  	receivedPrivacyMetadata *engine.ExtraMetadata
    33  	eph                     common.EncryptedPayloadHash
    34  }
    35  
    36  func (pmh *privateMessageHandler) mustVerify() bool {
    37  	return pmh.hasPrivatePayload && pmh.receivedPrivacyMetadata != nil && pmh.stAPI.IsPrivacyEnhancementsEnabled()
    38  }
    39  
    40  // checks the privacy metadata in the state transition context
    41  // returns false if TransitionDb needs to exit early
    42  // true otherwise
    43  func (pmh *privateMessageHandler) prepare() (bool, error) {
    44  	if pmh.receivedPrivacyMetadata != nil {
    45  		if !pmh.stAPI.IsPrivacyEnhancementsEnabled() && pmh.receivedPrivacyMetadata.PrivacyFlag.IsNotStandardPrivate() {
    46  			// This situation is only possible if the current node has been upgraded (both quorum and tessera) yet the
    47  			// node did not apply the privacyEnhancementsBlock configuration (with a network agreed block height).
    48  			// Since this would be considered node misconfiguration the behavior should be changed to return an error
    49  			// which would then cause the node not to apply the block (and potentially get stuck and not be able to
    50  			// continue to apply new blocks). The resolution should then be to revert to an appropriate block height and
    51  			// run geth init with the network agreed privacyEnhancementsBlock.
    52  			// The prepare method signature has been changed to allow returning the relevant error.
    53  			return false, fmt.Errorf("Privacy enhanced transaction received while privacy enhancements are disabled."+
    54  				" Please check your node configuration. EPH=%s", pmh.eph.ToBase64())
    55  		}
    56  
    57  		if pmh.receivedPrivacyMetadata.PrivacyFlag == engine.PrivacyFlagStateValidation && common.EmptyHash(pmh.receivedPrivacyMetadata.ACMerkleRoot) {
    58  			log.Error("Privacy metadata has empty MR for stateValidation flag")
    59  			return false, nil
    60  		}
    61  		privMetadata := types.NewTxPrivacyMetadata(pmh.receivedPrivacyMetadata.PrivacyFlag)
    62  		pmh.stAPI.SetTxPrivacyMetadata(privMetadata)
    63  	}
    64  	return true, nil
    65  }
    66  
    67  //If the list of affected CA Transactions by the time evm executes is different from the list of affected contract transactions returned from Tessera
    68  //an Error should be thrown and the state should not be updated
    69  //This validation is to prevent cases where the list of affected contract will have changed by the time the evm actually executes transaction
    70  // failed = true will make sure receipt is marked as "failure"
    71  // return error will crash the node and only use when that's the case
    72  func (pmh *privateMessageHandler) verify(vmerr error) (bool, error) {
    73  	// convenient function to return error. It has the same signature as the main function
    74  	returnErrorFunc := func(anError error, logMsg string, ctx ...interface{}) (exitEarly bool, err error) {
    75  		if logMsg != "" {
    76  			log.Debug(logMsg, ctx...)
    77  		}
    78  		pmh.stAPI.RevertToSnapshot(pmh.snapshot)
    79  		exitEarly = true
    80  		if anError != nil {
    81  			err = fmt.Errorf("vmerr=%s, err=%s", vmerr, anError)
    82  		}
    83  		return
    84  	}
    85  	actualACAddresses := pmh.stAPI.AffectedContracts()
    86  	log.Trace("Verify hashes of affected contracts", "expectedHashes", pmh.receivedPrivacyMetadata.ACHashes, "numberOfAffectedAddresses", len(actualACAddresses))
    87  	privacyFlag := pmh.receivedPrivacyMetadata.PrivacyFlag
    88  	for _, addr := range actualACAddresses {
    89  		// GetStatePrivacyMetadata is invoked on the privateState (as the tx is private) and it returns:
    90  		// 1. public contacts: privacyMetadata = nil, err = nil
    91  		// 2. private contracts of type:
    92  		// 2.1. StandardPrivate:     privacyMetadata = nil, err = "The provided contract does not have privacy metadata"
    93  		// 2.2. PartyProtection/PSV: privacyMetadata = <data>, err = nil
    94  		actualPrivacyMetadata, err := pmh.stAPI.GetStatePrivacyMetadata(addr)
    95  		//when privacyMetadata should have been recovered but wasnt (includes non-party)
    96  		//non party will only be caught here if sender provides privacyFlag
    97  		if err != nil && privacyFlag.IsNotStandardPrivate() {
    98  			return returnErrorFunc(nil, "Unable to find PrivacyMetadata for affected contract", "err", err, "addr", addr.Hex())
    99  		}
   100  		log.Trace("Privacy metadata", "affectedAddress", addr.Hex(), "metadata", actualPrivacyMetadata)
   101  		// both public and standard private contracts will be nil and can be skipped in acoth check
   102  		// public contracts - evm error for write, no error for reads
   103  		// standard private - only error if privacyFlag sent with tx or if no flag sent but other affecteds have privacyFlag
   104  		if actualPrivacyMetadata == nil {
   105  			continue
   106  		}
   107  		// Check that the affected contracts privacy flag matches the transaction privacy flag.
   108  		// I know that this is also checked by tessera, but it only checks for non standard private transactions.
   109  		if actualPrivacyMetadata.PrivacyFlag != pmh.receivedPrivacyMetadata.PrivacyFlag {
   110  			return returnErrorFunc(nil, "Mismatched privacy flags",
   111  				"affectedContract.Address", addr.Hex(),
   112  				"affectedContract.PrivacyFlag", actualPrivacyMetadata.PrivacyFlag,
   113  				"received.PrivacyFlag", pmh.receivedPrivacyMetadata.PrivacyFlag)
   114  		}
   115  		// acoth check - case where node isn't privy to one of actual affecteds
   116  		if pmh.receivedPrivacyMetadata.ACHashes.NotExist(actualPrivacyMetadata.CreationTxHash) {
   117  			return returnErrorFunc(nil, "Participation check failed",
   118  				"affectedContractAddress", addr.Hex(),
   119  				"missingCreationTxHash", actualPrivacyMetadata.CreationTxHash.Hex())
   120  		}
   121  	}
   122  
   123  	// check the psv merkle root comparison - for both creation and msg calls
   124  	if !common.EmptyHash(pmh.receivedPrivacyMetadata.ACMerkleRoot) {
   125  		log.Trace("Verify merkle root", "merkleRoot", pmh.receivedPrivacyMetadata.ACMerkleRoot)
   126  		actualACMerkleRoot, err := pmh.stAPI.CalculateMerkleRoot()
   127  		if err != nil {
   128  			return returnErrorFunc(err, "")
   129  		}
   130  		if actualACMerkleRoot != pmh.receivedPrivacyMetadata.ACMerkleRoot {
   131  			return returnErrorFunc(nil, "Merkle Root check failed", "actual", actualACMerkleRoot,
   132  				"expect", pmh.receivedPrivacyMetadata.ACMerkleRoot)
   133  		}
   134  	}
   135  	return false, nil
   136  }