@@ -63,30 +63,14 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
6363 // Using CodeAddr is favour over contract.Address()
6464 // During DelegateCall() CodeAddr is the address of the delegated account
6565 address := * contract .CodeAddr
66- if _ , ok := evm .affectedContracts [address ]; ! ok {
67- evm .affectedContracts [address ] = newAffectedType (MessageCall , ModeUnknown )
68- // for multitenancy, we cache the expectation of AffectedMode to be used later
69- // when an opcode is executed.
70- if evm .SupportsMultitenancy && evm .AuthorizeMessageCallFunc != nil {
71- // when contract code is empty, there's no execution hence the
72- // multitenancy check will not happen in captureOperationMode().
73- // This additional check to ensure we capture this case
74- if len (contract .Code ) == 0 {
75- return nil , multitenancy .ErrNotAuthorized
76- }
77- authorizedRead , authorizedWrite , err := evm .AuthorizeMessageCallFunc (address )
78- if err != nil {
79- return nil , err
80- }
81- expectedMode := evm .affectedContracts [address ].mode
82- if authorizedRead {
83- expectedMode = expectedMode | ModeRead
84- }
85- if authorizedWrite {
86- expectedMode = expectedMode | ModeWrite
87- }
88- evm .affectedContracts [address ].mode = expectedMode
89- }
66+ // during simulation/eth_call, when contract code is empty, there's no execution hence the
67+ // multitenancy check will not happen in captureOperationMode().
68+ // This additional check to ensure we capture this case
69+ if evm .SupportsMultitenancy && evm .AuthorizeMessageCallFunc != nil && len (contract .Code ) == 0 {
70+ return nil , multitenancy .ErrNotAuthorized
71+ }
72+ if err := evm .captureAffectedContract (address , ModeUnknown ); err != nil {
73+ return nil , err
9074 }
9175 // When delegatecall, need to capture the operation mode in the context of contract.Address()
9276 // the affected contract is required read only mode
@@ -152,7 +136,7 @@ type Context struct {
152136 // It's only injected during simulation
153137 AuthorizeCreateFunc multitenancy.AuthorizeCreateFunc
154138 // AuthorizeMessageCallFunc performs tenancy authorization check for message call to a contract.
155- // It's only injected during simulation
139+ // It's only injected during simulation/eth_call
156140 AuthorizeMessageCallFunc multitenancy.AuthorizeMessageCallFunc
157141}
158142
@@ -229,6 +213,10 @@ type AffectedType struct {
229213 mode AffectedMode
230214}
231215
216+ func (t AffectedType ) String () string {
217+ return fmt .Sprintf ("reason=%d,mode=%d" , t .reason , t .mode )
218+ }
219+
232220// AffectedReason defines a type of operation that was applied to a contract.
233221type AffectedReason byte
234222
@@ -248,6 +236,9 @@ const (
248236 ModeRead AffectedMode = iota
249237 // ModeWrite indicates that state has been modified as the result of contract code execution
250238 ModeWrite = ModeRead << 1
239+ // ModeUpdated indicates that the affected mode has been setup for multitenancy check.
240+ // This is mainly used during simulation and eth_call
241+ ModeUpdated = ModeRead << 7
251242)
252243
253244func ModeOf (isWrite bool ) AffectedMode {
@@ -257,8 +248,27 @@ func ModeOf(isWrite bool) AffectedMode {
257248 return ModeRead
258249}
259250
260- func (mode AffectedMode ) IsAuthorized (actualMode AffectedMode ) bool {
261- return mode & actualMode != actualMode
251+ func (mode AffectedMode ) IsNotAuthorized (actualMode AffectedMode ) bool {
252+ return mode .Has (ModeUpdated ) && ! mode .Has (actualMode )
253+ }
254+
255+ func (mode AffectedMode ) Update (authorizedRead bool , authorizedWrite bool ) AffectedMode {
256+ newMode := mode | ModeUpdated
257+ if authorizedRead {
258+ newMode = newMode | ModeRead
259+ }
260+ if authorizedWrite {
261+ newMode = newMode | ModeWrite
262+ }
263+ return newMode
264+ }
265+
266+ func (mode AffectedMode ) Has (modes ... AffectedMode ) bool {
267+ expectedMode := ModeUnknown
268+ for _ , m := range modes {
269+ expectedMode = expectedMode | m
270+ }
271+ return mode & expectedMode == expectedMode
262272}
263273
264274// NewEVM returns a new EVM. The returned EVM is not thread safe and should
@@ -705,6 +715,7 @@ func getDualState(evm *EVM, addr common.Address) StateDB {
705715
706716 if evm .PrivateState ().Exist (addr ) {
707717 state = evm .PrivateState ()
718+ evm .captureAffectedContract (addr , ModeUnknown )
708719 } else if evm .PublicState ().Exist (addr ) {
709720 state = evm .PublicState ()
710721 }
@@ -818,13 +829,13 @@ func (evm *EVM) peekAddress() common.Address {
818829func (evm * EVM ) captureOperationMode (isWriteOperation bool ) error {
819830 currentAddress := evm .peekAddress ()
820831 if (currentAddress == common.Address {}) {
821- return nil
832+ return evm . lastError
822833 }
823834 actualMode := ModeOf (isWriteOperation )
824835 if t , ok := evm .affectedContracts [currentAddress ]; ok {
825836 // perform multitenancy check
826837 if evm .enforceMultitenancyCheck () {
827- if t .mode .IsAuthorized (actualMode ) {
838+ if t .mode .IsNotAuthorized (actualMode ) {
828839 log .Trace ("Multitenancy check for captureOperationMode()" , "address" , currentAddress .Hex (), "actual" , actualMode , "expect" , t .mode )
829840 evm .lastError = multitenancy .ErrNotAuthorized
830841 }
@@ -838,7 +849,43 @@ func (evm *EVM) captureOperationMode(isWriteOperation bool) error {
838849 return nil
839850}
840851
841- // enforceMultitenancyCheck returns true if EVM is enforced to do multitenancy check, false otherwise
852+ // Quorum
853+ //
854+ // captureAffectedContract stores the contract address to the affectedContract list if not yet there.
855+ // The affected mode is also updated if required.
856+ // Default affected reason is MessageCall.
857+ // In simulation/eth_call for multitenancy, it sets the expectation of AffectedMode
858+ // to be verified later when an opcode is executed.
859+ func (evm * EVM ) captureAffectedContract (address common.Address , mode AffectedMode ) error {
860+ affectedType , found := evm .affectedContracts [address ]
861+ if ! found {
862+ affectedType = newAffectedType (MessageCall , mode )
863+ evm .affectedContracts [address ] = affectedType
864+ }
865+ if affectedType .mode != ModeUnknown {
866+ return nil
867+ }
868+ if evm .SupportsMultitenancy && evm .AuthorizeMessageCallFunc != nil {
869+ authorizedRead , authorizedWrite , err := evm .AuthorizeMessageCallFunc (address )
870+ if err != nil {
871+ return err
872+ }
873+ // if we don't authorize either read/write, it's unauthorized access
874+ // and we need to inform EVM
875+ if ! authorizedRead && ! authorizedWrite {
876+ evm .lastError = multitenancy .ErrNotAuthorized
877+ log .Debug ("Affected contract not authorized" , "address" , address .Hex (), "read" , authorizedRead , "write" , authorizedWrite )
878+ return multitenancy .ErrNotAuthorized
879+ }
880+ oldMode := affectedType .mode
881+ affectedType .mode = affectedType .mode .Update (authorizedRead , authorizedWrite )
882+ log .Debug ("AffectedMode changed" , "address" , address .Hex (), "old" , oldMode , "new" , affectedType .mode )
883+ }
884+ return nil
885+ }
886+
887+ // enforceMultitenancyCheck returns true if EVM is enforced to do multitenancy check
888+ // during simulation/eth_call, false otherwise
842889func (evm * EVM ) enforceMultitenancyCheck () bool {
843890 return evm .AuthorizeCreateFunc != nil || evm .AuthorizeMessageCallFunc != nil
844891}
0 commit comments