From 372c9be9ad447dad9d10c66d987a251de1bf82fd Mon Sep 17 00:00:00 2001 From: Rebegea Dragos-Alexandru Date: Fri, 13 Jan 2023 14:04:08 +0200 Subject: [PATCH 1/2] EN-13550: address generator --- core/addressGenerator/addressGenerator.go | 72 +++++++++++++++++ .../addressGenerator/addressGenerator_test.go | 79 +++++++++++++++++++ core/addressGenerator/errors.go | 9 +++ go.mod | 8 +- go.sum | 15 ++-- 5 files changed, 174 insertions(+), 9 deletions(-) create mode 100644 core/addressGenerator/addressGenerator.go create mode 100644 core/addressGenerator/addressGenerator_test.go create mode 100644 core/addressGenerator/errors.go diff --git a/core/addressGenerator/addressGenerator.go b/core/addressGenerator/addressGenerator.go new file mode 100644 index 000000000..8e3c0f2fc --- /dev/null +++ b/core/addressGenerator/addressGenerator.go @@ -0,0 +1,72 @@ +package addressGenerator + +import ( + "encoding/binary" + + "github.com/ElrondNetwork/elrond-go-core/core" + "github.com/ElrondNetwork/elrond-go-core/core/check" + "github.com/ElrondNetwork/elrond-go-core/hashing" + "github.com/ElrondNetwork/elrond-go-core/hashing/keccak" +) + +// addressGenerator is used to generate some addresses based on elrond-go logic +type addressGenerator struct { + pubkeyConv core.PubkeyConverter + hasher hashing.Hasher +} + +// NewAddressGenerator will create an address generator instance +func NewAddressGenerator(pubkeyConv core.PubkeyConverter) (*addressGenerator, error) { + if check.IfNil(pubkeyConv) { + return nil, core.ErrNilPubkeyConverter + } + + return &addressGenerator{ + pubkeyConv: pubkeyConv, + hasher: keccak.NewKeccak(), + }, nil +} + +// NewAddress is a hook which creates a new smart contract address from the creators address and nonce +// The address is created by applied keccak256 on the appended value off creator address and nonce +// Prefix mask is applied for first 8 bytes 0, and for bytes 9-10 - VM type +// Suffix mask is applied - last 2 bytes are for the shard ID - mask is applied as suffix mask +func (ag *addressGenerator) NewAddress(creatorAddress []byte, creatorNonce uint64, vmType []byte) ([]byte, error) { + addressLength := ag.pubkeyConv.Len() + if len(creatorAddress) != addressLength { + return nil, ErrAddressLengthNotCorrect + } + + if len(vmType) != core.VMTypeLen { + return nil, ErrVMTypeLengthIsNotCorrect + } + + base := hashFromAddressAndNonce(creatorAddress, creatorNonce) + prefixMask := createPrefixMask(vmType) + suffixMask := createSuffixMask(creatorAddress) + + copy(base[:core.NumInitCharactersForScAddress], prefixMask) + copy(base[len(base)-core.ShardIdentiferLen:], suffixMask) + + return base, nil +} + +func hashFromAddressAndNonce(creatorAddress []byte, creatorNonce uint64) []byte { + buffNonce := make([]byte, 8) + binary.LittleEndian.PutUint64(buffNonce, creatorNonce) + adrAndNonce := append(creatorAddress, buffNonce...) + scAddress := keccak.NewKeccak().Compute(string(adrAndNonce)) + + return scAddress +} + +func createPrefixMask(vmType []byte) []byte { + prefixMask := make([]byte, core.NumInitCharactersForScAddress-core.VMTypeLen) + prefixMask = append(prefixMask, vmType...) + + return prefixMask +} + +func createSuffixMask(creatorAddress []byte) []byte { + return creatorAddress[len(creatorAddress)-2:] +} diff --git a/core/addressGenerator/addressGenerator_test.go b/core/addressGenerator/addressGenerator_test.go new file mode 100644 index 000000000..0c8862a0e --- /dev/null +++ b/core/addressGenerator/addressGenerator_test.go @@ -0,0 +1,79 @@ +package addressGenerator + +import ( + "bytes" + "encoding/hex" + "fmt" + "testing" + + "github.com/ElrondNetwork/elrond-go-core/core/mock" + "github.com/ElrondNetwork/elrond-go-core/core/pubkeyConverter" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// AddressBytesLen represents the number of bytes of an address +const AddressBytesLen = 32 + +var AddressPublicKeyConverter, _ = pubkeyConverter.NewBech32PubkeyConverter(AddressBytesLen, &mock.LoggerMock{}) + +func TestBlockChainHookImpl_NewAddressLengthNoGood(t *testing.T) { + t.Parallel() + + ag, err := NewAddressGenerator(AddressPublicKeyConverter) + require.Nil(t, err) + + address := []byte("test") + nonce := uint64(10) + + scAddress, err := ag.NewAddress(address, nonce, []byte("00")) + assert.Equal(t, ErrAddressLengthNotCorrect, err) + assert.Nil(t, scAddress) + + address = []byte("1234567890123456789012345678901234567890") + scAddress, err = ag.NewAddress(address, nonce, []byte("00")) + assert.Equal(t, ErrAddressLengthNotCorrect, err) + assert.Nil(t, scAddress) +} + +func TestBlockChainHookImpl_NewAddressVMTypeTooLong(t *testing.T) { + t.Parallel() + + ag, err := NewAddressGenerator(AddressPublicKeyConverter) + require.Nil(t, err) + + address := []byte("01234567890123456789012345678900") + nonce := uint64(10) + + vmType := []byte("010") + scAddress, err := ag.NewAddress(address, nonce, vmType) + assert.Equal(t, ErrVMTypeLengthIsNotCorrect, err) + assert.Nil(t, scAddress) +} + +func TestBlockChainHookImpl_NewAddress(t *testing.T) { + t.Parallel() + + ag, err := NewAddressGenerator(AddressPublicKeyConverter) + require.Nil(t, err) + + address := []byte("01234567890123456789012345678900") + nonce := uint64(10) + + vmType := []byte("11") + scAddress1, err := ag.NewAddress(address, nonce, vmType) + assert.Nil(t, err) + + for i := 0; i < 8; i++ { + assert.Equal(t, scAddress1[i], uint8(0)) + } + assert.True(t, bytes.Equal(vmType, scAddress1[8:10])) + + nonce++ + scAddress2, err := ag.NewAddress(address, nonce, []byte("00")) + assert.Nil(t, err) + + assert.False(t, bytes.Equal(scAddress1, scAddress2)) + + fmt.Printf("%s \n%s \n", hex.EncodeToString(scAddress1), hex.EncodeToString(scAddress2)) +} diff --git a/core/addressGenerator/errors.go b/core/addressGenerator/errors.go new file mode 100644 index 000000000..2f0209b55 --- /dev/null +++ b/core/addressGenerator/errors.go @@ -0,0 +1,9 @@ +package addressGenerator + +import "errors" + +// ErrAddressLengthNotCorrect signals that an account does not have the correct address +var ErrAddressLengthNotCorrect = errors.New("address length is not correct") + +// ErrVMTypeLengthIsNotCorrect signals that the vm type length is not correct +var ErrVMTypeLengthIsNotCorrect = errors.New("vm type length is not correct") diff --git a/go.mod b/go.mod index 22eb60163..bee4d5068 100644 --- a/go.mod +++ b/go.mod @@ -8,12 +8,12 @@ require ( github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.5.2 github.com/gorilla/mux v1.8.0 - github.com/gorilla/websocket v1.4.2 + github.com/gorilla/websocket v1.5.0 github.com/hashicorp/golang-lru v0.5.4 github.com/mr-tron/base58 v1.2.0 github.com/pelletier/go-toml v1.9.3 github.com/pkg/errors v0.9.1 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.8.0 golang.org/x/crypto v0.3.0 ) @@ -21,8 +21,8 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.2.0 // indirect - google.golang.org/protobuf v1.26.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/gogo/protobuf => github.com/ElrondNetwork/protobuf v1.3.2 diff --git a/go.sum b/go.sum index 72281b735..5d1cacbd7 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,8 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -80,8 +80,11 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -141,8 +144,9 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -150,5 +154,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 2075fd801974cef8afdf05a53d99ec1307d66a99 Mon Sep 17 00:00:00 2001 From: Rebegea Dragos-Alexandru Date: Fri, 13 Jan 2023 14:24:32 +0200 Subject: [PATCH 2/2] EN-13550: add IsInterfaceNil and stub --- core/addressGenerator/addressGenerator.go | 5 +++++ .../addressGenerator/addressGenerator_test.go | 2 ++ core/mock/addressGeneratorStub.go | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 core/mock/addressGeneratorStub.go diff --git a/core/addressGenerator/addressGenerator.go b/core/addressGenerator/addressGenerator.go index 8e3c0f2fc..9a07c2295 100644 --- a/core/addressGenerator/addressGenerator.go +++ b/core/addressGenerator/addressGenerator.go @@ -51,6 +51,11 @@ func (ag *addressGenerator) NewAddress(creatorAddress []byte, creatorNonce uint6 return base, nil } +// IsInterfaceNil returns true if there is no value under the interface +func (ag *addressGenerator) IsInterfaceNil() bool { + return ag == nil +} + func hashFromAddressAndNonce(creatorAddress []byte, creatorNonce uint64) []byte { buffNonce := make([]byte, 8) binary.LittleEndian.PutUint64(buffNonce, creatorNonce) diff --git a/core/addressGenerator/addressGenerator_test.go b/core/addressGenerator/addressGenerator_test.go index 0c8862a0e..dec4af4a4 100644 --- a/core/addressGenerator/addressGenerator_test.go +++ b/core/addressGenerator/addressGenerator_test.go @@ -6,6 +6,7 @@ import ( "fmt" "testing" + "github.com/ElrondNetwork/elrond-go-core/core/check" "github.com/ElrondNetwork/elrond-go-core/core/mock" "github.com/ElrondNetwork/elrond-go-core/core/pubkeyConverter" "github.com/stretchr/testify/assert" @@ -22,6 +23,7 @@ func TestBlockChainHookImpl_NewAddressLengthNoGood(t *testing.T) { ag, err := NewAddressGenerator(AddressPublicKeyConverter) require.Nil(t, err) + assert.False(t, check.IfNil(ag)) address := []byte("test") nonce := uint64(10) diff --git a/core/mock/addressGeneratorStub.go b/core/mock/addressGeneratorStub.go new file mode 100644 index 000000000..75b17f19c --- /dev/null +++ b/core/mock/addressGeneratorStub.go @@ -0,0 +1,19 @@ +package mock + +// AddressGeneratorStub is a mock implementation of AddressGenerator interface +type AddressGeneratorStub struct { + NewAddressCalled func(address []byte, nonce uint64, vmType []byte) ([]byte, error) +} + +// NewAddress is a mock implementation of NewAddress method +func (ags *AddressGeneratorStub) NewAddress(address []byte, nonce uint64, vmType []byte) ([]byte, error) { + if ags.NewAddressCalled != nil { + return ags.NewAddressCalled(address, nonce, vmType) + } + return nil, nil +} + +// IsInterfaceNil returns true if there is no value under the interface +func (ags *AddressGeneratorStub) IsInterfaceNil() bool { + return ags == nil +}