github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/services/rpcsrv/params/txBuilder.go (about)

     1  package params
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
     8  	"github.com/nspcc-dev/neo-go/pkg/io"
     9  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    10  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
    11  	"github.com/nspcc-dev/neo-go/pkg/util"
    12  	"github.com/nspcc-dev/neo-go/pkg/vm/emit"
    13  	"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
    14  )
    15  
    16  // ExpandFuncParameterIntoScript pushes provided FuncParam parameter
    17  // into the given buffer.
    18  func ExpandFuncParameterIntoScript(script *io.BinWriter, fp FuncParam) error {
    19  	switch fp.Type {
    20  	case smartcontract.ByteArrayType:
    21  		str, err := fp.Value.GetBytesBase64()
    22  		if err != nil {
    23  			return err
    24  		}
    25  		emit.Bytes(script, str)
    26  	case smartcontract.SignatureType:
    27  		str, err := fp.Value.GetBytesBase64()
    28  		if err != nil {
    29  			return err
    30  		}
    31  		emit.Bytes(script, str)
    32  	case smartcontract.StringType:
    33  		str, err := fp.Value.GetString()
    34  		if err != nil {
    35  			return err
    36  		}
    37  		emit.String(script, str)
    38  	case smartcontract.Hash160Type:
    39  		hash, err := fp.Value.GetUint160FromHex()
    40  		if err != nil {
    41  			return err
    42  		}
    43  		emit.Bytes(script, hash.BytesBE())
    44  	case smartcontract.Hash256Type:
    45  		hash, err := fp.Value.GetUint256()
    46  		if err != nil {
    47  			return err
    48  		}
    49  		emit.Bytes(script, hash.BytesBE())
    50  	case smartcontract.PublicKeyType:
    51  		str, err := fp.Value.GetString()
    52  		if err != nil {
    53  			return err
    54  		}
    55  		key, err := keys.NewPublicKeyFromString(string(str))
    56  		if err != nil {
    57  			return err
    58  		}
    59  		emit.Bytes(script, key.Bytes())
    60  	case smartcontract.IntegerType:
    61  		bi, err := fp.Value.GetBigInt()
    62  		if err != nil {
    63  			return err
    64  		}
    65  		emit.BigInt(script, bi)
    66  	case smartcontract.BoolType:
    67  		val, err := fp.Value.GetBoolean() // not GetBooleanStrict(), because that's the way C# code works
    68  		if err != nil {
    69  			return errors.New("not a bool")
    70  		}
    71  		emit.Bool(script, val)
    72  	case smartcontract.ArrayType:
    73  		val, err := fp.Value.GetArray()
    74  		if err != nil {
    75  			return err
    76  		}
    77  		err = ExpandArrayIntoScriptAndPack(script, val)
    78  		if err != nil {
    79  			return err
    80  		}
    81  	case smartcontract.MapType:
    82  		val, err := fp.Value.GetArray()
    83  		if err != nil {
    84  			return err
    85  		}
    86  		err = ExpandMapIntoScriptAndPack(script, val)
    87  		if err != nil {
    88  			return err
    89  		}
    90  	case smartcontract.AnyType:
    91  		if fp.Value.IsNull() || len(fp.Value.RawMessage) == 0 {
    92  			emit.Opcodes(script, opcode.PUSHNULL)
    93  		}
    94  	default:
    95  		return fmt.Errorf("parameter type %v is not supported", fp.Type)
    96  	}
    97  	return script.Err
    98  }
    99  
   100  // ExpandArrayIntoScript pushes all FuncParam parameters from the given array
   101  // into the given buffer in the reverse order.
   102  func ExpandArrayIntoScript(script *io.BinWriter, slice []Param) error {
   103  	for j := len(slice) - 1; j >= 0; j-- {
   104  		fp, err := slice[j].GetFuncParam()
   105  		if err != nil {
   106  			return err
   107  		}
   108  		err = ExpandFuncParameterIntoScript(script, fp)
   109  		if err != nil {
   110  			return fmt.Errorf("param %d: %w", j, err)
   111  		}
   112  	}
   113  	return script.Err
   114  }
   115  
   116  // ExpandArrayIntoScriptAndPack expands provided array into script and packs the
   117  // resulting items in the array.
   118  func ExpandArrayIntoScriptAndPack(script *io.BinWriter, slice []Param) error {
   119  	if len(slice) == 0 {
   120  		emit.Opcodes(script, opcode.NEWARRAY0)
   121  		return script.Err
   122  	}
   123  	err := ExpandArrayIntoScript(script, slice)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	emit.Int(script, int64(len(slice)))
   128  	emit.Opcodes(script, opcode.PACK)
   129  	return script.Err
   130  }
   131  
   132  // ExpandMapIntoScriptAndPack expands provided array of key-value items into script
   133  // and packs the resulting pairs in the [stackitem.Map].
   134  func ExpandMapIntoScriptAndPack(script *io.BinWriter, slice []Param) error {
   135  	if len(slice) == 0 {
   136  		emit.Opcodes(script, opcode.NEWMAP)
   137  		return script.Err
   138  	}
   139  	for i := len(slice) - 1; i >= 0; i-- {
   140  		pair, err := slice[i].GetFuncParamPair()
   141  		if err != nil {
   142  			return err
   143  		}
   144  		err = ExpandFuncParameterIntoScript(script, pair.Value)
   145  		if err != nil {
   146  			return fmt.Errorf("map value %d: %w", i, err)
   147  		}
   148  		err = ExpandFuncParameterIntoScript(script, pair.Key)
   149  		if err != nil {
   150  			return fmt.Errorf("map key %d: %w", i, err)
   151  		}
   152  	}
   153  	emit.Int(script, int64(len(slice)))
   154  	emit.Opcodes(script, opcode.PACKMAP)
   155  	return script.Err
   156  }
   157  
   158  // CreateFunctionInvocationScript creates a script to invoke the given contract with
   159  // the given parameters.
   160  func CreateFunctionInvocationScript(contract util.Uint160, method string, param *Param) ([]byte, error) {
   161  	script := io.NewBufBinWriter()
   162  	if param == nil {
   163  		emit.Opcodes(script.BinWriter, opcode.NEWARRAY0)
   164  	} else if slice, err := param.GetArray(); err == nil {
   165  		err = ExpandArrayIntoScriptAndPack(script.BinWriter, slice)
   166  		if err != nil {
   167  			return nil, err
   168  		}
   169  	} else {
   170  		return nil, fmt.Errorf("failed to convert %s to script parameter", param)
   171  	}
   172  
   173  	emit.AppCallNoArgs(script.BinWriter, contract, method, callflag.All)
   174  	return script.Bytes(), nil
   175  }