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

Liquid staking unit and integration tests #3385

Merged
merged 15 commits into from
Aug 31, 2021
68 changes: 68 additions & 0 deletions integrationTests/testProcessorNode.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/hex"
"fmt"
"math"
"math/big"
"strconv"
"sync"
Expand Down Expand Up @@ -1779,6 +1780,73 @@ func (tpn *TestProcessorNode) InitDelegationManager() {
log.LogIfError(err)
}

// InitLiquidStaking will initialize the liquid staking contract whenever required
func (tpn *TestProcessorNode) InitLiquidStaking() []byte {
sasurobert marked this conversation as resolved.
Show resolved Hide resolved
if tpn.ShardCoordinator.SelfId() != core.MetachainShardId {
return nil
}

vmInput := &vmcommon.ContractCallInput{
VMInput: vmcommon.VMInput{
CallerAddr: vm.ESDTSCAddress,
CallValue: big.NewInt(0),
Arguments: [][]byte{},
GasProvided: math.MaxUint64,
},
RecipientAddr: vm.ESDTSCAddress,
Function: "initDelegationESDTOnMeta",
}

systemVM, err := tpn.VMContainer.Get(factory.SystemVirtualMachine)
log.LogIfError(err)

vmOutput, err := systemVM.RunSmartContractCall(vmInput)
log.LogIfError(err)
if vmOutput.ReturnCode != vmcommon.Ok {
log.Error("error while initializing system SC", "return code", vmOutput.ReturnCode)
}

err = tpn.processSCOutputAccounts(vmOutput)
log.LogIfError(err)

_, err = tpn.AccntState.Commit()
log.LogIfError(err)

codeMetaData := &vmcommon.CodeMetadata{
Upgradeable: false,
Payable: false,
Readable: true,
}

tokenID := vmOutput.ReturnData[0]
vmInputCreate := &vmcommon.ContractCreateInput{
VMInput: vmcommon.VMInput{
CallerAddr: vm.LiquidStakingSCAddress,
Arguments: [][]byte{tokenID},
CallValue: zero,
},
ContractCode: vm.DelegationManagerSCAddress,
ContractCodeMetadata: codeMetaData.ToBytes(),
}

vmOutput, err = systemVM.RunSmartContractCreate(vmInputCreate)
log.LogIfError(err)
if vmOutput.ReturnCode != vmcommon.Ok {
log.Error("error while initializing system SC", "return code", vmOutput.ReturnCode)
}

err = tpn.processSCOutputAccounts(vmOutput)
log.LogIfError(err)

err = tpn.updateSystemSCContractsCode(vmInputCreate.ContractCodeMetadata, vm.LiquidStakingSCAddress)
log.LogIfError(err)

_, err = tpn.AccntState.Commit()
log.LogIfError(err)

return tokenID
}

func (tpn *TestProcessorNode) updateSystemSCContractsCode(contractMetadata []byte, scAddress []byte) error {
userAcc, err := tpn.getUserAccount(scAddress)
if err != nil {
Expand Down
190 changes: 190 additions & 0 deletions integrationTests/vm/delegation/liquidStaking_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// +build !race

package delegation

import (
"bytes"
"math/big"
"testing"
"time"

"github.com/ElrondNetwork/elrond-go-core/core"
logger "github.com/ElrondNetwork/elrond-go-logger"
"github.com/ElrondNetwork/elrond-go/integrationTests"
"github.com/ElrondNetwork/elrond-go/integrationTests/vm/esdt"
"github.com/ElrondNetwork/elrond-go/testscommon/txDataBuilder"
"github.com/ElrondNetwork/elrond-go/vm"
vmcommon "github.com/ElrondNetwork/elrond-vm-common"
"github.com/stretchr/testify/require"
)

var log = logger.GetOrCreate("liquidStaking")

func TestDelegationSystemSCWithLiquidStaking(t *testing.T) {
if testing.Short() {
t.Skip("this is not a short test")
}

nodes, idxProposers, delegationAddress, tokenID, nonce, round := setupNodesDelegationContractInitLiquidStaking(t)
defer func() {
for _, n := range nodes {
_ = n.Messenger.Close()
}
}()

txData := txDataBuilder.NewBuilder().Clear().
Func("claimDelegatedPosition").
Bytes(big.NewInt(1).Bytes()).
Bytes(delegationAddress).
Bytes(big.NewInt(5000).Bytes()).
ToString()
for _, node := range nodes {
integrationTests.CreateAndSendTransaction(node, nodes, big.NewInt(0), vm.LiquidStakingSCAddress, txData, core.MinMetaTxExtraGasCost)
}

nrRoundsToPropagateMultiShard := 12
time.Sleep(time.Second)
nonce, round = integrationTests.WaitOperationToBeDone(t, nodes, nrRoundsToPropagateMultiShard, nonce, round, idxProposers)
time.Sleep(time.Second)

// claim again
for _, node := range nodes {
integrationTests.CreateAndSendTransaction(node, nodes, big.NewInt(0), vm.LiquidStakingSCAddress, txData, core.MinMetaTxExtraGasCost)
}

time.Sleep(time.Second)
nonce, round = integrationTests.WaitOperationToBeDone(t, nodes, nrRoundsToPropagateMultiShard, nonce, round, idxProposers)
time.Sleep(time.Second)

for i := 1; i < len(nodes); i++ {
checkLPPosition(t, nodes[i].OwnAccount.Address, nodes, tokenID, uint64(1), big.NewInt(10000))
}
// owner is not allowed to get LP position
checkLPPosition(t, nodes[0].OwnAccount.Address, nodes, tokenID, uint64(1), big.NewInt(0))
metaNode := getNodeWithShardID(nodes, core.MetachainShardId)
allDelegatorAddresses := make([][]byte, 0)
for i := 1; i < len(nodes); i++ {
allDelegatorAddresses = append(allDelegatorAddresses, nodes[i].OwnAccount.Address)
}
verifyDelegatorIsDeleted(t, metaNode, allDelegatorAddresses, delegationAddress)

oneTransfer := &vmcommon.ESDTTransfer{
ESDTValue: big.NewInt(1000),
ESDTTokenName: tokenID,
ESDTTokenType: uint32(core.NonFungible),
ESDTTokenNonce: 1,
}
esdtTransfers := []*vmcommon.ESDTTransfer{oneTransfer, oneTransfer, oneTransfer, oneTransfer, oneTransfer}
txBuilder := txDataBuilder.NewBuilder().MultiTransferESDTNFT(vm.LiquidStakingSCAddress, esdtTransfers)
txBuilder.Bytes([]byte("unDelegatePosition"))
for _, node := range nodes {
integrationTests.CreateAndSendTransaction(node, nodes, big.NewInt(0), node.OwnAccount.Address, txBuilder.ToString(), core.MinMetaTxExtraGasCost)
}

txBuilder = txDataBuilder.NewBuilder().MultiTransferESDTNFT(vm.LiquidStakingSCAddress, esdtTransfers)
txBuilder.Bytes([]byte("returnPosition"))
for _, node := range nodes {
integrationTests.CreateAndSendTransaction(node, nodes, big.NewInt(0), node.OwnAccount.Address, txBuilder.ToString(), core.MinMetaTxExtraGasCost)
}
time.Sleep(time.Second)
finalWait := 20
nonce, round = integrationTests.WaitOperationToBeDone(t, nodes, finalWait, nonce, round, idxProposers)
time.Sleep(time.Second)

for _, node := range nodes {
checkLPPosition(t, node.OwnAccount.Address, nodes, tokenID, uint64(1), big.NewInt(0))
}

verifyDelegatorsStake(t, metaNode, "getUserActiveStake", allDelegatorAddresses, delegationAddress, big.NewInt(5000))
verifyDelegatorsStake(t, metaNode, "getUserUnStakedValue", allDelegatorAddresses, delegationAddress, big.NewInt(5000))
}

func setupNodesDelegationContractInitLiquidStaking(
t *testing.T,
) ([]*integrationTests.TestProcessorNode, []int, []byte, []byte, uint64, uint64) {
numOfShards := 2
nodesPerShard := 2
numMetachainNodes := 2

nodes := integrationTests.CreateNodes(
numOfShards,
nodesPerShard,
numMetachainNodes,
)

integrationTests.DisplayAndStartNodes(nodes)

idxProposers := make([]int, numOfShards+1)
for i := 0; i < numOfShards; i++ {
idxProposers[i] = i * nodesPerShard
}
idxProposers[numOfShards] = numOfShards * nodesPerShard

tokenID := initDelegationManagementAndLiquidStaking(nodes)

initialVal := big.NewInt(10000000000)
initialVal.Mul(initialVal, initialVal)
integrationTests.MintAllNodes(nodes, initialVal)

delegationAddress := createNewDelegationSystemSC(nodes[0], nodes)

round := uint64(0)
nonce := uint64(0)
round = integrationTests.IncrementAndPrintRound(round)
nonce++

time.Sleep(time.Second)
nrRoundsToPropagateMultiShard := 6
nonce, round = integrationTests.WaitOperationToBeDone(t, nodes, nrRoundsToPropagateMultiShard, nonce, round, idxProposers)
time.Sleep(time.Second)

txData := "delegate"
for _, node := range nodes {
integrationTests.CreateAndSendTransaction(node, nodes, big.NewInt(10000), delegationAddress, txData, core.MinMetaTxExtraGasCost)
}

time.Sleep(time.Second)
nonce, round = integrationTests.WaitOperationToBeDone(t, nodes, nrRoundsToPropagateMultiShard, nonce, round, idxProposers)
time.Sleep(time.Second)

return nodes, idxProposers, delegationAddress, tokenID, nonce, round
}

func initDelegationManagementAndLiquidStaking(nodes []*integrationTests.TestProcessorNode) []byte {
var tokenID []byte
for _, node := range nodes {
node.InitDelegationManager()
tmpTokenID := node.InitLiquidStaking()
if len(tmpTokenID) != 0 {
if len(tokenID) == 0 {
tokenID = tmpTokenID
}

if !bytes.Equal(tokenID, tmpTokenID) {
log.Error("tokenID missmatch", "current", tmpTokenID, "old", tokenID)
}
}
}
return tokenID
}

func checkLPPosition(
t *testing.T,
address []byte,
nodes []*integrationTests.TestProcessorNode,
tokenID []byte,
nonce uint64,
value *big.Int,
) {
tokenIdentifierPlusNonce := append(tokenID, big.NewInt(0).SetUint64(nonce).Bytes()...)
esdtData := esdt.GetESDTTokenData(t, address, nodes, string(tokenIdentifierPlusNonce))

if value.Cmp(big.NewInt(0)) == 0 {
require.Nil(t, esdtData.TokenMetaData)
return
}

require.NotNil(t, esdtData.TokenMetaData)
require.Equal(t, vm.LiquidStakingSCAddress, esdtData.TokenMetaData.Creator)
require.Equal(t, value.Bytes(), esdtData.Value.Bytes())
}
12 changes: 11 additions & 1 deletion testscommon/txDataBuilder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"math/big"

"github.com/ElrondNetwork/elrond-go-core/core"
vmcommon "github.com/ElrondNetwork/elrond-vm-common"
)

// txDataBuilder constructs a string to be used for transaction arguments
Expand Down Expand Up @@ -147,11 +148,20 @@ func (builder *txDataBuilder) TransferESDT(token string, value int64) *txDataBui
return builder.Func(core.BuiltInFunctionESDTTransfer).Str(token).Int64(value)
}

//TransferESDTNFT appends to the data string all the elements required to request an ESDT NFT transfer.
// TransferESDTNFT appends to the data string all the elements required to request an ESDT NFT transfer.
func (builder *txDataBuilder) TransferESDTNFT(token string, nonce int, value int64) *txDataBuilder {
return builder.Func(core.BuiltInFunctionESDTNFTTransfer).Str(token).Int(nonce).Int64(value)
}

// MultiTransferESDTNFT appends to the data string all the elements required to request an Multi ESDT NFT transfer.
func (builder *txDataBuilder) MultiTransferESDTNFT(destinationAddress []byte, transfers []*vmcommon.ESDTTransfer) *txDataBuilder {
sasurobert marked this conversation as resolved.
Show resolved Hide resolved
txBuilder := builder.Func(core.BuiltInFunctionMultiESDTNFTTransfer).Bytes(destinationAddress).Int(len(transfers))
for _, transfer := range transfers {
txBuilder.Bytes(transfer.ESDTTokenName).Int(int(transfer.ESDTTokenNonce)).BigInt(transfer.ESDTValue)
}
return txBuilder
}

// BurnESDT appends to the data string all the elements required to burn ESDT tokens.
func (builder *txDataBuilder) BurnESDT(token string, value int64) *txDataBuilder {
return builder.Func(core.BuiltInFunctionESDTBurn).Str(token).Int64(value)
Expand Down
3 changes: 3 additions & 0 deletions vm/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,6 @@ var ErrInvalidDelegationTicker = errors.New("invalid delegation ticker name")

// ErrInvalidReturnData signals that invalid return data was provided
var ErrInvalidReturnData = errors.New("invalid return data")

// ErrNotEnoughRemainingFunds signals that operation is invalid as remaining funds are below minimum
var ErrNotEnoughRemainingFunds = errors.New("not enough remaining funds - do not leave dust behind")
2 changes: 0 additions & 2 deletions vm/factory/systemSCFactory.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,13 +294,11 @@ func (scf *systemSCFactory) createDelegationManagerContract() (vm.SystemSmartCon
func (scf *systemSCFactory) createLiquidStakingContract() (vm.SystemSmartContract, error) {
argsLiquidStaking := systemSmartContracts.ArgsNewLiquidStaking{
Eei: scf.systemEI,
DelegationMgrSCAddress: vm.DelegationManagerSCAddress,
LiquidStakingSCAddress: vm.LiquidStakingSCAddress,
GasCost: scf.gasCost,
Marshalizer: scf.marshalizer,
Hasher: scf.hasher,
EpochNotifier: scf.epochNotifier,
EndOfEpochAddress: vm.EndOfEpochAddress,
EpochConfig: *scf.epochConfig,
}
liquidStaking, err := systemSmartContracts.NewLiquidStakingSystemSC(argsLiquidStaking)
Expand Down