transaction: drop Claim TX type

This commit is contained in:
Roman Khimov 2020-06-04 23:58:13 +03:00
parent 3d18f09def
commit dfc7a9bfd1
14 changed files with 10 additions and 577 deletions

View file

@ -599,19 +599,6 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
return err
}
if prevTXOutput.AssetID.Equals(GoverningTokenID()) {
err = account.Unclaimed.Put(&state.UnclaimedBalance{
Tx: input.PrevHash,
Index: input.PrevIndex,
Start: unspent.Height,
End: block.Index,
Value: prevTXOutput.Amount,
})
if err != nil {
return err
}
}
balancesLen := len(account.Balances[prevTXOutput.AssetID])
if balancesLen <= 1 {
delete(account.Balances, prevTXOutput.AssetID)
@ -669,54 +656,6 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
}
}
}
case *transaction.ClaimTX:
// Remove claimed NEO from spent coins making it unavalaible for
// additional claims.
for _, input := range t.Claims {
scs, err := cache.GetUnspentCoinState(input.PrevHash)
if err == nil {
if len(scs.States) <= int(input.PrevIndex) {
err = errors.New("invalid claim index")
} else if scs.States[input.PrevIndex].State&state.CoinClaimed != 0 {
err = errors.New("double claim")
}
}
if err != nil {
// We can't really do anything about it
// as it's a transaction in a signed block.
bc.log.Warn("FALSE OR DOUBLE CLAIM",
zap.String("PrevHash", input.PrevHash.StringLE()),
zap.Uint16("PrevIndex", input.PrevIndex),
zap.String("tx", tx.Hash().StringLE()),
zap.Uint32("block", block.Index),
)
// "Strict" mode.
if bc.config.VerifyTransactions {
return err
}
break
}
acc, err := cache.GetAccountState(scs.States[input.PrevIndex].ScriptHash)
if err != nil {
return err
}
scs.States[input.PrevIndex].State |= state.CoinClaimed
if err = cache.PutUnspentCoinState(input.PrevHash, scs); err != nil {
return err
}
changed := acc.Unclaimed.Remove(input.PrevHash, input.PrevIndex)
if !changed {
bc.log.Warn("no spent coin in the account",
zap.String("tx", tx.Hash().StringLE()),
zap.String("input", input.PrevHash.StringLE()),
zap.String("account", acc.ScriptHash.String()))
} else if err := cache.PutAccountState(acc); err != nil {
return err
}
}
case *transaction.InvocationTX:
systemInterop := bc.newInteropContext(trigger.Application, cache, block, tx)
v := SpawnVM(systemInterop)
@ -1383,87 +1322,9 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
}
}
switch t.Type {
case transaction.ClaimType:
claim := t.Data.(*transaction.ClaimTX)
if transaction.HaveDuplicateInputs(claim.Claims) {
return errors.New("duplicate claims")
}
if bc.dao.IsDoubleClaim(claim) {
return errors.New("double claim")
}
if err := bc.verifyClaims(t, results); err != nil {
return err
}
}
return bc.verifyTxWitnesses(t, block)
}
func (bc *Blockchain) verifyClaims(tx *transaction.Transaction, results []*transaction.Result) (err error) {
t := tx.Data.(*transaction.ClaimTX)
var result *transaction.Result
for i := range results {
if results[i].AssetID == UtilityTokenID() {
result = results[i]
break
}
}
if result == nil || result.Amount.GreaterThan(0) {
return errors.New("invalid output in claim tx")
}
bonus, err := bc.calculateBonus(t.Claims)
if err == nil && bonus != -result.Amount {
return fmt.Errorf("wrong bonus calculated in claim tx: %s != %s",
bonus.String(), (-result.Amount).String())
}
return err
}
func (bc *Blockchain) calculateBonus(claims []transaction.Input) (util.Fixed8, error) {
unclaimed := []*spentCoin{}
inputs := transaction.GroupInputsByPrevHash(claims)
for _, group := range inputs {
h := group[0].PrevHash
unspent, err := bc.dao.GetUnspentCoinState(h)
if err != nil {
return 0, err
}
for _, c := range group {
if len(unspent.States) <= int(c.PrevIndex) {
return 0, fmt.Errorf("can't find spent coins for %s (%d)", c.PrevHash.StringLE(), c.PrevIndex)
}
if unspent.States[c.PrevIndex].State&state.CoinSpent == 0 {
return 0, fmt.Errorf("not spent yet: %s/%d", c.PrevHash.StringLE(), c.PrevIndex)
}
if unspent.States[c.PrevIndex].State&state.CoinClaimed != 0 {
return 0, fmt.Errorf("already claimed: %s/%d", c.PrevHash.StringLE(), c.PrevIndex)
}
unclaimed = append(unclaimed, &spentCoin{
Output: &unspent.States[c.PrevIndex].Output,
StartHeight: unspent.Height,
EndHeight: unspent.States[c.PrevIndex].SpendHeight,
})
}
}
return bc.calculateBonusInternal(unclaimed)
}
func (bc *Blockchain) calculateBonusInternal(scs []*spentCoin) (util.Fixed8, error) {
var claimed util.Fixed8
for _, sc := range scs {
claimed += bc.CalculateClaimable(sc.Output.Amount.IntegralValue(), sc.StartHeight, sc.EndHeight)
}
return claimed, nil
}
// isTxStillRelevant is a callback for mempool transaction filtering after the
// new block addition. It returns false for transactions already present in the
// chain (added by the new block), transactions using some inputs that are
@ -1480,12 +1341,6 @@ func (bc *Blockchain) isTxStillRelevant(t *transaction.Transaction) bool {
if bc.dao.IsDoubleSpend(t) {
return false
}
if t.Type == transaction.ClaimType {
claim := t.Data.(*transaction.ClaimTX)
if bc.dao.IsDoubleClaim(claim) {
return false
}
}
for i := range t.Scripts {
if !vm.IsStandardContract(t.Scripts[i].VerificationScript) {
recheckWitness = true
@ -1521,7 +1376,6 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction) error {
return err
}
// Policying.
if t.Type != transaction.ClaimType {
txSize := io.GetVarSize(t)
maxFree := bc.config.MaxFreeTransactionSize
if maxFree != 0 && txSize > maxFree {
@ -1530,7 +1384,6 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction) error {
return ErrPolicy
}
}
}
if err := bc.memPool.Add(t, bc); err != nil {
switch err {
case mempool.ErrOOM:
@ -1585,13 +1438,6 @@ func (bc *Blockchain) verifyResults(t *transaction.Transaction, results []*trans
}
switch t.Type {
case transaction.ClaimType:
for _, r := range resultsIssue {
if r.AssetID != UtilityTokenID() {
return errors.New("miner or claim tx issues non-utility tokens")
}
}
break
case transaction.IssueType:
for _, r := range resultsIssue {
if r.AssetID == UtilityTokenID() {
@ -1701,15 +1547,6 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([
hashes[c.Account] = true
}
switch t.Type {
case transaction.ClaimType:
claim := t.Data.(*transaction.ClaimTX)
refs, err := bc.references(claim.Claims)
if err != nil {
return nil, err
}
for i := range refs {
hashes[refs[i].Out.ScriptHash] = true
}
case transaction.IssueType:
for _, res := range refsAndOutsToResults(references, t.Outputs) {
if res.Amount < 0 {

View file

@ -40,7 +40,6 @@ type DAO interface {
GetVersion() (string, error)
GetWrapped() DAO
HasTransaction(hash util.Uint256) bool
IsDoubleClaim(claim *transaction.ClaimTX) bool
IsDoubleSpend(tx *transaction.Transaction) bool
Persist() (int, error)
PutAccountState(as *state.Account) error
@ -573,11 +572,6 @@ func (dao *Simple) IsDoubleSpend(tx *transaction.Transaction) bool {
return dao.checkUsedInputs(tx.Inputs, state.CoinSpent)
}
// IsDoubleClaim verifies that given claim inputs are not already claimed by another tx.
func (dao *Simple) IsDoubleClaim(claim *transaction.ClaimTX) bool {
return dao.checkUsedInputs(claim.Claims, state.CoinClaimed)
}
func (dao *Simple) checkUsedInputs(inputs []transaction.Input, coin state.Coin) bool {
if len(inputs) == 0 {
return false

View file

@ -52,7 +52,6 @@ type Pool struct {
verifiedMap map[util.Uint256]*item
verifiedTxes items
inputs []*transaction.Input
claims []*transaction.Input
fees map[util.Uint160]utilityBalanceAndFees
capacity int
@ -79,20 +78,6 @@ func (p *item) CompareTo(otherP *item) int {
return -1
}
if p.isLowPrio && otherP.isLowPrio {
thisIsClaimTx := p.txn.Type == transaction.ClaimType
otherIsClaimTx := otherP.txn.Type == transaction.ClaimType
if thisIsClaimTx != otherIsClaimTx {
// This is a claim Tx and other isn't.
if thisIsClaimTx {
return 1
}
// The other is claim Tx and this isn't.
return -1
}
}
// Fees sorted ascending.
if ret := p.txn.FeePerByte().CompareTo(otherP.txn.FeePerByte()); ret != 0 {
return ret
@ -249,12 +234,6 @@ func (mp *Pool) Add(t *transaction.Transaction, fee Feer) error {
for i := range t.Inputs {
pushInputToSortedSlice(&mp.inputs, &t.Inputs[i])
}
if t.Type == transaction.ClaimType {
claim := t.Data.(*transaction.ClaimTX)
for i := range claim.Claims {
pushInputToSortedSlice(&mp.claims, &claim.Claims[i])
}
}
updateMempoolMetrics(len(mp.verifiedTxes))
mp.lock.Unlock()
@ -284,12 +263,6 @@ func (mp *Pool) Remove(hash util.Uint256) {
for i := range it.txn.Inputs {
dropInputFromSortedSlice(&mp.inputs, &it.txn.Inputs[i])
}
if it.txn.Type == transaction.ClaimType {
claim := it.txn.Data.(*transaction.ClaimTX)
for i := range claim.Claims {
dropInputFromSortedSlice(&mp.claims, &claim.Claims[i])
}
}
}
updateMempoolMetrics(len(mp.verifiedTxes))
mp.lock.Unlock()
@ -304,7 +277,6 @@ func (mp *Pool) RemoveStale(isOK func(*transaction.Transaction) bool, feer Feer)
// because items are iterated one-by-one in increasing order.
newVerifiedTxes := mp.verifiedTxes[:0]
newInputs := mp.inputs[:0]
newClaims := mp.claims[:0]
mp.fees = make(map[util.Uint160]utilityBalanceAndFees) // it'd be nice to reuse existing map, but we can't easily clear it
for _, itm := range mp.verifiedTxes {
if isOK(itm.txn) && mp.tryAddSendersFee(itm.txn, feer) {
@ -312,12 +284,6 @@ func (mp *Pool) RemoveStale(isOK func(*transaction.Transaction) bool, feer Feer)
for i := range itm.txn.Inputs {
newInputs = append(newInputs, &itm.txn.Inputs[i])
}
if itm.txn.Type == transaction.ClaimType {
claim := itm.txn.Data.(*transaction.ClaimTX)
for i := range claim.Claims {
newClaims = append(newClaims, &claim.Claims[i])
}
}
} else {
delete(mp.verifiedMap, itm.txn.Hash())
}
@ -325,12 +291,8 @@ func (mp *Pool) RemoveStale(isOK func(*transaction.Transaction) bool, feer Feer)
sort.Slice(newInputs, func(i, j int) bool {
return newInputs[i].Cmp(newInputs[j]) < 0
})
sort.Slice(newClaims, func(i, j int) bool {
return newClaims[i].Cmp(newClaims[j]) < 0
})
mp.verifiedTxes = newVerifiedTxes
mp.inputs = newInputs
mp.claims = newClaims
mp.lock.Unlock()
}
@ -392,11 +354,6 @@ func (mp *Pool) checkTxConflicts(tx *transaction.Transaction, fee Feer) bool {
return false
}
switch tx.Type {
case transaction.ClaimType:
claim := tx.Data.(*transaction.ClaimTX)
if areInputsInPool(claim.Claims, mp.claims) {
return false
}
case transaction.IssueType:
// It's a hack, because technically we could check for
// available asset amount, but these transactions are so rare

View file

@ -55,7 +55,7 @@ func TestMemPoolAddRemove(t *testing.T) {
t.Run("high priority", func(t *testing.T) { testMemPoolAddRemoveWithFeer(t, fs) })
}
func TestMemPoolAddRemoveWithInputsAndClaims(t *testing.T) {
func TestMemPoolAddRemoveWithInputs(t *testing.T) {
mp := NewMemPool(50)
hash1, err := util.Uint256DecodeStringBE("a83ba6ede918a501558d3170a124324aedc89909e64c4ff2c6f863094f980b25")
require.NoError(t, err)
@ -64,62 +64,41 @@ func TestMemPoolAddRemoveWithInputsAndClaims(t *testing.T) {
mpLessInputs := func(i, j int) bool {
return mp.inputs[i].Cmp(mp.inputs[j]) < 0
}
mpLessClaims := func(i, j int) bool {
return mp.claims[i].Cmp(mp.claims[j]) < 0
}
txm1 := transaction.NewContractTX()
txm1.Nonce = 1
txc1, claim1 := newClaimTX()
for i := 0; i < 5; i++ {
txm1.Inputs = append(txm1.Inputs, transaction.Input{PrevHash: hash1, PrevIndex: uint16(100 - i)})
claim1.Claims = append(claim1.Claims, transaction.Input{PrevHash: hash1, PrevIndex: uint16(100 - i)})
}
require.NoError(t, mp.Add(txm1, &FeerStub{}))
require.NoError(t, mp.Add(txc1, &FeerStub{}))
// Look inside.
assert.Equal(t, len(txm1.Inputs), len(mp.inputs))
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
assert.Equal(t, len(claim1.Claims), len(mp.claims))
assert.True(t, sort.SliceIsSorted(mp.claims, mpLessClaims))
txm2 := transaction.NewContractTX()
txm2.Nonce = 1
txc2, claim2 := newClaimTX()
for i := 0; i < 10; i++ {
txm2.Inputs = append(txm2.Inputs, transaction.Input{PrevHash: hash2, PrevIndex: uint16(i)})
claim2.Claims = append(claim2.Claims, transaction.Input{PrevHash: hash2, PrevIndex: uint16(i)})
}
require.NoError(t, mp.Add(txm2, &FeerStub{}))
require.NoError(t, mp.Add(txc2, &FeerStub{}))
assert.Equal(t, len(txm1.Inputs)+len(txm2.Inputs), len(mp.inputs))
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
assert.Equal(t, len(claim1.Claims)+len(claim2.Claims), len(mp.claims))
assert.True(t, sort.SliceIsSorted(mp.claims, mpLessClaims))
mp.Remove(txm1.Hash())
mp.Remove(txc2.Hash())
assert.Equal(t, len(txm2.Inputs), len(mp.inputs))
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
assert.Equal(t, len(claim1.Claims), len(mp.claims))
assert.True(t, sort.SliceIsSorted(mp.claims, mpLessClaims))
require.NoError(t, mp.Add(txm1, &FeerStub{}))
require.NoError(t, mp.Add(txc2, &FeerStub{}))
assert.Equal(t, len(txm1.Inputs)+len(txm2.Inputs), len(mp.inputs))
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
assert.Equal(t, len(claim1.Claims)+len(claim2.Claims), len(mp.claims))
assert.True(t, sort.SliceIsSorted(mp.claims, mpLessClaims))
mp.RemoveStale(func(t *transaction.Transaction) bool {
if t.Hash() == txc1.Hash() || t.Hash() == txm2.Hash() {
if t.Hash() == txm2.Hash() {
return false
}
return true
}, &FeerStub{})
assert.Equal(t, len(txm1.Inputs), len(mp.inputs))
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
assert.Equal(t, len(claim2.Claims), len(mp.claims))
assert.True(t, sort.SliceIsSorted(mp.claims, mpLessClaims))
}
func TestMemPoolVerifyInputs(t *testing.T) {
@ -149,33 +128,6 @@ func TestMemPoolVerifyInputs(t *testing.T) {
require.Error(t, mp.Add(tx3, &FeerStub{}))
}
func TestMemPoolVerifyClaims(t *testing.T) {
mp := NewMemPool(50)
tx1, claim1 := newClaimTX()
hash1, err := util.Uint256DecodeStringBE("a83ba6ede918a501558d3170a124324aedc89909e64c4ff2c6f863094f980b25")
require.NoError(t, err)
hash2, err := util.Uint256DecodeStringBE("629397158f852e838077bb2715b13a2e29b0a51c2157e5466321b70ed7904ce9")
require.NoError(t, err)
for i := 0; i < 10; i++ {
claim1.Claims = append(claim1.Claims, transaction.Input{PrevHash: hash1, PrevIndex: uint16(i)})
claim1.Claims = append(claim1.Claims, transaction.Input{PrevHash: hash2, PrevIndex: uint16(i)})
}
require.Equal(t, true, mp.Verify(tx1, &FeerStub{}))
require.NoError(t, mp.Add(tx1, &FeerStub{}))
tx2, claim2 := newClaimTX()
for i := 0; i < 10; i++ {
claim2.Claims = append(claim2.Claims, transaction.Input{PrevHash: hash2, PrevIndex: uint16(i + 10)})
}
require.Equal(t, true, mp.Verify(tx2, &FeerStub{}))
require.NoError(t, mp.Add(tx2, &FeerStub{}))
tx3, claim3 := newClaimTX()
claim3.Claims = append(claim3.Claims, transaction.Input{PrevHash: hash1, PrevIndex: 0})
require.Equal(t, false, mp.Verify(tx3, &FeerStub{}))
require.Error(t, mp.Add(tx3, &FeerStub{}))
}
func TestMemPoolVerifyIssue(t *testing.T) {
mp := NewMemPool(50)
tx1 := newIssueTX()
@ -199,11 +151,6 @@ func newIssueTX() *transaction.Transaction {
return tx
}
func newClaimTX() (*transaction.Transaction, *transaction.ClaimTX) {
cl := &transaction.ClaimTX{}
return transaction.NewClaimTX(cl), cl
}
func TestOverCapacity(t *testing.T) {
var fs = &FeerStub{lowPriority: true}
const mempoolSize = 10
@ -218,18 +165,8 @@ func TestOverCapacity(t *testing.T) {
require.Equal(t, mempoolSize, mp.Count())
require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
// Claim TX has more priority than ordinary lowprio, so it should easily
// fit into the pool.
claim := &transaction.Transaction{
Type: transaction.ClaimType,
Data: &transaction.ClaimTX{},
}
require.NoError(t, mp.Add(claim, fs))
require.Equal(t, mempoolSize, mp.Count())
require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
// Fees are also prioritized.
for i := 0; i < mempoolSize-1; i++ {
for i := 0; i < mempoolSize; i++ {
tx := transaction.NewContractTX()
tx.Attributes = append(tx.Attributes, transaction.Attribute{
Usage: transaction.Hash1,
@ -249,16 +186,13 @@ func TestOverCapacity(t *testing.T) {
Usage: transaction.Hash1,
Data: util.Uint256{1, 2, 3, 4}.BytesBE(),
})
tx.NetworkFee = util.Fixed8FromFloat(0.00001)
tx.NetworkFee = util.Fixed8FromFloat(0.000001)
tx.Nonce = txcnt
txcnt++
require.Error(t, mp.Add(tx, fs))
require.Equal(t, mempoolSize, mp.Count())
require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
// But claim tx should still be there.
require.True(t, mp.ContainsKey(claim.Hash()))
// Low net fee, but higher per-byte fee is still a better combination.
tx = transaction.NewContractTX()
tx.Nonce = txcnt

View file

@ -13,16 +13,6 @@ type UnspentBalance struct {
Value util.Fixed8 `json:"value"`
}
// UnclaimedBalance represents transaction output which was spent and
// can be claimed.
type UnclaimedBalance struct {
Tx util.Uint256
Index uint16
Start uint32
End uint32
Value util.Fixed8
}
// UnspentBalances is a slice of UnspentBalance (mostly needed to sort them).
type UnspentBalances []UnspentBalance
@ -32,7 +22,6 @@ type Account struct {
ScriptHash util.Uint160
IsFrozen bool
Balances map[util.Uint256][]UnspentBalance
Unclaimed UnclaimedBalances
}
// NewAccount returns a new Account object.
@ -42,7 +31,6 @@ func NewAccount(scriptHash util.Uint160) *Account {
ScriptHash: scriptHash,
IsFrozen: false,
Balances: make(map[util.Uint256][]UnspentBalance),
Unclaimed: UnclaimedBalances{Raw: []byte{}},
}
}
@ -64,10 +52,6 @@ func (s *Account) DecodeBinary(br *io.BinReader) {
}
s.Balances[key] = ubs
}
lenBalances = br.ReadVarUint()
s.Unclaimed.Raw = make([]byte, lenBalances*UnclaimedBalanceSize)
br.ReadBytes(s.Unclaimed.Raw)
}
// EncodeBinary encodes Account to the given BinWriter.
@ -84,9 +68,6 @@ func (s *Account) EncodeBinary(bw *io.BinWriter) {
v[i].EncodeBinary(bw)
}
}
bw.WriteVarUint(uint64(s.Unclaimed.Size()))
bw.WriteBytes(s.Unclaimed.Raw)
}
// DecodeBinary implements io.Serializable interface.
@ -103,24 +84,6 @@ func (u *UnspentBalance) EncodeBinary(w *io.BinWriter) {
u.Value.EncodeBinary(w)
}
// DecodeBinary implements io.Serializable interface.
func (u *UnclaimedBalance) DecodeBinary(r *io.BinReader) {
u.Tx.DecodeBinary(r)
u.Index = r.ReadU16LE()
u.Start = r.ReadU32LE()
u.End = r.ReadU32LE()
u.Value.DecodeBinary(r)
}
// EncodeBinary implements io.Serializable interface.
func (u *UnclaimedBalance) EncodeBinary(w *io.BinWriter) {
u.Tx.EncodeBinary(w)
w.WriteU16LE(u.Index)
w.WriteU32LE(u.Start)
w.WriteU32LE(u.End)
u.Value.EncodeBinary(w)
}
// GetBalanceValues sums all unspent outputs and returns a map of asset IDs to
// overall balances.
func (s *Account) GetBalanceValues() map[util.Uint256]util.Fixed8 {

View file

@ -30,7 +30,6 @@ func TestDecodeEncodeAccountState(t *testing.T) {
ScriptHash: random.Uint160(),
IsFrozen: true,
Balances: balances,
Unclaimed: UnclaimedBalances{Raw: []byte{}},
}
testserdes.EncodeDecodeBinary(t, a, new(Account))

View file

@ -1,69 +0,0 @@
package state
import (
"bytes"
"encoding/binary"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// UnclaimedBalanceSize is a size of the UnclaimedBalance struct in bytes.
const UnclaimedBalanceSize = util.Uint256Size + 2 + 4 + 4 + 8
// UnclaimedBalances is a slice of UnclaimedBalance.
type UnclaimedBalances struct {
Raw []byte
}
// Size returns an amount of store unclaimed balances.
func (bs *UnclaimedBalances) Size() int {
return len(bs.Raw) / UnclaimedBalanceSize
}
// ForEach iterates over all unclaimed balances.
func (bs *UnclaimedBalances) ForEach(f func(*UnclaimedBalance) error) error {
b := new(UnclaimedBalance)
for i := 0; i < len(bs.Raw); i += UnclaimedBalanceSize {
r := io.NewBinReaderFromBuf(bs.Raw[i : i+UnclaimedBalanceSize])
b.DecodeBinary(r)
if r.Err != nil {
return r.Err
} else if err := f(b); err != nil {
return err
}
}
return nil
}
// Remove removes specified unclaim from the list and returns
// false if it wasn't found.
func (bs *UnclaimedBalances) Remove(tx util.Uint256, index uint16) bool {
const keySize = util.Uint256Size + 2
key := make([]byte, keySize)
copy(key, tx[:])
binary.LittleEndian.PutUint16(key[util.Uint256Size:], index)
for i := 0; i < len(bs.Raw); i += UnclaimedBalanceSize {
if bytes.Equal(bs.Raw[i:i+keySize], key) {
lastIndex := len(bs.Raw) - UnclaimedBalanceSize
if i != lastIndex {
copy(bs.Raw[i:i+UnclaimedBalanceSize], bs.Raw[lastIndex:])
}
bs.Raw = bs.Raw[:lastIndex]
return true
}
}
return false
}
// Put puts new unclaim in a list.
func (bs *UnclaimedBalances) Put(b *UnclaimedBalance) error {
w := io.NewBufBinWriter()
b.EncodeBinary(w.BinWriter)
if w.Err != nil {
return w.Err
}
bs.Raw = append(bs.Raw, w.Bytes()...)
return nil
}

View file

@ -1,73 +0,0 @@
package state
import (
"encoding/binary"
"math/rand"
"testing"
"github.com/nspcc-dev/neo-go/pkg/internal/random"
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
)
func TestUnclaimedBalance_Structure(t *testing.T) {
b := randomUnclaimed(t)
buf, err := testserdes.EncodeBinary(b)
require.NoError(t, err)
require.Equal(t, UnclaimedBalanceSize, len(buf))
require.Equal(t, b.Tx.BytesBE(), buf[:util.Uint256Size])
require.Equal(t, b.Index, binary.LittleEndian.Uint16(buf[util.Uint256Size:]))
}
func TestUnclaimedBalances_Put(t *testing.T) {
bs := new(UnclaimedBalances)
b1 := randomUnclaimed(t)
b2 := randomUnclaimed(t)
b3 := randomUnclaimed(t)
require.NoError(t, bs.Put(b1))
require.Equal(t, 1, bs.Size())
require.NoError(t, bs.Put(b2))
require.Equal(t, 2, bs.Size())
require.NoError(t, bs.Put(b3))
require.Equal(t, 3, bs.Size())
require.True(t, bs.Remove(b2.Tx, b2.Index))
require.Equal(t, 2, bs.Size())
require.False(t, bs.Remove(b2.Tx, b2.Index))
require.Equal(t, 2, bs.Size())
require.True(t, bs.Remove(b1.Tx, b1.Index))
require.Equal(t, 1, bs.Size())
require.True(t, bs.Remove(b3.Tx, b3.Index))
require.Equal(t, 0, bs.Size())
}
func TestUnclaimedBalances_ForEach(t *testing.T) {
bs := new(UnclaimedBalances)
b1 := randomUnclaimed(t)
b2 := randomUnclaimed(t)
b3 := randomUnclaimed(t)
require.NoError(t, bs.Put(b1))
require.NoError(t, bs.Put(b2))
require.NoError(t, bs.Put(b3))
var indices []uint16
err := bs.ForEach(func(b *UnclaimedBalance) error {
indices = append(indices, b.Index)
return nil
})
require.NoError(t, err)
require.Equal(t, []uint16{b1.Index, b2.Index, b3.Index}, indices)
}
func randomUnclaimed(t *testing.T) *UnclaimedBalance {
b := new(UnclaimedBalance)
b.Tx = random.Uint256()
b.Index = uint16(rand.Uint32())
b.Start = rand.Uint32()
b.End = rand.Uint32()
b.Value = util.Fixed8(rand.Int63())
return b
}

View file

@ -1,38 +0,0 @@
package transaction
import (
"math/rand"
"github.com/nspcc-dev/neo-go/pkg/io"
)
// ClaimTX represents a claim transaction.
type ClaimTX struct {
Claims []Input
}
// NewClaimTX creates Transaction of ClaimType type.
func NewClaimTX(claim *ClaimTX) *Transaction {
return &Transaction{
Type: ClaimType,
Version: 0,
Nonce: rand.Uint32(),
Data: claim,
Attributes: []Attribute{},
Cosigners: []Cosigner{},
Inputs: []Input{},
Outputs: []Output{},
Scripts: []Witness{},
Trimmed: false,
}
}
// DecodeBinary implements Serializable interface.
func (tx *ClaimTX) DecodeBinary(br *io.BinReader) {
br.ReadArray(&tx.Claims)
}
// EncodeBinary implements Serializable interface.
func (tx *ClaimTX) EncodeBinary(bw *io.BinWriter) {
bw.WriteArray(tx.Claims)
}

View file

@ -9,8 +9,6 @@ import (
var (
//TODO NEO3.0: Update binary
// https://neotracker.io/tx/2c6a45547b3898318e400e541628990a07acb00f3b9a15a8e966ae49525304da
rawClaimTX = "020004bc67ba325d6412ff4c55b10f7e9afb54bbb2228d201b37363c3d697ac7c198f70300591cd454d7318d2087c0196abfbbd1573230380672f0f0cd004dcb4857e58cbd010031bcfbed573f5318437e95edd603922a4455ff3326a979fdd1c149a84c4cb0290000b51eb6159c58cac4fe23d90e292ad2bcb7002b0da2c474e81e1889c0649d2c490000000001e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c603b555f00000000005d9de59d99c0d1f6ed1496444473f4a0b538302f014140456349cec43053009accdb7781b0799c6b591c812768804ab0a0b56b5eae7a97694227fcd33e70899c075848b2cee8fae733faac6865b484d3f7df8949e2aadb232103945fae1ed3c31d778f149192b76734fcc951b400ba3598faa81ff92ebe477eacac"
// https://neotracker.io/tx/fe4b3af60677204c57e573a57bdc97bc5059b05ad85b1474f84431f88d910f64
rawInvocationTX = "d101590400b33f7114839c33710da24cf8e7d536b8d244f3991cf565c8146063795d3b9b3cd55aef026eae992b91063db0db53c1087472616e7366657267c5cc1cb5392019e2cc4e6d6b5ea54c8d4b6d11acf166cb072961424c54f6000000000000000001206063795d3b9b3cd55aef026eae992b91063db0db0000014140c6a131c55ca38995402dff8e92ac55d89cbed4b98dfebbcb01acbc01bd78fa2ce2061be921b8999a9ab79c2958875bccfafe7ce1bbbaf1f56580815ea3a4feed232102d41ddce2c97be4c9aa571b8a32cbc305aa29afffbcae71b0ef568db0e93929aaac"
)

View file

@ -176,9 +176,6 @@ func (t *Transaction) decodeData(r *io.BinReader) {
case InvocationType:
t.Data = &InvocationTX{}
t.Data.(*InvocationTX).DecodeBinary(r)
case ClaimType:
t.Data = &ClaimTX{}
t.Data.(*ClaimTX).DecodeBinary(r)
case ContractType:
t.Data = &ContractTX{}
t.Data.(*ContractTX).DecodeBinary(r)
@ -313,7 +310,6 @@ type transactionJSON struct {
Outputs []Output `json:"vout"`
Scripts []Witness `json:"scripts"`
Claims []Input `json:"claims,omitempty"`
Script string `json:"script,omitempty"`
Asset *registeredAsset `json:"asset,omitempty"`
}
@ -337,8 +333,6 @@ func (t *Transaction) MarshalJSON() ([]byte, error) {
NetworkFee: t.NetworkFee,
}
switch t.Type {
case ClaimType:
tx.Claims = t.Data.(*ClaimTX).Claims
case InvocationType:
tx.Script = hex.EncodeToString(t.Data.(*InvocationTX).Script)
case RegisterType:
@ -378,10 +372,6 @@ func (t *Transaction) UnmarshalJSON(data []byte) error {
}
t.Sender = sender
switch tx.Type {
case ClaimType:
t.Data = &ClaimTX{
Claims: tx.Claims,
}
case InvocationType:
bytes, err := hex.DecodeString(tx.Script)
if err != nil {

View file

@ -36,31 +36,6 @@ func TestWitnessEncodeDecode(t *testing.T) {
// TODO NEO3.0: update binary
/*
func TestDecodeEncodeClaimTX(t *testing.T) {
tx := decodeTransaction(rawClaimTX, t)
assert.Equal(t, tx.Type, ClaimType)
assert.IsType(t, tx.Data, &ClaimTX{})
claimTX := tx.Data.(*ClaimTX)
assert.Equal(t, 4, len(claimTX.Claims))
assert.Equal(t, 0, len(tx.Attributes))
assert.Equal(t, 0, len(tx.Inputs))
assert.Equal(t, 1, len(tx.Outputs))
assert.Equal(t, "AQJseD8iBmCD4sgfHRhMahmoi9zvopG6yz", address.Uint160ToString(tx.Outputs[0].ScriptHash))
assert.Equal(t, "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7", tx.Outputs[0].AssetID.StringLE())
assert.Equal(t, tx.Outputs[0].Amount.String(), "0.06247739")
invoc := "40456349cec43053009accdb7781b0799c6b591c812768804ab0a0b56b5eae7a97694227fcd33e70899c075848b2cee8fae733faac6865b484d3f7df8949e2aadb"
verif := "2103945fae1ed3c31d778f149192b76734fcc951b400ba3598faa81ff92ebe477eacac"
assert.Equal(t, 1, len(tx.Scripts))
assert.Equal(t, invoc, hex.EncodeToString(tx.Scripts[0].InvocationScript))
assert.Equal(t, verif, hex.EncodeToString(tx.Scripts[0].VerificationScript))
data, err := testserdes.EncodeBinary(tx)
assert.NoError(t, err)
assert.Equal(t, rawClaimTX, hex.EncodeToString(data))
hash := "2c6a45547b3898318e400e541628990a07acb00f3b9a15a8e966ae49525304da"
assert.Equal(t, hash, tx.hash.StringLE())
}
func TestDecodeEncodeInvocationTX(t *testing.T) {
tx := decodeTransaction(rawInvocationTX, t)
@ -120,34 +95,6 @@ func TestMarshalUnmarshalJSONContractTX(t *testing.T) {
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
}
func TestMarshalUnmarshalJSONClaimTX(t *testing.T) {
tx := &Transaction{
Type: ClaimType,
Version: 0,
Data: &ClaimTX{Claims: []Input{
{
PrevHash: util.Uint256{1, 2, 3, 4},
PrevIndex: uint16(56),
},
}},
Attributes: []Attribute{},
Inputs: []Input{{
PrevHash: util.Uint256{5, 6, 7, 8},
PrevIndex: uint16(12),
}},
Outputs: []Output{{
AssetID: util.Uint256{1, 2, 3},
Amount: util.Fixed8FromInt64(1),
ScriptHash: util.Uint160{1, 2, 3},
Position: 0,
}},
Scripts: []Witness{},
Trimmed: false,
}
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
}
func TestMarshalUnmarshalJSONInvocationTX(t *testing.T) {
tx := &Transaction{
Type: InvocationType,

View file

@ -12,7 +12,6 @@ type TXType uint8
// Constants for all valid transaction types.
const (
IssueType TXType = 0x01
ClaimType TXType = 0x02
RegisterType TXType = 0x40
ContractType TXType = 0x80
InvocationType TXType = 0xd1
@ -23,8 +22,6 @@ func (t TXType) String() string {
switch t {
case IssueType:
return "IssueTransaction"
case ClaimType:
return "ClaimTransaction"
case RegisterType:
return "RegisterTransaction"
case ContractType:
@ -57,8 +54,6 @@ func TXTypeFromString(jsonString string) (TXType, error) {
switch jsonString = strings.TrimSpace(jsonString); jsonString {
case "IssueTransaction":
return IssueType, nil
case "ClaimTransaction":
return ClaimType, nil
case "RegisterTransaction":
return RegisterType, nil
case "ContractTransaction":

View file

@ -24,7 +24,6 @@ func GetHash(t Transaction) []byte {
// GetType returns the type of the given transaction. Possible values:
// MinerTransaction = 0x00
// IssueTransaction = 0x01
// ClaimTransaction = 0x02
// EnrollmentTransaction = 0x20
// RegisterTransaction = 0x40
// ContractTransaction = 0x80