Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented rpc server method GetRawTransaction #135

Merged
merged 25 commits into from
Feb 20, 2019
Merged

Implemented rpc server method GetRawTransaction #135

merged 25 commits into from
Feb 20, 2019

Conversation

dauTT
Copy link
Contributor

@dauTT dauTT commented Feb 10, 2019

Problem

Implemented #129

...

Solution

  • Added utility function GetVarSize. (reference)
  • Implemented serializable interface
  • Added fee calculation
  • Finally Implemented getrawtransaction rcp call.

To make sure that things are correct, I have added tests and checked the result against neo-python and neo-cli.

...

…ializable interface. 2) Added few arithmetic operation (Add, Sub, div): this will be used to calculated networkfeeand feePerByte. Changed return value of the Value() method to int instead of int64. Modified fixed8_test accordingly.
- Structs accepting the Size method implement the serializable interface.
- Structs accepting the MarshalJSON method implements the customized json marshaller interface.
config/config.go Outdated
EnrollmentTransaction int `yaml:"EnrollmentTransaction"`
IssueTransaction int `yaml:"IssueTransaction"`
PublishTransaction int `yaml:"PublishTransaction"`
RegisterTransaction int `yaml:"RegisterTransaction"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it must be int64

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or.. mb float64

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the C# implementation is int64

public Fixed8(long data)
        {
            this.value = data;
        }

Why do you suggest float64?

Shall I change everything so that the NewFixed8 method accept int64?

// NewFixed8 return a new Fixed8 type multiplied by decimals.
func NewFixed8(val int64) Fixed8 {
	return Fixed8(decimals * val)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dauTT yep, long / int64 for new.. but...

public Fixed8(long data)
        {
            this.value = data;
        }

and

        public static Fixed8 FromDecimal(decimal value)
        {
            value *= D;
            if (value < long.MinValue || value > long.MaxValue)
                throw new OverflowException();
            return new Fixed8
            {
                value = (long)value
            };
        }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private const long D = 100_000_000;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, I think I understood what you mean.

case transaction.RegisterType:
return util.NewFixed8(s.RegisterTransaction)
default:
return util.NewFixed8(0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we must fix NewFixed8 method:

// NewFixed8 return a new Fixed8 type multiplied by decimals.
func NewFixed8(val int64) Fixed8 {
	return Fixed8(decimals * val)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or mb to:

// NewFixed8 return a new Fixed8 type multiplied by decimals.
func NewFixed8(val float64) Fixed8 {
	return Fixed8(int64(decimals * val))
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see there

@@ -25,6 +25,8 @@ var (
genAmount = []int{8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
decrementInterval = 2000000
persistInterval = 1 * time.Second

BlockchainDefault *Blockchain
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it can't be exported

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean it can't be exported? I Do need to register a blockchain as a default one in order to use it in the References method in the fee.go file.

func References(t *transaction.Transaction) map[util.Uint256]*transaction.Output {
	references := make(map[util.Uint256]*transaction.Output)

 	for prevHash, inputs := range t.GroupInputsByPrevHash() {
		if BlockchainDefault == nil {
			panic("no default blockchain available! please register one.")
		} else if tx, _, err := BlockchainDefault.GetTransaction(prevHash); err != nil {
			tx = nil
		} else if tx != nil {
			for _, in := range inputs {
				references[in.PrevHash] = tx.Outputs[in.PrevIndex]
			}
		} else {
			references = nil
		}
	}
	return references
}

At least within the core package, I do not have any problem to use it BlockchainDefault.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data race in runtime...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a bad practice, to set globalVariable..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the link!

@im-kulikov
Copy link
Contributor

im-kulikov commented Feb 12, 2019

Package unsafe contains operations that step around the type safety of Go programs.
Packages that import unsafe may be non-portable and are not protected by the Go 1 compatibility guidelines.

https://golang.org/pkg/unsafe/

var AttrUsage uint8

it's 1 byte

can be simplified to...

// Size returns the size in number bytes of the Attribute
func (attr *Attribute) Size() int {
	switch attr.Usage {
	case ContractHash, ECDH02, ECDH03, Vote,
		Hash1, Hash2, Hash3, Hash4, Hash5, Hash6, Hash7, Hash8, Hash9, Hash10, Hash11, Hash12, Hash13, Hash14, Hash15:
		return 33 // uint8 + 32
	case Script:
		return 21 // uint8 + 20
	case Description:
		return 2 + len(attr.Data) // uint8 + uint8 + len of data
	default:
		return 1 + len(attr.Data) // uint8 + len of data
	}
}
// GetVarIntSize returns the size in number of bytes of a variable integer
// (reference: GetVarSize(int value),  https://github.com/neo-project/neo/blob/master/neo/IO/Helper.cs)
func GetVarIntSize(value int) int {
	var size uintptr

	if value < 0xFD {
		size = 1 // uint8
	} else if value <= 0xFFFF {
		size = 3 // uint8 + uint16
	} else {
		size = 5 // uint8 + uint32
	}
	return int(size)
}
// GetVarSize return the size om bytes of a variable. This implementation is not exactly like the C#
// (reference: GetVarSize<T>(this T[] value), https://github.com/neo-project/neo/blob/master/neo/IO/Helper.cs#L53) as in the C# variable
// like Uint160, Uint256 are not supported. @TODO: make sure to have full unit tests coverage.
func GetVarSize(value interface{}) int {
	v := reflect.ValueOf(value)
	switch v.Kind() {
	case reflect.String:
		return GetVarStringSize(v.String())
	case reflect.Int:
		return GetVarIntSize(int(v.Int()))
	case reflect.Slice, reflect.Array:
		valueLength := v.Len()
		valueSize := 0

		switch t := value.(type) {
		case io.Serializable:
			for i := 0; i < valueLength; i++ {
				valueSize += t.Size()
			}
		case []uint8, []int8,
			Uint160, Uint256,
			[20]uint8, [32]uint8,
			[20]int8, [32]int8:
			valueSize = valueLength
		case []uint16, []int16,
			[10]uint16, [10]int16:
			valueSize = valueLength * 2
		case []uint32, []int32,
			[30]uint32, [30]int32:
			valueSize = valueLength * 4
		case []uint64, []int64,
			[30]uint64, [30]int64:
			valueSize = valueLength * 8
		}
		return GetVarIntSize(valueLength) + valueSize
	default:
		panic(fmt.Sprintf("unable to calculate GetVarSize, %s", reflect.TypeOf(value)))
	}
}
// Size returns the size of the transaction in term of bytes
func (t *Transaction) Size() int {
	attrSize := util.GetVarSize(t.Attributes)
	inputSize := util.GetVarSize(t.Inputs)
	outputSize := util.GetVarSize(t.Outputs)
	witnesSize := util.GetVarSize(t.Scripts)
	// uint8 + uint8 + + attrSize + inputSize + outputSize + witnesSize
	return 2 + attrSize + inputSize + outputSize + witnesSize
}
// Size returns the size in bytes of the Input
func (in *Input) Size() int { return in.PrevHash.Size() + 2 } // uint16
// Size returns the lenght of the bytes representation of Uint256
func (u Uint256) Size() int { return uint256Size }
// Size returns the lenght of the bytes representation of Uint160
func (u Uint160) Size() int { return uint160Size }

@im-kulikov
Copy link
Contributor

or mb:

// GetVarSize return the size om bytes of a variable. This implementation is not exactly like the C#
// (reference: GetVarSize<T>(this T[] value), https://github.com/neo-project/neo/blob/master/neo/IO/Helper.cs#L53) as in the C# variable
// like Uint160, Uint256 are not supported. @TODO: make sure to have full unit tests coverage.
func GetVarSize(value interface{}) int {
	v := reflect.ValueOf(value)
	switch v.Kind() {
	case reflect.String:
		return GetVarStringSize(v.String())
	case reflect.Int,
		reflect.Int8,
		reflect.Int16,
		reflect.Int32,
		reflect.Int64:
		return GetVarIntSize(int(v.Int()))
	case reflect.Uint,
		reflect.Uint8,
		reflect.Uint16,
		reflect.Uint32,
		reflect.Uint64:
		return GetVarIntSize(int(v.Uint()))
	case reflect.Slice, reflect.Array:
		valueLength := v.Len()
		valueSize := 0

		switch t := value.(type) {
		case io.Serializable:
			for i := 0; i < valueLength; i++ {
				valueSize += t.Size()
			}
		case []uint8, []int8,
			Uint160, Uint256,
			[20]uint8, [32]uint8,
			[20]int8, [32]int8:
			valueSize = valueLength
		case []uint16, []int16,
			[10]uint16, [10]int16:
			valueSize = valueLength * 2
		case []uint32, []int32,
			[30]uint32, [30]int32:
			valueSize = valueLength * 4
		case []uint64, []int64,
			[30]uint64, [30]int64:
			valueSize = valueLength * 8
		}
		return GetVarIntSize(valueLength) + valueSize
	default:
		panic(fmt.Sprintf("unable to calculate GetVarSize, %s", reflect.TypeOf(value)))
	}
}

Copy link
Contributor

@im-kulikov im-kulikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove unsafe from code..

@dauTT
Copy link
Contributor Author

dauTT commented Feb 12, 2019

I agree that your suggestion above to change the GeVarSize is good. However the test TestSize in the fee_test.go file is failing. The reason is that case

case io.Serializable:
			for i := 0; i < valueLength; i++ {
				valueSize += t.Size()
			}

is not working properly because type is not a Struct but a Inteface. I still need to do a comprimise in this way:

func GetVarSize(value interface{}) int {
	v := reflect.ValueOf(value)
	switch v.Kind() {
	case reflect.String:
		return GetVarStringSize(v.String())
	case reflect.Int,
		reflect.Int8,
		reflect.Int16,
		reflect.Int32,
		reflect.Int64:
		return GetVarIntSize(int(v.Int()))
	case reflect.Uint,
		reflect.Uint8,
		reflect.Uint16,
		reflect.Uint32,
		reflect.Uint64:
		return GetVarIntSize(int(v.Uint()))
	case reflect.Slice, reflect.Array:
		valueLength := v.Len()
		valueSize := 0

		// case v is a slice / Array of io.Serializable
		typeArray := reflect.TypeOf(value).Elem()
		SerializableType := reflect.TypeOf((*io.Serializable)(nil)).Elem()
		if typeArray.Implements(SerializableType) {
			for i := 0; i < valueLength; i++ {
				elem := v.Index(i).Interface().(io.Serializable)
				valueSize += elem.Size()
			}
		}
		switch value.(type) {
		case []uint8, []int8,
			Uint160, Uint256,
			[20]uint8, [32]uint8,
			[20]int8, [32]int8:
			fmt.Println("t []uint8")
			valueSize = valueLength
		case []uint16, []int16,
			[10]uint16, [10]int16:
			fmt.Println("t []uint16")
			valueSize = valueLength * 2
		case []uint32, []int32,
			[30]uint32, [30]int32:
			fmt.Println("t []uint32")
			valueSize = valueLength * 4
		case []uint64, []int64,
			[30]uint64, [30]int64:
			fmt.Println("t", "[]uint64")
			valueSize = valueLength * 8
		}

		return GetVarIntSize(valueLength) + valueSize
	default:
		panic(fmt.Sprintf("unable to calculate GetVarSize, %s", reflect.TypeOf(value)))
	}
}

Are you okay with this?

@im-kulikov
Copy link
Contributor

Let me see what the reason is at the morning.

@im-kulikov
Copy link
Contributor

You can try []io.Serizable, if you're doing it now

1) NewFixed8 will accept as input int64
2) race condition affecting configDeafault, blockchainDefault
@dauTT
Copy link
Contributor Author

dauTT commented Feb 12, 2019

bad luck, still not working..

case []io.Serializable:
			for i := 0; i < valueLength; i++ {
				valueSize += t[i].Size()
			}

@im-kulikov
Copy link
Contributor

im-kulikov commented Feb 13, 2019

@dauTT

// GetVarSize return the size om bytes of a variable. This implementation is not exactly like the C#
// (reference: GetVarSize<T>(this T[] value), https://github.com/neo-project/neo/blob/master/neo/IO/Helper.cs#L53) as in the C# variable
// like Uint160, Uint256 are not supported. @TODO: make sure to have full unit tests coverage.
func GetVarSize(value interface{}) int {
	v := reflect.ValueOf(value)
	switch v.Kind() {
	case reflect.String:
		return GetVarStringSize(v.String())
	case reflect.Int,
		reflect.Int8,
		reflect.Int16,
		reflect.Int32,
		reflect.Int64:
		return GetVarIntSize(int(v.Int()))
	case reflect.Uint,
		reflect.Uint8,
		reflect.Uint16,
		reflect.Uint32,
		reflect.Uint64:
		return GetVarIntSize(int(v.Uint()))
	case reflect.Slice, reflect.Array:
		valueLength := v.Len()
		valueSize := 0

		switch reflect.ValueOf(value).Index(0).Interface().(type) {
		case io.Serializable:
			for i := 0; i < valueLength; i++ {
				elem := v.Index(i).Interface().(io.Serializable)
				valueSize += elem.Size()
			}
		case uint8, int8:
			valueSize = valueLength
		case uint16, int16:
			valueSize = valueLength * 2
		case uint32, int32:
			valueSize = valueLength * 4
		case uint64, int64:
			valueSize = valueLength * 8
		}

		return GetVarIntSize(valueLength) + valueSize
	default:
		panic(fmt.Sprintf("unable to calculate GetVarSize, %s", reflect.TypeOf(value)))
	}
}

config/config.go Outdated
@@ -27,6 +30,10 @@ var (
// BuildTime the time and date the current version of the node built,
// set at build time.
BuildTime string

// ConfigDefault is the Config which is defined in the Load method.
configDefault *Config
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't use global variables

@@ -25,6 +26,10 @@ var (
genAmount = []int{8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
decrementInterval = 2000000
persistInterval = 1 * time.Second

// BlockchainDefault is the Blockchain which is defined in the NewBlockchain method.
blockchainDefault *Blockchain
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't use global variables

dauTT and others added 3 commits February 13, 2019 18:40
2) Extended Blockchainer interface to include the methods: References, FeePerByte, SystemFee, NetworkFee
3) Deleted fee_test.go, fee.go. Moved corresponding methods to blockchain_test.go and blockchain.go respectively
4) Amended tx_raw_output.go
case "getrawtransaction":
var err error

param0, exists := reqParams.ValueAtAndType(0, "string")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you replace
param0, exists := reqParams.ValueAtAndType(0, "string")
to
param0, exists := reqParams.ValueWithType(0, "string")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Contributor

@im-kulikov im-kulikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose to gradually abandon ValueAtAndType and Value in favor of a more correct implementation of Value / ValueWithType

@im-kulikov
Copy link
Contributor

@dauTT if you do not mind, I will look at the weekend and fully review this PR

@dauTT
Copy link
Contributor Author

dauTT commented Feb 18, 2019

okay, I will change and use require

@im-kulikov
Copy link
Contributor

im-kulikov commented Feb 18, 2019

@dauTT thanks a lot. You did a great job.

Copy link
Contributor

@im-kulikov im-kulikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, you’re amazing 🚀🎉

@dauTT
Copy link
Contributor Author

dauTT commented Feb 18, 2019

I shall say, YOU are an amazing reviewer. 😄

@im-kulikov
Copy link
Contributor

@dauTT can you fix conflicts?

@im-kulikov
Copy link
Contributor

image

hm..

@dauTT
Copy link
Contributor Author

dauTT commented Feb 19, 2019

tests are failing. Will fix it.

@dauTT
Copy link
Contributor Author

dauTT commented Feb 19, 2019

The code is clean now.

Copy link
Contributor

@im-kulikov im-kulikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM, any thoughts @decentralisedkev / @fabwa

@dauTT
Copy link
Contributor Author

dauTT commented Feb 20, 2019

After the merge the tests are failing. Please do not merge yet.

@dauTT
Copy link
Contributor Author

dauTT commented Feb 20, 2019

The code is clean now. All tests pass

Copy link
Contributor

@im-kulikov im-kulikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@decentralisedkev @fabwa can we merge?

@fabwa fabwa merged commit 19201dc into nspcc-dev:master Feb 20, 2019
@im-kulikov
Copy link
Contributor

🙏

@dauTT dauTT deleted the dauTT/patch-5 branch February 21, 2019 07:23
@hal0x2328 hal0x2328 added the I1 High impact label Feb 26, 2019
@hal0x2328
Copy link
Contributor

Award dauTT 400 points
Award im-kulikov 150 points for review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
I1 High impact
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants