diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index f7ea2fed2..3cc9e2c18 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -210,6 +210,10 @@ func NewCommands() []cli.Command { symbols around array values to denote array bounds. Nested arrays are also supported. + There is ability to provide an argument of 'bytearray' type via file. Use a + special 'filebytes' argument type for this with a filepath specified after + the colon, e.g. 'filebytes:my_file.txt'. + Given values are type-checked against given types with the following restrictions applied: * 'signature' type values should be hex-encoded and have a (decoded) @@ -222,6 +226,7 @@ func NewCommands() []cli.Command { * 'hash256' type values should be hex-encoded and have a (decoded) length of 32 bytes. * 'bytes' type values are any hex-encoded things. + * 'filebytes' type values are filenames with the argument value inside. * 'key' type values are hex-encoded marshalled public keys. * 'string' type values are any valid UTF-8 strings. In the value's part of the string the colon looses it's special meaning as a separator between @@ -250,6 +255,7 @@ func NewCommands() []cli.Command { * 'bad' is a string with a value of 'bad' * 'dead' is a byte array with a value of 'dead' * 'string:dead' is a string with a value of 'dead' + * 'filebytes:my_data.txt' is bytes decoded from a content of my_data.txt * 'AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y' is a hash160 with a value of '23ba2703c53263e8d6e522dc32203339dcd8eee9' * '\4\2' is an integer with a value of 42 diff --git a/pkg/smartcontract/param_type.go b/pkg/smartcontract/param_type.go index 775ac49ee..2875e60bd 100644 --- a/pkg/smartcontract/param_type.go +++ b/pkg/smartcontract/param_type.go @@ -35,6 +35,9 @@ const ( VoidType ParamType = 0xff ) +// fileBytesParamType is a string representation of `filebytes` parameter type used in cli. +const fileBytesParamType string = "filebytes" + // validParamTypes contains a map of known ParamTypes var validParamTypes = map[ParamType]bool{ UnknownType: true, @@ -142,7 +145,7 @@ func (pt *ParamType) DecodeBinary(r *io.BinReader) { // int, integer -> IntegerType // hash160 -> Hash160Type // hash256 -> Hash256Type -// bytes, bytearray -> ByteArrayType +// bytes, bytearray, filebytes -> ByteArrayType // key, publickey -> PublicKeyType // string -> StringType // array, struct -> ArrayType @@ -162,7 +165,7 @@ func ParseParamType(typ string) (ParamType, error) { return Hash160Type, nil case "hash256": return Hash256Type, nil - case "bytes", "bytearray", "bytestring": + case "bytes", "bytearray", "bytestring", fileBytesParamType: return ByteArrayType, nil case "key", "publickey": return PublicKeyType, nil @@ -223,11 +226,7 @@ func adjustValToType(typ ParamType, val string) (interface{}, error) { } return u, nil case ByteArrayType: - b, err := hex.DecodeString(val) - if err != nil { - return nil, err - } - return b, nil + return hex.DecodeString(val) case PublicKeyType: pub, err := keys.NewPublicKeyFromString(val) if err != nil { diff --git a/pkg/smartcontract/param_type_test.go b/pkg/smartcontract/param_type_test.go index c55e41f4d..1d3c54819 100644 --- a/pkg/smartcontract/param_type_test.go +++ b/pkg/smartcontract/param_type_test.go @@ -59,7 +59,11 @@ func TestParseParamType(t *testing.T) { }, { in: "qwerty", err: true, - }} + }, { + in: "filebytes", + out: ByteArrayType, + }, + } for _, inout := range inouts { out, err := ParseParamType(inout.in) if inout.err { diff --git a/pkg/smartcontract/parameter.go b/pkg/smartcontract/parameter.go index 08d453916..5c870add7 100644 --- a/pkg/smartcontract/parameter.go +++ b/pkg/smartcontract/parameter.go @@ -7,6 +7,7 @@ import ( "encoding/json" "errors" "fmt" + "io/ioutil" "math" "math/bits" "strconv" @@ -387,6 +388,7 @@ func NewParameterFromString(in string) (*Parameter, error) { escaped bool hadType bool res = &Parameter{} + typStr string ) r = strings.NewReader(in) for char, _, err = r.ReadRune(); err == nil && char != utf8.RuneError; char, _, err = r.ReadRune() { @@ -395,7 +397,7 @@ func NewParameterFromString(in string) (*Parameter, error) { continue } if char == ':' && !escaped && !hadType { - typStr := buf.String() + typStr = buf.String() res.Type, err = ParseParamType(typStr) if err != nil { return nil, err @@ -422,6 +424,13 @@ func NewParameterFromString(in string) (*Parameter, error) { if !hadType { res.Type = inferParamType(val) } + if res.Type == ByteArrayType && typStr == fileBytesParamType { + res.Value, err = ioutil.ReadFile(val) + if err != nil { + return nil, fmt.Errorf("failed to read '%s' parameter from file '%s': %w", fileBytesParamType, val, err) + } + return res, nil + } res.Value, err = adjustValToType(res.Type, val) if err != nil { return nil, err diff --git a/pkg/smartcontract/parameter_test.go b/pkg/smartcontract/parameter_test.go index 0a9c49251..45b421568 100644 --- a/pkg/smartcontract/parameter_test.go +++ b/pkg/smartcontract/parameter_test.go @@ -481,6 +481,12 @@ func TestNewParameterFromString(t *testing.T) { }, { in: `Map:[]`, err: true, + }, { + in: "filebytes:./testdata/adjustValToType_filebytes_good.txt", + out: Parameter{Type: ByteArrayType, Value: []byte{0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x65, 0x66}}, + }, { + in: "filebytes:./testdata/does_not_exists.txt", + err: true, }} for _, inout := range inouts { out, err := NewParameterFromString(inout.in) diff --git a/pkg/smartcontract/testdata/adjustValToType_filebytes_good.txt b/pkg/smartcontract/testdata/adjustValToType_filebytes_good.txt new file mode 100644 index 000000000..593cb22e1 --- /dev/null +++ b/pkg/smartcontract/testdata/adjustValToType_filebytes_good.txt @@ -0,0 +1 @@ +010203ef \ No newline at end of file