Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions core/addressGenerator/addressGenerator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
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
}

// 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)
adrAndNonce := append(creatorAddress, buffNonce...)
scAddress := keccak.NewKeccak().Compute(string(adrAndNonce))
Copy link

Copilot AI Aug 28, 2025

Choose a reason for hiding this comment

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

Creating a new keccak hasher instance here instead of using the instance stored in ag.hasher field is inefficient and inconsistent.

Copilot uses AI. Check for mistakes.

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:]
}
81 changes: 81 additions & 0 deletions core/addressGenerator/addressGenerator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package addressGenerator

import (
"bytes"
"encoding/hex"
"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"
"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)
assert.False(t, check.IfNil(ag))

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))
}
9 changes: 9 additions & 0 deletions core/addressGenerator/errors.go
Original file line number Diff line number Diff line change
@@ -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")
19 changes: 19 additions & 0 deletions core/mock/addressGeneratorStub.go
Original file line number Diff line number Diff line change
@@ -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
}
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ 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
)

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
15 changes: 10 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down Expand Up @@ -141,14 +144,16 @@ 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=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
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=