mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-26 19:42:23 +00:00
nef: treat Version as string
Following changes in C# code and simpilifying things a lot.
This commit is contained in:
parent
e12c52f588
commit
4d0eaef510
2 changed files with 25 additions and 175 deletions
|
@ -4,9 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
|
@ -21,7 +18,7 @@ import (
|
||||||
// +------------+-----------+------------------------------------------------------------+
|
// +------------+-----------+------------------------------------------------------------+
|
||||||
// | Magic | 4 bytes | Magic header |
|
// | Magic | 4 bytes | Magic header |
|
||||||
// | Compiler | 32 bytes | Compiler used |
|
// | Compiler | 32 bytes | Compiler used |
|
||||||
// | Version | 16 bytes | Compiler version (Major, Minor, Build, Version) |
|
// | Version | 16 bytes | Compiler version |
|
||||||
// +------------+-----------+------------------------------------------------------------+
|
// +------------+-----------+------------------------------------------------------------+
|
||||||
// | Script | Var bytes | Var bytes for the payload |
|
// | Script | Var bytes | Var bytes for the payload |
|
||||||
// +------------+-----------+------------------------------------------------------------+
|
// +------------+-----------+------------------------------------------------------------+
|
||||||
|
@ -35,6 +32,8 @@ const (
|
||||||
MaxScriptLength = 1024 * 1024
|
MaxScriptLength = 1024 * 1024
|
||||||
// compilerFieldSize is the length of `Compiler` File header field in bytes.
|
// compilerFieldSize is the length of `Compiler` File header field in bytes.
|
||||||
compilerFieldSize = 32
|
compilerFieldSize = 32
|
||||||
|
// versionFieldSize is the length of `Version` File header field in bytes.
|
||||||
|
versionFieldSize = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
// File represents compiled contract file structure according to the NEF3 standard.
|
// File represents compiled contract file structure according to the NEF3 standard.
|
||||||
|
@ -48,97 +47,26 @@ type File struct {
|
||||||
type Header struct {
|
type Header struct {
|
||||||
Magic uint32
|
Magic uint32
|
||||||
Compiler string
|
Compiler string
|
||||||
Version Version
|
Version string
|
||||||
}
|
|
||||||
|
|
||||||
// Version represents compiler version.
|
|
||||||
type Version struct {
|
|
||||||
Major int32
|
|
||||||
Minor int32
|
|
||||||
Build int32
|
|
||||||
Revision int32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFile returns new NEF3 file with script specified.
|
// NewFile returns new NEF3 file with script specified.
|
||||||
func NewFile(script []byte) (File, error) {
|
func NewFile(script []byte) (*File, error) {
|
||||||
file := File{
|
file := &File{
|
||||||
Header: Header{
|
Header: Header{
|
||||||
Magic: Magic,
|
Magic: Magic,
|
||||||
Compiler: "neo-go",
|
Compiler: "neo-go",
|
||||||
|
Version: config.Version,
|
||||||
},
|
},
|
||||||
Script: script,
|
Script: script,
|
||||||
}
|
}
|
||||||
v, err := GetVersion(config.Version)
|
if len(config.Version) > versionFieldSize {
|
||||||
if err != nil {
|
return nil, errors.New("too long version")
|
||||||
return file, err
|
|
||||||
}
|
}
|
||||||
file.Header.Version = v
|
|
||||||
file.Checksum = file.CalculateChecksum()
|
file.Checksum = file.CalculateChecksum()
|
||||||
return file, nil
|
return file, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVersion returns Version from the given string. It accepts the following formats:
|
|
||||||
// `major[-...].minor[-...].build[-...]` and `major[-...].minor[-...].build[-...].revision[-...]`
|
|
||||||
// where `major`, `minor`, `build` and `revision` are 32-bit integers with base=10
|
|
||||||
func GetVersion(version string) (Version, error) {
|
|
||||||
var (
|
|
||||||
result Version
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
versions := strings.SplitN(version, ".", 4)
|
|
||||||
if len(versions) < 3 {
|
|
||||||
return result, errors.New("invalid version format")
|
|
||||||
}
|
|
||||||
result.Major, err = parseDashedVersion(versions[0])
|
|
||||||
if err != nil {
|
|
||||||
return result, fmt.Errorf("failed to parse major version: %w", err)
|
|
||||||
}
|
|
||||||
result.Minor, err = parseDashedVersion(versions[1])
|
|
||||||
if err != nil {
|
|
||||||
return result, fmt.Errorf("failed to parse minor version: %w", err)
|
|
||||||
|
|
||||||
}
|
|
||||||
result.Build, err = parseDashedVersion(versions[2])
|
|
||||||
if err != nil {
|
|
||||||
return result, fmt.Errorf("failed to parse build version: %w", err)
|
|
||||||
}
|
|
||||||
if len(versions) == 4 {
|
|
||||||
result.Revision, err = parseDashedVersion(versions[3])
|
|
||||||
if err != nil {
|
|
||||||
return result, fmt.Errorf("failed to parse revision version: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseDashedVersion extracts int from string of the format `int[-...]` where `int` is
|
|
||||||
// a 32-bit integer with base=10.
|
|
||||||
func parseDashedVersion(version string) (int32, error) {
|
|
||||||
version = strings.SplitN(version, "-", 2)[0]
|
|
||||||
result, err := strconv.ParseInt(version, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return int32(result), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable interface.
|
|
||||||
func (v *Version) EncodeBinary(w *io.BinWriter) {
|
|
||||||
w.WriteU32LE(uint32(v.Major))
|
|
||||||
w.WriteU32LE(uint32(v.Minor))
|
|
||||||
w.WriteU32LE(uint32(v.Build))
|
|
||||||
w.WriteU32LE(uint32(v.Revision))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable interface.
|
|
||||||
func (v *Version) DecodeBinary(r *io.BinReader) {
|
|
||||||
v.Major = int32(r.ReadU32LE())
|
|
||||||
v.Minor = int32(r.ReadU32LE())
|
|
||||||
v.Build = int32(r.ReadU32LE())
|
|
||||||
v.Revision = int32(r.ReadU32LE())
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable interface.
|
// EncodeBinary implements io.Serializable interface.
|
||||||
func (h *Header) EncodeBinary(w *io.BinWriter) {
|
func (h *Header) EncodeBinary(w *io.BinWriter) {
|
||||||
w.WriteU32LE(h.Magic)
|
w.WriteU32LE(h.Magic)
|
||||||
|
@ -146,12 +74,15 @@ func (h *Header) EncodeBinary(w *io.BinWriter) {
|
||||||
w.Err = errors.New("invalid compiler name length")
|
w.Err = errors.New("invalid compiler name length")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bytes := []byte(h.Compiler)
|
var b = make([]byte, compilerFieldSize)
|
||||||
w.WriteBytes(bytes)
|
copy(b, []byte(h.Compiler))
|
||||||
if len(bytes) < compilerFieldSize {
|
w.WriteBytes(b)
|
||||||
w.WriteBytes(make([]byte, compilerFieldSize-len(bytes)))
|
b = b[:versionFieldSize]
|
||||||
|
for i := range b {
|
||||||
|
b[i] = 0
|
||||||
}
|
}
|
||||||
h.Version.EncodeBinary(w)
|
copy(b, []byte(h.Version))
|
||||||
|
w.WriteBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable interface.
|
// DecodeBinary implements io.Serializable interface.
|
||||||
|
@ -167,7 +98,12 @@ func (h *Header) DecodeBinary(r *io.BinReader) {
|
||||||
return r == 0
|
return r == 0
|
||||||
})
|
})
|
||||||
h.Compiler = string(buf)
|
h.Compiler = string(buf)
|
||||||
h.Version.DecodeBinary(r)
|
buf = buf[:versionFieldSize]
|
||||||
|
r.ReadBytes(buf)
|
||||||
|
buf = bytes.TrimRightFunc(buf, func(r rune) bool {
|
||||||
|
return r == 0
|
||||||
|
})
|
||||||
|
h.Version = string(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CalculateChecksum returns first 4 bytes of double-SHA256(Header) converted to uint32.
|
// CalculateChecksum returns first 4 bytes of double-SHA256(Header) converted to uint32.
|
||||||
|
|
|
@ -13,12 +13,7 @@ func TestEncodeDecodeBinary(t *testing.T) {
|
||||||
Header: Header{
|
Header: Header{
|
||||||
Magic: Magic,
|
Magic: Magic,
|
||||||
Compiler: "the best compiler ever",
|
Compiler: "the best compiler ever",
|
||||||
Version: Version{
|
Version: "1.2.3.4",
|
||||||
Major: 1,
|
|
||||||
Minor: 2,
|
|
||||||
Build: 3,
|
|
||||||
Revision: 4,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Script: script,
|
Script: script,
|
||||||
}
|
}
|
||||||
|
@ -67,12 +62,7 @@ func TestBytesFromBytes(t *testing.T) {
|
||||||
Header: Header{
|
Header: Header{
|
||||||
Magic: Magic,
|
Magic: Magic,
|
||||||
Compiler: "the best compiler ever",
|
Compiler: "the best compiler ever",
|
||||||
Version: Version{
|
Version: "1.2.3.4",
|
||||||
Major: 1,
|
|
||||||
Minor: 2,
|
|
||||||
Build: 3,
|
|
||||||
Revision: 4,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Script: script,
|
Script: script,
|
||||||
}
|
}
|
||||||
|
@ -84,79 +74,3 @@ func TestBytesFromBytes(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetVersion(t *testing.T) {
|
|
||||||
testCases := map[string]struct {
|
|
||||||
input string
|
|
||||||
fails bool
|
|
||||||
expected Version
|
|
||||||
}{
|
|
||||||
"major only": {
|
|
||||||
input: "1",
|
|
||||||
fails: true,
|
|
||||||
},
|
|
||||||
"major and minor only": {
|
|
||||||
input: "1.1",
|
|
||||||
fails: true,
|
|
||||||
},
|
|
||||||
"major, minor and revision only": {
|
|
||||||
input: "1.1.1",
|
|
||||||
expected: Version{
|
|
||||||
Major: 1,
|
|
||||||
Minor: 1,
|
|
||||||
Build: 1,
|
|
||||||
Revision: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"full version": {
|
|
||||||
input: "1.1.1.1",
|
|
||||||
expected: Version{
|
|
||||||
Major: 1,
|
|
||||||
Minor: 1,
|
|
||||||
Build: 1,
|
|
||||||
Revision: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"dashed, without revision": {
|
|
||||||
input: "1-pre.2-pre.3-pre",
|
|
||||||
expected: Version{
|
|
||||||
Major: 1,
|
|
||||||
Minor: 2,
|
|
||||||
Build: 3,
|
|
||||||
Revision: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"dashed, full version": {
|
|
||||||
input: "1-pre.2-pre.3-pre.4-pre",
|
|
||||||
expected: Version{
|
|
||||||
Major: 1,
|
|
||||||
Minor: 2,
|
|
||||||
Build: 3,
|
|
||||||
Revision: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"dashed build": {
|
|
||||||
input: "1.2.3-pre.4",
|
|
||||||
expected: Version{
|
|
||||||
Major: 1,
|
|
||||||
Minor: 2,
|
|
||||||
Build: 3,
|
|
||||||
Revision: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"extra versions": {
|
|
||||||
input: "1.2.3.4.5",
|
|
||||||
fails: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for name, test := range testCases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
actual, err := GetVersion(test.input)
|
|
||||||
if test.fails {
|
|
||||||
require.NotNil(t, err)
|
|
||||||
} else {
|
|
||||||
require.Equal(t, test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue