core: add duplicate Claim verification check
Refactor HaveInputsDuplicate() out of the core and Blockchain, it doesn't depend on the Blockchain state. Make it more efficient.
This commit is contained in:
parent
9b1ad40273
commit
29586f2aa7
3 changed files with 113 additions and 21 deletions
|
@ -1121,7 +1121,7 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
|
|||
if io.GetVarSize(t) > transaction.MaxTransactionSize {
|
||||
return errors.Errorf("invalid transaction size = %d. It shoud be less then MaxTransactionSize = %d", io.GetVarSize(t), transaction.MaxTransactionSize)
|
||||
}
|
||||
if ok := bc.verifyInputs(t); !ok {
|
||||
if transaction.HaveDuplicateInputs(t.Inputs) {
|
||||
return errors.New("invalid transaction's inputs")
|
||||
}
|
||||
if block == nil {
|
||||
|
@ -1145,6 +1145,13 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
|
|||
}
|
||||
}
|
||||
|
||||
if t.Type == transaction.ClaimType {
|
||||
claim := t.Data.(*transaction.ClaimTX)
|
||||
if transaction.HaveDuplicateInputs(claim.Claims) {
|
||||
return errors.New("duplicate claims")
|
||||
}
|
||||
}
|
||||
|
||||
return bc.verifyTxWitnesses(t, block)
|
||||
}
|
||||
|
||||
|
@ -1223,18 +1230,6 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (bc *Blockchain) verifyInputs(t *transaction.Transaction) bool {
|
||||
for i := 1; i < len(t.Inputs); i++ {
|
||||
for j := 0; j < i; j++ {
|
||||
if t.Inputs[i].PrevHash == t.Inputs[j].PrevHash && t.Inputs[i].PrevIndex == t.Inputs[j].PrevIndex {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (bc *Blockchain) verifyOutputs(t *transaction.Transaction) error {
|
||||
for assetID, outputs := range t.GroupOutputByAssetID() {
|
||||
assetState := bc.GetAssetState(assetID)
|
||||
|
|
|
@ -28,6 +28,23 @@ func (in *Input) EncodeBinary(bw *io.BinWriter) {
|
|||
bw.WriteU16LE(in.PrevIndex)
|
||||
}
|
||||
|
||||
// MapInputsToSorted maps given slice of inputs into a new slice of pointers
|
||||
// to inputs sorted by their PrevHash and PrevIndex.
|
||||
func MapInputsToSorted(ins []Input) []*Input {
|
||||
ptrs := make([]*Input, len(ins))
|
||||
for i := range ins {
|
||||
ptrs[i] = &ins[i]
|
||||
}
|
||||
sort.Slice(ptrs, func(i, j int) bool {
|
||||
hashcmp := ptrs[i].PrevHash.CompareTo(ptrs[j].PrevHash)
|
||||
if hashcmp == 0 {
|
||||
return ptrs[i].PrevIndex < ptrs[j].PrevIndex
|
||||
}
|
||||
return hashcmp < 0
|
||||
})
|
||||
return ptrs
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@ -36,14 +53,7 @@ func GroupInputsByPrevHash(ins []Input) [][]*Input {
|
|||
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
|
||||
})
|
||||
|
||||
ptrs := MapInputsToSorted(ins)
|
||||
var first int
|
||||
res := make([][]*Input, 0)
|
||||
currentHash := ptrs[0].PrevHash
|
||||
|
@ -58,3 +68,21 @@ func GroupInputsByPrevHash(ins []Input) [][]*Input {
|
|||
res = append(res, ptrs[first:])
|
||||
return res
|
||||
}
|
||||
|
||||
// HaveDuplicateInputs checks inputs for duplicates and returns true if there are
|
||||
// any.
|
||||
func HaveDuplicateInputs(ins []Input) bool {
|
||||
if len(ins) < 2 {
|
||||
return false
|
||||
}
|
||||
if len(ins) == 2 {
|
||||
return ins[0] == ins[1]
|
||||
}
|
||||
ptrs := MapInputsToSorted(ins)
|
||||
for i := 1; i < len(ptrs); i++ {
|
||||
if *ptrs[i] == *ptrs[i-1] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -73,3 +73,72 @@ func TestGroupInputsByPrevHashMany(t *testing.T) {
|
|||
seen[res[2][i].PrevIndex] = true
|
||||
}
|
||||
}
|
||||
|
||||
func TestHaveDuplicateInputs0(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
require.False(t, HaveDuplicateInputs(inputs))
|
||||
}
|
||||
|
||||
func TestHaveDuplicateInputs1(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
hash, err := util.Uint256DecodeStringLE("46168f963d6d8168a870405f66cc9e13a235791013b8ee2f90cc20a8293bd1af")
|
||||
require.NoError(t, err)
|
||||
inputs = append(inputs, Input{PrevHash: hash, PrevIndex: 42})
|
||||
require.False(t, HaveDuplicateInputs(inputs))
|
||||
}
|
||||
|
||||
func TestHaveDuplicateInputs2True(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
hash, err := util.Uint256DecodeStringLE("46168f963d6d8168a870405f66cc9e13a235791013b8ee2f90cc20a8293bd1af")
|
||||
require.NoError(t, err)
|
||||
inputs = append(inputs, Input{PrevHash: hash, PrevIndex: 42})
|
||||
inputs = append(inputs, Input{PrevHash: hash, PrevIndex: 42})
|
||||
require.True(t, HaveDuplicateInputs(inputs))
|
||||
}
|
||||
|
||||
func TestHaveDuplicateInputs2FalseInd(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
hash, err := util.Uint256DecodeStringLE("46168f963d6d8168a870405f66cc9e13a235791013b8ee2f90cc20a8293bd1af")
|
||||
require.NoError(t, err)
|
||||
inputs = append(inputs, Input{PrevHash: hash, PrevIndex: 42})
|
||||
inputs = append(inputs, Input{PrevHash: hash, PrevIndex: 41})
|
||||
require.False(t, HaveDuplicateInputs(inputs))
|
||||
}
|
||||
|
||||
func TestHaveDuplicateInputs2FalseHash(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
hash1, err := util.Uint256DecodeStringBE("a83ba6ede918a501558d3170a124324aedc89909e64c4ff2c6f863094f980b25")
|
||||
require.NoError(t, err)
|
||||
hash2, err := util.Uint256DecodeStringBE("629397158f852e838077bb2715b13a2e29b0a51c2157e5466321b70ed7904ce9")
|
||||
require.NoError(t, err)
|
||||
inputs = append(inputs, Input{PrevHash: hash1, PrevIndex: 42})
|
||||
inputs = append(inputs, Input{PrevHash: hash2, PrevIndex: 42})
|
||||
require.False(t, HaveDuplicateInputs(inputs))
|
||||
}
|
||||
|
||||
func TestHaveDuplicateInputsMFalse(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
hash1, err := util.Uint256DecodeStringBE("a83ba6ede918a501558d3170a124324aedc89909e64c4ff2c6f863094f980b25")
|
||||
require.NoError(t, err)
|
||||
hash2, err := util.Uint256DecodeStringBE("629397158f852e838077bb2715b13a2e29b0a51c2157e5466321b70ed7904ce9")
|
||||
require.NoError(t, err)
|
||||
for i := 0; i < 10; i++ {
|
||||
inputs = append(inputs, Input{PrevHash: hash1, PrevIndex: uint16(i)})
|
||||
inputs = append(inputs, Input{PrevHash: hash2, PrevIndex: uint16(i)})
|
||||
}
|
||||
require.False(t, HaveDuplicateInputs(inputs))
|
||||
}
|
||||
|
||||
func TestHaveDuplicateInputsMTrue(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
hash1, err := util.Uint256DecodeStringBE("a83ba6ede918a501558d3170a124324aedc89909e64c4ff2c6f863094f980b25")
|
||||
require.NoError(t, err)
|
||||
hash2, err := util.Uint256DecodeStringBE("629397158f852e838077bb2715b13a2e29b0a51c2157e5466321b70ed7904ce9")
|
||||
require.NoError(t, err)
|
||||
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: hash1, PrevIndex: 0})
|
||||
require.True(t, HaveDuplicateInputs(inputs))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue