diff --git a/pkg/core/native/native_test/ledger_test.go b/pkg/core/native/native_test/ledger_test.go index a7f43b383..b3fb555db 100644 --- a/pkg/core/native/native_test/ledger_test.go +++ b/pkg/core/native/native_test/ledger_test.go @@ -1,9 +1,12 @@ package native_test import ( + "fmt" "math/big" + "strings" "testing" + "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/core/transaction" @@ -209,3 +212,92 @@ func TestLedger_GetTransactionSigners(t *testing.T) { ledgerInvoker.InvokeFail(t, "expected []byte of size 32", "getTransactionSigners", []byte{1, 2, 3}) }) } + +func TestLedger_GetTransactionSignersInteropAPI(t *testing.T) { + c := newLedgerClient(t) + e := c.Executor + ledgerInvoker := c.WithSigners(c.Committee) + + // Firstly, add transaction with CalledByEntry rule-based signer scope to the chain. + tx := e.NewUnsignedTx(t, ledgerInvoker.Hash, "currentIndex") + tx.Signers = []transaction.Signer{{ + Account: c.Committee.ScriptHash(), + Scopes: transaction.Rules, + Rules: []transaction.WitnessRule{ + { + Action: transaction.WitnessAllow, + Condition: transaction.ConditionCalledByEntry{}, + }, + }, + }} + neotest.AddNetworkFee(e.Chain, tx, c.Committee) + neotest.AddSystemFee(e.Chain, tx, -1) + require.NoError(t, c.Committee.SignTx(e.Chain.GetConfig().Magic, tx)) + c.AddNewBlock(t, tx) + c.CheckHalt(t, tx.Hash(), stackitem.Make(e.Chain.BlockHeight()-1)) + + var ( + hashStr string + accStr string + txHash = tx.Hash().BytesBE() + acc = c.Committee.ScriptHash().BytesBE() + ) + for i := 0; i < util.Uint256Size; i++ { + hashStr += fmt.Sprintf("%#x", txHash[i]) + if i != util.Uint256Size-1 { + hashStr += ", " + } + } + for i := 0; i < util.Uint160Size; i++ { + accStr += fmt.Sprintf("%#x", acc[i]) + if i != util.Uint160Size-1 { + accStr += ", " + } + } + + // After that ensure interop API allows to retrieve signer with CalledByEntry rule-based scope. + src := `package callledger + import ( + "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/util" + ) + func CallLedger(accessValue bool) int { + signers := ledger.GetTransactionSigners(interop.Hash256{` + hashStr + `}) + if len(signers) != 1 { + panic("bad length") + } + s0 := signers[0] + expectedAcc := interop.Hash160{` + accStr + `} + if !util.Equals(string(s0.Account), string(expectedAcc)) { + panic("bad account") + } + if s0.Scopes != ledger.Rules { + panic("bad signer scope") + } + if len(s0.Rules) != 1 { + panic("bad rules length") + } + r0 := s0.Rules[0] + if r0.Action != ledger.WitnessAllow { + panic("bad action") + } + c0 := r0.Condition + if c0.Type != ledger.WitnessCalledByEntry { + panic("bad condition type") + } + if accessValue { + // Panic should occur here, because there's only Type inside the CalledByEntry condition. + _ = c0.Value + } + return 1 + }` + ctr := neotest.CompileSource(t, c.Committee.ScriptHash(), strings.NewReader(src), &compiler.Options{ + Name: "calledger_contract", + }) + e.DeployContract(t, ctr, nil) + + ctrInvoker := e.NewInvoker(ctr.Hash, e.Committee) + ctrInvoker.Invoke(t, 1, "callLedger", false) // Firstly, don't access CalledByEnrty Condition value => the call should be successful. + ctrInvoker.InvokeFail(t, `(PICKITEM): unhandled exception: "The value 1 is out of range."`, "callLedger", true) // Then, access the value to ensure it will panic. +} diff --git a/pkg/neotest/basic.go b/pkg/neotest/basic.go index 7904914e2..9ddfd72cf 100644 --- a/pkg/neotest/basic.go +++ b/pkg/neotest/basic.go @@ -107,8 +107,8 @@ func (e *Executor) SignTx(t testing.TB, tx *transaction.Transaction, sysFee int6 Scopes: transaction.Global, }) } - addNetworkFee(e.Chain, tx, signers...) - addSystemFee(e.Chain, tx, sysFee) + AddNetworkFee(e.Chain, tx, signers...) + AddSystemFee(e.Chain, tx, sysFee) for _, acc := range signers { require.NoError(t, acc.SignTx(e.Chain.GetConfig().Magic, tx)) @@ -276,12 +276,14 @@ func NewDeployTxBy(t testing.TB, bc blockchainer.Blockchainer, signer Signer, c Account: signer.ScriptHash(), Scopes: transaction.Global, }} - addNetworkFee(bc, tx, signer) + AddNetworkFee(bc, tx, signer) require.NoError(t, signer.SignTx(netmode.UnitTestNet, tx)) return tx } -func addSystemFee(bc blockchainer.Blockchainer, tx *transaction.Transaction, sysFee int64) { +// AddSystemFee adds system fee to the transaction. If negative value specified, +// then system fee is defined by test invocation. +func AddSystemFee(bc blockchainer.Blockchainer, tx *transaction.Transaction, sysFee int64) { if sysFee >= 0 { tx.SystemFee = sysFee return @@ -290,7 +292,8 @@ func addSystemFee(bc blockchainer.Blockchainer, tx *transaction.Transaction, sys tx.SystemFee = v.GasConsumed() } -func addNetworkFee(bc blockchainer.Blockchainer, tx *transaction.Transaction, signers ...Signer) { +// AddNetworkFee adds network fee to the transaction. +func AddNetworkFee(bc blockchainer.Blockchainer, tx *transaction.Transaction, signers ...Signer) { baseFee := bc.GetBaseExecFee() size := io.GetVarSize(tx) for _, sgr := range signers {