diff --git a/Makefile b/Makefile index 8e5624b015..398484fc60 100644 --- a/Makefile +++ b/Makefile @@ -77,13 +77,13 @@ test-e2e-legacy: check-go test-e2e-polybft: check-go go build -o artifacts/blade . env EDGE_BINARY=${PWD}/artifacts/blade E2E_TESTS=true E2E_LOGS=true \ - go test -v -timeout=1h30m ./e2e-polybft/e2e/... + go test -v -timeout=5h ./e2e-polybft/e2e/... .PHONY: test-e2e-apex-bridge test-e2e-apex-bridge: check-go go build -o artifacts/blade . env EDGE_BINARY=${PWD}/artifacts/blade E2E_TESTS=true E2E_LOGS=true \ - go test -v -timeout=1h30m ./e2e-polybft/e2e/... -run "ApexBridge" + go test -v -timeout=5h ./e2e-polybft/e2e/... -run "ApexBridge" .PHONY: test-property-polybft test-property-polybft: check-go diff --git a/e2e-polybft/e2e/apex_bridge_test.go b/e2e-polybft/e2e/apex_bridge_test.go index 035b0fb65b..c2125f6ef4 100644 --- a/e2e-polybft/e2e/apex_bridge_test.go +++ b/e2e-polybft/e2e/apex_bridge_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "math/big" + "math/rand" "os" "os/signal" "path" @@ -515,7 +516,7 @@ func TestE2E_ApexBridge_ValidScenarios(t *testing.T) { t, ctx, cardanofw.WithAPIKey(apiKey), ) - user := apex.CreateAndFundUser(t, ctx, uint64(1_000_000_000)) + user := apex.CreateAndFundUser(t, ctx, uint64(20_000_000_000)) txProviderPrime := apex.GetPrimeTxProvider() txProviderVector := apex.GetVectorTxProvider() @@ -907,3 +908,365 @@ func TestE2E_ApexBridge_ValidScenarios(t *testing.T) { fmt.Printf("on prime: prevAmount: %v. newAmount: %v\n", prevAmountOnPrime, newAmountOnPrime) }) } + +func TestE2E_ApexBridge_ValidScenarios_BigTests(t *testing.T) { + if shouldRun := os.Getenv("RUN_E2E_BIG_TESTS"); shouldRun != "true" { + t.Skip() + } + + const ( + apiKey = "test_api_key" + ) + + ctx, cncl := context.WithCancel(context.Background()) + defer cncl() + + apex := cardanofw.RunApexBridge( + t, ctx, + cardanofw.WithAPIKey(apiKey), + ) + user := apex.CreateAndFundUser(t, ctx, uint64(20_000_000_000)) + + txProviderPrime := apex.GetPrimeTxProvider() + txProviderVector := apex.GetVectorTxProvider() + + primeGenesisWallet := apex.GetPrimeGenesisWallet(t) + vectorGenesisWallet := apex.GetVectorGenesisWallet(t) + + primeGenesisAddress, _, err := wallet.GetWalletAddressCli(primeGenesisWallet, uint(apex.PrimeCluster.Config.NetworkMagic)) + require.NoError(t, err) + + vectorGenesisAddress, _, err := wallet.GetWalletAddressCli(vectorGenesisWallet, uint(apex.VectorCluster.Config.NetworkMagic)) + require.NoError(t, err) + + fmt.Println("prime genesis addr: ", primeGenesisAddress) + fmt.Println("vector genesis addr: ", vectorGenesisAddress) + fmt.Println("prime user addr: ", user.PrimeAddress) + fmt.Println("vector user addr: ", user.VectorAddress) + fmt.Println("prime multisig addr: ", apex.Bridge.PrimeMultisigAddr) + fmt.Println("prime fee addr: ", apex.Bridge.PrimeMultisigFeeAddr) + fmt.Println("vector multisig addr: ", apex.Bridge.VectorMultisigAddr) + fmt.Println("vector fee addr: ", apex.Bridge.VectorMultisigFeeAddr) + + //nolint:dupl + t.Run("From prime to vector 200x 5min 90%", func(t *testing.T) { + instances := 200 + maxWaitTime := 300 + fundSendAmount := uint64(5_000_000) + sendAmount := uint64(1_000_000) + successChance := 90 // 90% + succeededCount := 0 + walletKeys := make([]wallet.IWallet, instances) + + prevAmount, err := cardanofw.GetTokenAmount(ctx, txProviderVector, user.VectorAddress) + require.NoError(t, err) + + fmt.Printf("Funding %v Wallets\n", instances) + + for i := 0; i < instances; i++ { + if (i+1)%100 == 0 { + fmt.Printf("Funded %v..%v\n", i-99, i) + } + + walletKeys[i], err = wallet.NewStakeWalletManager().Create(path.Join(apex.PrimeCluster.Config.Dir("keys")), true) + require.NoError(t, err) + + walletAddress, _, err := wallet.GetWalletAddressCli(walletKeys[i], uint(apex.PrimeCluster.Config.NetworkMagic)) + require.NoError(t, err) + + user.SendToAddress(t, ctx, txProviderPrime, primeGenesisWallet, fundSendAmount, walletAddress, true) + } + + fmt.Printf("Funding Complete\n") + fmt.Printf("Sending %v transactions in %v seconds\n", instances, maxWaitTime) + + var wg sync.WaitGroup + for i := 0; i < instances; i++ { + wg.Add(1) + + go func(idx int) { + defer wg.Done() + + if successChance > rand.Intn(100) { + succeededCount++ + sleepTime := rand.Intn(maxWaitTime) + time.Sleep(time.Second * time.Duration(sleepTime)) + + cardanofw.BridgeAmountFull(t, ctx, txProviderPrime, uint(apex.PrimeCluster.Config.NetworkMagic), + apex.Bridge.PrimeMultisigAddr, apex.Bridge.VectorMultisigFeeAddr, walletKeys[idx], user.VectorAddress, sendAmount, + cardanofw.GetDestinationChainID(true)) + } else { + feeAmount := uint64(1_100_000) + receivers := map[string]uint64{ + user.VectorAddress: sendAmount * 10, // 10Ada + apex.Bridge.VectorMultisigFeeAddr: feeAmount, + } + + bridgingRequestMetadata, err := cardanofw.CreateMetaData( + user.PrimeAddress, receivers, cardanofw.GetDestinationChainID(true)) + require.NoError(t, err) + + _, err = cardanofw.SendTx( + ctx, txProviderPrime, walletKeys[idx], (sendAmount + feeAmount), apex.Bridge.PrimeMultisigAddr, + apex.PrimeCluster.Config.NetworkMagic, bridgingRequestMetadata) + require.NoError(t, err) + } + }(i) + } + + wg.Wait() + + fmt.Printf("All tx sent, waiting for confirmation.\n") + + expectedAmount := prevAmount.Uint64() + uint64(succeededCount)*sendAmount + + var newAmount *big.Int + for i := 0; i < instances; i++ { + newAmount, err = cardanofw.GetTokenAmount(ctx, txProviderVector, user.VectorAddress) + require.NoError(t, err) + fmt.Printf("Current Amount Prime: %v. Expected: %v\n", newAmount, expectedAmount) + + if newAmount.Uint64() == expectedAmount { + break + } + + time.Sleep(time.Second * 10) + } + + fmt.Printf("Success count: %v. prevAmount: %v. newAmount: %v. expectedAmount: %v\n", succeededCount, prevAmount, newAmount, expectedAmount) + }) + + //nolint:dupl + t.Run("From prime to vector 1000x 20min 90%", func(t *testing.T) { + instances := 1000 + maxWaitTime := 1200 + fundSendAmount := uint64(5_000_000) + sendAmount := uint64(1_000_000) + successChance := 90 // 90% + succeededCount := 0 + walletKeys := make([]wallet.IWallet, instances) + + prevAmount, err := cardanofw.GetTokenAmount(ctx, txProviderVector, user.VectorAddress) + require.NoError(t, err) + + fmt.Printf("Funding %v Wallets\n", instances) + + for i := 0; i < instances; i++ { + if (i+1)%100 == 0 { + fmt.Printf("Funded %v..%v\n", i-99, i) + } + + walletKeys[i], err = wallet.NewStakeWalletManager().Create(path.Join(apex.PrimeCluster.Config.Dir("keys")), true) + require.NoError(t, err) + + walletAddress, _, err := wallet.GetWalletAddressCli(walletKeys[i], uint(apex.PrimeCluster.Config.NetworkMagic)) + require.NoError(t, err) + + user.SendToAddress(t, ctx, txProviderPrime, primeGenesisWallet, fundSendAmount, walletAddress, true) + } + + fmt.Printf("Funding Complete\n") + fmt.Printf("Sending %v transactions in %v seconds\n", instances, maxWaitTime) + + var wg sync.WaitGroup + for i := 0; i < instances; i++ { + wg.Add(1) + + go func(idx int) { + defer wg.Done() + + if successChance > rand.Intn(100) { + succeededCount++ + sleepTime := rand.Intn(maxWaitTime) + time.Sleep(time.Second * time.Duration(sleepTime)) + + cardanofw.BridgeAmountFull(t, ctx, txProviderPrime, uint(apex.PrimeCluster.Config.NetworkMagic), + apex.Bridge.PrimeMultisigAddr, apex.Bridge.VectorMultisigFeeAddr, walletKeys[idx], user.VectorAddress, sendAmount, + cardanofw.GetDestinationChainID(true)) + } else { + feeAmount := uint64(1_100_000) + receivers := map[string]uint64{ + user.VectorAddress: sendAmount * 10, // 10Ada+ + apex.Bridge.VectorMultisigFeeAddr: feeAmount, + } + + bridgingRequestMetadata, err := cardanofw.CreateMetaData( + user.PrimeAddress, receivers, cardanofw.GetDestinationChainID(true)) + require.NoError(t, err) + + _, err = cardanofw.SendTx( + ctx, txProviderPrime, walletKeys[idx], (sendAmount + feeAmount), apex.Bridge.PrimeMultisigAddr, + apex.PrimeCluster.Config.NetworkMagic, bridgingRequestMetadata) + require.NoError(t, err) + } + }(i) + } + + wg.Wait() + + fmt.Printf("All tx sent, waiting for confirmation.\n") + + expectedAmount := prevAmount.Uint64() + uint64(succeededCount)*sendAmount + + var newAmount *big.Int + for i := 0; i < instances; i++ { + newAmount, err = cardanofw.GetTokenAmount(ctx, txProviderVector, user.VectorAddress) + require.NoError(t, err) + fmt.Printf("Current Amount Prime: %v. Expected: %v\n", newAmount, expectedAmount) + + if newAmount.Uint64() == expectedAmount { + break + } + + time.Sleep(time.Second * 10) + } + + fmt.Printf("Success count: %v. prevAmount: %v. newAmount: %v. expectedAmount: %v\n", succeededCount, prevAmount, newAmount.Uint64(), expectedAmount) + }) + + t.Run("Both directions 1000x 60min 90%", func(t *testing.T) { + instances := 1000 + maxWaitTime := 3600 + fundSendAmount := uint64(5_000_000) + sendAmount := uint64(1_000_000) + successChance := 90 // 90% + succeededCountPrime := 0 + succeededCountVector := 0 + walletKeysPrime := make([]wallet.IWallet, instances) + walletKeysVector := make([]wallet.IWallet, instances) + + prevAmountPrime, err := cardanofw.GetTokenAmount(ctx, txProviderVector, user.VectorAddress) + require.NoError(t, err) + prevAmountVector, err := cardanofw.GetTokenAmount(ctx, txProviderPrime, user.PrimeAddress) + require.NoError(t, err) + + fmt.Printf("Funding %v Wallets\n", instances*2) + + for i := 0; i < instances; i++ { + if (i+1)%100 == 0 { + fmt.Printf("Funded %v..%v on prime\n", i-99, i) + } + + walletKeysPrime[i], err = wallet.NewStakeWalletManager().Create(path.Join(apex.PrimeCluster.Config.Dir("keys")), true) + require.NoError(t, err) + + walletAddress, _, err := wallet.GetWalletAddressCli(walletKeysPrime[i], uint(apex.PrimeCluster.Config.NetworkMagic)) + require.NoError(t, err) + + user.SendToAddress(t, ctx, txProviderPrime, primeGenesisWallet, fundSendAmount, walletAddress, true) + } + + for i := 0; i < instances; i++ { + if (i+1)%100 == 0 { + fmt.Printf("Funded %v..%v on vector\n", i-99, i) + } + + walletKeysVector[i], err = wallet.NewStakeWalletManager().Create(path.Join(apex.VectorCluster.Config.Dir("keys")), true) + require.NoError(t, err) + + walletAddress, _, err := wallet.GetWalletAddressCli(walletKeysVector[i], uint(apex.VectorCluster.Config.NetworkMagic)) + require.NoError(t, err) + + user.SendToAddress(t, ctx, txProviderVector, vectorGenesisWallet, fundSendAmount, walletAddress, false) + } + + fmt.Printf("Funding Complete\n") + fmt.Printf("Sending %v transactions in %v seconds\n", instances*2, maxWaitTime) + + var wg sync.WaitGroup + for i := 0; i < instances; i++ { + wg.Add(2) + + //nolint:dupl + go func(idx int) { + defer wg.Done() + + if successChance > rand.Intn(100) { + succeededCountPrime++ + sleepTime := rand.Intn(maxWaitTime) + time.Sleep(time.Second * time.Duration(sleepTime)) + + cardanofw.BridgeAmountFull(t, ctx, txProviderPrime, uint(apex.PrimeCluster.Config.NetworkMagic), + apex.Bridge.PrimeMultisigAddr, apex.Bridge.VectorMultisigFeeAddr, walletKeysPrime[idx], user.VectorAddress, sendAmount, + cardanofw.GetDestinationChainID(true)) + } else { + feeAmount := uint64(1_100_000) + receivers := map[string]uint64{ + user.VectorAddress: sendAmount * 10, // 10Ada+ + apex.Bridge.VectorMultisigFeeAddr: feeAmount, + } + + bridgingRequestMetadata, err := cardanofw.CreateMetaData( + user.PrimeAddress, receivers, cardanofw.GetDestinationChainID(true)) + require.NoError(t, err) + + _, err = cardanofw.SendTx( + ctx, txProviderPrime, walletKeysPrime[idx], (sendAmount + feeAmount), apex.Bridge.PrimeMultisigAddr, + apex.PrimeCluster.Config.NetworkMagic, bridgingRequestMetadata) + require.NoError(t, err) + } + }(i) + + //nolint:dupl + go func(idx int) { + defer wg.Done() + + if successChance > rand.Intn(100) { + succeededCountVector++ + sleepTime := rand.Intn(maxWaitTime) + time.Sleep(time.Second * time.Duration(sleepTime)) + + cardanofw.BridgeAmountFull(t, ctx, txProviderVector, uint(apex.VectorCluster.Config.NetworkMagic), + apex.Bridge.VectorMultisigAddr, apex.Bridge.PrimeMultisigFeeAddr, walletKeysVector[idx], user.PrimeAddress, sendAmount, + cardanofw.GetDestinationChainID(false)) + } else { + feeAmount := uint64(1_100_000) + receivers := map[string]uint64{ + user.PrimeAddress: sendAmount * 10, // 10Ada+ + apex.Bridge.PrimeMultisigFeeAddr: feeAmount, + } + + bridgingRequestMetadata, err := cardanofw.CreateMetaData( + user.VectorAddress, receivers, cardanofw.GetDestinationChainID(false)) + require.NoError(t, err) + + _, err = cardanofw.SendTx( + ctx, txProviderVector, walletKeysVector[idx], (sendAmount + feeAmount), apex.Bridge.VectorMultisigAddr, + apex.VectorCluster.Config.NetworkMagic, bridgingRequestMetadata) + require.NoError(t, err) + } + }(i) + } + + wg.Wait() + + fmt.Printf("All tx sent, waiting for confirmation.\n") + + expectedAmountPrime := prevAmountPrime.Uint64() + uint64(succeededCountPrime)*sendAmount + expectedAmountVector := prevAmountVector.Uint64() + uint64(succeededCountVector)*sendAmount + + for i := 0; i < instances; i++ { + newAmountPrime, err := cardanofw.GetTokenAmount(ctx, txProviderVector, user.VectorAddress) + require.NoError(t, err) + fmt.Printf("Current Amount Prime: %v. Expected: %v\n", newAmountPrime, expectedAmountPrime) + + newAmountVector, err := cardanofw.GetTokenAmount(ctx, txProviderPrime, user.PrimeAddress) + require.NoError(t, err) + fmt.Printf("Current Amount Vector: %v. Expected: %v\n", newAmountVector, expectedAmountVector) + + if newAmountPrime.Uint64() == expectedAmountPrime && newAmountVector.Uint64() == expectedAmountVector { + break + } + + time.Sleep(time.Second * 10) + } + + newAmountPrime, err := cardanofw.GetTokenAmount(ctx, txProviderVector, user.VectorAddress) + require.NoError(t, err) + fmt.Printf("Success count: %v. prevAmount: %v. newAmount: %v. expectedAmount: %v\n", succeededCountPrime, prevAmountPrime, newAmountPrime, expectedAmountPrime) + + newAmountVector, err := cardanofw.GetTokenAmount(ctx, txProviderPrime, user.PrimeAddress) + require.NoError(t, err) + fmt.Printf("Success count: %v. prevAmount: %v. newAmount: %v. expectedAmount: %v\n", succeededCountVector, prevAmountVector, newAmountVector, expectedAmountVector) + }) +} diff --git a/state/runtime/precompiled/cardano_verify_signature_precompile.go b/state/runtime/precompiled/cardano_verify_signature_precompile.go index 86763db0d6..7cd2dee37c 100644 --- a/state/runtime/precompiled/cardano_verify_signature_precompile.go +++ b/state/runtime/precompiled/cardano_verify_signature_precompile.go @@ -2,6 +2,8 @@ package precompiled import ( "encoding/hex" + "errors" + "fmt" "strconv" "strings" @@ -41,15 +43,18 @@ func (c *cardanoVerifySignaturePrecompile) run(input []byte, caller types.Addres dataBytes := [3][]byte{} for i := range dataBytes { - str, ok := data[strconv.Itoa(i)].(string) - if !ok { - return nil, runtime.ErrInvalidInputData - } - - dataBytes[i], err = hex.DecodeString(strings.TrimPrefix(str, "0x")) - if err != nil { - // if not hex encoded then its just a plain string -> convert it to byte slice - dataBytes[i] = []byte(str) + switch dt := data[strconv.Itoa(i)].(type) { + case string: + dataBytes[i], err = hex.DecodeString(strings.TrimPrefix(dt, "0x")) + if err != nil { + dataBytes[i] = []byte(dt) + } + case []byte: + dataBytes[i] = dt + case [32]byte: + dataBytes[i] = dt[:] + default: + return nil, fmt.Errorf("%w: index %d", runtime.ErrInvalidInputData, i) } } @@ -79,14 +84,14 @@ func (c *cardanoVerifySignaturePrecompile) run(input []byte, caller types.Addres } } - // golang unexpected behaviour fix?!?! - // `switch cardano_wallet.VerifyMessage(message, verifyingKey, signature)` acts strange - switch err = cardano_wallet.VerifyMessage(rawTxOrMessage, verifyingKey, signature); err { //nolint:errorlint - case nil: - return abiBoolTrue, nil - case cardano_wallet.ErrInvalidSignature: - return abiBoolFalse, nil - default: + err = cardano_wallet.VerifyMessage(rawTxOrMessage, verifyingKey, signature) + if err != nil { + if errors.Is(err, cardano_wallet.ErrInvalidSignature) { + return abiBoolFalse, nil + } + return nil, err } + + return abiBoolTrue, nil }