Skip to content
Snippets Groups Projects
Unverified Commit 8b208392 authored by Angelo Capossele's avatar Angelo Capossele Committed by GitHub
Browse files

Merge pull request #89 from Wollac/wip/pow

Require PoW as one form of rate control
parents 58924245 61a25ece
No related branches found
No related tags found
No related merge requests found
package meta_transaction
import (
"github.com/iotaledger/iota.go/consts"
"github.com/iotaledger/iota.go/trinary"
)
......@@ -12,6 +13,7 @@ const (
TAIL_OFFSET = HEAD_END
TRANSACTION_TYPE_OFFSET = TAIL_END
DATA_OFFSET = TRANSACTION_TYPE_END
NONCE_OFFSET = DATA_END
SHARD_MARKER_SIZE = 11
TRUNK_TRANSACTION_HASH_SIZE = 243
......@@ -20,6 +22,7 @@ const (
TAIL_SIZE = 1
TRANSACTION_TYPE_SIZE = 8
DATA_SIZE = 6993
NONCE_SIZE = consts.NonceTrinarySize
SHARD_MARKER_END = SHARD_MARKER_OFFSET + SHARD_MARKER_SIZE
TRUNK_TRANSACTION_HASH_END = TRUNK_TRANSACTION_HASH_OFFSET + TRUNK_TRANSACTION_HASH_SIZE
......@@ -28,8 +31,11 @@ const (
TAIL_END = TAIL_OFFSET + TAIL_SIZE
TRANSACTION_TYPE_END = TRANSACTION_TYPE_OFFSET + TRANSACTION_TYPE_SIZE
DATA_END = DATA_OFFSET + DATA_SIZE
NONCE_END = NONCE_OFFSET + NONCE_SIZE
MARSHALED_TOTAL_SIZE = DATA_END
MARSHALED_TOTAL_SIZE = NONCE_END
BRANCH_NULL_HASH = trinary.Trytes("999999999999999999999999999999999999999999999999999999999999999999999999999999999")
MIN_WEIGHT_MAGNITUDE = 12
)
package meta_transaction
import (
"fmt"
"sync"
"github.com/iotaledger/goshimmer/packages/curl"
"github.com/iotaledger/iota.go/consts"
"github.com/iotaledger/iota.go/curl"
"github.com/iotaledger/iota.go/pow"
"github.com/iotaledger/iota.go/trinary"
"github.com/pkg/errors"
)
type MetaTransaction struct {
......@@ -19,6 +23,7 @@ type MetaTransaction struct {
transactionType *trinary.Trytes
data trinary.Trits
modified bool
nonce *trinary.Trytes
hasherMutex sync.RWMutex
hashMutex sync.RWMutex
......@@ -31,6 +36,7 @@ type MetaTransaction struct {
dataMutex sync.RWMutex
bytesMutex sync.RWMutex
modifiedMutex sync.RWMutex
nonceMutex sync.RWMutex
trits trinary.Trits
bytes []byte
......@@ -85,7 +91,7 @@ func (this *MetaTransaction) GetHash() (result trinary.Trytes) {
defer this.hashMutex.Unlock()
if this.hash == nil {
this.hasherMutex.Lock()
this.parseHashRelatedDetails()
this.computeHashDetails()
this.hasherMutex.Unlock()
}
} else {
......@@ -106,7 +112,7 @@ func (this *MetaTransaction) GetWeightMagnitude() (result int) {
defer this.hashMutex.Unlock()
if this.hash == nil {
this.hasherMutex.Lock()
this.parseHashRelatedDetails()
this.computeHashDetails()
this.hasherMutex.Unlock()
}
} else {
......@@ -118,9 +124,26 @@ func (this *MetaTransaction) GetWeightMagnitude() (result int) {
return
}
// returns the trytes that are relevant for the transaction hash
func (this *MetaTransaction) getHashEssence() trinary.Trits {
txTrits := this.trits
// very dirty hack, to get an iota.go compatible size
if len(txTrits) > consts.TransactionTrinarySize {
panic("transaction too large")
}
essenceTrits := make([]int8, consts.TransactionTrinarySize)
copy(essenceTrits[consts.TransactionTrinarySize-len(txTrits):], txTrits)
return essenceTrits
}
// hashes the transaction using curl (without locking - internal usage)
func (this *MetaTransaction) parseHashRelatedDetails() {
hashTrits := curl.CURLP81.Hash(this.trits)
func (this *MetaTransaction) computeHashDetails() {
hashTrits, err := curl.HashTrits(this.getHashEssence())
if err != nil {
panic(err)
}
hashTrytes := trinary.MustTritsToTrytes(hashTrits)
this.hash = &hashTrytes
......@@ -466,6 +489,49 @@ func (this *MetaTransaction) GetBytes() (result []byte) {
return
}
func (this *MetaTransaction) GetNonce() trinary.Trytes {
this.nonceMutex.RLock()
if this.nonce == nil {
this.nonceMutex.RUnlock()
this.nonceMutex.Lock()
defer this.nonceMutex.Unlock()
if this.nonce == nil {
nonce := trinary.MustTritsToTrytes(this.trits[NONCE_OFFSET:NONCE_END])
this.nonce = &nonce
}
} else {
defer this.nonceMutex.RUnlock()
}
return *this.nonce
}
func (this *MetaTransaction) SetNonce(nonce trinary.Trytes) bool {
this.nonceMutex.RLock()
if this.nonce == nil || *this.nonce != nonce {
this.nonceMutex.RUnlock()
this.nonceMutex.Lock()
defer this.nonceMutex.Unlock()
if this.nonce == nil || *this.nonce != nonce {
this.nonce = &nonce
this.hasherMutex.RLock()
copy(this.trits[NONCE_OFFSET:NONCE_END], trinary.MustTrytesToTrits(nonce)[:NONCE_SIZE])
this.hasherMutex.RUnlock()
this.SetModified(true)
this.ReHash()
return true
}
} else {
this.nonceMutex.RUnlock()
}
return false
}
// returns true if the transaction contains unsaved changes (supports concurrency)
func (this *MetaTransaction) GetModified() bool {
this.modifiedMutex.RLock()
......@@ -481,3 +547,28 @@ func (this *MetaTransaction) SetModified(modified bool) {
this.modified = modified
}
func (this *MetaTransaction) DoProofOfWork(mwm int) error {
this.hasherMutex.Lock()
powTrytes := trinary.MustTritsToTrytes(this.getHashEssence())
_, pow := pow.GetFastestProofOfWorkImpl()
nonce, err := pow(powTrytes, mwm)
this.hasherMutex.Unlock()
if err != nil {
return errors.Wrap(err, "PoW failed")
}
this.SetNonce(nonce)
return nil
}
func (this *MetaTransaction) Validate() error {
// check that the weight magnitude is valid
weightMagnitude := this.GetWeightMagnitude()
if weightMagnitude < MIN_WEIGHT_MAGNITUDE {
return fmt.Errorf("insufficient weight magnitude: got=%d, want=%d", weightMagnitude, MIN_WEIGHT_MAGNITUDE)
}
return nil
}
package meta_transaction
import (
"fmt"
"sync"
"testing"
"github.com/iotaledger/iota.go/trinary"
"github.com/magiconair/properties/assert"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const (
shardMarker = trinary.Trytes("NPHTQORL9XKA")
trunkTransactionHash = trinary.Trytes("99999999999999999999999999999999999999999999999999999999999999999999999999999999A")
branchTransactionHash = trinary.Trytes("99999999999999999999999999999999999999999999999999999999999999999999999999999999B")
head = true
tail = true
transactionType = trinary.Trytes("9999999999999999999999")
)
func newTestTransaction() *MetaTransaction {
tx := New()
tx.SetShardMarker(shardMarker)
tx.SetTrunkTransactionHash(trunkTransactionHash)
tx.SetBranchTransactionHash(branchTransactionHash)
tx.SetHead(head)
tx.SetTail(tail)
tx.SetTransactionType(transactionType)
return tx
}
func TestDoPow(t *testing.T) {
tx := newTestTransaction()
require.NoError(t, tx.DoProofOfWork(10))
assert.GreaterOrEqual(t, tx.GetWeightMagnitude(), 10)
}
func TestMetaTransaction_SettersGetters(t *testing.T) {
shardMarker := trinary.Trytes("NPHTQORL9XKA")
trunkTransactionHash := trinary.Trytes("99999999999999999999999999999999999999999999999999999999999999999999999999999999A")
branchTransactionHash := trinary.Trytes("99999999999999999999999999999999999999999999999999999999999999999999999999999999B")
head := true
tail := true
transactionType := trinary.Trytes("9999999999999999999999")
transaction := New()
transaction.SetShardMarker(shardMarker)
transaction.SetTrunkTransactionHash(trunkTransactionHash)
transaction.SetBranchTransactionHash(branchTransactionHash)
transaction.SetHead(head)
transaction.SetTail(tail)
transaction.SetTransactionType(transactionType)
assert.Equal(t, transaction.GetWeightMagnitude(), 0)
assert.Equal(t, transaction.GetShardMarker(), shardMarker)
assert.Equal(t, transaction.GetTrunkTransactionHash(), trunkTransactionHash)
assert.Equal(t, transaction.GetBranchTransactionHash(), branchTransactionHash)
assert.Equal(t, transaction.IsHead(), head)
assert.Equal(t, transaction.IsTail(), tail)
assert.Equal(t, transaction.GetTransactionType(), transactionType)
assert.Equal(t, transaction.GetHash(), FromBytes(transaction.GetBytes()).GetHash())
fmt.Println(transaction.GetHash())
tx := newTestTransaction()
assert.Equal(t, tx.GetWeightMagnitude(), 0)
assert.Equal(t, tx.GetShardMarker(), shardMarker)
assert.Equal(t, tx.GetTrunkTransactionHash(), trunkTransactionHash)
assert.Equal(t, tx.GetBranchTransactionHash(), branchTransactionHash)
assert.Equal(t, tx.IsHead(), head)
assert.Equal(t, tx.IsTail(), tail)
assert.Equal(t, tx.GetTransactionType(), transactionType)
assert.Equal(t, tx.GetHash(), FromBytes(tx.GetBytes()).GetHash())
assert.EqualValues(t, "KKDVHBENVLQUNO9WOWWEJPBBHUSYRSRKIMZWCFCDB9RYZKYWLAYWRIBRQETBFKE9TIVWQPCKFWAMCLCAV", tx.GetHash())
}
func BenchmarkMetaTransaction_GetHash(b *testing.B) {
......
......@@ -10,20 +10,17 @@ const (
ADDRESS_OFFSET = 0
VALUE_OFFSET = ADDRESS_END
TIMESTAMP_OFFSET = VALUE_END
NONCE_OFFSET = TIMESTAMP_END
SIGNATURE_MESSAGE_FRAGMENT_OFFSET = NONCE_END
SIGNATURE_MESSAGE_FRAGMENT_OFFSET = TIMESTAMP_SIZE
ADDRESS_SIZE = 243
VALUE_SIZE = 81
TIMESTAMP_SIZE = 27
NONCE_SIZE = 81
SIGNATURE_MESSAGE_FRAGMENT_SIZE = 6561
BUNDLE_ESSENCE_SIZE = ADDRESS_SIZE + VALUE_SIZE + SIGNATURE_MESSAGE_FRAGMENT_SIZE
ADDRESS_END = ADDRESS_OFFSET + ADDRESS_SIZE
VALUE_END = VALUE_OFFSET + VALUE_SIZE
TIMESTAMP_END = TIMESTAMP_OFFSET + TIMESTAMP_SIZE
NONCE_END = NONCE_OFFSET + NONCE_SIZE
SIGNATURE_MESSAGE_FRAGMENT_END = SIGNATURE_MESSAGE_FRAGMENT_OFFSET + SIGNATURE_MESSAGE_FRAGMENT_SIZE
TOTAL_SIZE = SIGNATURE_MESSAGE_FRAGMENT_END
......
......@@ -16,8 +16,6 @@ type ValueTransaction struct {
valueMutex sync.RWMutex
timestamp *uint
timestampMutex sync.RWMutex
nonce *trinary.Trytes
nonceMutex sync.RWMutex
signatureMessageFragment *trinary.Trytes
signatureMessageFragmentMutex sync.RWMutex
......@@ -215,53 +213,6 @@ func (this *ValueTransaction) GetBundleEssence(includeSignatureMessageFragment b
return
}
// getter for the nonce (supports concurrency)
func (this *ValueTransaction) GetNonce() (result trinary.Trytes) {
this.nonceMutex.RLock()
if this.nonce == nil {
this.nonceMutex.RUnlock()
this.nonceMutex.Lock()
defer this.nonceMutex.Unlock()
if this.nonce == nil {
nonce := trinary.MustTritsToTrytes(this.trits[NONCE_OFFSET:NONCE_END])
this.nonce = &nonce
}
} else {
defer this.nonceMutex.RUnlock()
}
result = *this.nonce
return
}
// setter for the nonce (supports concurrency)
func (this *ValueTransaction) SetNonce(nonce trinary.Trytes) bool {
this.nonceMutex.RLock()
if this.nonce == nil || *this.nonce != nonce {
this.nonceMutex.RUnlock()
this.nonceMutex.Lock()
defer this.nonceMutex.Unlock()
if this.nonce == nil || *this.nonce != nonce {
this.nonce = &nonce
this.BlockHasher()
copy(this.trits[NONCE_OFFSET:NONCE_END], trinary.MustTrytesToTrits(nonce)[:NONCE_SIZE])
this.UnblockHasher()
this.SetModified(true)
this.ReHash()
return true
}
} else {
this.nonceMutex.RUnlock()
}
return false
}
// getter for the signatureMessageFragmetn (supports concurrency)
func (this *ValueTransaction) GetSignatureMessageFragment() (result trinary.Trytes) {
this.signatureMessageFragmentMutex.RLock()
......
......@@ -7,12 +7,16 @@ import (
"github.com/golang/protobuf/proto"
"github.com/iotaledger/goshimmer/packages/gossip"
pb "github.com/iotaledger/goshimmer/packages/gossip/proto"
"github.com/iotaledger/goshimmer/packages/model/meta_transaction"
"github.com/iotaledger/goshimmer/packages/model/value_transaction"
"github.com/iotaledger/goshimmer/plugins/autopeering/local"
"github.com/iotaledger/goshimmer/plugins/tipselection"
"github.com/iotaledger/hive.go/daemon"
"github.com/iotaledger/hive.go/logger"
)
var log = logger.NewLogger("Transaction Spammer")
var spamming = false
var spammingMutex sync.Mutex
......@@ -54,6 +58,11 @@ func Start(tps uint) {
tx.SetValue(totalSentCounter)
tx.SetBranchTransactionHash(tipselection.GetRandomTip())
tx.SetTrunkTransactionHash(tipselection.GetRandomTip())
tx.SetTimestamp(uint(time.Now().Unix()))
if err := tx.DoProofOfWork(meta_transaction.MIN_WEIGHT_MAGNITUDE); err != nil {
log.Warning("PoW failed", err)
continue
}
mtx := &pb.Transaction{Body: tx.MetaTransaction.GetBytes()}
b, _ := proto.Marshal(mtx)
......
......@@ -127,7 +127,6 @@ func TestProcessSolidBundleHead_Value(t *testing.T) {
testResults := events.NewClosure(func(bundle *bundle.Bundle, transactions []*value_transaction.ValueTransaction) {
assert.Equal(t, bundle.GetHash(), generatedBundle.GetTransactions()[0].GetHash(), "invalid bundle hash")
assert.Equal(t, bundle.GetBundleEssenceHash(), generatedBundle.GetEssenceHash(), "invalid bundle essence hash")
assert.Equal(t, bundle.IsValueBundle(), true, "invalid value bundle status")
wg.Done()
......
......@@ -98,7 +98,7 @@ func configureEvents() {
}))
tangle.Events.TransactionSolid.Attach(events.NewClosure(func(tx *value_transaction.ValueTransaction) {
log.Info("Tx solidified:", tx.MetaTransaction.GetHash())
log.Info("gossip solid tx", tx.MetaTransaction.GetHash())
t := &pb.Transaction{
Body: tx.MetaTransaction.GetBytes(),
}
......
......@@ -38,10 +38,18 @@ func configureSolidifier(plugin *node.Plugin) {
unsolidTxs = NewUnsolidTxs()
gossip.Events.TransactionReceived.Attach(events.NewClosure(func(ev *gossip.TransactionReceivedEvent) {
//log.Info("New Transaction", ev.Body)
pTx := &pb.Transaction{}
proto.Unmarshal(ev.Body, pTx)
if err := proto.Unmarshal(ev.Body, pTx); err != nil {
log.Warningf("invalid transaction: %s", err)
return
}
metaTx := meta_transaction.FromBytes(pTx.GetBody())
if err := metaTx.Validate(); err != nil {
log.Warningf("invalid transaction: %s", err)
return
}
workerPool.Submit(metaTx)
}))
......
......@@ -8,11 +8,12 @@ import (
"github.com/golang/protobuf/proto"
"github.com/iotaledger/goshimmer/packages/gossip"
pb "github.com/iotaledger/goshimmer/packages/gossip/proto"
"github.com/iotaledger/goshimmer/packages/model/meta_transaction"
"github.com/iotaledger/goshimmer/packages/model/value_transaction"
"github.com/iotaledger/hive.go/events"
"github.com/iotaledger/hive.go/node"
"github.com/iotaledger/hive.go/parameter"
"github.com/iotaledger/iota.go/trinary"
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
......@@ -29,16 +30,23 @@ func TestSolidifier(t *testing.T) {
// create transactions and chain them together
transaction1 := value_transaction.New()
transaction1.SetNonce(trinary.Trytes("99999999999999999999999999A"))
transaction1.SetValue(1)
require.NoError(t, transaction1.DoProofOfWork(meta_transaction.MIN_WEIGHT_MAGNITUDE))
transaction2 := value_transaction.New()
transaction2.SetValue(2)
transaction2.SetBranchTransactionHash(transaction1.GetHash())
require.NoError(t, transaction2.DoProofOfWork(meta_transaction.MIN_WEIGHT_MAGNITUDE))
transaction3 := value_transaction.New()
transaction3.SetValue(3)
transaction3.SetBranchTransactionHash(transaction2.GetHash())
require.NoError(t, transaction3.DoProofOfWork(meta_transaction.MIN_WEIGHT_MAGNITUDE))
transaction4 := value_transaction.New()
transaction4.SetValue(4)
transaction4.SetBranchTransactionHash(transaction3.GetHash())
require.NoError(t, transaction4.DoProofOfWork(meta_transaction.MIN_WEIGHT_MAGNITUDE))
// setup event handlers
var wg sync.WaitGroup
......
......@@ -7,6 +7,7 @@ import (
"github.com/golang/protobuf/proto"
"github.com/iotaledger/goshimmer/packages/gossip"
pb "github.com/iotaledger/goshimmer/packages/gossip/proto"
"github.com/iotaledger/goshimmer/packages/model/meta_transaction"
"github.com/iotaledger/goshimmer/packages/model/value_transaction"
"github.com/iotaledger/goshimmer/plugins/autopeering/local"
"github.com/iotaledger/goshimmer/plugins/tipselection"
......@@ -53,6 +54,10 @@ func SendDataHandler(c echo.Context) error {
tx.SetSignatureMessageFragment(trytes)
tx.SetBranchTransactionHash(tipselection.GetRandomTip())
tx.SetTrunkTransactionHash(tipselection.GetRandomTip())
tx.SetTimestamp(uint(time.Now().Unix()))
if err := tx.DoProofOfWork(meta_transaction.MIN_WEIGHT_MAGNITUDE); err != nil {
log.Warning("PoW failed", err)
}
transactionHash := tx.GetHash()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment