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 {
|
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)
|
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")
|
return errors.New("invalid transaction's inputs")
|
||||||
}
|
}
|
||||||
if block == nil {
|
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)
|
return bc.verifyTxWitnesses(t, block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1223,18 +1230,6 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction) error {
|
||||||
return nil
|
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 {
|
func (bc *Blockchain) verifyOutputs(t *transaction.Transaction) error {
|
||||||
for assetID, outputs := range t.GroupOutputByAssetID() {
|
for assetID, outputs := range t.GroupOutputByAssetID() {
|
||||||
assetState := bc.GetAssetState(assetID)
|
assetState := bc.GetAssetState(assetID)
|
||||||
|
|
|
@ -28,6 +28,23 @@ func (in *Input) EncodeBinary(bw *io.BinWriter) {
|
||||||
bw.WriteU16LE(in.PrevIndex)
|
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
|
// GroupInputsByPrevHash groups all TX inputs by their previous hash into
|
||||||
// several slices (which actually are subslices of one new slice with pointers).
|
// several slices (which actually are subslices of one new slice with pointers).
|
||||||
// Each of these slices contains at least one element.
|
// Each of these slices contains at least one element.
|
||||||
|
@ -36,14 +53,7 @@ func GroupInputsByPrevHash(ins []Input) [][]*Input {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ptrs := make([]*Input, len(ins))
|
ptrs := MapInputsToSorted(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
|
var first int
|
||||||
res := make([][]*Input, 0)
|
res := make([][]*Input, 0)
|
||||||
currentHash := ptrs[0].PrevHash
|
currentHash := ptrs[0].PrevHash
|
||||||
|
@ -58,3 +68,21 @@ func GroupInputsByPrevHash(ins []Input) [][]*Input {
|
||||||
res = append(res, ptrs[first:])
|
res = append(res, ptrs[first:])
|
||||||
return res
|
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
|
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