forked from TrueCloudLab/neoneo-go
transaction: drop Claim TX type
This commit is contained in:
parent
3d18f09def
commit
dfc7a9bfd1
14 changed files with 10 additions and 577 deletions
|
@ -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,14 +1376,12 @@ 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 {
|
||||
if bc.IsLowPriority(t.NetworkFee) ||
|
||||
t.NetworkFee < util.Fixed8FromFloat(bc.config.FeePerExtraByte)*util.Fixed8(txSize-maxFree) {
|
||||
return ErrPolicy
|
||||
}
|
||||
txSize := io.GetVarSize(t)
|
||||
maxFree := bc.config.MaxFreeTransactionSize
|
||||
if maxFree != 0 && txSize > maxFree {
|
||||
if bc.IsLowPriority(t.NetworkFee) ||
|
||||
t.NetworkFee < util.Fixed8FromFloat(bc.config.FeePerExtraByte)*util.Fixed8(txSize-maxFree) {
|
||||
return ErrPolicy
|
||||
}
|
||||
}
|
||||
if err := bc.memPool.Add(t, bc); err != nil {
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue