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 }