transaction: unbind GroupInputsByPrevHash from Transaction
Which allows to use it for ClaimTX. Make it also a bit more efficient as maps are expensive.
This commit is contained in:
parent
c258adb532
commit
80de208a68
5 changed files with 114 additions and 13 deletions
|
@ -431,7 +431,8 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process TX inputs that are grouped by previous hash.
|
// Process TX inputs that are grouped by previous hash.
|
||||||
for prevHash, inputs := range tx.GroupInputsByPrevHash() {
|
for _, inputs := range transaction.GroupInputsByPrevHash(tx.Inputs) {
|
||||||
|
prevHash := inputs[0].PrevHash
|
||||||
prevTX, prevTXHeight, err := bc.dao.GetTransaction(prevHash)
|
prevTX, prevTXHeight, err := bc.dao.GetTransaction(prevHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not find previous TX: %s", prevHash)
|
return fmt.Errorf("could not find previous TX: %s", prevHash)
|
||||||
|
@ -1016,7 +1017,8 @@ func (bc *Blockchain) GetConfig() config.ProtocolConfiguration {
|
||||||
func (bc *Blockchain) References(t *transaction.Transaction) map[transaction.Input]*transaction.Output {
|
func (bc *Blockchain) References(t *transaction.Transaction) map[transaction.Input]*transaction.Output {
|
||||||
references := make(map[transaction.Input]*transaction.Output)
|
references := make(map[transaction.Input]*transaction.Output)
|
||||||
|
|
||||||
for prevHash, inputs := range t.GroupInputsByPrevHash() {
|
for _, inputs := range transaction.GroupInputsByPrevHash(t.Inputs) {
|
||||||
|
prevHash := inputs[0].PrevHash
|
||||||
tx, _, err := bc.dao.GetTransaction(prevHash)
|
tx, _, err := bc.dao.GetTransaction(prevHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -547,7 +547,8 @@ func (dao *dao) IsDoubleSpend(tx *transaction.Transaction) bool {
|
||||||
if len(tx.Inputs) == 0 {
|
if len(tx.Inputs) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for prevHash, inputs := range tx.GroupInputsByPrevHash() {
|
for _, inputs := range transaction.GroupInputsByPrevHash(tx.Inputs) {
|
||||||
|
prevHash := inputs[0].PrevHash
|
||||||
unspent, err := dao.GetUnspentCoinState(prevHash)
|
unspent, err := dao.GetUnspentCoinState(prevHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package transaction
|
package transaction
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/io"
|
"github.com/CityOfZion/neo-go/pkg/io"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
@ -25,3 +27,34 @@ func (in *Input) EncodeBinary(bw *io.BinWriter) {
|
||||||
bw.WriteBytes(in.PrevHash[:])
|
bw.WriteBytes(in.PrevHash[:])
|
||||||
bw.WriteU16LE(in.PrevIndex)
|
bw.WriteU16LE(in.PrevIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GroupInputsByPrevHash groups all TX inputs by their previous hash into
|
||||||
|
// several slices (which actually are subslices of one new slice with pointers).
|
||||||
|
// Each of these slices contains at least one element.
|
||||||
|
func GroupInputsByPrevHash(ins []Input) [][]*Input {
|
||||||
|
if len(ins) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ptrs := make([]*Input, len(ins))
|
||||||
|
for i := range ins {
|
||||||
|
ptrs[i] = &ins[i]
|
||||||
|
}
|
||||||
|
sort.Slice(ptrs, func(i, j int) bool {
|
||||||
|
return ptrs[i].PrevHash.CompareTo(ptrs[j].PrevHash) < 0
|
||||||
|
})
|
||||||
|
|
||||||
|
var first int
|
||||||
|
res := make([][]*Input, 0)
|
||||||
|
currentHash := ptrs[0].PrevHash
|
||||||
|
|
||||||
|
for i := range ptrs {
|
||||||
|
if !currentHash.Equals(ptrs[i].PrevHash) {
|
||||||
|
res = append(res, ptrs[first:i])
|
||||||
|
first = i
|
||||||
|
currentHash = ptrs[i].PrevHash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res = append(res, ptrs[first:])
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
75
pkg/core/transaction/input_test.go
Normal file
75
pkg/core/transaction/input_test.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package transaction
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGroupInputsByPrevHash0(t *testing.T) {
|
||||||
|
inputs := make([]Input, 0)
|
||||||
|
res := GroupInputsByPrevHash(inputs)
|
||||||
|
require.Equal(t, 0, len(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroupInputsByPrevHash1(t *testing.T) {
|
||||||
|
inputs := make([]Input, 0)
|
||||||
|
hash, err := util.Uint256DecodeStringLE("46168f963d6d8168a870405f66cc9e13a235791013b8ee2f90cc20a8293bd1af")
|
||||||
|
require.NoError(t, err)
|
||||||
|
inputs = append(inputs, Input{PrevHash: hash, PrevIndex: 42})
|
||||||
|
res := GroupInputsByPrevHash(inputs)
|
||||||
|
require.Equal(t, 1, len(res))
|
||||||
|
require.Equal(t, 1, len(res[0]))
|
||||||
|
assert.Equal(t, hash, res[0][0].PrevHash)
|
||||||
|
assert.Equal(t, uint16(42), res[0][0].PrevIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroupInputsByPrevHashMany(t *testing.T) {
|
||||||
|
hash1, err := util.Uint256DecodeStringBE("a83ba6ede918a501558d3170a124324aedc89909e64c4ff2c6f863094f980b25")
|
||||||
|
require.NoError(t, err)
|
||||||
|
hash2, err := util.Uint256DecodeStringBE("629397158f852e838077bb2715b13a2e29b0a51c2157e5466321b70ed7904ce9")
|
||||||
|
require.NoError(t, err)
|
||||||
|
hash3, err := util.Uint256DecodeStringBE("caa41245c3e48ddc13dabe989ba8fbc59418e9228fef9efb62855b0b17d7448b")
|
||||||
|
require.NoError(t, err)
|
||||||
|
inputs := make([]Input, 0)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
inputs = append(inputs, Input{PrevHash: hash1, PrevIndex: uint16(i)})
|
||||||
|
inputs = append(inputs, Input{PrevHash: hash2, PrevIndex: uint16(i)})
|
||||||
|
inputs = append(inputs, Input{PrevHash: hash3, PrevIndex: uint16(i)})
|
||||||
|
}
|
||||||
|
for i := 15; i < 20; i++ {
|
||||||
|
inputs = append(inputs, Input{PrevHash: hash3, PrevIndex: uint16(i)})
|
||||||
|
}
|
||||||
|
for i := 10; i < 15; i++ {
|
||||||
|
inputs = append(inputs, Input{PrevHash: hash2, PrevIndex: uint16(i)})
|
||||||
|
inputs = append(inputs, Input{PrevHash: hash3, PrevIndex: uint16(i)})
|
||||||
|
}
|
||||||
|
seen := make(map[uint16]bool)
|
||||||
|
res := GroupInputsByPrevHash(inputs)
|
||||||
|
require.Equal(t, 3, len(res))
|
||||||
|
assert.Equal(t, hash2, res[0][0].PrevHash)
|
||||||
|
assert.Equal(t, 15, len(res[0]))
|
||||||
|
for i := range res[0] {
|
||||||
|
assert.Equal(t, res[0][i].PrevHash, res[0][0].PrevHash)
|
||||||
|
assert.Equal(t, false, seen[res[0][i].PrevIndex])
|
||||||
|
seen[res[0][i].PrevIndex] = true
|
||||||
|
}
|
||||||
|
seen = make(map[uint16]bool)
|
||||||
|
assert.Equal(t, hash1, res[1][0].PrevHash)
|
||||||
|
assert.Equal(t, 10, len(res[1]))
|
||||||
|
for i := range res[1] {
|
||||||
|
assert.Equal(t, res[1][i].PrevHash, res[1][0].PrevHash)
|
||||||
|
assert.Equal(t, false, seen[res[1][i].PrevIndex])
|
||||||
|
seen[res[1][i].PrevIndex] = true
|
||||||
|
}
|
||||||
|
seen = make(map[uint16]bool)
|
||||||
|
assert.Equal(t, hash3, res[2][0].PrevHash)
|
||||||
|
assert.Equal(t, 20, len(res[2]))
|
||||||
|
for i := range res[2] {
|
||||||
|
assert.Equal(t, res[2][i].PrevHash, res[2][0].PrevHash)
|
||||||
|
assert.Equal(t, false, seen[res[2][i].PrevIndex])
|
||||||
|
seen[res[2][i].PrevIndex] = true
|
||||||
|
}
|
||||||
|
}
|
|
@ -187,16 +187,6 @@ func (t *Transaction) createHash() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GroupInputsByPrevHash groups all TX inputs by their previous hash.
|
|
||||||
func (t *Transaction) GroupInputsByPrevHash() map[util.Uint256][]*Input {
|
|
||||||
m := make(map[util.Uint256][]*Input)
|
|
||||||
for i := range t.Inputs {
|
|
||||||
hash := t.Inputs[i].PrevHash
|
|
||||||
m[hash] = append(m[hash], &t.Inputs[i])
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// GroupOutputByAssetID groups all TX outputs by their assetID.
|
// GroupOutputByAssetID groups all TX outputs by their assetID.
|
||||||
func (t Transaction) GroupOutputByAssetID() map[util.Uint256][]*Output {
|
func (t Transaction) GroupOutputByAssetID() map[util.Uint256][]*Output {
|
||||||
m := make(map[util.Uint256][]*Output)
|
m := make(map[util.Uint256][]*Output)
|
||||||
|
|
Loading…
Reference in a new issue