Skip to content

Commit

Permalink
Feat: Add UpdateAccountNum
Browse files Browse the repository at this point in the history
Signed-off-by: Emanuel Pargov <[email protected]>
  • Loading branch information
bamzedev committed Aug 9, 2023
1 parent f086701 commit a51a057
Show file tree
Hide file tree
Showing 7 changed files with 299 additions and 3 deletions.
48 changes: 47 additions & 1 deletion account_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package hedera
*
* Hedera Go SDK
*
* Copyright (C) 2020 - 2023 Hedera Hashgraph, LLC
* Copyright (C) 2020 - 2022 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,7 +22,10 @@ package hedera

import (
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"

"github.com/pkg/errors"
Expand Down Expand Up @@ -334,6 +337,49 @@ func AccountIDFromBytes(data []byte) (AccountID, error) {
return *_AccountIDFromProtobuf(&pb), nil
}

// PopulateAccountNum gets the actual `Account` field of the `AccountId` from the Mirror Node.
// Should be used after generating `AccountId.FromEvmAddress()` because it sets the `Account` field to `0`
// automatically since there is no connection between the `Account` and the `evmAddress`
func (id *AccountID) PopulateAccountNum(client *Client) error {
if client.mirrorNetwork == nil || len(client.GetMirrorNetwork()) == 0 {
return errors.New("mirror node is not set")
}
mirrorUrl := client.GetMirrorNetwork()[0]
index := strings.Index(mirrorUrl, ":")
if index == -1 {
return errors.New("invalid mirrorUrl format")
}
mirrorUrl = mirrorUrl[:index]
url := fmt.Sprintf("https://%s/api/v1/accounts/%s", mirrorUrl, hex.EncodeToString(*id.AliasEvmAddress))
if client.GetLedgerID().String() == "" {
url = fmt.Sprintf("http://%s:5551/api/v1/accounts/%s", mirrorUrl, hex.EncodeToString(*id.AliasEvmAddress))
}

resp, err := http.Get(url) // #nosec
if err != nil {
return err
}
defer resp.Body.Close()

var result map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return err
}

mirrorAccountId, ok := result["account"].(string)
if !ok {
return errors.New("unexpected response format")
}

numStr := mirrorAccountId[strings.LastIndex(mirrorAccountId, ".")+1:]
num, err := strconv.ParseInt(numStr, 10, 64)
if err != nil {
return err
}
id.Account = uint64(num)
return nil
}

// Compare returns 0 if the two AccountID are identical, -1 if not.
func (id AccountID) Compare(given AccountID) int {
if id.Shard > given.Shard { //nolint
Expand Down
56 changes: 56 additions & 0 deletions account_id_e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//go:build all || e2e
// +build all e2e

package hedera

/*-
*
* Hedera Go SDK
*
* Copyright (C) 2020 - 2023 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import (
"testing"
"time"

"github.com/stretchr/testify/require"
)

func TestIntegrationAccountIDCanPopulateAccountNumber(t *testing.T) {
t.Parallel()
env := NewIntegrationTestEnv(t)

privateKey, err := PrivateKeyGenerateEcdsa()
require.NoError(t, err)
publicKey := privateKey.PublicKey()
evmAddress := publicKey.ToEvmAddress()
evmAddressAccount, err := AccountIDFromEvmPublicAddress(evmAddress)
require.NoError(t, err)
tx, err := NewTransferTransaction().AddHbarTransfer(evmAddressAccount, NewHbar(1)).
AddHbarTransfer(env.OperatorID, NewHbar(-1)).Execute(env.Client)
require.NoError(t, err)
receipt, err := tx.GetReceiptQuery().SetIncludeChildren(true).Execute(env.Client)
require.NoError(t, err)
newAccountId := *receipt.Children[0].AccountID

idMirror, err := AccountIDFromEvmPublicAddress(evmAddress)
require.NoError(t, err)
time.Sleep(5 * time.Second)
error := idMirror.PopulateAccountNum(env.Client)
require.NoError(t, error)
require.Equal(t, newAccountId.Account, idMirror.Account)
}
36 changes: 36 additions & 0 deletions account_id_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ package hedera
*/

import (
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -110,3 +111,38 @@ func TestUnitAccountIDEvm(t *testing.T) {

require.Equal(t, id.String(), "0.0.0011223344556677889900112233445566778899")
}

func TestUnitAccountIDPopulateFailForWrongMirrorHost(t *testing.T) {
t.Parallel()

client, err := _NewMockClient()
require.NoError(t, err)
client.SetLedgerID(*NewLedgerIDTestnet())
privateKey, err := PrivateKeyGenerateEcdsa()
require.NoError(t, err)
publicKey := privateKey.PublicKey()
evmAddress := publicKey.ToEvmAddress()
evmAddressAccountID, err := AccountIDFromEvmPublicAddress(evmAddress)
require.NoError(t, err)
err = evmAddressAccountID.PopulateAccountNum(client)
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), "no such host"))
}

func TestUnitAccountIDPopulateFailWithNoMirror(t *testing.T) {
t.Parallel()

client, err := _NewMockClient()
require.NoError(t, err)
client.mirrorNetwork = nil
client.SetLedgerID(*NewLedgerIDTestnet())
privateKey, err := PrivateKeyGenerateEcdsa()
require.NoError(t, err)
publicKey := privateKey.PublicKey()
evmAddress := publicKey.ToEvmAddress()
evmAddressAccountID, err := AccountIDFromEvmPublicAddress(evmAddress)
require.NoError(t, err)
err = evmAddressAccountID.PopulateAccountNum(client)
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), "mirror node is not set"))
}
48 changes: 48 additions & 0 deletions contract_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ package hedera

import (
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"

"github.com/hashgraph/hedera-protobufs-go/services"
"github.com/pkg/errors"
Expand Down Expand Up @@ -102,6 +106,7 @@ func (id *ContractID) Validate(client *Client) error {
return id.ValidateChecksum(client)
}

// ContractIDFromEvmAddress constructs a ContractID from a string representation of an EVM address
func ContractIDFromEvmAddress(shard uint64, realm uint64, evmAddress string) (ContractID, error) {
temp, err := hex.DecodeString(evmAddress)
if err != nil {
Expand Down Expand Up @@ -166,6 +171,49 @@ func (id ContractID) ToSolidityAddress() string {
return _IdToSolidityAddress(id.Shard, id.Realm, id.Contract)
}

// PopulateAccountNum gets the actual `Contract` field of the `ContractId` from the Mirror Node.
// Should be used after generating `ContractId.FromEvmAddress()` because it sets the `Contract` field to `0`
// automatically since there is no connection between the `Contract` and the `evmAddress`
func (id *ContractID) PopulateAccountNum(client *Client) error {
if client.mirrorNetwork == nil || len(client.GetMirrorNetwork()) == 0 {
return errors.New("mirror node is not set")
}
mirrorUrl := client.GetMirrorNetwork()[0]
index := strings.Index(mirrorUrl, ":")
if index == -1 {
return errors.New("invalid mirrorUrl format")
}
mirrorUrl = mirrorUrl[:index]
url := fmt.Sprintf("https://%s/api/v1/contracts/%s", mirrorUrl, hex.EncodeToString(id.EvmAddress))
if client.GetLedgerID().String() == "" {
url = fmt.Sprintf("http://%s:5551/api/v1/contracts/%s", mirrorUrl, hex.EncodeToString(id.EvmAddress))
}

resp, err := http.Get(url) // #nosec
if err != nil {
return err
}
defer resp.Body.Close()

var result map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return err
}

mirrorContractId, ok := result["contract_id"].(string)
if !ok {
return errors.New("unexpected response format")
}

numStr := mirrorContractId[strings.LastIndex(mirrorContractId, ".")+1:]
num, err := strconv.ParseInt(numStr, 10, 64)
if err != nil {
return err
}
id.Contract = uint64(num)
return nil
}

func (id ContractID) _ToProtobuf() *services.ContractID {
resultID := services.ContractID{
ShardNum: int64(id.Shard),
Expand Down
74 changes: 74 additions & 0 deletions contract_id_e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//go:build all || e2e
// +build all e2e

package hedera

/*-
*
* Hedera Go SDK
*
* Copyright (C) 2020 - 2023 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestIntegrationContractIDCanPopulateAccountNumber(t *testing.T) {
t.Parallel()
env := NewIntegrationTestEnv(t)
testContractByteCode := []byte(`608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101cb806100606000396000f3fe608060405260043610610046576000357c01000000000000000000000000000000000000000000000000000000009004806341c0e1b51461004b578063cfae321714610062575b600080fd5b34801561005757600080fd5b506100606100f2565b005b34801561006e57600080fd5b50610077610162565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100b757808201518184015260208101905061009c565b50505050905090810190601f1680156100e45780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415610160573373ffffffffffffffffffffffffffffffffffffffff16ff5b565b60606040805190810160405280600d81526020017f48656c6c6f2c20776f726c64210000000000000000000000000000000000000081525090509056fea165627a7a72305820ae96fb3af7cde9c0abfe365272441894ab717f816f07f41f07b1cbede54e256e0029`)

resp, err := NewFileCreateTransaction().
SetNodeAccountIDs(env.NodeAccountIDs).
SetKeys(env.Client.GetOperatorPublicKey()).
SetContents(testContractByteCode).
Execute(env.Client)
require.NoError(t, err)

receipt, err := resp.SetValidateStatus(true).GetReceipt(env.Client)
require.NoError(t, err)

fileID := *receipt.FileID
require.NotNil(t, fileID)

_, err = resp.SetValidateStatus(true).GetReceipt(env.Client)
require.NoError(t, err)

resp, err = NewContractCreateTransaction().
SetAdminKey(env.Client.GetOperatorPublicKey()).
SetGas(100000).
SetNodeAccountIDs([]AccountID{resp.NodeID}).
SetConstructorParameters(NewContractFunctionParameters().AddString("hello from hedera")).
SetBytecodeFileID(fileID).
SetContractMemo("hedera-sdk-go::TestContractInfoQuery_Execute").
Execute(env.Client)
require.NoError(t, err)

receipt, err = resp.SetValidateStatus(true).GetReceipt(env.Client)
require.NoError(t, err)

require.NotNil(t, receipt.ContractID)
contractID := *receipt.ContractID
info, err := NewContractInfoQuery().SetContractID(contractID).Execute(env.Client)
require.NoError(t, err)
idMirror, err := ContractIDFromSolidityAddress(info.ContractAccountID)
require.NoError(t, err)
idMirror.PopulateAccountNum(env.Client)
require.Equal(t, contractID.Contract, idMirror.Contract)
}
36 changes: 36 additions & 0 deletions contract_id_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ package hedera
import (
"encoding/hex"
"fmt"
"strings"
"testing"

"github.com/hashgraph/hedera-protobufs-go/services"
Expand Down Expand Up @@ -129,3 +130,38 @@ func TestUnitContractIDEvm(t *testing.T) {
Contract: &services.ContractID_ContractNum{ContractNum: 123},
})
}

func TestUnitContractIDPopulateFailForWrongMirrorHost(t *testing.T) {
t.Parallel()

client, err := _NewMockClient()
require.NoError(t, err)
client.SetLedgerID(*NewLedgerIDTestnet())
privateKey, err := PrivateKeyGenerateEcdsa()
require.NoError(t, err)
publicKey := privateKey.PublicKey()
evmAddress := publicKey.ToEvmAddress()
evmAddressAccountID, err := ContractIDFromEvmAddress(0, 0, evmAddress)
require.NoError(t, err)
err = evmAddressAccountID.PopulateAccountNum(client)
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), "no such host"))
}

func TestUnitContractIDPopulateFailWithNoMirror(t *testing.T) {
t.Parallel()

client, err := _NewMockClient()
require.NoError(t, err)
client.mirrorNetwork = nil
client.SetLedgerID(*NewLedgerIDTestnet())
privateKey, err := PrivateKeyGenerateEcdsa()
require.NoError(t, err)
publicKey := privateKey.PublicKey()
evmAddress := publicKey.ToEvmAddress()
evmAddressAccountID, err := ContractIDFromEvmAddress(0, 0, evmAddress)
require.NoError(t, err)
err = evmAddressAccountID.PopulateAccountNum(client)
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), "mirror node is not set"))
}
4 changes: 2 additions & 2 deletions utilities_for_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ func NewIntegrationTestEnv(t *testing.T) IntegrationTestEnv {
} else if os.Getenv("HEDERA_NETWORK") == "localhost" {
network := make(map[string]AccountID)
network["127.0.0.1:50213"] = AccountID{Account: 3}

mirror := []string{"127.0.0.1:5600"}
env.Client = ClientForNetwork(network)
env.Client.cancelNetworkUpdate()
env.Client.SetMirrorNetwork(mirror)
} else if os.Getenv("HEDERA_NETWORK") == "testnet" {
env.Client = ClientForTestnet()
} else if os.Getenv("CONFIG_FILE") != "" {
Expand Down

0 comments on commit a51a057

Please sign in to comment.