Skip to content
Snippets Groups Projects
Unverified Commit f6793e0d authored by Luca Moser's avatar Luca Moser
Browse files

renames integration test

parent 9f42d25a
No related branches found
No related tags found
No related merge requests found
......@@ -3,6 +3,7 @@ package consensus
import (
"time"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
"github.com/iotaledger/hive.go/events"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle"
......@@ -138,9 +139,10 @@ func (fcob *FCOB) setFinalized(cachedTransactionMetadata *tangle.CachedTransacti
// onFork triggers a voting process whenever a Transaction gets forked into a new Branch. The initial opinion is derived
// from the preferred flag that was set using the FCOB rule.
func (fcob *FCOB) onFork(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *tangle.CachedTransactionMetadata) {
func (fcob *FCOB) onFork(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *tangle.CachedTransactionMetadata, cachedTargetBranch *branchmanager.CachedBranch, conflictingInputs []transaction.OutputID) {
defer cachedTransaction.Release()
defer cachedTransactionMetadata.Release()
defer cachedTargetBranch.Release()
transactionMetadata := cachedTransactionMetadata.Unwrap()
if transactionMetadata == nil {
......
......@@ -4,6 +4,7 @@ import (
"container/list"
"errors"
"fmt"
"log"
"math"
"time"
......@@ -77,8 +78,10 @@ func (tangle *Tangle) AttachPayload(payload *payload.Payload) {
// AttachPayloadSync is the worker function that stores the payload and calls the corresponding storage events.
func (tangle *Tangle) AttachPayloadSync(payloadToStore *payload.Payload) {
// store the payload models or abort if we have seen the payload already
log.Println("storing payload", payloadToStore.ID())
cachedPayload, cachedPayloadMetadata, payloadStored := tangle.storePayload(payloadToStore)
if !payloadStored {
log.Println("could not store payload", payloadToStore.ID())
return
}
defer cachedPayload.Release()
......@@ -235,7 +238,7 @@ func (tangle *Tangle) Fork(transactionID transaction.ID, conflictingInputs []tra
}
// trigger events + set result
tangle.Events.Fork.Trigger(cachedTransaction, cachedTransactionMetadata)
tangle.Events.Fork.Trigger(cachedTransaction, cachedTransactionMetadata, cachedTargetBranch, conflictingInputs)
forked = true
return
......@@ -944,6 +947,7 @@ func (tangle *Tangle) storeTransactionModels(solidPayload *payload.Payload) (cac
})}
if transactionIsNew {
log.Println("stored transaction", cachedTransaction.Unwrap().ID())
cachedTransactionMetadata = &CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Store(NewTransactionMetadata(solidPayload.Transaction().ID()))}
// store references to the consumed outputs
......@@ -953,6 +957,7 @@ func (tangle *Tangle) storeTransactionModels(solidPayload *payload.Payload) (cac
return true
})
} else {
log.Println("transaction was already stored", cachedTransaction.Unwrap().ID())
cachedTransactionMetadata = &CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Load(solidPayload.Transaction().ID().Bytes())}
}
......
package gettransactionbyid
import (
"log"
"net/http"
"github.com/iotaledger/goshimmer/dapps/valuetransfers"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
"github.com/iotaledger/goshimmer/plugins/webapi/value/utils"
"github.com/labstack/echo"
"github.com/labstack/gommon/log"
)
// Handler gets the transaction by id.
func Handler(c echo.Context) error {
txnID, err := transaction.IDFromBase58(c.QueryParam("txnID"))
if err != nil {
log.Info(err)
log.Println(err)
return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
}
......@@ -22,16 +22,19 @@ func Handler(c echo.Context) error {
cachedTxnMetaObj := valuetransfers.Tangle.TransactionMetadata(txnID)
defer cachedTxnMetaObj.Release()
if !cachedTxnMetaObj.Exists() {
log.Println("transaction meta doesn't exist for", txnID)
return c.JSON(http.StatusNotFound, Response{Error: "Transaction not found"})
}
cachedTxnObj := valuetransfers.Tangle.Transaction(txnID)
defer cachedTxnObj.Release()
if !cachedTxnObj.Exists() {
log.Println("transaction doesn't exist for", txnID)
return c.JSON(http.StatusNotFound, Response{Error: "Transaction not found"})
}
txn := utils.ParseTransaction(cachedTxnObj.Unwrap())
txnMeta := cachedTxnMetaObj.Unwrap()
txnMeta.Preferred()
return c.JSON(http.StatusOK, Response{
Transaction: txn,
InclusionState: utils.InclusionState{
......@@ -41,6 +44,7 @@ func Handler(c echo.Context) error {
Solid: txnMeta.Solid(),
Rejected: txnMeta.Rejected(),
Finalized: txnMeta.Finalized(),
Preferred: txnMeta.Preferred(),
},
})
}
......
package sendtransaction
import (
"log"
"net/http"
"github.com/iotaledger/goshimmer/dapps/valuetransfers"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
"github.com/iotaledger/goshimmer/plugins/issuer"
"github.com/labstack/echo"
"github.com/labstack/gommon/log"
)
// Handler sends a transaction.
func Handler(c echo.Context) error {
var request Request
if err := c.Bind(&request); err != nil {
log.Info(err.Error())
log.Println(err.Error())
return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
}
// prepare transaction
tx, _, err := transaction.FromBytes(request.TransactionBytes)
if err != nil {
log.Info(err.Error())
log.Println(err.Error())
return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
}
// Prepare value payload and send the message to tangle
payload := valuetransfers.ValueObjectFactory().IssueTransaction(tx)
log.Println("issued transaction")
_, err = issuer.IssuePayload(payload)
if err != nil {
log.Info(err.Error())
log.Println(err.Error())
return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
}
log.Println("issued payload")
return c.JSON(http.StatusOK, Response{TransactionID: tx.ID().String()})
}
......
......@@ -70,4 +70,5 @@ type InclusionState struct {
Solid bool `json:"solid,omitempty"`
Rejected bool `json:"rejected,omitempty"`
Finalized bool `json:"finalized,omitempty"`
Preferred bool `json:"preferred,omitempty"`
}
......@@ -6,4 +6,4 @@ chmod 777 /assets/*
echo "assets:"
ls /assets
echo "running tests..."
go test ./tests/"${TEST_NAME}" -run TestConsensusConflicts -v -timeout 30m
go test ./tests/"${TEST_NAME}" -run TestConsensusFiftyFiftyOpinionSplit -v -timeout 30m
......@@ -80,7 +80,7 @@ func (f *Framework) CreateNetwork(name string, peers int, minimumNeighbors int,
// create peers/GoShimmer nodes
for i := 0; i < peers; i++ {
config := GoShimmerConfig{
Bootstrap: i == 0,
Bootstrap: true,
BootstrapInitialIssuanceTimePeriodSec: bootstrapInitialIssuanceTimePeriodSec,
Faucet: i == 0,
}
......@@ -128,7 +128,7 @@ func (f *Framework) CreateNetworkWithPartitions(name string, peers, partitions,
// create peers/GoShimmer nodes
for i := 0; i < peers; i++ {
config := GoShimmerConfig{Bootstrap: i == 0}
config := GoShimmerConfig{Bootstrap: true}
if _, err = network.CreatePeer(config); err != nil {
return nil, err
}
......
......@@ -18,14 +18,15 @@ import (
"github.com/stretchr/testify/require"
)
// TestConsensusConflicts issues valid conflicting value objects and makes sure that
// the conflicts are resolved via FPC.
func TestConsensusConflicts(t *testing.T) {
// TestConsensusFiftyFiftyOpinionSplit spawns two network partitions with their own peers,
// then issues valid value objects spending the genesis in both, deletes the partitions (and lets them merge)
// and then checks that the conflicts are resolved via FPC.
func TestConsensusFiftyFiftyOpinionSplit(t *testing.T) {
n, err := f.CreateNetworkWithPartitions("consensus_TestConsensusConflicts", 8, 2, 4)
require.NoError(t, err)
defer tests.ShutdownNetwork(t, n)
time.Sleep(10 * time.Second)
time.Sleep(5 * time.Second)
// split the network
for i, partition := range n.Partitions() {
......@@ -58,7 +59,7 @@ func TestConsensusConflicts(t *testing.T) {
transaction.NewInputs(genesisOutputID),
transaction.NewOutputs(map[address.Address][]*balance.Balance{
destAddr: {
{Value: genesisBalance / 2, Color: balance.ColorIOTA},
{Value: genesisBalance, Color: balance.ColorIOTA},
},
}))
tx = tx.Sign(signaturescheme.ED25519(*genesisWallet.Seed().KeyPair(0)))
......@@ -72,7 +73,7 @@ func TestConsensusConflicts(t *testing.T) {
assert.NoError(t, err)
// check that the transaction is actually available on all the peers of the partition
missing, err := tests.AwaitTransactionAvailability(partition.Peers(), []string{txID}, 4*time.Second)
missing, err := tests.AwaitTransactionAvailability(partition.Peers(), []string{txID}, 15*time.Second)
if err != nil {
assert.NoError(t, err, "transactions should have been available in partition")
for p, missingOnPeer := range missing {
......@@ -90,6 +91,22 @@ func TestConsensusConflicts(t *testing.T) {
// sleep the avg. network delay so both partitions prefer their own first seen transaction
time.Sleep(valuetransfers.AverageNetworkDelay)
// check that each partition is preferring its corresponding transaction
log.Println("checking that each partition likes its corresponding transaction before the conflict:")
for i, partition := range n.Partitions() {
tests.CheckTransactions(t, partition.Peers(), map[string]*tests.ExpectedTransaction{
conflictingTxIDs[i]: nil,
}, true, tests.ExpectedInclusionState{
Confirmed: tests.False(),
Finalized: tests.False(),
Conflict: tests.False(),
Solid: tests.True(),
Rejected: tests.False(),
Liked: tests.True(),
Preferred: tests.True(),
})
}
// merge back the partitions
log.Println("merging partitions...")
assert.NoError(t, n.DeletePartitions(), "merging the network partitions should work")
......@@ -125,13 +142,42 @@ func TestConsensusConflicts(t *testing.T) {
}
}
// check that the transactions are marked as conflicting
tests.CheckTransactions(t, n.Peers(), expectations, true, tests.ExpectedInclusionState{
Confirmed: tests.False(),
Finalized: tests.False(),
// should be part of a conflict set
Conflict: tests.True(),
Solid: tests.True(),
Rejected: tests.False(),
Liked: tests.False(),
Finalized: tests.True(),
Conflict: tests.True(),
Solid: tests.True(),
})
// now all transactions must be finalized and at most one must be confirmed
var confirmedOverConflictSet int
for _, conflictingTx := range conflictingTxIDs {
var rejected, confirmed int
for _, p := range n.Peers() {
tx, err := p.GetTransactionByID(conflictingTx)
assert.NoError(t, err)
if tx.InclusionState.Confirmed {
confirmed++
continue
}
if tx.InclusionState.Rejected {
rejected++
}
}
if rejected != 0 {
assert.Len(t, n.Peers(), rejected, "the rejected count for %s should be equal to the amount of peers", conflictingTx)
}
if confirmed != 0 {
assert.Len(t, n.Peers(), confirmed, "the confirmed count for %s should be equal to the amount of peers", conflictingTx)
confirmedOverConflictSet++
}
assert.False(t, rejected == 0 && confirmed == 0, "a transaction must either be rejected or confirmed")
}
// there must only be one confirmed transaction out of the conflict set
if confirmedOverConflictSet != 0 {
assert.Equal(t, 1, confirmedOverConflictSet, "only one transaction can be confirmed out of the conflict set. %d of %d are confirmed", confirmedOverConflictSet, len(conflictingTxIDs))
}
}
......@@ -383,6 +383,8 @@ type ExpectedInclusionState struct {
Rejected *bool
// The optional liked state to check against.
Liked *bool
// The optional preferred state to check against.
Preferred *bool
}
// True returns a pointer to a true bool.
......@@ -426,30 +428,33 @@ func CheckTransactions(t *testing.T, peers []*framework.Peer, transactionIDs map
// check inclusion state
if expectedInclusionState.Confirmed != nil {
assert.Equal(t, *expectedInclusionState.Confirmed, resp.InclusionState.Confirmed, "confirmed state doesn't match")
assert.Equal(t, *expectedInclusionState.Confirmed, resp.InclusionState.Confirmed, "confirmed state doesn't match - %s", txId)
}
if expectedInclusionState.Conflict != nil {
assert.Equal(t, *expectedInclusionState.Conflict, resp.InclusionState.Conflict, "conflict state doesn't match")
assert.Equal(t, *expectedInclusionState.Conflict, resp.InclusionState.Conflict, "conflict state doesn't match - %s", txId)
}
if expectedInclusionState.Solid != nil {
assert.Equal(t, *expectedInclusionState.Solid, resp.InclusionState.Solid, "solid state doesn't match")
assert.Equal(t, *expectedInclusionState.Solid, resp.InclusionState.Solid, "solid state doesn't match - %s", txId)
}
if expectedInclusionState.Rejected != nil {
assert.Equal(t, *expectedInclusionState.Rejected, resp.InclusionState.Rejected, "rejected state doesn't match")
assert.Equal(t, *expectedInclusionState.Rejected, resp.InclusionState.Rejected, "rejected state doesn't match - %s", txId)
}
if expectedInclusionState.Liked != nil {
assert.Equal(t, *expectedInclusionState.Liked, resp.InclusionState.Liked, "liked state doesn't match")
assert.Equal(t, *expectedInclusionState.Liked, resp.InclusionState.Liked, "liked state doesn't match - %s", txId)
}
if expectedInclusionState.Preferred != nil {
assert.Equal(t, *expectedInclusionState.Preferred, resp.InclusionState.Preferred, "preferred state doesn't match - %s", txId)
}
if expectedTransaction != nil {
if expectedTransaction.Inputs != nil {
assert.Equal(t, *expectedTransaction.Inputs, resp.Transaction.Inputs, "inputs do not match")
assert.Equal(t, *expectedTransaction.Inputs, resp.Transaction.Inputs, "inputs do not match - %s", txId)
}
if expectedTransaction.Outputs != nil {
assert.Equal(t, *expectedTransaction.Outputs, resp.Transaction.Outputs, "outputs do not match")
assert.Equal(t, *expectedTransaction.Outputs, resp.Transaction.Outputs, "outputs do not match - %s", txId)
}
if expectedTransaction.Signature != nil {
assert.Equal(t, *expectedTransaction.Signature, resp.Transaction.Signature, "signatures do not match")
assert.Equal(t, *expectedTransaction.Signature, resp.Transaction.Signature, "signatures do not match - %s", txId)
}
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment