mirror of
https://github.com/planetmint/planetmint-go.git
synced 2025-03-30 15:08:28 +00:00
fixed crashes when two machines are attested at the same time (#343)
* * added creation of random machines to prepare a test case * setting all the consensus timeout values at once (if changed) * mutex protection of the elements tx crafting methods (sequential processing) * Extending the TestMachineNFTIssuance test case to parallel threads and threading issues * moving all elements-rpc usage to the elementd-connector.go file * removed call to fatal * added WaitForNextBlock to be out of sync with PoP to avoid the following error PoP broadcast tx failed: node0.info: key not found after the test --------- Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
This commit is contained in:
parent
8a5e7b5da6
commit
e6f6e43754
@ -184,6 +184,7 @@ func (s *E2ETestSuite) TestMachineAllowanceAttestation() {
|
|||||||
s.Require().NoError(s.network.WaitForNextBlock())
|
s.Require().NoError(s.network.WaitForNextBlock())
|
||||||
s.Require().NoError(s.network.WaitForNextBlock())
|
s.Require().NoError(s.network.WaitForNextBlock())
|
||||||
s.Require().NoError(s.network.WaitForNextBlock())
|
s.Require().NoError(s.network.WaitForNextBlock())
|
||||||
|
s.Require().NoError(s.network.WaitForNextBlock())
|
||||||
|
|
||||||
// reset clientCtx to validator ctx
|
// reset clientCtx to validator ctx
|
||||||
libConfig.SetClientCtx(val.ClientCtx)
|
libConfig.SetClientCtx(val.ClientCtx)
|
||||||
|
@ -2,6 +2,7 @@ package moduleobject
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcutil/hdkeychain"
|
"github.com/btcsuite/btcd/btcutil/hdkeychain"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
@ -51,7 +52,40 @@ func Machine(name, pubKey string, prvKey string, address string) machinetypes.Ma
|
|||||||
m := machinetypes.Machine{
|
m := machinetypes.Machine{
|
||||||
Name: name,
|
Name: name,
|
||||||
Ticker: name + "_ticker",
|
Ticker: name + "_ticker",
|
||||||
Domain: "lab.r3c.network",
|
Domain: "testnet-assets.rddl.iok",
|
||||||
|
Reissue: true,
|
||||||
|
Amount: 1000,
|
||||||
|
Precision: 8,
|
||||||
|
IssuerPlanetmint: planetmintPubKey,
|
||||||
|
IssuerLiquid: liquidPubKey,
|
||||||
|
MachineId: pubKey,
|
||||||
|
Metadata: &metadata,
|
||||||
|
Type: 1,
|
||||||
|
MachineIdSignature: signatureHex,
|
||||||
|
Address: address,
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func MachineRandom(name, pubKey string, prvKey string, address string, random int) machinetypes.Machine {
|
||||||
|
metadata := Metadata()
|
||||||
|
_, liquidPubKey := ExtendedKeyPair(config.LiquidNetParams)
|
||||||
|
_, planetmintPubKey := ExtendedKeyPair(config.PlmntNetParams)
|
||||||
|
|
||||||
|
prvKeyBytes, _ := hex.DecodeString(prvKey)
|
||||||
|
sk := &secp256k1.PrivKey{Key: prvKeyBytes}
|
||||||
|
pubKeyBytes, _ := hex.DecodeString(pubKey)
|
||||||
|
sign, _ := sk.Sign(pubKeyBytes)
|
||||||
|
signatureHex := hex.EncodeToString(sign)
|
||||||
|
|
||||||
|
if address == "" {
|
||||||
|
address = sample.Secp256k1AccAddress().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
m := machinetypes.Machine{
|
||||||
|
Name: name + strconv.Itoa(random),
|
||||||
|
Ticker: name + strconv.Itoa(random) + "_ticker",
|
||||||
|
Domain: "testnet-assets.rddl.io",
|
||||||
Reissue: true,
|
Reissue: true,
|
||||||
Amount: 1000,
|
Amount: 1000,
|
||||||
Precision: 8,
|
Precision: 8,
|
||||||
|
@ -334,6 +334,9 @@ func New(l Logger, baseDir string, cfg Config) (*Network, error) {
|
|||||||
ctx := server.NewDefaultContext()
|
ctx := server.NewDefaultContext()
|
||||||
tmCfg := ctx.Config
|
tmCfg := ctx.Config
|
||||||
tmCfg.Consensus.TimeoutCommit = cfg.TimeoutCommit
|
tmCfg.Consensus.TimeoutCommit = cfg.TimeoutCommit
|
||||||
|
tmCfg.Consensus.TimeoutPrecommit = cfg.TimeoutCommit
|
||||||
|
tmCfg.Consensus.TimeoutPrevote = cfg.TimeoutCommit
|
||||||
|
tmCfg.Consensus.TimeoutPropose = cfg.TimeoutCommit
|
||||||
|
|
||||||
// Only allow the first validator to expose an RPC, API and gRPC
|
// Only allow the first validator to expose an RPC, API and gRPC
|
||||||
// server/client due to Tendermint in-process constraints.
|
// server/client due to Tendermint in-process constraints.
|
||||||
|
@ -1,17 +1,29 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/planetmint/planetmint-go/config"
|
"github.com/planetmint/planetmint-go/config"
|
||||||
|
"github.com/planetmint/planetmint-go/x/machine/types"
|
||||||
elements "github.com/rddl-network/elements-rpc"
|
elements "github.com/rddl-network/elements-rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// this mutex has to protect all signing and crafting of transactions and their inputs
|
||||||
|
// so that UTXOs are not spend twice by accident
|
||||||
|
elementsSyncAccess sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
func ReissueAsset(reissueTx string) (txID string, err error) {
|
func ReissueAsset(reissueTx string) (txID string, err error) {
|
||||||
conf := config.GetConfig()
|
conf := config.GetConfig()
|
||||||
url := conf.GetRPCURL()
|
url := conf.GetRPCURL()
|
||||||
cmdArgs := strings.Split(reissueTx, " ")
|
cmdArgs := strings.Split(reissueTx, " ")
|
||||||
|
elementsSyncAccess.Lock()
|
||||||
|
defer elementsSyncAccess.Unlock()
|
||||||
result, err := elements.ReissueAsset(url, []string{cmdArgs[1], cmdArgs[2]})
|
result, err := elements.ReissueAsset(url, []string{cmdArgs[1], cmdArgs[2]})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -24,6 +36,8 @@ func DistributeAsset(address string, amount string, reissuanceAsset string) (txI
|
|||||||
conf := config.GetConfig()
|
conf := config.GetConfig()
|
||||||
url := conf.GetRPCURL()
|
url := conf.GetRPCURL()
|
||||||
|
|
||||||
|
elementsSyncAccess.Lock()
|
||||||
|
defer elementsSyncAccess.Unlock()
|
||||||
txID, err = elements.SendToAddress(url, []string{
|
txID, err = elements.SendToAddress(url, []string{
|
||||||
address,
|
address,
|
||||||
`"` + amount + `"`,
|
`"` + amount + `"`,
|
||||||
@ -38,3 +52,97 @@ func DistributeAsset(address string, amount string, reissuanceAsset string) (txI
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IssueNFTAsset(name string, machineAddress string, domain string) (assetID string, contract string, hexTx string, err error) {
|
||||||
|
conf := config.GetConfig()
|
||||||
|
url := conf.GetRPCURL()
|
||||||
|
|
||||||
|
address, err := elements.GetNewAddress(url, []string{``})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
addressInfo, err := elements.GetAddressInfo(url, []string{address})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
elementsSyncAccess.Lock()
|
||||||
|
defer elementsSyncAccess.Unlock()
|
||||||
|
hex, err := elements.CreateRawTransaction(url, []string{`[]`, `[{"data":"00"}]`})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fundRawTransactionResult, err := elements.FundRawTransaction(url, []string{hex, `{"feeRate":0.00001000}`})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c := types.Contract{
|
||||||
|
Entity: types.Entity{
|
||||||
|
Domain: domain,
|
||||||
|
},
|
||||||
|
IssuerPubkey: addressInfo.Pubkey,
|
||||||
|
MachineAddr: machineAddress,
|
||||||
|
Name: name,
|
||||||
|
Precision: 0,
|
||||||
|
Version: 0,
|
||||||
|
}
|
||||||
|
contractBytes, err := json.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// e.g. {"entity":{"domain":"testnet-assets.rddl.io"}, "issuer_pubkey":"02...}
|
||||||
|
contract = string(contractBytes)
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
_, err = h.Write(contractBytes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// e.g. 7ca8bb403ee5dccddef7b89b163048cf39439553f0402351217a4a03d2224df8
|
||||||
|
hash := h.Sum(nil)
|
||||||
|
|
||||||
|
// Reverse hash, e.g. f84d22d2034a7a21512340f053954339cf4830169bb8f7decddce53e40bba87c
|
||||||
|
for i, j := 0, len(hash)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
hash[i], hash[j] = hash[j], hash[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
rawIssueAssetResults, err := elements.RawIssueAsset(url, []string{fundRawTransactionResult.Hex,
|
||||||
|
`[{"asset_amount":0.00000001, "asset_address":"` + address + `", "blind":false, "contract_hash":"` + fmt.Sprintf("%+x", hash) + `"}]`,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rawIssueAssetResult := rawIssueAssetResults[len(rawIssueAssetResults)-1]
|
||||||
|
hex, err = elements.BlindRawTransaction(url, []string{rawIssueAssetResult.Hex, `true`, `[]`, `false`})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assetID = rawIssueAssetResult.Asset
|
||||||
|
|
||||||
|
signRawTransactionWithWalletResult, err := elements.SignRawTransactionWithWallet(url, []string{hex})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
testMempoolAcceptResults, err := elements.TestMempoolAccept(url, []string{`["` + signRawTransactionWithWalletResult.Hex + `"]`})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
testMempoolAcceptResult := testMempoolAcceptResults[len(testMempoolAcceptResults)-1]
|
||||||
|
if !testMempoolAcceptResult.Allowed {
|
||||||
|
err = fmt.Errorf("not accepted by mempool: %+v %+v", testMempoolAcceptResult, signRawTransactionWithWalletResult)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hex, err = elements.SendRawTransaction(url, []string{signRawTransactionWithWalletResult.Hex})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return assetID, contract, hex, err
|
||||||
|
}
|
||||||
|
@ -3,20 +3,16 @@ package util
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
errorsmod "cosmossdk.io/errors"
|
errorsmod "cosmossdk.io/errors"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
config "github.com/planetmint/planetmint-go/config"
|
|
||||||
"github.com/planetmint/planetmint-go/x/machine/types"
|
"github.com/planetmint/planetmint-go/x/machine/types"
|
||||||
elements "github.com/rddl-network/elements-rpc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type HTTPClient interface {
|
type HTTPClient interface {
|
||||||
@ -31,97 +27,6 @@ func init() {
|
|||||||
RegisterAssetServiceHTTPClient = &http.Client{}
|
RegisterAssetServiceHTTPClient = &http.Client{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func IssueNFTAsset(name string, machineAddress string, domain string) (assetID string, contract string, hexTx string, err error) {
|
|
||||||
conf := config.GetConfig()
|
|
||||||
|
|
||||||
url := conf.GetRPCURL()
|
|
||||||
address, err := elements.GetNewAddress(url, []string{``})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
addressInfo, err := elements.GetAddressInfo(url, []string{address})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hex, err := elements.CreateRawTransaction(url, []string{`[]`, `[{"data":"00"}]`})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fundRawTransactionResult, err := elements.FundRawTransaction(url, []string{hex, `{"feeRate":0.00001000}`})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c := types.Contract{
|
|
||||||
Entity: types.Entity{
|
|
||||||
Domain: domain,
|
|
||||||
},
|
|
||||||
IssuerPubkey: addressInfo.Pubkey,
|
|
||||||
MachineAddr: machineAddress,
|
|
||||||
Name: name,
|
|
||||||
Precision: 0,
|
|
||||||
Version: 0,
|
|
||||||
}
|
|
||||||
contractBytes, err := json.Marshal(c)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// e.g. {"entity":{"domain":"testnet-assets.rddl.io"}, "issuer_pubkey":"02...}
|
|
||||||
contract = string(contractBytes)
|
|
||||||
|
|
||||||
h := sha256.New()
|
|
||||||
_, err = h.Write(contractBytes)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// e.g. 7ca8bb403ee5dccddef7b89b163048cf39439553f0402351217a4a03d2224df8
|
|
||||||
hash := h.Sum(nil)
|
|
||||||
|
|
||||||
// Reverse hash, e.g. f84d22d2034a7a21512340f053954339cf4830169bb8f7decddce53e40bba87c
|
|
||||||
for i, j := 0, len(hash)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
hash[i], hash[j] = hash[j], hash[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
rawIssueAssetResults, err := elements.RawIssueAsset(url, []string{fundRawTransactionResult.Hex,
|
|
||||||
`[{"asset_amount":0.00000001, "asset_address":"` + address + `", "blind":false, "contract_hash":"` + fmt.Sprintf("%+x", hash) + `"}]`,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rawIssueAssetResult := rawIssueAssetResults[len(rawIssueAssetResults)-1]
|
|
||||||
hex, err = elements.BlindRawTransaction(url, []string{rawIssueAssetResult.Hex, `true`, `[]`, `false`})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
assetID = rawIssueAssetResult.Asset
|
|
||||||
|
|
||||||
signRawTransactionWithWalletResult, err := elements.SignRawTransactionWithWallet(url, []string{hex})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
testMempoolAcceptResults, err := elements.TestMempoolAccept(url, []string{`["` + signRawTransactionWithWalletResult.Hex + `"]`})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
testMempoolAcceptResult := testMempoolAcceptResults[len(testMempoolAcceptResults)-1]
|
|
||||||
if !testMempoolAcceptResult.Allowed {
|
|
||||||
log.Fatalln("not accepted by mempool")
|
|
||||||
}
|
|
||||||
|
|
||||||
hex, err = elements.SendRawTransaction(url, []string{signRawTransactionWithWalletResult.Hex})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return assetID, contract, hex, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func IssueMachineNFT(goCtx context.Context, machine *types.Machine, scheme string, domain string, path string) error {
|
func IssueMachineNFT(goCtx context.Context, machine *types.Machine, scheme string, domain string, path string) error {
|
||||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||||
// asset registration is in order to have the contact published
|
// asset registration is in order to have the contact published
|
||||||
|
@ -3,6 +3,9 @@ package util_test
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
@ -47,11 +50,21 @@ func TestMachineNFTIssuance(t *testing.T) {
|
|||||||
elements.Client = &elementsmocks.MockClient{}
|
elements.Client = &elementsmocks.MockClient{}
|
||||||
util.RegisterAssetServiceHTTPClient = &mocks.MockClient{}
|
util.RegisterAssetServiceHTTPClient = &mocks.MockClient{}
|
||||||
_, ctx := keeper.MachineKeeper(t)
|
_, ctx := keeper.MachineKeeper(t)
|
||||||
sk, pk := sample.KeyPair()
|
|
||||||
machine := moduleobject.Machine(pk, pk, sk, "")
|
|
||||||
goCtx := sdk.WrapSDKContext(ctx)
|
|
||||||
|
|
||||||
params := types.DefaultParams()
|
params := types.DefaultParams()
|
||||||
err := util.IssueMachineNFT(goCtx, &machine, params.AssetRegistryScheme, params.AssetRegistryDomain, params.AssetRegistryPath)
|
var wg sync.WaitGroup
|
||||||
assert.NoError(t, err)
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
randomInt := rand.Int()
|
||||||
|
sk, pk := sample.KeyPair(randomInt)
|
||||||
|
machine := moduleobject.MachineRandom(pk, pk, sk, "address "+strconv.Itoa(randomInt), randomInt)
|
||||||
|
goCtx := sdk.WrapSDKContext(ctx)
|
||||||
|
|
||||||
|
err := util.IssueMachineNFT(goCtx, &machine, params.AssetRegistryScheme, params.AssetRegistryDomain, params.AssetRegistryPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
@ -2,4 +2,6 @@ package util
|
|||||||
|
|
||||||
import "sync"
|
import "sync"
|
||||||
|
|
||||||
var TerminationWaitGroup sync.WaitGroup
|
var (
|
||||||
|
TerminationWaitGroup sync.WaitGroup
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user