diff --git a/actors/evm/tests/contracts/NotificationReceiver.hex b/actors/evm/tests/contracts/NotificationReceiver.hex new file mode 100644 index 000000000..49623527e --- /dev/null +++ b/actors/evm/tests/contracts/NotificationReceiver.hex @@ -0,0 +1 @@ +60806040525f60035f6101000a81548160ff0219169083151502179055503480156027575f80fd5b50613035806100355f395ff3fe608060405234801561000f575f80fd5b5060043610610086575f3560e01c8063868e10c411610059578063868e10c41461013c5780638777d4d71461016e578063c153e97f1461018c578063ec680ea2146101c057610086565b806334c1f9441461008a57806344794f4f146100be5780634cbb3601146100ee578063529825951461010c575b5f80fd5b6100a4600480360381019061009f9190611a5d565b6101f0565b6040516100b5959493929190611b35565b60405180910390f35b6100d860048036038101906100d39190611bbe565b61036e565b6040516100e59190611bf8565b60405180910390f35b6100f661039f565b6040516101039190611c2b565b60405180910390f35b61012660048036038101906101219190611bbe565b6103b1565b6040516101339190611cfb565b60405180910390f35b61015660048036038101906101519190611e47565b61042c565b60405161016593929190611eb3565b60405180910390f35b610176610501565b6040516101839190611bf8565b60405180910390f35b6101a660048036038101906101a19190611a5d565b610507565b6040516101b7959493929190611b35565b60405180910390f35b6101da60048036038101906101d59190611eef565b61074a565b6040516101e79190611bf8565b60405180910390f35b5f81815481106101fe575f80fd5b905f5260205f2090600402015f91509050805f015f9054906101000a900467ffffffffffffffff1690805f0160089054906101000a900460070b9080600101805461024890611f5a565b80601f016020809104026020016040519081016040528092919081815260200182805461027490611f5a565b80156102bf5780601f10610296576101008083540402835291602001916102bf565b820191905f5260205f20905b8154815290600101906020018083116102a257829003601f168201915b505050505090806002015f9054906101000a900467ffffffffffffffff16908060030180546102ed90611f5a565b80601f016020809104026020016040519081016040528092919081815260200182805461031990611f5a565b80156103645780601f1061033b57610100808354040283529160200191610364565b820191905f5260205f20905b81548152906001019060200180831161034757829003601f168201915b5050505050905085565b5f60015f8367ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f20805490509050919050565b60035f9054906101000a900460ff1681565b606060015f8367ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f2080548060200260200160405190810160405280929190818152602001828054801561042057602002820191905f5260205f20905b81548152602001906001019080831161040c575b50505050509050919050565b5f80606060518567ffffffffffffffff161461047d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161047490611fe4565b60405180910390fd5b637942460367ffffffffffffffff168667ffffffffffffffff16036104bd575f6104a685610775565b90505f605190505f818394509450945050506104f8565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104ef9061204c565b60405180910390fd5b93509350939050565b60025481565b5f8060605f60605f805490508610610554576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161054b906120b4565b60405180910390fd5b5f808781548110610568576105676120d2565b5b905f5260205f2090600402016040518060a00160405290815f82015f9054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681526020015f820160089054906101000a900460070b60070b60070b81526020016001820180546105db90611f5a565b80601f016020809104026020016040519081016040528092919081815260200182805461060790611f5a565b80156106525780601f1061062957610100808354040283529160200191610652565b820191905f5260205f20905b81548152906001019060200180831161063557829003601f168201915b50505050508152602001600282015f9054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff16815260200160038201805461069c90611f5a565b80601f01602080910402602001604051908101604052809291908181526020018280546106c890611f5a565b80156107135780601f106106ea57610100808354040283529160200191610713565b820191905f5260205f20905b8154815290600101906020018083116106f657829003601f168201915b5050505050815250509050805f01518160200151826040015183606001518460800151955095509550955095505091939590929450565b6001602052815f5260405f208181548110610763575f80fd5b905f5260205f20015f91509150505481565b606061078033610b3b565b6107bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b69061216f565b60405180910390fd5b5f805f90505f6107cf8583610bdf565b80935081925050505f8111610819576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610810906121fd565b60405180910390fd5b6108216119e7565b61082b6040610c5e565b90506108378183610c78565b5f5b82811015610b265761084b8785610bdf565b809550819650505060038514610896576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161088d90612265565b60405180910390fd5b5f6108a18886610c88565b80965081925050505f6108b48987610d06565b80975081925050505f6108c78a88610bdf565b80985081925050506108d98582610c78565b5f5b81811015610b15576108ed8b89610bdf565b809950819a50505060038914610938576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161092f906122cd565b60405180910390fd5b60606109448c8a610d95565b809a5081925050505f6109578d8b610c88565b809b508192505050606061096b8e8c610d95565b809c5081925050505f808054905090505f6040518060a001604052808a67ffffffffffffffff1681526020018960070b81526020018681526020018567ffffffffffffffff16815260200184815250908060018154018082558091505060019003905f5260205f2090600402015f909190919091505f820151815f015f6101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506020820151815f0160086101000a81548167ffffffffffffffff021916908360070b67ffffffffffffffff1602179055506040820151816001019081610a529190612488565b506060820151816002015f6101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506080820151816003019081610a969190612488565b50505060015f8967ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f2081908060018154018082558091505060019003905f5260205f20015f909190919091505560025f815480929190610af490612584565b9190505550610b048a6001610f97565b5050505080806001019150506108db565b505050508080600101915050610839565b50610b3081610fc0565b945050505050919050565b5f805f610b4784610fd0565b9150915081610b8b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b8290612615565b60405180910390fd5b5f610b966008610c5e565b9050610ba2818361101c565b5f610bac82610fc0565b90505f610bcd600463dfb85e2667ffffffffffffffff166051855f8061102b565b5090505f811495505050505050919050565b5f805f80610bed868661113b565b8167ffffffffffffffff169150809750819350829450505050600460ff168260ff1614610c4f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c469061267d565b60405180910390fd5b80859350935050509250929050565b610c666119e7565b610c73815f015183611357565b919050565b610c84826004836113c0565b5050565b5f805f80610c96868661113b565b8167ffffffffffffffff1691508097508193508294505050505f60ff168260ff1614610cf7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610cee9061270b565b60405180910390fd5b80859350935050509250929050565b5f805f80610d14868661113b565b8167ffffffffffffffff169150809750819350829450505050600160ff168260ff161480610d4757505f60ff168260ff16145b610d86576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d7d90612799565b60405180910390fd5b80859350935050509250929050565b60605f805f610da4868661113b565b8167ffffffffffffffff169150809750819350829450505050600660ff168260ff161480610dd85750600260ff168260ff16145b610e17576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e0e90612827565b60405180910390fd5b600660ff168260ff1603610e9257610e2f868661113b565b8167ffffffffffffffff169150809750819350829450505050600260ff168260ff1614610e91576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e889061288f565b60405180910390fd5b5b5f8186610e9f91906128ad565b90505f8267ffffffffffffffff811115610ebc57610ebb611d23565b5b6040519080825280601f01601f191660200182016040528015610eee5781602001600182028036833780820191505090505b5090505f808890505b83811015610f7957898181518110610f1257610f116120d2565b5b602001015160f81c60f81b838381518110610f3057610f2f6120d2565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053508180610f6990612584565b9250508080600101915050610ef7565b50818489610f8791906128ad565b9650965050505050509250929050565b610fbc825f015182610faa576014610fad565b60155b6005600760ff16901b17611502565b5050565b6060815f01515f01519050919050565b5f8073ffffffffffffffffffffffff0000000000000000831673ff000000000000000000000000000000000000008103611016576001925067ffffffffffffffff841691505b50915091565b611027825f836113c0565b5050565b5f60605f8073fe0000000000000000000000000000000000000573ffffffffffffffffffffffffffffffffffffffff16898787611068575f61106b565b60015b8b8b8f604051602001611083969594939291906128e0565b60405160208183030381529060405260405161109f9190612980565b5f60405180830381855af49150503d805f81146110d7576040519150601f19603f3d011682016040523d82523d5f602084013e6110dc565b606091505b509150915081611121576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611118906129e0565b60405180910390fd5b61112a8161155c565b935093505050965096945050505050565b5f805f8061114986866116ad565b905060018561115891906128ad565b94505f600560e0831660ff16901c90505f601f83169050601c8160ff16106111b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ac90612a6e565b60405180910390fd5b60188160ff1610156111d8578181888160ff169150955095509550505050611350565b60188160ff160361125e575f6111ee89896116ad565b90506001886111fd91906128ad565b975060188160ff161015611246576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161123d90612ad6565b60405180910390fd5b8281898160ff16915096509650965050505050611350565b60198160ff160361129e575f6112748989611727565b905060028861128391906128ad565b97508281898161ffff16915096509650965050505050611350565b601a8160ff16036112e0575f6112b4898961178f565b90506004886112c391906128ad565b97508281898163ffffffff16915096509650965050505050611350565b601b8160ff1614611326576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161131d90612b3e565b60405180910390fd5b5f61133189896117f7565b905060088861134091906128ad565b9750828189965096509650505050505b9250925092565b5f6020826113659190612b89565b14611391576020816113779190612b89565b60206113839190612bb9565b8161138e91906128ad565b90505b808260200181815250506040518083525f8152818101602001818110156113b6575f80fd5b8060405250505050565b60178167ffffffffffffffff16116113f0576113eb835f01518260058560ff16901b60ff1617611502565b6114fd565b60ff8167ffffffffffffffff161161143757611419835f0151601860058560ff16901b17611502565b611432835f01518267ffffffffffffffff16600161185f565b6114fc565b61ffff8167ffffffffffffffff161161147f57611461835f0151601960058560ff16901b17611502565b61147a835f01518267ffffffffffffffff16600261185f565b6114fb565b63ffffffff8167ffffffffffffffff16116114c9576114ab835f0151601a60058560ff16901b17611502565b6114c4835f01518267ffffffffffffffff16600461185f565b6114fa565b6114e0835f0151601b60058560ff16901b17611502565b6114f9835f01518267ffffffffffffffff16600861185f565b5b5b5b5b505050565b5f825f01515190505f60018261151891906128ad565b90508360200151821061153c5761153b846002836115369190612bec565b6118db565b5b83516020838201018481538151831115611554578282525b505050505050565b5f60605f805f858060200190518101906115769190612ce2565b9250925092505f67ffffffffffffffff168267ffffffffffffffff16036115df575f8151146115da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115d190612d98565b60405180910390fd5b61169f565b605167ffffffffffffffff168267ffffffffffffffff1614806116165750607167ffffffffffffffff168267ffffffffffffffff16145b15611663575f81510361165e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161165590612d98565b60405180910390fd5b61169e565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161169590612e00565b60405180910390fd5b5b828194509450505050915091565b5f6001826116bb91906128ad565b835110156116fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116f590612e68565b60405180910390fd5b828281518110611711576117106120d2565b5b602001015160f81c60f81b60f81c905092915050565b5f60028261173591906128ad565b83511015611778576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161176f90612e68565b60405180910390fd5b5f8260200184015190508060f01c91505092915050565b5f60048261179d91906128ad565b835110156117e0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117d790612e68565b60405180910390fd5b5f8260200184015190508060e01c91505092915050565b5f60088261180591906128ad565b83511015611848576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161183f90612e68565b60405180910390fd5b5f8260200184015190508060c01c91505092915050565b5f835f01515190505f818361187491906128ad565b9050846020015181111561189957611898856002836118939190612bec565b6118db565b5b5f6001846101006118aa9190612fb5565b6118b49190612bb9565b9050855182810186831982511617815281518411156118d1578382525b5050505050505050565b5f825f015190506118ec8383611357565b6118f683826118fb565b505050565b5f815190505f835f01515190505f828261191591906128ad565b9050846020015181111561193a57611939856002836119349190612bec565b6118db565b5b5f8086518051856020830101935080851115611954578482525b60208801925050505b6020851061199b578051825260208261197691906128ad565b915060208161198591906128ad565b90506020856119949190612bb9565b945061195d565b5f8511156119de575f60018660206119b39190612bb9565b6101006119c09190612fb5565b6119ca9190612bb9565b905080198251168184511681811785525050505b50505050505050565b60405180602001604052806119fa611a00565b81525090565b6040518060400160405280606081526020015f81525090565b5f604051905090565b5f80fd5b5f80fd5b5f819050919050565b611a3c81611a2a565b8114611a46575f80fd5b50565b5f81359050611a5781611a33565b92915050565b5f60208284031215611a7257611a71611a22565b5b5f611a7f84828501611a49565b91505092915050565b5f67ffffffffffffffff82169050919050565b611aa481611a88565b82525050565b5f8160070b9050919050565b611abf81611aaa565b82525050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f611b0782611ac5565b611b118185611acf565b9350611b21818560208601611adf565b611b2a81611aed565b840191505092915050565b5f60a082019050611b485f830188611a9b565b611b556020830187611ab6565b8181036040830152611b678186611afd565b9050611b766060830185611a9b565b8181036080830152611b888184611afd565b90509695505050505050565b611b9d81611a88565b8114611ba7575f80fd5b50565b5f81359050611bb881611b94565b92915050565b5f60208284031215611bd357611bd2611a22565b5b5f611be084828501611baa565b91505092915050565b611bf281611a2a565b82525050565b5f602082019050611c0b5f830184611be9565b92915050565b5f8115159050919050565b611c2581611c11565b82525050565b5f602082019050611c3e5f830184611c1c565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b611c7681611a2a565b82525050565b5f611c878383611c6d565b60208301905092915050565b5f602082019050919050565b5f611ca982611c44565b611cb38185611c4e565b9350611cbe83611c5e565b805f5b83811015611cee578151611cd58882611c7c565b9750611ce083611c93565b925050600181019050611cc1565b5085935050505092915050565b5f6020820190508181035f830152611d138184611c9f565b905092915050565b5f80fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b611d5982611aed565b810181811067ffffffffffffffff82111715611d7857611d77611d23565b5b80604052505050565b5f611d8a611a19565b9050611d968282611d50565b919050565b5f67ffffffffffffffff821115611db557611db4611d23565b5b611dbe82611aed565b9050602081019050919050565b828183375f83830152505050565b5f611deb611de684611d9b565b611d81565b905082815260208101848484011115611e0757611e06611d1f565b5b611e12848285611dcb565b509392505050565b5f82601f830112611e2e57611e2d611d1b565b5b8135611e3e848260208601611dd9565b91505092915050565b5f805f60608486031215611e5e57611e5d611a22565b5b5f611e6b86828701611baa565b9350506020611e7c86828701611baa565b925050604084013567ffffffffffffffff811115611e9d57611e9c611a26565b5b611ea986828701611e1a565b9150509250925092565b5f606082019050611ec65f830186611a9b565b611ed36020830185611a9b565b8181036040830152611ee58184611afd565b9050949350505050565b5f8060408385031215611f0557611f04611a22565b5b5f611f1285828601611baa565b9250506020611f2385828601611a49565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f6002820490506001821680611f7157607f821691505b602082108103611f8457611f83611f2d565b5b50919050565b5f82825260208201905092915050565b7f496e76616c696420636f646563000000000000000000000000000000000000005f82015250565b5f611fce600d83611f8a565b9150611fd982611f9a565b602082019050919050565b5f6020820190508181035f830152611ffb81611fc2565b9050919050565b7f496e76616c6964206d6574686f640000000000000000000000000000000000005f82015250565b5f612036600e83611f8a565b915061204182612002565b602082019050919050565b5f6020820190508181035f8301526120638161202a565b9050919050565b7f496e76616c6964206e6f74696669636174696f6e20696e6465780000000000005f82015250565b5f61209e601a83611f8a565b91506120a98261206a565b602082019050919050565b5f6020820190508181035f8301526120cb81612092565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4f6e6c79206d696e6572206163746f722063616e2063616c6c207468697320665f8201527f756e6374696f6e00000000000000000000000000000000000000000000000000602082015250565b5f612159602783611f8a565b9150612164826120ff565b604082019050919050565b5f6020820190508181035f8301526121868161214d565b9050919050565b7f496e76616c6964206e6f6e20706f73697469766520736563746f7273206669655f8201527f6c64000000000000000000000000000000000000000000000000000000000000602082015250565b5f6121e7602283611f8a565b91506121f28261218d565b604082019050919050565b5f6020820190508181035f830152612214816121db565b9050919050565b7f496e76616c696420536563746f724368616e676573207475706c6500000000005f82015250565b5f61224f601b83611f8a565b915061225a8261221b565b602082019050919050565b5f6020820190508181035f83015261227c81612243565b9050919050565b7f496e76616c696420706172616d7320696e6e65720000000000000000000000005f82015250565b5f6122b7601483611f8a565b91506122c282612283565b602082019050919050565b5f6020820190508181035f8301526122e4816122ab565b9050919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f600883026123477fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8261230c565b612351868361230c565b95508019841693508086168417925050509392505050565b5f819050919050565b5f61238c61238761238284611a2a565b612369565b611a2a565b9050919050565b5f819050919050565b6123a583612372565b6123b96123b182612393565b848454612318565b825550505050565b5f90565b6123cd6123c1565b6123d881848461239c565b505050565b5b818110156123fb576123f05f826123c5565b6001810190506123de565b5050565b601f82111561244057612411816122eb565b61241a846122fd565b81016020851015612429578190505b61243d612435856122fd565b8301826123dd565b50505b505050565b5f82821c905092915050565b5f6124605f1984600802612445565b1980831691505092915050565b5f6124788383612451565b9150826002028217905092915050565b61249182611ac5565b67ffffffffffffffff8111156124aa576124a9611d23565b5b6124b48254611f5a565b6124bf8282856123ff565b5f60209050601f8311600181146124f0575f84156124de578287015190505b6124e8858261246d565b86555061254f565b601f1984166124fe866122eb565b5f5b8281101561252557848901518255600182019150602085019450602081019050612500565b86831015612542578489015161253e601f891682612451565b8355505b6001600288020188555050505b505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61258e82611a2a565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036125c0576125bf612557565b5b600182019050919050565b7f63616c6c6572206973206e6f7420616e204944206164647200000000000000005f82015250565b5f6125ff601883611f8a565b915061260a826125cb565b602082019050919050565b5f6020820190508181035f83015261262c816125f3565b9050919050565b7f696e76616c6964206d616a20286578706563746564204d616a417272617929005f82015250565b5f612667601f83611f8a565b915061267282612633565b602082019050919050565b5f6020820190508181035f8301526126948161265b565b9050919050565b7f696e76616c6964206d616a20286578706563746564204d616a556e7369676e655f8201527f64496e7429000000000000000000000000000000000000000000000000000000602082015250565b5f6126f5602583611f8a565b91506127008261269b565b604082019050919050565b5f6020820190508181035f830152612722816126e9565b9050919050565b7f696e76616c6964206d616a20286578706563746564204d616a5369676e6564495f8201527f6e74206f72204d616a556e7369676e6564496e74290000000000000000000000602082015250565b5f612783603583611f8a565b915061278e82612729565b604082019050919050565b5f6020820190508181035f8301526127b081612777565b9050919050565b7f696e76616c6964206d616a20286578706563746564204d616a546167206f72205f8201527f4d616a42797465537472696e6729000000000000000000000000000000000000602082015250565b5f612811602e83611f8a565b915061281c826127b7565b604082019050919050565b5f6020820190508181035f83015261283e81612805565b9050919050565b7f6578706563746564204d616a42797465537472696e67000000000000000000005f82015250565b5f612879601683611f8a565b915061288482612845565b602082019050919050565b5f6020820190508181035f8301526128a68161286d565b9050919050565b5f6128b782611a2a565b91506128c283611a2a565b92508282019050808211156128da576128d9612557565b5b92915050565b5f60c0820190506128f35f830189611a9b565b6129006020830188611be9565b61290d6040830187611a9b565b61291a6060830186611a9b565b818103608083015261292c8185611afd565b905061293b60a0830184611a9b565b979650505050505050565b5f81905092915050565b5f61295a82611ac5565b6129648185612946565b9350612974818560208601611adf565b80840191505092915050565b5f61298b8284612950565b915081905092915050565b7f6661696c20746f2063616c6c206163746f7200000000000000000000000000005f82015250565b5f6129ca601283611f8a565b91506129d582612996565b602082019050919050565b5f6020820190508181035f8301526129f7816129be565b9050919050565b7f63616e6e6f742068616e646c65206865616465727320776974682065787472615f8201527f203e203237000000000000000000000000000000000000000000000000000000602082015250565b5f612a58602583611f8a565b9150612a63826129fe565b604082019050919050565b5f6020820190508181035f830152612a8581612a4c565b9050919050565b7f696e76616c69642063626f7200000000000000000000000000000000000000005f82015250565b5f612ac0600c83611f8a565b9150612acb82612a8c565b602082019050919050565b5f6020820190508181035f830152612aed81612ab4565b9050919050565b7f45787065637465644c6f7756616c7565323700000000000000000000000000005f82015250565b5f612b28601283611f8a565b9150612b3382612af4565b602082019050919050565b5f6020820190508181035f830152612b5581612b1c565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f612b9382611a2a565b9150612b9e83611a2a565b925082612bae57612bad612b5c565b5b828206905092915050565b5f612bc382611a2a565b9150612bce83611a2a565b9250828203905081811115612be657612be5612557565b5b92915050565b5f612bf682611a2a565b9150612c0183611a2a565b9250828202612c0f81611a2a565b91508282048414831517612c2657612c25612557565b5b5092915050565b5f819050919050565b612c3f81612c2d565b8114612c49575f80fd5b50565b5f81519050612c5a81612c36565b92915050565b5f81519050612c6e81611b94565b92915050565b5f612c86612c8184611d9b565b611d81565b905082815260208101848484011115612ca257612ca1611d1f565b5b612cad848285611adf565b509392505050565b5f82601f830112612cc957612cc8611d1b565b5b8151612cd9848260208601612c74565b91505092915050565b5f805f60608486031215612cf957612cf8611a22565b5b5f612d0686828701612c4c565b9350506020612d1786828701612c60565b925050604084015167ffffffffffffffff811115612d3857612d37611a26565b5b612d4486828701612cb5565b9150509250925092565b7f696e76616c696420726573706f6e7365206c656e6774680000000000000000005f82015250565b5f612d82601783611f8a565b9150612d8d82612d4e565b602082019050919050565b5f6020820190508181035f830152612daf81612d76565b9050919050565b7f696e76616c696420636f646563000000000000000000000000000000000000005f82015250565b5f612dea600d83611f8a565b9150612df582612db6565b602082019050919050565b5f6020820190508181035f830152612e1781612dde565b9050919050565b7f736c6963696e67206f7574206f662072616e67650000000000000000000000005f82015250565b5f612e52601483611f8a565b9150612e5d82612e1e565b602082019050919050565b5f6020820190508181035f830152612e7f81612e46565b9050919050565b5f8160011c9050919050565b5f808291508390505b6001851115612edb57808604811115612eb757612eb6612557565b5b6001851615612ec65780820291505b8081029050612ed485612e86565b9450612e9b565b94509492505050565b5f82612ef35760019050612fae565b81612f00575f9050612fae565b8160018114612f165760028114612f2057612f4f565b6001915050612fae565b60ff841115612f3257612f31612557565b5b8360020a915084821115612f4957612f48612557565b5b50612fae565b5060208310610133831016604e8410600b8410161715612f845782820a905083811115612f7f57612f7e612557565b5b612fae565b612f918484846001612e92565b92509050818404811115612fa857612fa7612557565b5b81810290505b9392505050565b5f612fbf82611a2a565b9150612fca83611a2a565b9250612ff77fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8484612ee4565b90509291505056fea26469706673582212200c1908ec017c97a13a77a90455592bd02b2fe8bb0cae4469e97ef9d3b28a573364736f6c63430008190033 diff --git a/actors/evm/tests/contracts/NotificationReceiver.sol b/actors/evm/tests/contracts/NotificationReceiver.sol new file mode 100644 index 000000000..935d34f63 --- /dev/null +++ b/actors/evm/tests/contracts/NotificationReceiver.sol @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +// ======================================================== +// NOTE: If using this contract as an example, consider depending on utilities +// available in https://github.com/filecoin-project/filecoin-solidity instead of +// copying reusable utilities from here. +// ======================================================== + +contract NotificationReceiver { + // State variables to track received notifications + struct SectorNotification { + uint64 sector; + int64 minimumCommitmentEpoch; + bytes dataCid; + uint64 pieceSize; + bytes payload; + } + + SectorNotification[] public notifications; + mapping(uint64 => uint256[]) public sectorNotificationIndices; + + // Counter for total notifications received + uint256 public totalNotifications; + + // Flag to test different response behaviors + bool public shouldRejectNotifications = false; + + // Method selector for handle_filecoin_method + bytes4 constant NATIVE_METHOD_SELECTOR = 0x868e10c4; + + // Sector content changed method number + uint64 constant SECTOR_CONTENT_CHANGED = 2034386435; + + // Get the count of notifications for a specific sector + function getNotificationCount(uint64 sector) public view returns (uint256) { + return sectorNotificationIndices[sector].length; + } + + // Get all notification indices for a sector + function getSectorNotifications(uint64 sector) public view returns (uint256[] memory) { + return sectorNotificationIndices[sector]; + } + + // Get a specific notification by index + function getNotification(uint256 index) public view returns ( + uint64 sector, + int64 minimumCommitmentEpoch, + bytes memory dataCid, + uint64 pieceSize, + bytes memory payload + ) { + require(index < notifications.length, "Invalid notification index"); + SectorNotification memory notif = notifications[index]; + return ( + notif.sector, + notif.minimumCommitmentEpoch, + notif.dataCid, + notif.pieceSize, + notif.payload + ); + } + + // Handle incoming Filecoin method calls + // This is the main entry point for receiving notifications from the miner actor + function handle_filecoin_method(uint64 method, uint64 inCodec, bytes memory params) public returns (uint64, uint64,bytes memory) { + // 0x51 is IPLD CBOR codec + require(inCodec == 0x51, "Invalid codec"); + // Check if this is a sector content changed notification + if (method == SECTOR_CONTENT_CHANGED) { + bytes memory ret = processSectorContentChanged(params); + uint64 codec = 0x51; + return (0, codec, ret); + } + + // For other methods, just revert + revert("Invalid method"); + } + + // Process sector content changed notification + // Expected params structure (CBOR encoded): + // { + // sectors: [{ + // sector: uint64, + // minimum_commitment_epoch: int64, + // added: [{ + // data: Cid, + // size: uint64, + // payload: bytes + // }] + // }] + // } + // + // All notifications are accepted so CBOR true returned for every piece of every notified sector + function processSectorContentChanged(bytes memory params) internal returns (bytes memory) { + require(isMinerActor(msg.sender), "Only miner actor can call this function"); + + uint checkTupleLen; + uint byteIdx = 0; + + // We don't need to parse the SectorContentChangedParams as a tuple because + // the type is encoded as serde transparent. So just parse the sectors array directly + uint nSectors; + (nSectors, byteIdx) = readFixedArray(params, byteIdx); + require(nSectors > 0, "Invalid non positive sectors field"); + + CBORBuffer memory ret_acc; + { + // Setup return value ret_acc + // ret_acc accumulates return cbor array + ret_acc = createCBOR(64); + // No SectorContentChangedReturn outer tuple as it is serde transparent + startFixedArray(ret_acc, uint64(nSectors)); // sectors: Vec + } + for (uint i = 0; i < nSectors; i++) { + + /* We now need to parse a tuple of 3 cbor objects: + (sector, minimum_commitment_epoch, added_pieces) */ + (checkTupleLen, byteIdx) = readFixedArray(params, byteIdx); + require(checkTupleLen == 3, "Invalid SectorChanges tuple"); + + + uint64 sector; + (sector, byteIdx) = readUInt64(params, byteIdx); + + int64 minimumCommitmentEpoch; + (minimumCommitmentEpoch, byteIdx) = readInt64(params, byteIdx); + + uint256 pieceCnt; + (pieceCnt, byteIdx) = readFixedArray(params, byteIdx); + + { + // No SectorReturn outer tuple as it is serde transparent + startFixedArray(ret_acc, uint64(pieceCnt)); // added: Vec + } + + for (uint j = 0; j < pieceCnt; j++) { + /* We now need to parse a tuple of 3 cbor objects: + (data, size, payload) + */ + (checkTupleLen, byteIdx) = readFixedArray(params, byteIdx); + require(checkTupleLen == 3, "Invalid params inner"); + + bytes memory dataCid; + (dataCid, byteIdx) = readBytes(params, byteIdx); + + uint64 pieceSize; + (pieceSize, byteIdx) = readUInt64(params, byteIdx); + + bytes memory payload; + (payload, byteIdx) = readBytes(params, byteIdx); + + // Store the notification + uint256 notificationIndex = notifications.length; + notifications.push(SectorNotification({ + sector: sector, + minimumCommitmentEpoch: minimumCommitmentEpoch, + dataCid: dataCid, + pieceSize: pieceSize, + payload: payload + })); + + sectorNotificationIndices[sector].push(notificationIndex); + totalNotifications++; + { + // No PieceReturn outer tuple as it is serde transparent + writeBool(ret_acc, true); // accepted (set all to true) + } + } + } + + return getCBORData(ret_acc); + } + + /* Filecoin internal call helpers to enable isMiner check */ + + // FVM specific precompiles + address constant RESOLVE_ADDRESS_PRECOMPILE_ADDR = 0xFE00000000000000000000000000000000000001; + address constant CALL_ACTOR_ID = 0xfe00000000000000000000000000000000000005; + + // FVM system flags + uint64 constant READ_ONLY_FLAG = 0x00000001; + uint64 constant DEFAULT_FLAG = 0x00000000; + uint64 constant DAG_CBOR_CODEC = 0x71; + uint64 constant CBOR_CODEC = 0x51; + uint64 constant NONE_CODEC = 0x00; + + + // Power actor constants + uint64 constant MINER_RAW_POWER_METHOD_NUMBER = 3753401894; + uint64 constant POWER_ACTOR_ID = 4; + + // msg.sender to actor id conversion + address constant U64_MASK = 0xFffFfFffffFfFFffffFFFffF0000000000000000; + address constant ZERO_ID_ADDRESS = 0xfF00000000000000000000000000000000000000; + address constant MAX_U64 = 0x000000000000000000000000fFFFFFffFFFFfffF; + + + function isMinerActor(address caller) internal returns (bool) { + (bool isNative, uint64 minerID) = isIDAddress(caller); + require(isNative, "caller is not an ID addr"); + CBORBuffer memory buf = createCBOR(8); + writeUInt64(buf, minerID); + bytes memory rawRequest = getCBORData(buf); + (int256 exit,) = callById(POWER_ACTOR_ID, MINER_RAW_POWER_METHOD_NUMBER, CBOR_CODEC, rawRequest, 0, false); + // If the call succeeds, the address is a registered miner + return exit == 0; + + } + + function isIDAddress(address _a) internal pure returns (bool isID, uint64 id) { + /// @solidity memory-safe-assembly + assembly { + // Zeroes out the last 8 bytes of _a + let a_mask := and(_a, U64_MASK) + + // If the result is equal to the ZERO_ID_ADDRESS, + // _a is an ID address. + if eq(a_mask, ZERO_ID_ADDRESS) { + isID := true + id := and(_a, MAX_U64) + } + } + } + + // Stripped down version of callByID to query power actor in our use case + function callById( + uint64 target, + uint256 method_num, + uint64 codec, + bytes memory raw_request, + uint256 value, + bool static_call + ) internal returns (int256, bytes memory) { + (bool success, bytes memory data) = address(CALL_ACTOR_ID).delegatecall( + abi.encode(uint64(method_num), value, static_call ? READ_ONLY_FLAG : DEFAULT_FLAG, codec, raw_request, target) + ); + if (!success) { + revert("fail to call actor"); + } + + return readRespData(data); + } + + function readRespData(bytes memory raw_response) internal pure returns (int256, bytes memory) { + (int256 exit, uint64 return_codec, bytes memory return_value) = abi.decode(raw_response, (int256, uint64, bytes)); + + if (return_codec == NONE_CODEC) { + if (return_value.length != 0) { + revert("invalid response length"); + } + } else if (return_codec == CBOR_CODEC || return_codec == DAG_CBOR_CODEC) { + if (return_value.length == 0) { + revert("invalid response length"); + } + } else { + revert("invalid codec"); + } + + return (exit, return_value); + } + + + + + /* *** CBOR parsing *** */ + + uint8 constant MajUnsignedInt = 0; + uint8 constant MajSignedInt = 1; + uint8 constant MajByteString = 2; + uint8 constant MajTextString = 3; + uint8 constant MajArray = 4; + uint8 constant MajMap = 5; + uint8 constant MajTag = 6; + uint8 constant MajOther = 7; + + uint8 constant TagTypeBigNum = 2; + uint8 constant TagTypeNegativeBigNum = 3; + + uint8 constant True_Type = 21; + uint8 constant False_Type = 20; + + /// @notice attempt to read the length of a fixed array + /// @param cborData cbor encoded bytes to parse from + /// @param byteIdx current position to read on the cbor encoded bytes + /// @return length of the fixed array decoded from input bytes and the byte index after moving past the value + function readFixedArray(bytes memory cborData, uint byteIdx) internal pure returns (uint, uint) { + uint8 maj; + uint len; + + (maj, len, byteIdx) = parseCborHeader(cborData, byteIdx); + require(maj == MajArray, "invalid maj (expected MajArray)"); + + return (len, byteIdx); + } + + /// @notice attempt to read an arbitrary byte string value + /// @param cborData cbor encoded bytes to parse from + /// @param byteIdx current position to read on the cbor encoded bytes + /// @return arbitrary byte string decoded from input bytes and the byte index after moving past the value + function readBytes(bytes memory cborData, uint byteIdx) internal pure returns (bytes memory, uint) { + uint8 maj; + uint len; + + (maj, len, byteIdx) = parseCborHeader(cborData, byteIdx); + require(maj == MajTag || maj == MajByteString, "invalid maj (expected MajTag or MajByteString)"); + + if (maj == MajTag) { + (maj, len, byteIdx) = parseCborHeader(cborData, byteIdx); + if (!(maj == MajByteString)) { + revert("expected MajByteString"); + } + } + + uint max_len = byteIdx + len; + bytes memory slice = new bytes(len); + uint slice_index = 0; + for (uint256 i = byteIdx; i < max_len; i++) { + slice[slice_index] = cborData[i]; + slice_index++; + } + + return (slice, byteIdx + len); + } + + /// @notice attempt to read a uint64 value + /// @param cborData cbor encoded bytes to parse from + /// @param byteIdx current position to read on the cbor encoded bytes + /// @return an uint64 decoded from input bytes and the byte index after moving past the value + function readUInt64(bytes memory cborData, uint byteIdx) internal pure returns (uint64, uint) { + uint8 maj; + uint value; + + (maj, value, byteIdx) = parseCborHeader(cborData, byteIdx); + require(maj == MajUnsignedInt, "invalid maj (expected MajUnsignedInt)"); + + return (uint64(value), byteIdx); + } + + /// @notice attempt to read a int64 value + /// @param cborData cbor encoded bytes to parse from + /// @param byteIdx current position to read on the cbor encoded bytes + /// @return an int64 decoded from input bytes and the byte index after moving past the value + function readInt64(bytes memory cborData, uint byteIdx) internal pure returns (int64, uint) { + uint8 maj; + uint value; + + (maj, value, byteIdx) = parseCborHeader(cborData, byteIdx); + require(maj == MajSignedInt || maj == MajUnsignedInt, "invalid maj (expected MajSignedInt or MajUnsignedInt)"); + + return (int64(uint64(value)), byteIdx); + } + + /// @notice Parse cbor header for major type and extra info. + /// @param cbor cbor encoded bytes to parse from + /// @param byteIndex current position to read on the cbor encoded bytes + /// @return major type, extra info and the byte index after moving past header bytes + function parseCborHeader(bytes memory cbor, uint byteIndex) internal pure returns (uint8, uint64, uint) { + uint8 first = sliceUInt8(cbor, byteIndex); + byteIndex += 1; + uint8 maj = (first & 0xe0) >> 5; + uint8 low = first & 0x1f; + // We don't handle CBOR headers with extra > 27, i.e. no indefinite lengths + require(low < 28, "cannot handle headers with extra > 27"); + + // extra is lower bits + if (low < 24) { + return (maj, low, byteIndex); + } + + // extra in next byte + if (low == 24) { + uint8 next = sliceUInt8(cbor, byteIndex); + byteIndex += 1; + require(next >= 24, "invalid cbor"); // otherwise this is invalid cbor + return (maj, next, byteIndex); + } + + // extra in next 2 bytes + if (low == 25) { + uint16 extra16 = sliceUInt16(cbor, byteIndex); + byteIndex += 2; + return (maj, extra16, byteIndex); + } + + // extra in next 4 bytes + if (low == 26) { + uint32 extra32 = sliceUInt32(cbor, byteIndex); + byteIndex += 4; + return (maj, extra32, byteIndex); + } + + // extra in next 8 bytes + if (!(low == 27)) { + revert("ExpectedLowValue27"); + } + uint64 extra64 = sliceUInt64(cbor, byteIndex); + byteIndex += 8; + return (maj, extra64, byteIndex); + } + + /// @notice slice uint8 from bytes starting at a given index + /// @param bs bytes to slice from + /// @param start current position to slice from bytes + /// @return uint8 sliced from bytes + function sliceUInt8(bytes memory bs, uint start) internal pure returns (uint8) { + require(bs.length >= start + 1, "slicing out of range"); + return uint8(bs[start]); + } + + /// @notice slice uint16 from bytes starting at a given index + /// @param bs bytes to slice from + /// @param start current position to slice from bytes + /// @return uint16 sliced from bytes + function sliceUInt16(bytes memory bs, uint start) internal pure returns (uint16) { + require(bs.length >= start + 2, "slicing out of range"); + bytes2 x; + assembly { + x := mload(add(bs, add(0x20, start))) + } + return uint16(x); + } + + /// @notice slice uint32 from bytes starting at a given index + /// @param bs bytes to slice from + /// @param start current position to slice from bytes + /// @return uint32 sliced from bytes + function sliceUInt32(bytes memory bs, uint start) internal pure returns (uint32) { + require(bs.length >= start + 4, "slicing out of range"); + bytes4 x; + assembly { + x := mload(add(bs, add(0x20, start))) + } + return uint32(x); + } + + /// @notice slice uint64 from bytes starting at a given index + /// @param bs bytes to slice from + /// @param start current position to slice from bytes + /// @return uint64 sliced from bytes + function sliceUInt64(bytes memory bs, uint start) internal pure returns (uint64) { + require(bs.length >= start + 8, "slicing out of range"); + bytes8 x; + assembly { + x := mload(add(bs, add(0x20, start))) + } + return uint64(x); + } + + /* *** CBOR writing *** */ + // === MINIMAL CBOR ENCODING FOR SectorContentChangedReturn === + + // Buffer struct + struct Buffer { + bytes buf; + uint capacity; + } + + struct CBORBuffer { + Buffer buf; + } + + // Create a new CBOR buffer with given capacity + function createCBOR(uint256 capacity) internal pure returns(CBORBuffer memory cbor) { + initBuffer(cbor.buf, capacity); + return cbor; + } + + // Get the encoded bytes from the buffer + function getCBORData(CBORBuffer memory buf) internal pure returns(bytes memory) { + return buf.buf.buf; + } + + // Start a fixed-length array + function startFixedArray(CBORBuffer memory buf, uint64 length) internal pure { + writeFixedNumeric(buf, MajArray, length); + } + + // Write a boolean value + function writeBool(CBORBuffer memory buf, bool val) internal pure { + appendUint8(buf.buf, uint8((MajOther << 5) | (val ? True_Type : False_Type))); + } + + // Write a Uint64 value + function writeUInt64(CBORBuffer memory buf, uint64 value) internal pure { + writeFixedNumeric(buf, MajUnsignedInt, value); + } + + // === INTERNAL HELPER FUNCTIONS === + + function initBuffer(Buffer memory buf, uint capacity) private pure { + if (capacity % 32 != 0) { + capacity += 32 - (capacity % 32); + } + buf.capacity = capacity; + assembly { + let ptr := mload(0x40) + mstore(buf, ptr) + mstore(ptr, 0) + let fpm := add(32, add(ptr, capacity)) + if lt(fpm, ptr) { + revert(0, 0) + } + mstore(0x40, fpm) + } + } + + function writeFixedNumeric(CBORBuffer memory buf, uint8 major, uint64 val) private pure { + if (val <= 23) { + appendUint8(buf.buf, uint8((major << 5) | val)); + } else if (val <= 0xFF) { + appendUint8(buf.buf, uint8((major << 5) | 24)); + appendInt(buf.buf, val, 1); + } else if (val <= 0xFFFF) { + appendUint8(buf.buf, uint8((major << 5) | 25)); + appendInt(buf.buf, val, 2); + } else if (val <= 0xFFFFFFFF) { + appendUint8(buf.buf, uint8((major << 5) | 26)); + appendInt(buf.buf, val, 4); + } else { + appendUint8(buf.buf, uint8((major << 5) | 27)); + appendInt(buf.buf, val, 8); + } + } + + function appendUint8(Buffer memory buf, uint8 val) private pure { + uint off = buf.buf.length; + uint offPlusOne = off + 1; + if (off >= buf.capacity) { + resizeBuffer(buf, offPlusOne * 2); + } + + assembly { + let bufptr := mload(buf) + let dest := add(add(bufptr, off), 32) + mstore8(dest, val) + if gt(offPlusOne, mload(bufptr)) { + mstore(bufptr, offPlusOne) + } + } + } + + function appendInt(Buffer memory buf, uint val, uint len) private pure { + uint off = buf.buf.length; + uint newCapacity = len + off; + if (newCapacity > buf.capacity) { + resizeBuffer(buf, newCapacity * 2); + } + + uint mask = (256 ** len) - 1; + assembly { + let bufptr := mload(buf) + let dest := add(bufptr, newCapacity) + mstore(dest, or(and(mload(dest), not(mask)), val)) + if gt(newCapacity, mload(bufptr)) { + mstore(bufptr, newCapacity) + } + } + } + + function resizeBuffer(Buffer memory buf, uint capacity) private pure { + bytes memory oldbuf = buf.buf; + initBuffer(buf, capacity); + appendBytes(buf, oldbuf); + } + + function appendBytes(Buffer memory buf, bytes memory val) private pure { + uint len = val.length; + uint off = buf.buf.length; + uint newCapacity = off + len; + if (newCapacity > buf.capacity) { + resizeBuffer(buf, newCapacity * 2); + } + + uint dest; + uint src; + assembly { + let bufptr := mload(buf) + let buflen := mload(bufptr) + dest := add(add(bufptr, 32), off) + if gt(newCapacity, buflen) { + mstore(bufptr, newCapacity) + } + src := add(val, 32) + } + + // Copy word-length chunks + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + // Copy remaining bytes + if (len > 0) { + uint mask = (256 ** (32 - len)) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + } + } +} + diff --git a/actors/miner/src/notifications.rs b/actors/miner/src/notifications.rs index 354c30482..679f3bf55 100644 --- a/actors/miner/src/notifications.rs +++ b/actors/miner/src/notifications.rs @@ -3,9 +3,7 @@ use crate::{ SectorContentChangedParams, SectorContentChangedReturn, }; use fil_actors_runtime::runtime::Runtime; -use fil_actors_runtime::{ - ActorError, AsActorError, STORAGE_MARKET_ACTOR_ADDR, SendError, actor_error, -}; +use fil_actors_runtime::{ActorError, AsActorError, SendError}; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::address::Address; @@ -58,15 +56,6 @@ pub fn notify_data_consumers( } for (notifee, payloads) in activations_by_notifee { - // Reject notifications to any actor other than the built-in market. - if notifee != STORAGE_MARKET_ACTOR_ADDR { - if require_success { - return Err( - actor_error!(illegal_argument; "disallowed notification receiver: {}", notifee), - ); - } - continue; - } let sectors_changes: Vec = payloads .into_iter() .map(|(sector_number, pieces)| SectorChanges { diff --git a/integration_tests/src/tests/evm_notification_test.rs b/integration_tests/src/tests/evm_notification_test.rs new file mode 100644 index 000000000..064de5802 --- /dev/null +++ b/integration_tests/src/tests/evm_notification_test.rs @@ -0,0 +1,384 @@ +use alloy_core::sol_types::{SolCall, SolValue}; +use alloy_core::{primitives::U256 as AlloyU256, sol}; +use cid::Cid; +use export_macro::vm_test; +use fil_actor_miner::{ + CompactCommD, DataActivationNotification, Method as MinerMethod, PieceActivationManifest, + PieceChange, ProveCommitSectors3Params, SECTOR_CONTENT_CHANGED, SectorActivationManifest, + SectorChanges, max_prove_commit_duration, +}; +use fil_actors_runtime::{EAM_ACTOR_ADDR, runtime::Policy, test_utils::make_piece_cid}; +use fvm_ipld_encoding::{BytesDe, CBOR, RawBytes, ipld_block::IpldBlock}; +use fvm_shared::address::Address; +use fvm_shared::{ + econ::TokenAmount, + piece::PaddedPieceSize, + piece::PieceInfo, + sector::{RegisteredSealProof, SectorNumber}, +}; +use num_traits::Zero; +use vm_api::VM; +use vm_api::util::serialize_ok; + +use crate::util::{ + PrecommitMetadata, advance_by_deadline_to_epoch, create_accounts, create_miner, + precommit_sectors_v2, +}; + +// Generate a statically typed interface for the NotificationReceiver contract +sol!("../actors/evm/tests/contracts/NotificationReceiver.sol"); + +// Use ContractParams from evm_test module to avoid duplicate definition +use super::evm_test::ContractParams; + +#[vm_test] +pub fn evm_receives_ddo_notifications_test(v: &dyn VM) { + // Create accounts + let addrs = create_accounts(v, 2, &TokenAmount::from_whole(10_000)); + let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; + let (owner, worker) = (addrs[0], addrs[1]); + let (miner_addr, _) = create_miner( + v, + &owner, + &worker, + seal_proof.registered_window_post_proof().unwrap(), + &TokenAmount::from_whole(10_000), + ); + + // Deploy the NotificationReceiver EVM contract + let hex_str = std::fs::read_to_string("../actors/evm/tests/contracts/NotificationReceiver.hex") + .expect("Failed to read contract bytecode hex file"); + let hex_str = hex_str.trim(); + let contract_bytecode = hex::decode(hex_str).expect("Failed to decode contract bytecode hex"); + + // Create an EVM actor to receive notifications + let params = + IpldBlock::serialize_cbor(&fil_actor_eam::CreateExternalParams(contract_bytecode)).unwrap(); + + let create_result = v + .execute_message( + &worker, + &EAM_ACTOR_ADDR, + &TokenAmount::zero(), + fil_actor_eam::Method::CreateExternal as u64, + params, + ) + .unwrap(); + + assert!( + create_result.code.is_success(), + "Failed to create EVM contract: {}", + create_result.message + ); + + let create_return: fil_actor_eam::CreateReturn = + create_result.ret.unwrap().deserialize().expect("Failed to decode create return"); + let evm_robust_addr = create_return.robust_address.unwrap(); + let _evm_eth_addr = create_return.eth_address; + + // Precommit sectors + let sector_number: SectorNumber = 100; + + // Create piece activation manifests with notifications to EVM contract + let piece_size0 = PaddedPieceSize(32 << 30); // 32 GiB + let piece_cid0 = make_piece_cid(format!("piece-{}", 0).as_bytes()); + let notification_payload = RawBytes::from(hex::decode("cafe").unwrap()); + + let manifests: Vec = vec![SectorActivationManifest { + sector_number, + pieces: vec![PieceActivationManifest { + cid: piece_cid0, + size: piece_size0, + verified_allocation_key: None, + notify: vec![ + // Send notification to our EVM contract + DataActivationNotification { + address: evm_robust_addr, + payload: notification_payload.clone(), + }, + ], + }], + }]; + + let meta: Vec = manifests + .iter() + .map(|sector| { + let pis: Vec = + sector.pieces.iter().map(|p| PieceInfo { size: p.size, cid: p.cid }).collect(); + let commd = v.primitives().compute_unsealed_sector_cid(seal_proof, &pis).unwrap(); + PrecommitMetadata { deals: vec![], commd: CompactCommD::of(commd) } + }) + .collect(); + + // Track the precommit epoch for later verification + let precommit_epoch = v.epoch(); + + precommit_sectors_v2(v, 1, meta, &worker, &miner_addr, seal_proof, sector_number, true, None); + + // Before prove commit no notifications have been received + check_receiver_notification_count(v, &worker, &evm_robust_addr, 0); + + // Advance time to prove commit epoch + let policy = Policy::default(); + let prove_time = v.epoch() + policy.pre_commit_challenge_delay + 1; + advance_by_deadline_to_epoch(v, &miner_addr, prove_time); + + // ProveCommitSectors3 with notifications + let proofs = vec![RawBytes::new(vec![8, 8, 8, 8]); manifests.len()]; // dummy value for faked proof syscalls in test vm + let prove_params = ProveCommitSectors3Params { + sector_activations: manifests, + sector_proofs: proofs, // Empty proofs for testing + aggregate_proof: RawBytes::default(), + aggregate_proof_type: None, + require_activation_success: false, + require_notification_success: true, + }; + + let prove_result = v + .execute_message( + &worker, + &miner_addr, + &TokenAmount::zero(), + MinerMethod::ProveCommitSectors3 as u64, + IpldBlock::serialize_cbor(&prove_params).unwrap(), + ) + .unwrap(); + + assert!(prove_result.code.is_success(), "ProveCommit failed: {}", prove_result.message); + + /* ***Verify that the EVM contract received the notifications correctly*** */ + let expected_notification = ExpectedNotification { + sector: sector_number, + minimum_commitment_epoch: precommit_epoch + + policy.min_sector_expiration + + max_prove_commit_duration(&policy, seal_proof).unwrap(), + piece_cid: piece_cid0, + piece_size: piece_size0.0, + payload: notification_payload.to_vec(), + }; + + check_receiver_notification_count(v, &worker, &evm_robust_addr, 1); + check_receiver_notification_at(v, &worker, &evm_robust_addr, 0, &expected_notification); +} + +// Helper functions checking state of receiver contract + +pub fn check_receiver_notification_count( + v: &dyn VM, + sender_addr: &Address, + receiver_addr: &Address, + expected_count: u64, +) { + let call_params = NotificationReceiver::totalNotificationsCall::new(()).abi_encode(); + let call_result = v + .execute_message( + sender_addr, + receiver_addr, + &TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(serialize_ok(&ContractParams(call_params.to_vec()))), + ) + .unwrap(); + + assert!( + call_result.code.is_success(), + "Failed to call totalNotifications: {}", + call_result.message + ); + + // Decode the return value + let return_data: BytesDe = call_result.ret.unwrap().deserialize().unwrap(); + let total_notifications = AlloyU256::abi_decode(&return_data.0) + .expect("Failed to decode totalNotifications return value"); + assert_eq!( + total_notifications, + AlloyU256::from(expected_count), + "Expected {} notification(s), got {}", + expected_count, + total_notifications + ); +} + +/// Struct to hold all notification values for checking against contract state. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ExpectedNotification { + pub sector: u64, + pub minimum_commitment_epoch: i64, + pub piece_cid: Cid, + pub piece_size: u64, + pub payload: Vec, +} + +pub fn check_receiver_notification_at( + v: &dyn VM, + sender_addr: &Address, + receiver_addr: &Address, + index: u64, + expected: &ExpectedNotification, +) { + let call_params = + NotificationReceiver::getNotificationCall::new((AlloyU256::from(index),)).abi_encode(); + let call_result = v + .execute_message( + sender_addr, + receiver_addr, + &TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(serialize_ok(&ContractParams(call_params.to_vec()))), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "Failed to call getNotification: {}", + call_result.message + ); + + // Decode the return value - it returns a tuple of (uint64, int64, bytes, uint64, bytes) + let return_data: BytesDe = call_result.ret.unwrap().deserialize().unwrap(); + + // Use the generated abi_decode_returns function + let notification_result = + NotificationReceiver::getNotificationCall::abi_decode_returns(&return_data.0) + .expect("Failed to decode getNotification return value"); + + let received_sector = notification_result.sector; + let minimum_commitment_epoch = notification_result.minimumCommitmentEpoch; + let data_cid_bytes = notification_result.dataCid; + let received_piece_size = notification_result.pieceSize; + let received_payload = notification_result.payload; + + assert_eq!( + received_sector, expected.sector, + "Sector number mismatch: expected {}, got {}", + expected.sector, received_sector + ); + + assert_eq!( + received_piece_size, expected.piece_size, + "Piece size mismatch: expected {}, got {}", + expected.piece_size, received_piece_size + ); + + let expected_payload_bytes = expected.payload.to_vec(); + assert_eq!( + received_payload.as_ref(), + expected_payload_bytes.as_slice(), + "Payload mismatch: expected 0x{:x?}, got 0x{:x?}", + &expected_payload_bytes, + &received_payload.as_ref() + ); + + // Check the piece CID data is present + // The contract receives the CID with an extra leading byte from the CBOR encoding, + // so we verify it contains the expected CID data after the first byte + let expected_cid_bytes = expected.piece_cid.to_bytes(); + assert!(!data_cid_bytes.is_empty(), "Data CID should not be empty"); + // Verify the CID data matches + assert_eq!(data_cid_bytes[0], 0, "Data CID should start with 0x00 for ipld cbor reasons"); + assert_eq!( + &data_cid_bytes[1..], + expected_cid_bytes, + "Piece CID data mismatch: expected {:x?}, got {:x?}", + &expected_cid_bytes, + &data_cid_bytes[1..] + ); + + assert_eq!( + minimum_commitment_epoch, expected.minimum_commitment_epoch, + "Minimum commitment epoch mismatch: expected {}, got {}", + expected.minimum_commitment_epoch, minimum_commitment_epoch + ); +} + +#[vm_test] +pub fn evm_direct_call_fails_non_miner_test(v: &dyn VM) { + // This test is sanity checking that our `isMiner` logic in our NotificationReceiver contract is working, + // it does not actually check our miner actor logic. + + // Create accounts + let addrs = create_accounts(v, 2, &TokenAmount::from_whole(10_000)); + let (_owner, worker) = (addrs[0], addrs[1]); + + // Deploy the NotificationReceiver EVM contract + let hex_str = std::fs::read_to_string("../actors/evm/tests/contracts/NotificationReceiver.hex") + .expect("Failed to read contract bytecode hex file"); + let hex_str = hex_str.trim(); + let contract_bytecode = hex::decode(hex_str).expect("Failed to decode contract bytecode hex"); + + // Create an EVM actor to receive notifications + let params = + IpldBlock::serialize_cbor(&fil_actor_eam::CreateExternalParams(contract_bytecode)).unwrap(); + + let create_result = v + .execute_message( + &worker, + &EAM_ACTOR_ADDR, + &TokenAmount::zero(), + fil_actor_eam::Method::CreateExternal as u64, + params, + ) + .unwrap(); + + assert!( + create_result.code.is_success(), + "Failed to create EVM contract: {}", + create_result.message + ); + + let create_return: fil_actor_eam::CreateReturn = + create_result.ret.unwrap().deserialize().expect("Failed to decode create return"); + let evm_robust_addr = create_return.robust_address.unwrap(); + + // Now attempt to call handle_filecoin_method directly from an account actor (not a miner) + // We'll construct the CBOR parameters for a SectorContentChanged notification + + // Create a dummy notification payload that matches the expected format + let piece_cid = make_piece_cid(b"test-piece"); + let piece_size = PaddedPieceSize(32 << 30); // 32 GiB + let notification_payload = hex::decode("cafe").unwrap(); + + // Build CBOR encoded params for handle_filecoin_method using builtin miner types + // The structure should be: + // [{sector: 100, minimum_commitment_epoch: 1000, added: [{data: piece_cid, size: piece_size, payload: notification_payload}]}] + use fvm_ipld_encoding::to_vec; + + let sector_changes = vec![SectorChanges { + sector: 100, + minimum_commitment_epoch: 1000, + added: vec![PieceChange { + data: piece_cid, + size: piece_size, + payload: RawBytes::from(notification_payload), + }], + }]; + + let cbor_params = to_vec(§or_changes).expect("Failed to serialize CBOR params"); + + // Now call handle_filecoin_method using the alloy interface + let _method_selector = NotificationReceiver::handle_filecoin_methodCall::SELECTOR; + + // Encode the call using alloy's ABI encoding + let call_params = NotificationReceiver::handle_filecoin_methodCall::new(( + SECTOR_CONTENT_CHANGED, + CBOR, + cbor_params.into(), + )) + .abi_encode(); + + // Attempt to invoke the contract method from a regular account (not a miner) + let call_result = v.execute_message( + &worker, + &evm_robust_addr, + &TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(serialize_ok(&ContractParams(call_params.to_vec()))), + ); + + // Verify the error message contains information about the miner check + if let Ok(result) = call_result { + assert!(!result.code.is_success(), "Call should have failed with non-miner actor"); + } + + // Verify that no notifications were stored + check_receiver_notification_count(v, &worker, &evm_robust_addr, 0); +} diff --git a/integration_tests/src/tests/mod.rs b/integration_tests/src/tests/mod.rs index 7df8e9f4e..b82e3a94f 100644 --- a/integration_tests/src/tests/mod.rs +++ b/integration_tests/src/tests/mod.rs @@ -14,6 +14,8 @@ mod datacap_tests; pub use datacap_tests::*; mod evm_test; pub use evm_test::*; +mod evm_notification_test; +pub use evm_notification_test::*; mod extend_sectors_test; pub use extend_sectors_test::*; mod market_miner_withdrawal_test; diff --git a/test_vm/tests/suite/evm_notification_test.rs b/test_vm/tests/suite/evm_notification_test.rs new file mode 100644 index 000000000..6560db906 --- /dev/null +++ b/test_vm/tests/suite/evm_notification_test.rs @@ -0,0 +1,21 @@ +use fil_actors_integration_tests::tests::{ + evm_direct_call_fails_non_miner_test, evm_receives_ddo_notifications_test, +}; +use fil_actors_runtime::test_blockstores::MemoryBlockstore; +use test_vm::TestVM; + +/* Test out ddo notifications against a real solidity smart contract */ +#[test] +fn evm_notification() { + let store = MemoryBlockstore::new(); + let v = TestVM::new_with_singletons(store); + evm_receives_ddo_notifications_test(&v); +} + +/* Test that direct EVM calls to notification receiver fail from non-miner actors */ +#[test] +fn evm_direct_call_fails_non_miner() { + let store = MemoryBlockstore::new(); + let v = TestVM::new_with_singletons(store); + evm_direct_call_fails_non_miner_test(&v); +} diff --git a/test_vm/tests/suite/mod.rs b/test_vm/tests/suite/mod.rs index 729c855b2..aaca31a93 100644 --- a/test_vm/tests/suite/mod.rs +++ b/test_vm/tests/suite/mod.rs @@ -5,6 +5,7 @@ mod change_beneficiary_test; mod change_owner_test; mod commit_post_test; mod datacap_tests; +mod evm_notification_test; mod evm_test; mod extend_sectors_test; mod init_test;