transaction: drop Inputs and Outputs, forget UTXO
This commit is contained in:
parent
21efccd300
commit
709146f295
37 changed files with 30 additions and 1509 deletions
|
@ -121,15 +121,4 @@ func writeParameterContext(c *context.ParameterContext, filename string) error {
|
|||
|
||||
func printTxInfo(t *transaction.Transaction) {
|
||||
fmt.Printf("Hash: %s\n", t.Hash().StringLE())
|
||||
for i := range t.Inputs {
|
||||
fmt.Printf("Input%02d: [%2d] %s\n", i, t.Inputs[i].PrevIndex, t.Inputs[i].PrevHash.StringLE())
|
||||
}
|
||||
for i := range t.Outputs {
|
||||
fmt.Printf("Output%02d:\n", i)
|
||||
fmt.Printf("\tAssetID : %s\n", t.Outputs[i].AssetID.StringLE())
|
||||
fmt.Printf("\tAmount : %s\n", t.Outputs[i].Amount.String())
|
||||
h := t.Outputs[i].ScriptHash
|
||||
fmt.Printf("\tScriptHash: %s\n", t.Outputs[i].ScriptHash.StringLE())
|
||||
fmt.Printf("\tToAddr : %s\n", address.Uint160ToString(h))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,13 +65,9 @@ var syscalls = map[string]map[string]string{
|
|||
"GetTransaction": "Neo.Block.GetTransaction",
|
||||
},
|
||||
"transaction": {
|
||||
"GetAttributes": "Neo.Transaction.GetAttributes",
|
||||
"GetHash": "Neo.Transaction.GetHash",
|
||||
"GetInputs": "Neo.Transaction.GetInputs",
|
||||
"GetOutputs": "Neo.Transaction.GetOutputs",
|
||||
"GetReferences": "Neo.Transaction.GetReferences",
|
||||
"GetUnspentCoins": "Neo.Transaction.GetUnspentCoins",
|
||||
"GetWitnesses": "Neo.Transaction.GetWitnesses",
|
||||
"GetAttributes": "Neo.Transaction.GetAttributes",
|
||||
"GetHash": "Neo.Transaction.GetHash",
|
||||
"GetWitnesses": "Neo.Transaction.GetWitnesses",
|
||||
},
|
||||
"asset": {
|
||||
"Create": "Neo.Asset.Create",
|
||||
|
@ -93,15 +89,6 @@ var syscalls = map[string]map[string]string{
|
|||
"Migrate": "Neo.Contract.Migrate",
|
||||
"GetStorageContext": "Neo.Contract.GetStorageContext",
|
||||
},
|
||||
"input": {
|
||||
"GetHash": "Neo.Input.GetHash",
|
||||
"GetIndex": "Neo.Input.GetIndex",
|
||||
},
|
||||
"output": {
|
||||
"GetAssetID": "Neo.Output.GetAssetID",
|
||||
"GetValue": "Neo.Output.GetValue",
|
||||
"GetScriptHash": "Neo.Output.GetScriptHash",
|
||||
},
|
||||
"engine": {
|
||||
"GetScriptContainer": "System.ExecutionEngine.GetScriptContainer",
|
||||
"GetCallingScriptHash": "System.ExecutionEngine.GetCallingScriptHash",
|
||||
|
|
|
@ -2,7 +2,6 @@ package core
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
"sync"
|
||||
|
@ -559,65 +558,6 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := cache.PutUnspentCoinState(tx.Hash(), state.NewUnspentCoin(block.Index, tx)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Process TX outputs.
|
||||
if err := processOutputs(tx, cache); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Process TX inputs that are grouped by previous hash.
|
||||
for _, inputs := range transaction.GroupInputsByPrevHash(tx.Inputs) {
|
||||
prevHash := inputs[0].PrevHash
|
||||
unspent, err := cache.GetUnspentCoinState(prevHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, input := range inputs {
|
||||
if len(unspent.States) <= int(input.PrevIndex) {
|
||||
return fmt.Errorf("bad input: %s/%d", input.PrevHash.StringLE(), input.PrevIndex)
|
||||
}
|
||||
if unspent.States[input.PrevIndex].State&state.CoinSpent != 0 {
|
||||
return fmt.Errorf("double spend: %s/%d", input.PrevHash.StringLE(), input.PrevIndex)
|
||||
}
|
||||
unspent.States[input.PrevIndex].State |= state.CoinSpent
|
||||
unspent.States[input.PrevIndex].SpendHeight = block.Index
|
||||
prevTXOutput := &unspent.States[input.PrevIndex].Output
|
||||
account, err := cache.GetAccountStateOrNew(prevTXOutput.ScriptHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
balancesLen := len(account.Balances[prevTXOutput.AssetID])
|
||||
if balancesLen <= 1 {
|
||||
delete(account.Balances, prevTXOutput.AssetID)
|
||||
} else {
|
||||
var index = -1
|
||||
for i, balance := range account.Balances[prevTXOutput.AssetID] {
|
||||
if balance.Tx.Equals(input.PrevHash) && balance.Index == input.PrevIndex {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if index >= 0 {
|
||||
last := balancesLen - 1
|
||||
if last > index {
|
||||
account.Balances[prevTXOutput.AssetID][index] = account.Balances[prevTXOutput.AssetID][last]
|
||||
}
|
||||
account.Balances[prevTXOutput.AssetID] = account.Balances[prevTXOutput.AssetID][:last]
|
||||
}
|
||||
}
|
||||
if err = cache.PutAccountState(account); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = cache.PutUnspentCoinState(prevHash, unspent); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
systemInterop := bc.newInteropContext(trigger.Application, cache, block, tx)
|
||||
v := SpawnVM(systemInterop)
|
||||
v.LoadScript(tx.Script)
|
||||
|
@ -837,25 +777,6 @@ func (bc *Blockchain) LastBatch() *storage.MemBatch {
|
|||
return bc.lastBatch
|
||||
}
|
||||
|
||||
// processOutputs processes transaction outputs.
|
||||
func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error {
|
||||
for index, output := range tx.Outputs {
|
||||
account, err := dao.GetAccountStateOrNew(output.ScriptHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account.Balances[output.AssetID] = append(account.Balances[output.AssetID], state.UnspentBalance{
|
||||
Tx: tx.Hash(),
|
||||
Index: uint16(index),
|
||||
Value: output.Amount,
|
||||
})
|
||||
if err = dao.PutAccountState(account); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// persist flushes current in-memory Store contents to the persistent storage.
|
||||
func (bc *Blockchain) persist() error {
|
||||
var (
|
||||
|
@ -1046,15 +967,6 @@ func (bc *Blockchain) GetAccountState(scriptHash util.Uint160) *state.Account {
|
|||
return as
|
||||
}
|
||||
|
||||
// GetUnspentCoinState returns unspent coin state for given tx hash.
|
||||
func (bc *Blockchain) GetUnspentCoinState(hash util.Uint256) *state.UnspentCoin {
|
||||
ucs, err := bc.dao.GetUnspentCoinState(hash)
|
||||
if ucs == nil && err != storage.ErrKeyNotFound {
|
||||
bc.log.Warn("failed to get unspent coin state", zap.Error(err))
|
||||
}
|
||||
return ucs
|
||||
}
|
||||
|
||||
// GetConfig returns the config stored in the blockchain.
|
||||
func (bc *Blockchain) GetConfig() config.ProtocolConfiguration {
|
||||
return bc.config
|
||||
|
@ -1155,36 +1067,6 @@ func (bc *Blockchain) CalculateClaimable(value int64, startHeight, endHeight uin
|
|||
return amount * util.Fixed8(value)
|
||||
}
|
||||
|
||||
// References maps transaction's inputs into a slice of InOuts, effectively
|
||||
// joining each Input with the corresponding Output.
|
||||
// @TODO: unfortunately we couldn't attach this method to the Transaction struct in the
|
||||
// transaction package because of a import cycle problem. Perhaps we should think to re-design
|
||||
// the code base to avoid this situation.
|
||||
func (bc *Blockchain) References(t *transaction.Transaction) ([]transaction.InOut, error) {
|
||||
return bc.references(t.Inputs)
|
||||
}
|
||||
|
||||
// references is an internal implementation of References that operates directly
|
||||
// on a slice of Input.
|
||||
func (bc *Blockchain) references(ins []transaction.Input) ([]transaction.InOut, error) {
|
||||
references := make([]transaction.InOut, 0, len(ins))
|
||||
|
||||
for _, inputs := range transaction.GroupInputsByPrevHash(ins) {
|
||||
prevHash := inputs[0].PrevHash
|
||||
unspent, err := bc.dao.GetUnspentCoinState(prevHash)
|
||||
if err != nil {
|
||||
return nil, errors.New("bad input reference")
|
||||
}
|
||||
for _, in := range inputs {
|
||||
if int(in.PrevIndex) > len(unspent.States)-1 {
|
||||
return nil, errors.New("bad input reference")
|
||||
}
|
||||
references = append(references, transaction.InOut{In: *in, Out: unspent.States[in.PrevIndex].Output})
|
||||
}
|
||||
}
|
||||
return references, nil
|
||||
}
|
||||
|
||||
// FeePerByte returns transaction network fee per byte.
|
||||
// TODO: should be implemented as part of PolicyContract
|
||||
func (bc *Blockchain) FeePerByte() util.Fixed8 {
|
||||
|
@ -1253,28 +1135,11 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
|
|||
if netFee < 0 {
|
||||
return errors.Errorf("insufficient funds: net fee is %v, need %v", t.NetworkFee, needNetworkFee)
|
||||
}
|
||||
if transaction.HaveDuplicateInputs(t.Inputs) {
|
||||
return errors.New("invalid transaction's inputs")
|
||||
}
|
||||
if block == nil {
|
||||
if ok := bc.memPool.Verify(t, bc); !ok {
|
||||
return errors.New("invalid transaction due to conflicts with the memory pool")
|
||||
}
|
||||
}
|
||||
if bc.dao.IsDoubleSpend(t) {
|
||||
return errors.New("invalid transaction caused by double spending")
|
||||
}
|
||||
if err := bc.verifyOutputs(t); err != nil {
|
||||
return errors.Wrap(err, "wrong outputs")
|
||||
}
|
||||
refs, err := bc.References(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
results := refsAndOutsToResults(refs, t.Outputs)
|
||||
if err := bc.verifyResults(t, results); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, a := range t.Attributes {
|
||||
if a.Usage == transaction.ECDH02 || a.Usage == transaction.ECDH03 {
|
||||
|
@ -1298,9 +1163,6 @@ func (bc *Blockchain) isTxStillRelevant(t *transaction.Transaction) bool {
|
|||
if bc.dao.HasTransaction(t.Hash()) {
|
||||
return false
|
||||
}
|
||||
if bc.dao.IsDoubleSpend(t) {
|
||||
return false
|
||||
}
|
||||
for i := range t.Scripts {
|
||||
if !vm.IsStandardContract(t.Scripts[i].VerificationScript) {
|
||||
recheckWitness = true
|
||||
|
@ -1357,88 +1219,6 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (bc *Blockchain) verifyOutputs(t *transaction.Transaction) error {
|
||||
for assetID, outputs := range t.GroupOutputByAssetID() {
|
||||
assetState := bc.GetAssetState(assetID)
|
||||
if assetState == nil {
|
||||
return fmt.Errorf("no asset state for %s", assetID.StringLE())
|
||||
}
|
||||
|
||||
if assetState.Expiration < bc.blockHeight+1 && assetState.AssetType != transaction.GoverningToken && assetState.AssetType != transaction.UtilityToken {
|
||||
return fmt.Errorf("asset %s expired", assetID.StringLE())
|
||||
}
|
||||
|
||||
for _, out := range outputs {
|
||||
if int64(out.Amount)%int64(math.Pow10(8-int(assetState.Precision))) != 0 {
|
||||
return fmt.Errorf("output is not compliant with %s asset precision", assetID.StringLE())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bc *Blockchain) verifyResults(t *transaction.Transaction, results []*transaction.Result) error {
|
||||
var resultsDestroy []*transaction.Result
|
||||
var resultsIssue []*transaction.Result
|
||||
for _, re := range results {
|
||||
if re.Amount.GreaterThan(util.Fixed8(0)) {
|
||||
resultsDestroy = append(resultsDestroy, re)
|
||||
}
|
||||
|
||||
if re.Amount.LessThan(util.Fixed8(0)) {
|
||||
resultsIssue = append(resultsIssue, re)
|
||||
}
|
||||
}
|
||||
if len(resultsDestroy) > 1 {
|
||||
return errors.New("tx has more than 1 destroy output")
|
||||
}
|
||||
|
||||
if len(resultsIssue) > 0 {
|
||||
return errors.New("non issue/miner/claim tx issues tokens")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTransactionResults returns the transaction results aggregate by assetID.
|
||||
// Golang of GetTransationResults method in C# (https://github.com/neo-project/neo/blob/master/neo/Network/P2P/Payloads/Transaction.cs#L207)
|
||||
func (bc *Blockchain) GetTransactionResults(t *transaction.Transaction) []*transaction.Result {
|
||||
references, err := bc.References(t)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return refsAndOutsToResults(references, t.Outputs)
|
||||
}
|
||||
|
||||
// mapReferencesToResults returns cumulative results of transaction based in its
|
||||
// references and outputs.
|
||||
func refsAndOutsToResults(references []transaction.InOut, outputs []transaction.Output) []*transaction.Result {
|
||||
var results []*transaction.Result
|
||||
tempResult := make(map[util.Uint256]util.Fixed8)
|
||||
|
||||
for _, inout := range references {
|
||||
c := tempResult[inout.Out.AssetID]
|
||||
tempResult[inout.Out.AssetID] = c.Add(inout.Out.Amount)
|
||||
}
|
||||
for _, output := range outputs {
|
||||
c := tempResult[output.AssetID]
|
||||
tempResult[output.AssetID] = c.Sub(output.Amount)
|
||||
}
|
||||
|
||||
results = []*transaction.Result{} // this assignment is necessary. (Most of the time amount == 0 and results is the empty slice.)
|
||||
for assetID, amount := range tempResult {
|
||||
if amount != util.Fixed8(0) {
|
||||
results = append(results, &transaction.Result{
|
||||
AssetID: assetID,
|
||||
Amount: amount,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
//GetStandByValidators returns validators from the configuration.
|
||||
func (bc *Blockchain) GetStandByValidators() (keys.PublicKeys, error) {
|
||||
return getValidators(bc.config)
|
||||
|
@ -1458,29 +1238,7 @@ func (bc *Blockchain) GetEnrollments() ([]state.Validator, error) {
|
|||
// to verify whether the transaction is bonafide or not.
|
||||
// Golang implementation of GetScriptHashesForVerifying method in C# (https://github.com/neo-project/neo/blob/master/neo/Network/P2P/Payloads/Transaction.cs#L190)
|
||||
func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([]util.Uint160, error) {
|
||||
references, err := bc.References(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hashes := make(map[util.Uint160]bool)
|
||||
for i := range references {
|
||||
hashes[references[i].Out.ScriptHash] = true
|
||||
}
|
||||
|
||||
for a, outputs := range t.GroupOutputByAssetID() {
|
||||
as := bc.GetAssetState(a)
|
||||
if as == nil {
|
||||
return nil, errors.New("Invalid operation")
|
||||
}
|
||||
if as.AssetType&transaction.DutyFlag != 0 {
|
||||
for _, o := range outputs {
|
||||
h := o.ScriptHash
|
||||
if _, ok := hashes[h]; !ok {
|
||||
hashes[h] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hashes[t.Sender] = true
|
||||
for _, c := range t.Cosigners {
|
||||
hashes[c.Account] = true
|
||||
|
@ -1567,7 +1325,6 @@ func (bc *Blockchain) verifyHashAgainstScript(hash util.Uint160, witness *transa
|
|||
// is used for easy interop access and can be omitted for transactions that are
|
||||
// not yet added into any block.
|
||||
// Golang implementation of VerifyWitnesses method in C# (https://github.com/neo-project/neo/blob/master/neo/SmartContract/Helper.cs#L87).
|
||||
// Unfortunately the IVerifiable interface could not be implemented because we can't move the References method in blockchain.go to the transaction.go file.
|
||||
func (bc *Blockchain) verifyTxWitnesses(t *transaction.Transaction, block *block.Block) error {
|
||||
hashes, err := bc.GetScriptHashesForVerifying(t)
|
||||
if err != nil {
|
||||
|
|
|
@ -44,8 +44,6 @@ type Blockchainer interface {
|
|||
GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error)
|
||||
GetTestVM() *vm.VM
|
||||
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
|
||||
GetUnspentCoinState(util.Uint256) *state.UnspentCoin
|
||||
References(t *transaction.Transaction) ([]transaction.InOut, error)
|
||||
mempool.Feer // fee interface
|
||||
PoolTx(*transaction.Transaction) error
|
||||
SubscribeForBlocks(ch chan<- *block.Block)
|
||||
|
|
|
@ -15,7 +15,6 @@ type Cached struct {
|
|||
DAO
|
||||
accounts map[util.Uint160]*state.Account
|
||||
contracts map[util.Uint160]*state.Contract
|
||||
unspents map[util.Uint256]*state.UnspentCoin
|
||||
balances map[util.Uint160]*state.NEP5Balances
|
||||
transfers map[util.Uint160]map[uint32]*state.NEP5TransferLog
|
||||
}
|
||||
|
@ -24,10 +23,9 @@ type Cached struct {
|
|||
func NewCached(d DAO) *Cached {
|
||||
accs := make(map[util.Uint160]*state.Account)
|
||||
ctrs := make(map[util.Uint160]*state.Contract)
|
||||
unspents := make(map[util.Uint256]*state.UnspentCoin)
|
||||
balances := make(map[util.Uint160]*state.NEP5Balances)
|
||||
transfers := make(map[util.Uint160]map[uint32]*state.NEP5TransferLog)
|
||||
return &Cached{d.GetWrapped(), accs, ctrs, unspents, balances, transfers}
|
||||
return &Cached{d.GetWrapped(), accs, ctrs, balances, transfers}
|
||||
}
|
||||
|
||||
// GetAccountStateOrNew retrieves Account from cache or underlying store
|
||||
|
@ -77,20 +75,6 @@ func (cd *Cached) DeleteContractState(hash util.Uint160) error {
|
|||
return cd.DAO.DeleteContractState(hash)
|
||||
}
|
||||
|
||||
// GetUnspentCoinState retrieves UnspentCoin from cache or underlying store.
|
||||
func (cd *Cached) GetUnspentCoinState(hash util.Uint256) (*state.UnspentCoin, error) {
|
||||
if cd.unspents[hash] != nil {
|
||||
return cd.unspents[hash], nil
|
||||
}
|
||||
return cd.DAO.GetUnspentCoinState(hash)
|
||||
}
|
||||
|
||||
// PutUnspentCoinState saves given UnspentCoin in the cache.
|
||||
func (cd *Cached) PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error {
|
||||
cd.unspents[hash] = ucs
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNEP5Balances retrieves NEP5Balances for the acc.
|
||||
func (cd *Cached) GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error) {
|
||||
if bs := cd.balances[acc]; bs != nil {
|
||||
|
@ -167,13 +151,6 @@ func (cd *Cached) Persist() (int, error) {
|
|||
}
|
||||
buf.Reset()
|
||||
}
|
||||
for hash := range cd.unspents {
|
||||
err := cd.DAO.putUnspentCoinState(hash, cd.unspents[hash], buf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
buf.Reset()
|
||||
}
|
||||
for acc, bs := range cd.balances {
|
||||
err := cd.DAO.putNEP5Balances(acc, bs, buf)
|
||||
if err != nil {
|
||||
|
@ -197,7 +174,6 @@ func (cd *Cached) GetWrapped() DAO {
|
|||
return &Cached{cd.DAO.GetWrapped(),
|
||||
cd.accounts,
|
||||
cd.contracts,
|
||||
cd.unspents,
|
||||
cd.balances,
|
||||
cd.transfers,
|
||||
}
|
||||
|
|
|
@ -36,11 +36,9 @@ type DAO interface {
|
|||
GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error)
|
||||
GetStorageItemsWithPrefix(hash util.Uint160, prefix []byte) (map[string]*state.StorageItem, error)
|
||||
GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error)
|
||||
GetUnspentCoinState(hash util.Uint256) (*state.UnspentCoin, error)
|
||||
GetVersion() (string, error)
|
||||
GetWrapped() DAO
|
||||
HasTransaction(hash util.Uint256) bool
|
||||
IsDoubleSpend(tx *transaction.Transaction) bool
|
||||
Persist() (int, error)
|
||||
PutAccountState(as *state.Account) error
|
||||
PutAppExecResult(aer *state.AppExecResult) error
|
||||
|
@ -50,14 +48,12 @@ type DAO interface {
|
|||
PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error
|
||||
PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error
|
||||
PutStorageItem(scripthash util.Uint160, key []byte, si *state.StorageItem) error
|
||||
PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error
|
||||
PutVersion(v string) error
|
||||
StoreAsBlock(block *block.Block) error
|
||||
StoreAsCurrentBlock(block *block.Block) error
|
||||
StoreAsTransaction(tx *transaction.Transaction, index uint32) error
|
||||
putAccountState(as *state.Account, buf *io.BufBinWriter) error
|
||||
putNEP5Balances(acc util.Uint160, bs *state.NEP5Balances, buf *io.BufBinWriter) error
|
||||
putUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin, buf *io.BufBinWriter) error
|
||||
}
|
||||
|
||||
// Simple is memCached wrapper around DB, simple DAO implementation.
|
||||
|
@ -275,31 +271,6 @@ func (dao *Simple) AppendNEP5Transfer(acc util.Uint160, index uint32, tr *state.
|
|||
|
||||
// -- end transfer log.
|
||||
|
||||
// -- start unspent coins.
|
||||
|
||||
// GetUnspentCoinState retrieves UnspentCoinState from the given store.
|
||||
func (dao *Simple) GetUnspentCoinState(hash util.Uint256) (*state.UnspentCoin, error) {
|
||||
unspent := &state.UnspentCoin{}
|
||||
key := storage.AppendPrefix(storage.STCoin, hash.BytesLE())
|
||||
err := dao.GetAndDecode(unspent, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return unspent, nil
|
||||
}
|
||||
|
||||
// PutUnspentCoinState puts given UnspentCoinState into the given store.
|
||||
func (dao *Simple) PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error {
|
||||
return dao.putUnspentCoinState(hash, ucs, io.NewBufBinWriter())
|
||||
}
|
||||
|
||||
func (dao *Simple) putUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin, buf *io.BufBinWriter) error {
|
||||
key := storage.AppendPrefix(storage.STCoin, hash.BytesLE())
|
||||
return dao.putWithBuffer(ucs, key, buf)
|
||||
}
|
||||
|
||||
// -- end unspent coins.
|
||||
|
||||
// -- start notification event.
|
||||
|
||||
// GetAppExecResult gets application execution result from the
|
||||
|
@ -567,30 +538,6 @@ func (dao *Simple) StoreAsTransaction(tx *transaction.Transaction, index uint32)
|
|||
return dao.Store.Put(key, buf.Bytes())
|
||||
}
|
||||
|
||||
// IsDoubleSpend verifies that the input transactions are not double spent.
|
||||
func (dao *Simple) IsDoubleSpend(tx *transaction.Transaction) bool {
|
||||
return dao.checkUsedInputs(tx.Inputs, state.CoinSpent)
|
||||
}
|
||||
|
||||
func (dao *Simple) checkUsedInputs(inputs []transaction.Input, coin state.Coin) bool {
|
||||
if len(inputs) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, inputs := range transaction.GroupInputsByPrevHash(inputs) {
|
||||
prevHash := inputs[0].PrevHash
|
||||
unspent, err := dao.GetUnspentCoinState(prevHash)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
for _, input := range inputs {
|
||||
if int(input.PrevIndex) >= len(unspent.States) || (unspent.States[input.PrevIndex].State&coin) != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Persist flushes all the changes made into the (supposedly) persistent
|
||||
// underlying store.
|
||||
func (dao *Simple) Persist() (int, error) {
|
||||
|
|
|
@ -94,25 +94,6 @@ func TestDeleteContractState(t *testing.T) {
|
|||
require.Nil(t, gotContractState)
|
||||
}
|
||||
|
||||
func TestGetUnspentCoinState_Err(t *testing.T) {
|
||||
dao := NewSimple(storage.NewMemoryStore())
|
||||
hash := random.Uint256()
|
||||
gotUnspentCoinState, err := dao.GetUnspentCoinState(hash)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, gotUnspentCoinState)
|
||||
}
|
||||
|
||||
func TestPutGetUnspentCoinState(t *testing.T) {
|
||||
dao := NewSimple(storage.NewMemoryStore())
|
||||
hash := random.Uint256()
|
||||
unspentCoinState := &state.UnspentCoin{Height: 42, States: []state.OutputState{}}
|
||||
err := dao.PutUnspentCoinState(hash, unspentCoinState)
|
||||
require.NoError(t, err)
|
||||
gotUnspentCoinState, err := dao.GetUnspentCoinState(hash)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, unspentCoinState, gotUnspentCoinState)
|
||||
}
|
||||
|
||||
func TestPutGetAppExecResult(t *testing.T) {
|
||||
dao := NewSimple(storage.NewMemoryStore())
|
||||
hash := random.Uint256()
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
|
@ -88,95 +87,6 @@ func txGetAttributes(ic *interop.Context, v *vm.VM) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// txGetInputs returns current transaction inputs.
|
||||
func txGetInputs(ic *interop.Context, v *vm.VM) error {
|
||||
txInterface := v.Estack().Pop().Value()
|
||||
tx, ok := txInterface.(*transaction.Transaction)
|
||||
if !ok {
|
||||
return errors.New("value is not a transaction")
|
||||
}
|
||||
if len(tx.Inputs) > vm.MaxArraySize {
|
||||
return errors.New("too many inputs")
|
||||
}
|
||||
inputs := make([]vm.StackItem, 0, len(tx.Inputs))
|
||||
for i := range tx.Inputs {
|
||||
inputs = append(inputs, vm.NewInteropItem(&tx.Inputs[i]))
|
||||
}
|
||||
v.Estack().PushVal(inputs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// txGetOutputs returns current transaction outputs.
|
||||
func txGetOutputs(ic *interop.Context, v *vm.VM) error {
|
||||
txInterface := v.Estack().Pop().Value()
|
||||
tx, ok := txInterface.(*transaction.Transaction)
|
||||
if !ok {
|
||||
return errors.New("value is not a transaction")
|
||||
}
|
||||
if len(tx.Outputs) > vm.MaxArraySize {
|
||||
return errors.New("too many outputs")
|
||||
}
|
||||
outputs := make([]vm.StackItem, 0, len(tx.Outputs))
|
||||
for i := range tx.Outputs {
|
||||
outputs = append(outputs, vm.NewInteropItem(&tx.Outputs[i]))
|
||||
}
|
||||
v.Estack().PushVal(outputs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// txGetReferences returns current transaction references.
|
||||
func txGetReferences(ic *interop.Context, v *vm.VM) error {
|
||||
txInterface := v.Estack().Pop().Value()
|
||||
tx, ok := txInterface.(*transaction.Transaction)
|
||||
if !ok {
|
||||
return fmt.Errorf("type mismatch: %T is not a Transaction", txInterface)
|
||||
}
|
||||
refs, err := ic.Chain.References(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(refs) > vm.MaxArraySize {
|
||||
return errors.New("too many references")
|
||||
}
|
||||
|
||||
stackrefs := make([]vm.StackItem, 0, len(refs))
|
||||
for i := range tx.Inputs {
|
||||
for j := range refs {
|
||||
if refs[j].In == tx.Inputs[i] {
|
||||
stackrefs = append(stackrefs, vm.NewInteropItem(refs[j]))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
v.Estack().PushVal(stackrefs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// txGetUnspentCoins returns current transaction unspent coins.
|
||||
func txGetUnspentCoins(ic *interop.Context, v *vm.VM) error {
|
||||
txInterface := v.Estack().Pop().Value()
|
||||
tx, ok := txInterface.(*transaction.Transaction)
|
||||
if !ok {
|
||||
return errors.New("value is not a transaction")
|
||||
}
|
||||
ucs, err := ic.DAO.GetUnspentCoinState(tx.Hash())
|
||||
if err == storage.ErrKeyNotFound {
|
||||
v.Estack().PushVal([]vm.StackItem{})
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return errors.New("no unspent coin state found")
|
||||
}
|
||||
|
||||
items := make([]vm.StackItem, 0, len(ucs.States))
|
||||
for i := range items {
|
||||
if ucs.States[i].State&state.CoinSpent == 0 {
|
||||
items = append(items, vm.NewInteropItem(&ucs.States[i].Output))
|
||||
}
|
||||
}
|
||||
v.Estack().PushVal(items)
|
||||
return nil
|
||||
}
|
||||
|
||||
// txGetWitnesses returns current transaction witnesses.
|
||||
func txGetWitnesses(ic *interop.Context, v *vm.VM) error {
|
||||
txInterface := v.Estack().Pop().Value()
|
||||
|
@ -209,84 +119,6 @@ func witnessGetVerificationScript(ic *interop.Context, v *vm.VM) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// popInputFromVM returns transaction.Input from the first estack element.
|
||||
func popInputFromVM(v *vm.VM) (*transaction.Input, error) {
|
||||
inInterface := v.Estack().Pop().Value()
|
||||
input, ok := inInterface.(*transaction.Input)
|
||||
if !ok {
|
||||
txio, ok := inInterface.(transaction.InOut)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type mismatch: %T is not an Input or InOut", inInterface)
|
||||
}
|
||||
input = &txio.In
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
// inputGetHash returns hash from the given input.
|
||||
func inputGetHash(ic *interop.Context, v *vm.VM) error {
|
||||
input, err := popInputFromVM(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(input.PrevHash.BytesBE())
|
||||
return nil
|
||||
}
|
||||
|
||||
// inputGetIndex returns index from the given input.
|
||||
func inputGetIndex(ic *interop.Context, v *vm.VM) error {
|
||||
input, err := popInputFromVM(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(input.PrevIndex)
|
||||
return nil
|
||||
}
|
||||
|
||||
// popOutputFromVM returns transaction.Input from the first estack element.
|
||||
func popOutputFromVM(v *vm.VM) (*transaction.Output, error) {
|
||||
outInterface := v.Estack().Pop().Value()
|
||||
output, ok := outInterface.(*transaction.Output)
|
||||
if !ok {
|
||||
txio, ok := outInterface.(transaction.InOut)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type mismatch: %T is not an Output or InOut", outInterface)
|
||||
}
|
||||
output = &txio.Out
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// outputGetAssetId returns asset ID from the given output.
|
||||
func outputGetAssetID(ic *interop.Context, v *vm.VM) error {
|
||||
output, err := popOutputFromVM(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(output.AssetID.BytesBE())
|
||||
return nil
|
||||
}
|
||||
|
||||
// outputGetScriptHash returns scripthash from the given output.
|
||||
func outputGetScriptHash(ic *interop.Context, v *vm.VM) error {
|
||||
output, err := popOutputFromVM(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(output.ScriptHash.BytesBE())
|
||||
return nil
|
||||
}
|
||||
|
||||
// outputGetValue returns value (amount) from the given output.
|
||||
func outputGetValue(ic *interop.Context, v *vm.VM) error {
|
||||
output, err := popOutputFromVM(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(int64(output.Amount))
|
||||
return nil
|
||||
}
|
||||
|
||||
// attrGetData returns tx attribute data.
|
||||
func attrGetData(ic *interop.Context, v *vm.VM) error {
|
||||
attrInterface := v.Estack().Pop().Value()
|
||||
|
|
|
@ -25,8 +25,6 @@ import (
|
|||
)
|
||||
|
||||
/* Missing tests:
|
||||
* TestTxGetReferences
|
||||
* TestTxGetUnspentCoins
|
||||
* TestTxGetWitnesses
|
||||
* TestBcGetAccount
|
||||
* TestBcGetAsset
|
||||
|
@ -192,26 +190,6 @@ func TestTxGetAttributes(t *testing.T) {
|
|||
require.Equal(t, tx.Attributes[0].Usage, value[0].Value().(*transaction.Attribute).Usage)
|
||||
}
|
||||
|
||||
func TestTxGetInputs(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndPushTX(t)
|
||||
defer chain.Close()
|
||||
|
||||
err := txGetInputs(context, v)
|
||||
require.NoError(t, err)
|
||||
value := v.Estack().Pop().Value().([]vm.StackItem)
|
||||
require.Equal(t, tx.Inputs[0], *value[0].Value().(*transaction.Input))
|
||||
}
|
||||
|
||||
func TestTxGetOutputs(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndPushTX(t)
|
||||
defer chain.Close()
|
||||
|
||||
err := txGetOutputs(context, v)
|
||||
require.NoError(t, err)
|
||||
value := v.Estack().Pop().Value().([]vm.StackItem)
|
||||
require.Equal(t, tx.Outputs[0], *value[0].Value().(*transaction.Output))
|
||||
}
|
||||
|
||||
func TestWitnessGetVerificationScript(t *testing.T) {
|
||||
v := vm.New()
|
||||
script := []byte{byte(opcode.PUSHM1), byte(opcode.RET)}
|
||||
|
@ -304,81 +282,6 @@ func TestECDSAVerify(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestPopInputFromVM(t *testing.T) {
|
||||
v, tx, _, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
|
||||
|
||||
input, err := popInputFromVM(v)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tx.Inputs[0], *input)
|
||||
}
|
||||
|
||||
func TestInputGetHash(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
|
||||
|
||||
err := inputGetHash(context, v)
|
||||
require.NoError(t, err)
|
||||
hash := v.Estack().Pop().Value()
|
||||
require.Equal(t, tx.Inputs[0].PrevHash.BytesBE(), hash)
|
||||
}
|
||||
|
||||
func TestInputGetIndex(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
|
||||
|
||||
err := inputGetIndex(context, v)
|
||||
require.NoError(t, err)
|
||||
index := v.Estack().Pop().Value()
|
||||
require.Equal(t, big.NewInt(int64(tx.Inputs[0].PrevIndex)), index)
|
||||
}
|
||||
|
||||
func TestPopOutputFromVM(t *testing.T) {
|
||||
v, tx, _, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
||||
|
||||
output, err := popOutputFromVM(v)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tx.Outputs[0], *output)
|
||||
}
|
||||
|
||||
func TestOutputGetAssetID(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
||||
|
||||
err := outputGetAssetID(context, v)
|
||||
require.NoError(t, err)
|
||||
assetID := v.Estack().Pop().Value()
|
||||
require.Equal(t, tx.Outputs[0].AssetID.BytesBE(), assetID)
|
||||
}
|
||||
|
||||
func TestOutputGetScriptHash(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
||||
|
||||
err := outputGetScriptHash(context, v)
|
||||
require.NoError(t, err)
|
||||
scriptHash := v.Estack().Pop().Value()
|
||||
require.Equal(t, tx.Outputs[0].ScriptHash.BytesBE(), scriptHash)
|
||||
}
|
||||
|
||||
func TestOutputGetValue(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
||||
|
||||
err := outputGetValue(context, v)
|
||||
require.NoError(t, err)
|
||||
amount := v.Estack().Pop().Value()
|
||||
require.Equal(t, big.NewInt(int64(tx.Outputs[0].Amount)), amount)
|
||||
}
|
||||
|
||||
func TestAttrGetData(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
|
@ -604,21 +507,7 @@ func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Con
|
|||
Data: bytes,
|
||||
})
|
||||
|
||||
inputs := append(tx.Inputs, transaction.Input{
|
||||
PrevHash: random.Uint256(),
|
||||
PrevIndex: 1,
|
||||
})
|
||||
|
||||
outputs := append(tx.Outputs, transaction.Output{
|
||||
AssetID: random.Uint256(),
|
||||
Amount: 10,
|
||||
ScriptHash: random.Uint160(),
|
||||
Position: 1,
|
||||
})
|
||||
|
||||
tx.Attributes = attributes
|
||||
tx.Inputs = inputs
|
||||
tx.Outputs = outputs
|
||||
chain := newTestChain(t)
|
||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, tx)
|
||||
return v, tx, context, chain
|
||||
|
|
|
@ -148,17 +148,12 @@ var neoInterops = []interop.Function{
|
|||
{Name: "Neo.Header.GetPrevHash", Func: headerGetPrevHash, Price: 1},
|
||||
{Name: "Neo.Header.GetTimestamp", Func: headerGetTimestamp, Price: 1},
|
||||
{Name: "Neo.Header.GetVersion", Func: headerGetVersion, Price: 1},
|
||||
{Name: "Neo.Input.GetHash", Func: inputGetHash, Price: 1},
|
||||
{Name: "Neo.Input.GetIndex", Func: inputGetIndex, Price: 1},
|
||||
{Name: "Neo.Iterator.Concat", Func: iterator.Concat, Price: 1},
|
||||
{Name: "Neo.Iterator.Create", Func: iterator.Create, Price: 1},
|
||||
{Name: "Neo.Iterator.Key", Func: iterator.Key, Price: 1},
|
||||
{Name: "Neo.Iterator.Keys", Func: iterator.Keys, Price: 1},
|
||||
{Name: "Neo.Iterator.Values", Func: iterator.Values, Price: 1},
|
||||
{Name: "Neo.Native.Deploy", Func: native.Deploy, Price: 1},
|
||||
{Name: "Neo.Output.GetAssetId", Func: outputGetAssetID, Price: 1},
|
||||
{Name: "Neo.Output.GetScriptHash", Func: outputGetScriptHash, Price: 1},
|
||||
{Name: "Neo.Output.GetValue", Func: outputGetValue, Price: 1},
|
||||
{Name: "Neo.Runtime.CheckWitness", Func: runtime.CheckWitness, Price: 200},
|
||||
{Name: "Neo.Runtime.Deserialize", Func: runtimeDeserialize, Price: 1},
|
||||
{Name: "Neo.Runtime.GetTime", Func: runtimeGetTime, Price: 1},
|
||||
|
@ -175,10 +170,6 @@ var neoInterops = []interop.Function{
|
|||
{Name: "Neo.StorageContext.AsReadOnly", Func: storageContextAsReadOnly, Price: 1},
|
||||
{Name: "Neo.Transaction.GetAttributes", Func: txGetAttributes, Price: 1},
|
||||
{Name: "Neo.Transaction.GetHash", Func: txGetHash, Price: 1},
|
||||
{Name: "Neo.Transaction.GetInputs", Func: txGetInputs, Price: 1},
|
||||
{Name: "Neo.Transaction.GetOutputs", Func: txGetOutputs, Price: 1},
|
||||
{Name: "Neo.Transaction.GetReferences", Func: txGetReferences, Price: 200},
|
||||
{Name: "Neo.Transaction.GetUnspentCoins", Func: txGetUnspentCoins, Price: 200},
|
||||
{Name: "Neo.Transaction.GetWitnesses", Func: txGetWitnesses, Price: 200},
|
||||
{Name: "Neo.Witness.GetVerificationScript", Func: witnessGetVerificationScript, Price: 100},
|
||||
|
||||
|
@ -222,11 +213,6 @@ var neoInterops = []interop.Function{
|
|||
{Name: "AntShares.Header.GetPrevHash", Func: headerGetPrevHash, Price: 1},
|
||||
{Name: "AntShares.Header.GetTimestamp", Func: headerGetTimestamp, Price: 1},
|
||||
{Name: "AntShares.Header.GetVersion", Func: headerGetVersion, Price: 1},
|
||||
{Name: "AntShares.Input.GetHash", Func: inputGetHash, Price: 1},
|
||||
{Name: "AntShares.Input.GetIndex", Func: inputGetIndex, Price: 1},
|
||||
{Name: "AntShares.Output.GetAssetId", Func: outputGetAssetID, Price: 1},
|
||||
{Name: "AntShares.Output.GetScriptHash", Func: outputGetScriptHash, Price: 1},
|
||||
{Name: "AntShares.Output.GetValue", Func: outputGetValue, Price: 1},
|
||||
{Name: "AntShares.Runtime.CheckWitness", Func: runtime.CheckWitness, Price: 200},
|
||||
{Name: "AntShares.Runtime.Log", Func: runtimeLog, Price: 1},
|
||||
{Name: "AntShares.Runtime.Notify", Func: runtimeNotify, Price: 1},
|
||||
|
@ -236,9 +222,6 @@ var neoInterops = []interop.Function{
|
|||
{Name: "AntShares.Storage.Put", Func: storagePut, Price: 0},
|
||||
{Name: "AntShares.Transaction.GetAttributes", Func: txGetAttributes, Price: 1},
|
||||
{Name: "AntShares.Transaction.GetHash", Func: txGetHash, Price: 1},
|
||||
{Name: "AntShares.Transaction.GetInputs", Func: txGetInputs, Price: 1},
|
||||
{Name: "AntShares.Transaction.GetOutputs", Func: txGetOutputs, Price: 1},
|
||||
{Name: "AntShares.Transaction.GetReferences", Func: txGetReferences, Price: 200},
|
||||
}
|
||||
|
||||
// initIDinInteropsSlice initializes IDs from names in one given
|
||||
|
|
|
@ -58,11 +58,6 @@ func TestUnexpectedNonInterops(t *testing.T) {
|
|||
headerGetPrevHash,
|
||||
headerGetTimestamp,
|
||||
headerGetVersion,
|
||||
inputGetHash,
|
||||
inputGetIndex,
|
||||
outputGetAssetID,
|
||||
outputGetScriptHash,
|
||||
outputGetValue,
|
||||
storageContextAsReadOnly,
|
||||
storageDelete,
|
||||
storageFind,
|
||||
|
@ -71,10 +66,6 @@ func TestUnexpectedNonInterops(t *testing.T) {
|
|||
storagePutEx,
|
||||
txGetAttributes,
|
||||
txGetHash,
|
||||
txGetInputs,
|
||||
txGetOutputs,
|
||||
txGetReferences,
|
||||
txGetUnspentCoins,
|
||||
txGetWitnesses,
|
||||
witnessGetVerificationScript,
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ import (
|
|||
|
||||
var (
|
||||
// ErrConflict is returned when transaction being added is incompatible
|
||||
// with the contents of the memory pool (using the same inputs as some
|
||||
// other transaction in the pool)
|
||||
// with the contents of the memory pool (Sender doesn't have enough GAS
|
||||
// to pay for all transactions in the pool).
|
||||
ErrConflict = errors.New("conflicts with the memory pool")
|
||||
// ErrDup is returned when transaction being added is already present
|
||||
// in the memory pool.
|
||||
|
@ -51,7 +51,6 @@ type Pool struct {
|
|||
lock sync.RWMutex
|
||||
verifiedMap map[util.Uint256]*item
|
||||
verifiedTxes items
|
||||
inputs []*transaction.Input
|
||||
fees map[util.Uint160]utilityBalanceAndFees
|
||||
|
||||
capacity int
|
||||
|
@ -120,35 +119,6 @@ func (mp *Pool) containsKey(hash util.Uint256) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// findIndexForInput finds an index in a sorted Input pointers slice that is
|
||||
// appropriate to place this input into (or which contains an identical Input).
|
||||
func findIndexForInput(slice []*transaction.Input, input *transaction.Input) int {
|
||||
return sort.Search(len(slice), func(n int) bool {
|
||||
return input.Cmp(slice[n]) <= 0
|
||||
})
|
||||
}
|
||||
|
||||
// pushInputToSortedSlice pushes new Input into the given slice.
|
||||
func pushInputToSortedSlice(slice *[]*transaction.Input, input *transaction.Input) {
|
||||
n := findIndexForInput(*slice, input)
|
||||
*slice = append(*slice, input)
|
||||
if n != len(*slice)-1 {
|
||||
copy((*slice)[n+1:], (*slice)[n:])
|
||||
(*slice)[n] = input
|
||||
}
|
||||
}
|
||||
|
||||
// dropInputFromSortedSlice removes given input from the given slice.
|
||||
func dropInputFromSortedSlice(slice *[]*transaction.Input, input *transaction.Input) {
|
||||
n := findIndexForInput(*slice, input)
|
||||
if n == len(*slice) || *input != *(*slice)[n] {
|
||||
// Not present.
|
||||
return
|
||||
}
|
||||
copy((*slice)[n:], (*slice)[n+1:])
|
||||
*slice = (*slice)[:len(*slice)-1]
|
||||
}
|
||||
|
||||
// tryAddSendersFee tries to add system fee and network fee to the total sender`s fee in mempool
|
||||
// and returns false if sender has not enough GAS to pay
|
||||
func (mp *Pool) tryAddSendersFee(tx *transaction.Transaction, feer Feer) bool {
|
||||
|
@ -229,12 +199,6 @@ func (mp *Pool) Add(t *transaction.Transaction, fee Feer) error {
|
|||
}
|
||||
mp.addSendersFee(pItem.txn)
|
||||
|
||||
// For lots of inputs it might be easier to push them all and sort
|
||||
// afterwards, but that requires benchmarking.
|
||||
for i := range t.Inputs {
|
||||
pushInputToSortedSlice(&mp.inputs, &t.Inputs[i])
|
||||
}
|
||||
|
||||
updateMempoolMetrics(len(mp.verifiedTxes))
|
||||
mp.lock.Unlock()
|
||||
return nil
|
||||
|
@ -260,9 +224,6 @@ func (mp *Pool) Remove(hash util.Uint256) {
|
|||
senderFee := mp.fees[it.txn.Sender]
|
||||
senderFee.feeSum -= it.txn.SystemFee + it.txn.NetworkFee
|
||||
mp.fees[it.txn.Sender] = senderFee
|
||||
for i := range it.txn.Inputs {
|
||||
dropInputFromSortedSlice(&mp.inputs, &it.txn.Inputs[i])
|
||||
}
|
||||
}
|
||||
updateMempoolMetrics(len(mp.verifiedTxes))
|
||||
mp.lock.Unlock()
|
||||
|
@ -276,23 +237,15 @@ func (mp *Pool) RemoveStale(isOK func(*transaction.Transaction) bool, feer Feer)
|
|||
// We can reuse already allocated slice
|
||||
// because items are iterated one-by-one in increasing order.
|
||||
newVerifiedTxes := mp.verifiedTxes[:0]
|
||||
newInputs := mp.inputs[: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) {
|
||||
newVerifiedTxes = append(newVerifiedTxes, itm)
|
||||
for i := range itm.txn.Inputs {
|
||||
newInputs = append(newInputs, &itm.txn.Inputs[i])
|
||||
}
|
||||
} else {
|
||||
delete(mp.verifiedMap, itm.txn.Hash())
|
||||
}
|
||||
}
|
||||
sort.Slice(newInputs, func(i, j int) bool {
|
||||
return newInputs[i].Cmp(newInputs[j]) < 0
|
||||
})
|
||||
mp.verifiedTxes = newVerifiedTxes
|
||||
mp.inputs = newInputs
|
||||
mp.lock.Unlock()
|
||||
}
|
||||
|
||||
|
@ -317,8 +270,7 @@ func (mp *Pool) TryGetValue(hash util.Uint256) (*transaction.Transaction, util.F
|
|||
return nil, 0, false
|
||||
}
|
||||
|
||||
// GetVerifiedTransactions returns a slice of Input from all the transactions in the memory pool
|
||||
// whose hash is not included in excludedHashes.
|
||||
// GetVerifiedTransactions returns a slice of transactions with their fees.
|
||||
func (mp *Pool) GetVerifiedTransactions() []TxWithFee {
|
||||
mp.lock.RLock()
|
||||
defer mp.lock.RUnlock()
|
||||
|
@ -333,32 +285,15 @@ func (mp *Pool) GetVerifiedTransactions() []TxWithFee {
|
|||
return t
|
||||
}
|
||||
|
||||
// areInputsInPool tries to find inputs in a given sorted pool and returns true
|
||||
// if it finds any.
|
||||
func areInputsInPool(inputs []transaction.Input, pool []*transaction.Input) bool {
|
||||
for i := range inputs {
|
||||
n := findIndexForInput(pool, &inputs[i])
|
||||
if n < len(pool) && *pool[n] == inputs[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// checkTxConflicts is an internal unprotected version of Verify.
|
||||
func (mp *Pool) checkTxConflicts(tx *transaction.Transaction, fee Feer) bool {
|
||||
if areInputsInPool(tx.Inputs, mp.inputs) {
|
||||
return false
|
||||
}
|
||||
if !mp.checkBalanceAndUpdate(tx, fee) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return mp.checkBalanceAndUpdate(tx, fee)
|
||||
}
|
||||
|
||||
// Verify verifies if the inputs of a transaction tx are already used in any other transaction in the memory pool.
|
||||
// If yes, the transaction tx is not a valid transaction and the function return false.
|
||||
// If no, the transaction tx is a valid transaction and the function return true.
|
||||
// Verify checks if a Sender of tx is able to pay for it (and all the other
|
||||
// transactions in the pool). If yes, the transaction tx is a valid
|
||||
// transaction and the function returns true. If no, the transaction tx is
|
||||
// considered to be invalid the function returns false.
|
||||
func (mp *Pool) Verify(tx *transaction.Transaction, feer Feer) bool {
|
||||
mp.lock.RLock()
|
||||
defer mp.lock.RUnlock()
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -56,79 +55,6 @@ func TestMemPoolAddRemove(t *testing.T) {
|
|||
t.Run("high priority", func(t *testing.T) { testMemPoolAddRemoveWithFeer(t, fs) })
|
||||
}
|
||||
|
||||
func TestMemPoolAddRemoveWithInputs(t *testing.T) {
|
||||
mp := NewMemPool(50)
|
||||
hash1, err := util.Uint256DecodeStringBE("a83ba6ede918a501558d3170a124324aedc89909e64c4ff2c6f863094f980b25")
|
||||
require.NoError(t, err)
|
||||
hash2, err := util.Uint256DecodeStringBE("629397158f852e838077bb2715b13a2e29b0a51c2157e5466321b70ed7904ce9")
|
||||
require.NoError(t, err)
|
||||
mpLessInputs := func(i, j int) bool {
|
||||
return mp.inputs[i].Cmp(mp.inputs[j]) < 0
|
||||
}
|
||||
txm1 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
txm1.Nonce = 1
|
||||
for i := 0; i < 5; i++ {
|
||||
txm1.Inputs = append(txm1.Inputs, transaction.Input{PrevHash: hash1, PrevIndex: uint16(100 - i)})
|
||||
}
|
||||
require.NoError(t, mp.Add(txm1, &FeerStub{}))
|
||||
// Look inside.
|
||||
assert.Equal(t, len(txm1.Inputs), len(mp.inputs))
|
||||
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
|
||||
|
||||
txm2 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
txm2.Nonce = 1
|
||||
for i := 0; i < 10; i++ {
|
||||
txm2.Inputs = append(txm2.Inputs, transaction.Input{PrevHash: hash2, PrevIndex: uint16(i)})
|
||||
}
|
||||
require.NoError(t, mp.Add(txm2, &FeerStub{}))
|
||||
assert.Equal(t, len(txm1.Inputs)+len(txm2.Inputs), len(mp.inputs))
|
||||
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
|
||||
|
||||
mp.Remove(txm1.Hash())
|
||||
assert.Equal(t, len(txm2.Inputs), len(mp.inputs))
|
||||
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
|
||||
|
||||
require.NoError(t, mp.Add(txm1, &FeerStub{}))
|
||||
assert.Equal(t, len(txm1.Inputs)+len(txm2.Inputs), len(mp.inputs))
|
||||
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
|
||||
|
||||
mp.RemoveStale(func(t *transaction.Transaction) bool {
|
||||
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))
|
||||
}
|
||||
|
||||
func TestMemPoolVerifyInputs(t *testing.T) {
|
||||
mp := NewMemPool(10)
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.Nonce = 1
|
||||
inhash1 := random.Uint256()
|
||||
tx.Inputs = append(tx.Inputs, transaction.Input{PrevHash: inhash1, PrevIndex: 0})
|
||||
require.Equal(t, true, mp.Verify(tx, &FeerStub{}))
|
||||
require.NoError(t, mp.Add(tx, &FeerStub{}))
|
||||
|
||||
tx2 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx2.Nonce = 2
|
||||
inhash2 := random.Uint256()
|
||||
tx2.Inputs = append(tx2.Inputs, transaction.Input{PrevHash: inhash2, PrevIndex: 0})
|
||||
require.Equal(t, true, mp.Verify(tx2, &FeerStub{}))
|
||||
require.NoError(t, mp.Add(tx2, &FeerStub{}))
|
||||
|
||||
tx3 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx3.Nonce = 3
|
||||
// Different index number, but the same PrevHash as in tx1.
|
||||
tx3.Inputs = append(tx3.Inputs, transaction.Input{PrevHash: inhash1, PrevIndex: 1})
|
||||
require.Equal(t, true, mp.Verify(tx3, &FeerStub{}))
|
||||
// The same input as in tx2.
|
||||
tx3.Inputs = append(tx3.Inputs, transaction.Input{PrevHash: inhash2, PrevIndex: 0})
|
||||
require.Equal(t, false, mp.Verify(tx3, &FeerStub{}))
|
||||
require.Error(t, mp.Add(tx3, &FeerStub{}))
|
||||
}
|
||||
|
||||
func TestOverCapacity(t *testing.T) {
|
||||
var fs = &FeerStub{lowPriority: true}
|
||||
const mempoolSize = 10
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
package core
|
||||
|
||||
import "github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
|
||||
// spentCoin represents the state of a single spent coin output.
|
||||
type spentCoin struct {
|
||||
Output *transaction.Output
|
||||
StartHeight uint32
|
||||
EndHeight uint32
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
)
|
||||
|
||||
// UnspentCoin hold the state of a unspent coin.
|
||||
type UnspentCoin struct {
|
||||
Height uint32
|
||||
States []OutputState
|
||||
}
|
||||
|
||||
// OutputState combines transaction output (UTXO) and its state
|
||||
// (spent/claimed...) along with the height of spend (if it's spent).
|
||||
type OutputState struct {
|
||||
transaction.Output
|
||||
|
||||
SpendHeight uint32
|
||||
State Coin
|
||||
}
|
||||
|
||||
// NewUnspentCoin returns a new unspent coin state with N confirmed states.
|
||||
func NewUnspentCoin(height uint32, tx *transaction.Transaction) *UnspentCoin {
|
||||
u := &UnspentCoin{
|
||||
Height: height,
|
||||
States: make([]OutputState, len(tx.Outputs)),
|
||||
}
|
||||
for i := range tx.Outputs {
|
||||
u.States[i] = OutputState{Output: tx.Outputs[i]}
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// EncodeBinary encodes UnspentCoin to the given BinWriter.
|
||||
func (s *UnspentCoin) EncodeBinary(bw *io.BinWriter) {
|
||||
bw.WriteU32LE(s.Height)
|
||||
bw.WriteArray(s.States)
|
||||
bw.WriteVarUint(uint64(len(s.States)))
|
||||
}
|
||||
|
||||
// DecodeBinary decodes UnspentCoin from the given BinReader.
|
||||
func (s *UnspentCoin) DecodeBinary(br *io.BinReader) {
|
||||
s.Height = br.ReadU32LE()
|
||||
br.ReadArray(&s.States)
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (o *OutputState) EncodeBinary(w *io.BinWriter) {
|
||||
o.Output.EncodeBinary(w)
|
||||
w.WriteU32LE(o.SpendHeight)
|
||||
w.WriteB(byte(o.State))
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (o *OutputState) DecodeBinary(r *io.BinReader) {
|
||||
o.Output.DecodeBinary(r)
|
||||
o.SpendHeight = r.ReadU32LE()
|
||||
o.State = Coin(r.ReadB())
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"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"
|
||||
)
|
||||
|
||||
func TestDecodeEncodeUnspentCoin(t *testing.T) {
|
||||
unspent := &UnspentCoin{
|
||||
Height: 100500,
|
||||
States: []OutputState{
|
||||
{
|
||||
Output: transaction.Output{
|
||||
AssetID: random.Uint256(),
|
||||
Amount: util.Fixed8(42),
|
||||
ScriptHash: random.Uint160(),
|
||||
},
|
||||
SpendHeight: 201000,
|
||||
State: CoinSpent,
|
||||
},
|
||||
{
|
||||
Output: transaction.Output{
|
||||
AssetID: random.Uint256(),
|
||||
Amount: util.Fixed8(420),
|
||||
ScriptHash: random.Uint160(),
|
||||
},
|
||||
SpendHeight: 0,
|
||||
State: CoinConfirmed,
|
||||
},
|
||||
{
|
||||
Output: transaction.Output{
|
||||
AssetID: random.Uint256(),
|
||||
Amount: util.Fixed8(4200),
|
||||
ScriptHash: random.Uint160(),
|
||||
},
|
||||
SpendHeight: 111000,
|
||||
State: CoinSpent & CoinClaimed,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testserdes.EncodeDecodeBinary(t, unspent, new(UnspentCoin))
|
||||
}
|
|
@ -10,8 +10,6 @@ const (
|
|||
DataBlock KeyPrefix = 0x01
|
||||
DataTransaction KeyPrefix = 0x02
|
||||
STAccount KeyPrefix = 0x40
|
||||
STCoin KeyPrefix = 0x44
|
||||
STSpentCoin KeyPrefix = 0x45
|
||||
STValidator KeyPrefix = 0x48
|
||||
STAsset KeyPrefix = 0x4c
|
||||
STNotification KeyPrefix = 0x4d
|
||||
|
|
|
@ -11,7 +11,6 @@ var (
|
|||
DataBlock,
|
||||
DataTransaction,
|
||||
STAccount,
|
||||
STCoin,
|
||||
STValidator,
|
||||
STAsset,
|
||||
STContract,
|
||||
|
@ -26,7 +25,6 @@ var (
|
|||
0x01,
|
||||
0x02,
|
||||
0x40,
|
||||
0x44,
|
||||
0x48,
|
||||
0x4c,
|
||||
0x50,
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
package transaction
|
||||
|
||||
// InOut represents an Input bound to its corresponding Output which is a useful
|
||||
// combination for many purposes.
|
||||
type InOut struct {
|
||||
In Input
|
||||
Out Output
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Input represents a Transaction input (CoinReference).
|
||||
type Input struct {
|
||||
// The hash of the previous transaction.
|
||||
PrevHash util.Uint256 `json:"txid"`
|
||||
|
||||
// The index of the previous transaction.
|
||||
PrevIndex uint16 `json:"vout"`
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (in *Input) DecodeBinary(br *io.BinReader) {
|
||||
br.ReadBytes(in.PrevHash[:])
|
||||
in.PrevIndex = br.ReadU16LE()
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (in *Input) EncodeBinary(bw *io.BinWriter) {
|
||||
bw.WriteBytes(in.PrevHash[:])
|
||||
bw.WriteU16LE(in.PrevIndex)
|
||||
}
|
||||
|
||||
// Cmp compares two Inputs by their hash and index allowing to make a set of
|
||||
// transactions ordered.
|
||||
func (in *Input) Cmp(other *Input) int {
|
||||
hashcmp := in.PrevHash.CompareTo(other.PrevHash)
|
||||
if hashcmp == 0 {
|
||||
return int(in.PrevIndex) - int(other.PrevIndex)
|
||||
}
|
||||
return hashcmp
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return ptrs[i].Cmp(ptrs[j]) < 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.
|
||||
func GroupInputsByPrevHash(ins []Input) [][]*Input {
|
||||
if len(ins) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ptrs := MapInputsToSorted(ins)
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/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
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Output represents a Transaction output.
|
||||
type Output struct {
|
||||
// The NEO asset id used in the transaction.
|
||||
AssetID util.Uint256 `json:"asset"`
|
||||
|
||||
// Amount of AssetType send or received.
|
||||
Amount util.Fixed8 `json:"value"`
|
||||
|
||||
// The address of the recipient.
|
||||
ScriptHash util.Uint160 `json:"address"`
|
||||
|
||||
// The position of the Output in slice []Output. This is actually set in NewTransactionOutputRaw
|
||||
// and used for displaying purposes.
|
||||
Position int `json:"n"`
|
||||
}
|
||||
|
||||
type outputAux struct {
|
||||
AssetID util.Uint256 `json:"asset"`
|
||||
Amount util.Fixed8 `json:"value"`
|
||||
ScriptHash string `json:"address"`
|
||||
Position int `json:"n"`
|
||||
}
|
||||
|
||||
// NewOutput returns a new transaction output.
|
||||
func NewOutput(assetID util.Uint256, amount util.Fixed8, scriptHash util.Uint160) *Output {
|
||||
return &Output{
|
||||
AssetID: assetID,
|
||||
Amount: amount,
|
||||
ScriptHash: scriptHash,
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (out *Output) DecodeBinary(br *io.BinReader) {
|
||||
br.ReadBytes(out.AssetID[:])
|
||||
out.Amount.DecodeBinary(br)
|
||||
br.ReadBytes(out.ScriptHash[:])
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (out *Output) EncodeBinary(bw *io.BinWriter) {
|
||||
bw.WriteBytes(out.AssetID[:])
|
||||
out.Amount.EncodeBinary(bw)
|
||||
bw.WriteBytes(out.ScriptHash[:])
|
||||
}
|
||||
|
||||
// MarshalJSON implements the Marshaler interface.
|
||||
func (out *Output) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]interface{}{
|
||||
"asset": out.AssetID,
|
||||
"value": out.Amount,
|
||||
"address": address.Uint160ToString(out.ScriptHash),
|
||||
"n": out.Position,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||
func (out *Output) UnmarshalJSON(data []byte) error {
|
||||
var outAux outputAux
|
||||
err := json.Unmarshal(data, &outAux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.ScriptHash, err = address.StringToUint160(outAux.ScriptHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Amount = outAux.Amount
|
||||
out.AssetID = outAux.AssetID
|
||||
out.Position = outAux.Position
|
||||
return nil
|
||||
}
|
|
@ -54,12 +54,6 @@ type Transaction struct {
|
|||
// Transaction cosigners (not include Sender).
|
||||
Cosigners []Cosigner
|
||||
|
||||
// The inputs of the transaction.
|
||||
Inputs []Input
|
||||
|
||||
// The outputs of the transaction.
|
||||
Outputs []Output
|
||||
|
||||
// The scripts that comes with this transaction.
|
||||
// Scripts exist out of the verification script
|
||||
// and invocation script.
|
||||
|
@ -95,8 +89,6 @@ func New(script []byte, gas util.Fixed8) *Transaction {
|
|||
SystemFee: gas,
|
||||
Attributes: []Attribute{},
|
||||
Cosigners: []Cosigner{},
|
||||
Inputs: []Input{},
|
||||
Outputs: []Output{},
|
||||
Scripts: []Witness{},
|
||||
}
|
||||
}
|
||||
|
@ -121,16 +113,6 @@ func (t *Transaction) VerificationHash() util.Uint256 {
|
|||
return t.verificationHash
|
||||
}
|
||||
|
||||
// AddOutput adds the given output to the transaction outputs.
|
||||
func (t *Transaction) AddOutput(out *Output) {
|
||||
t.Outputs = append(t.Outputs, *out)
|
||||
}
|
||||
|
||||
// AddInput adds the given input to the transaction inputs.
|
||||
func (t *Transaction) AddInput(in *Input) {
|
||||
t.Inputs = append(t.Inputs, *in)
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (t *Transaction) DecodeBinary(br *io.BinReader) {
|
||||
t.Version = uint8(br.ReadB())
|
||||
|
@ -174,14 +156,6 @@ func (t *Transaction) DecodeBinary(br *io.BinReader) {
|
|||
return
|
||||
}
|
||||
|
||||
br.ReadArray(&t.Inputs)
|
||||
br.ReadArray(&t.Outputs)
|
||||
for i := range t.Outputs {
|
||||
if t.Outputs[i].Amount.LessThan(0) {
|
||||
br.Err = errors.New("negative output")
|
||||
return
|
||||
}
|
||||
}
|
||||
br.ReadArray(&t.Scripts)
|
||||
|
||||
// Create the hash of the transaction at decode, so we dont need
|
||||
|
@ -218,11 +192,6 @@ func (t *Transaction) encodeHashableFields(bw *io.BinWriter) {
|
|||
bw.WriteArray(t.Cosigners)
|
||||
|
||||
bw.WriteVarBytes(t.Script)
|
||||
// Inputs
|
||||
bw.WriteArray(t.Inputs)
|
||||
|
||||
// Outputs
|
||||
bw.WriteArray(t.Outputs)
|
||||
}
|
||||
|
||||
// createHash creates the hash of the transaction.
|
||||
|
@ -240,16 +209,6 @@ func (t *Transaction) createHash() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GroupOutputByAssetID groups all TX outputs by their assetID.
|
||||
func (t Transaction) GroupOutputByAssetID() map[util.Uint256][]*Output {
|
||||
m := make(map[util.Uint256][]*Output)
|
||||
for i := range t.Outputs {
|
||||
hash := t.Outputs[i].AssetID
|
||||
m[hash] = append(m[hash], &t.Outputs[i])
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// GetSignedPart returns a part of the transaction which must be signed.
|
||||
func (t *Transaction) GetSignedPart() []byte {
|
||||
buf := io.NewBufBinWriter()
|
||||
|
@ -301,8 +260,6 @@ type transactionJSON struct {
|
|||
Attributes []Attribute `json:"attributes"`
|
||||
Cosigners []Cosigner `json:"cosigners"`
|
||||
Script string `json:"script"`
|
||||
Inputs []Input `json:"vin"`
|
||||
Outputs []Output `json:"vout"`
|
||||
Scripts []Witness `json:"scripts"`
|
||||
}
|
||||
|
||||
|
@ -318,8 +275,6 @@ func (t *Transaction) MarshalJSON() ([]byte, error) {
|
|||
Attributes: t.Attributes,
|
||||
Cosigners: t.Cosigners,
|
||||
Script: hex.EncodeToString(t.Script),
|
||||
Inputs: t.Inputs,
|
||||
Outputs: t.Outputs,
|
||||
Scripts: t.Scripts,
|
||||
SystemFee: t.SystemFee,
|
||||
NetworkFee: t.NetworkFee,
|
||||
|
@ -338,8 +293,6 @@ func (t *Transaction) UnmarshalJSON(data []byte) error {
|
|||
t.ValidUntilBlock = tx.ValidUntilBlock
|
||||
t.Attributes = tx.Attributes
|
||||
t.Cosigners = tx.Cosigners
|
||||
t.Inputs = tx.Inputs
|
||||
t.Outputs = tx.Outputs
|
||||
t.Scripts = tx.Scripts
|
||||
t.SystemFee = tx.SystemFee
|
||||
t.NetworkFee = tx.NetworkFee
|
||||
|
|
|
@ -88,18 +88,8 @@ func TestMarshalUnmarshalJSONInvocationTX(t *testing.T) {
|
|||
Version: 0,
|
||||
Script: []byte{1, 2, 3, 4},
|
||||
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,
|
||||
Scripts: []Witness{},
|
||||
Trimmed: false,
|
||||
}
|
||||
|
||||
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||
|
|
|
@ -20,7 +20,7 @@ func TestGenesisBlockMainNet(t *testing.T) {
|
|||
// have been changed. Consequently, hash of the genesis block has been changed.
|
||||
// Update expected genesis block hash for better times.
|
||||
// Old hash is "d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf"
|
||||
expect := "2535d4b8b244c8a036ed356ee4448e041dc6e8f1d81abdcc7e3d0640c66f6cc8"
|
||||
expect := "e4cfc549c87d4ab7b570c368d05853ffb70eb9ef0f7d9c7a2e6e9e5d713ebbf4"
|
||||
assert.Equal(t, expect, block.Hash().StringLE())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
Package input provides functions dealing with transaction inputs.
|
||||
*/
|
||||
package input
|
||||
|
||||
// Input is an opaque data structure that can only be created by
|
||||
// transaction.GetInputs and it represents transaction's input. It's similar
|
||||
// to Neo .net framework's TransactionInput.
|
||||
type Input struct{}
|
||||
|
||||
// GetHash returns the hash stored in the given input (which also is a
|
||||
// transaction ID represented as 32 byte slice containing 256 bit BE value).
|
||||
// It uses `Neo.Input.GetHash` syscall.
|
||||
func GetHash(in Input) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetIndex returns the index stored in the given input (which is a
|
||||
// transaction's output number). It uses `Neo.Input.GetIndex` syscall.
|
||||
func GetIndex(in Input) int {
|
||||
return 0
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
Package output provides functions dealing with transaction outputs.
|
||||
*/
|
||||
package output
|
||||
|
||||
// Output is an opaque data structure that can only be created by
|
||||
// transaction.GetOutputs and it represents transaction's output. It's similar
|
||||
// to Neo .net framework's TransactionOutput.
|
||||
type Output struct{}
|
||||
|
||||
// GetAssetID returns the asset ID (256 bit BE value in a 32 byte slice) of the
|
||||
// given output. It uses `Neo.Output.GetAssetId` syscall.
|
||||
func GetAssetID(out Output) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetValue returns the value (asset quantity) of the given output. It uses
|
||||
// `Neo.Output.GetValue` syscall.
|
||||
func GetValue(out Output) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetScriptHash returns the script hash (receiver's address represented as
|
||||
// 20 byte slice containing 160 bit BE value) of the given output. It uses
|
||||
// `Neo.Output.GetScriptHash` syscall.
|
||||
func GetScriptHash(out Output) []byte {
|
||||
return nil
|
||||
}
|
|
@ -5,8 +5,6 @@ package transaction
|
|||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/attribute"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/input"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/output"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/witness"
|
||||
)
|
||||
|
||||
|
@ -28,35 +26,6 @@ func GetAttributes(t Transaction) []attribute.Attribute {
|
|||
return []attribute.Attribute{}
|
||||
}
|
||||
|
||||
// GetReferences returns a slice of references for a given Transaction. Elements
|
||||
// of this slice can be casted to any of input.Input or output.Output, depending
|
||||
// on which information you're interested in (as reference technically contains
|
||||
// both input and corresponding output), refer to input and output package on
|
||||
// how to use them. This function uses `Neo.Transaction.GetReferences` syscall.
|
||||
func GetReferences(t Transaction) []interface{} {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
// GetUnspentCoins returns a slice of not yet spent ouputs of a given transaction.
|
||||
// This function uses `Neo.Transaction.GetUnspentCoint` syscall.
|
||||
func GetUnspentCoins(t Transaction) []output.Output {
|
||||
return []output.Output{}
|
||||
}
|
||||
|
||||
// GetInputs returns a slice of inputs of a given Transaction. Refer to input
|
||||
// package on how to use them. This function uses `Neo.Transaction.GetInputs`
|
||||
// syscall.
|
||||
func GetInputs(t Transaction) []input.Input {
|
||||
return []input.Input{}
|
||||
}
|
||||
|
||||
// GetOutputs returns a slice of outputs of a given Transaction. Refer to output
|
||||
// package on how to use them. This function uses `Neo.Transaction.GetOutputs`
|
||||
// syscall.
|
||||
func GetOutputs(t Transaction) []output.Output {
|
||||
return []output.Output{}
|
||||
}
|
||||
|
||||
// GetWitnesses returns a slice of witnesses of a given Transaction. Refer to
|
||||
// witness package on how to use them. This function uses
|
||||
// `Neo.Transaction.GetWitnesses` syscall.
|
||||
|
|
|
@ -35,10 +35,6 @@ func (chain testChain) CalculateClaimable(int64, uint32, uint32) util.Fixed8 {
|
|||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) References(t *transaction.Transaction) ([]transaction.InOut, error) {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) FeePerByte() util.Fixed8 {
|
||||
panic("TODO")
|
||||
}
|
||||
|
@ -126,10 +122,6 @@ func (chain testChain) GetTransaction(util.Uint256) (*transaction.Transaction, u
|
|||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) GetUnspentCoinState(util.Uint256) *state.UnspentCoin {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) GetMemPool() *mempool.Pool {
|
||||
panic("TODO")
|
||||
}
|
||||
|
|
|
@ -8,16 +8,12 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -197,34 +193,3 @@ func (c *Client) Ping() error {
|
|||
_ = conn.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// unspentsToInputs uses UnspentBalances to create a slice of inputs for a new
|
||||
// transcation containing the required amount of asset.
|
||||
func unspentsToInputs(utxos state.UnspentBalances, required util.Fixed8) ([]transaction.Input, util.Fixed8, error) {
|
||||
var (
|
||||
num, i uint16
|
||||
selected = util.Fixed8(0)
|
||||
)
|
||||
sort.Sort(utxos)
|
||||
|
||||
for _, us := range utxos {
|
||||
if selected >= required {
|
||||
break
|
||||
}
|
||||
selected += us.Value
|
||||
num++
|
||||
}
|
||||
if selected < required {
|
||||
return nil, util.Fixed8(0), errors.New("cannot compose inputs for transaction; check sender balance")
|
||||
}
|
||||
|
||||
inputs := make([]transaction.Input, 0, num)
|
||||
for i = 0; i < num; i++ {
|
||||
inputs = append(inputs, transaction.Input{
|
||||
PrevHash: utxos[i].Tx,
|
||||
PrevIndex: utxos[i].Index,
|
||||
})
|
||||
}
|
||||
|
||||
return inputs, selected, nil
|
||||
}
|
||||
|
|
|
@ -31,17 +31,17 @@ type rpcClientTestCase struct {
|
|||
check func(t *testing.T, c *Client, result interface{})
|
||||
}
|
||||
|
||||
const hexB1 = "000000001409900d6f330df2624b9ad49e4e68c04bab6757b684b4ae46e995ec9278002d6aa5da3b3922db2ae0fb51e6058d123d441650db8a187e7b7a34c55927fae217b91274847201000001000000abec5362f11e75b6e02e407bb98d63675d14384101fd08010c40bac8992a77bd21ba670f2bf5097387a42b227878b361c69bfeeeb55c4b68bdff7db5c93b8f6d84876e473bba84bf27dc7de8d35b8179e120a60a6bc01562ac740c401c876fdb2fbffc599ac9ffbc85f7e6087855fd40e12b23888718e381c93de2614cce5bdfb1108ff72a73b85d4a6556dad6578a59bfa0e61b93c775cb1c1924f20c40fd992b9d776da1d2da390915e743f4213ce8a6db862b6354cccbcb3bf8b4edd14562cb546cbbcb3e2d096c9e527d4ee7879c5bbfd01d9a2bb00a1986820e1c320c40fcf3d76b809078a872970bd3bde5836589a7f62660098bbfb82a1cb470b75cc480d527a3829de89c897bacc6c29dfc3e4b84368aa1f69c9bce704fb3399fc04e94130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb030057040000000000000002000000abec5362f11e75b6e02e407bb98d63675d14384100000000000000000e670d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d14384101590218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b5238000001fd08010c4015b24aa11983bbf098384be4ebf5c283c3b4b2d2f6bf17b1a2a32f59627c7eb6370748b231acce2eb1ab5db3cdbf6943084634d12f69bf7b0aa447f96ebaf7d40c40a6777566463018e3f82fbbd370eef65b8454e50978401d1d97df05d9eda3b2bd5f5f882a94f8adbeaa2a55f64bd4ec23398fca26e8b364e5f57c771d71b3ec3d0c4032f5b1e3af669f4f14c2579de8f7ce46e30b35f7ed70892389251d50779477d2506b330dc9dd47f8c406218656d60b669479433bd9f762888091dc94415de5690c404b6ca943c34be335466c25ed442d17abc24d6a78db407a8c2a2c43fb911f0cbdbb28ec791b5f9de01b2db26105aa35193113a3a382c1ed02fbaf82b42468853a94130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb0003000000abec5362f11e75b6e02e407bb98d63675d1438410000000000000000ae760d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d143841015d0300e87648170000000c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5238000001fd08010c4001c81faa8317a390c211d5e03451ed852ff02ec6c401c450558bc90c60a01912c3e1018de98e7458f6f96c68aad71ffb0e92327fcdfa4aabb5ea5834ec7afbf10c40a84e9bc36ffbf18a1ee79254182d2939dd3a7b9278e13893856f187ab44dec259c8a15d4f449a21aa6970234d79d81fee0aca1807341b977ff66d503a78c82920c4085495bb8d86b95a4afc1659ce1d993e1a7843e3dcb19696ce0422294dadddc73438780d5bc629560f576e0e75cff062c03e237d4e609dfaa9869135e23a487650c40ae25f2c563b978f931c3a8b5a18854d9bb77abf48c7554f2008f97696caca180cd40d4d0e47b3f4d86082e631547d1a4729c7ed60dda64fab9cd9a2270dd113294130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"
|
||||
const hexB1 = "00000000e862e7907fc987cd58ddb3abb754aeb8812c9377c45e737a036fe88a622c3b8f301f2e84a86b207270830e7929530ccb841a3df7379fe6f0ac8865b33316839501cdd0847201000001000000abec5362f11e75b6e02e407bb98d63675d14384101fd08010c40ab634ce91590e77b246cee8b204e8a270268ee1ef32434cece73f425a7dbc90f1bed1dbe914edcaa2653167ad170ae10e16a9b2c6b7e0af1f711fb848fbb1b7f0c40232de6ad07ee3846bafa96302d37602349501a556df575e7df0743e45b076d6a0c6c6dd4cad3898f9e8848dd054abd303b229fd12984042f241f0e668f39a0fb0c408b4af43057df189a9d471010b5150bab442040403147c5e502bda38cde3ff8bce803f01245e07e2bfb95d57349c55dcc27e3710b82f2735d0f40eb4342908e330c40cda66f743d4ed8d856f5376953f9169581c668a9370245aef16202ebef9bb3f7f81234be62ec287d701ad7d8bf5042648019af9fe5baa0a8e05d279bfdb1d4c994130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb030057040000000000000002000000abec5362f11e75b6e02e407bb98d63675d14384100000000000000003e5f0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d14384101590218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801fd08010c402d96d8fde4ba266f89bc71ef117361967e0d11ed84cd60942a27bc99994dc58adf36a0e74ce976aac657a87a3c19c38e8ca450c67420046b81d98c60fd8feb040c40b3c15d5d23e0403a36cf559caee2979ca6ef00fe255df0e5c3daac4da051016b41eba42668934cd3308359451bafdd5419d059179fd40859684a3b91388bf9d80c407ac048cf8540b091955a374a0f36dae560c92c0134886507a589edf58b9dfbb4e3dbd5450be34e269d2e5454eb14eb7d6280d6101b4529410f829d37634849be0c403bba4113a687ff8507c1753f8519557531cf9df51ecc20deeb2c2b003ec5a1f7588cdd50b99e40b4f8039bb56c5df7ec9e7d6ea4b02fe23792510da21c7557f394130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb0003000000abec5362f11e75b6e02e407bb98d63675d1438410000000000000000de6e0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d143841015d0300e87648170000000c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b523801fd08010c4063fb12aa9f3fb83f6324ea9c7ec11fa9e995b51140f480409d68cf4d625e598b0632d6610602984bfa2f5e5ea9bcc62a0e6d818dd271b38530c0d1b8a71b4e0c0c4013e091eac6f304668d647c5c032fd1020597ea5204545e21c38655a6343d58492118f1231ede91af848af7e1d987d1a8816966f5fc1a7821c6c6f62734267bde0c40daadd04a7a4141d96c58de2d373e672ca071e2b82138ef52df016ac522710385db2ac73743d2fe73061fa5d6cb0ff73a7ec7f0667e4c8bff6aa0d5783128d36e0c40dab85cd87d3f92be9532292bdc6f420b0ecbf2f877c70c6a9921ee0fc900dfc53998cf020a51fa9af3d0608f6a2b9048cea3c0b586485802bbd278b261eee8a494130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"
|
||||
|
||||
const hexTxMoveNeo = "0002000000abec5362f11e75b6e02e407bb98d63675d14384100000000000000000e670d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d14384101590218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b5238000001fd08010c4015b24aa11983bbf098384be4ebf5c283c3b4b2d2f6bf17b1a2a32f59627c7eb6370748b231acce2eb1ab5db3cdbf6943084634d12f69bf7b0aa447f96ebaf7d40c40a6777566463018e3f82fbbd370eef65b8454e50978401d1d97df05d9eda3b2bd5f5f882a94f8adbeaa2a55f64bd4ec23398fca26e8b364e5f57c771d71b3ec3d0c4032f5b1e3af669f4f14c2579de8f7ce46e30b35f7ed70892389251d50779477d2506b330dc9dd47f8c406218656d60b669479433bd9f762888091dc94415de5690c404b6ca943c34be335466c25ed442d17abc24d6a78db407a8c2a2c43fb911f0cbdbb28ec791b5f9de01b2db26105aa35193113a3a382c1ed02fbaf82b42468853a94130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"
|
||||
const hexTxMoveNeo = "0002000000abec5362f11e75b6e02e407bb98d63675d14384100000000000000003e5f0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d14384101590218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801fd08010c402d96d8fde4ba266f89bc71ef117361967e0d11ed84cd60942a27bc99994dc58adf36a0e74ce976aac657a87a3c19c38e8ca450c67420046b81d98c60fd8feb040c40b3c15d5d23e0403a36cf559caee2979ca6ef00fe255df0e5c3daac4da051016b41eba42668934cd3308359451bafdd5419d059179fd40859684a3b91388bf9d80c407ac048cf8540b091955a374a0f36dae560c92c0134886507a589edf58b9dfbb4e3dbd5450be34e269d2e5454eb14eb7d6280d6101b4529410f829d37634849be0c403bba4113a687ff8507c1753f8519557531cf9df51ecc20deeb2c2b003ec5a1f7588cdd50b99e40b4f8039bb56c5df7ec9e7d6ea4b02fe23792510da21c7557f394130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"
|
||||
|
||||
const b1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"size":1685,"nextblockhash":"0x14a61952de5bf47e2550bb97439de44ea60f79e052419e8b263b1b41d7065b0f","confirmations":4,"hash":"0xc826db8aa7a05fbd336eb0fca66efea7330cdc09cc8cf8e3cd9a3d9a87b751ac","version":0,"previousblockhash":"0x2d007892ec95e946aeb484b65767ab4bc0684e9ed49a4b62f20d336f0d900914","merkleroot":"0x17e2fa2759c5347a7b7e188adb5016443d128d05e651fbe02adb22393bdaa56a","time":1591360099001,"index":1,"nextconsensus":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","witnesses":[{"invocation":"0c40bac8992a77bd21ba670f2bf5097387a42b227878b361c69bfeeeb55c4b68bdff7db5c93b8f6d84876e473bba84bf27dc7de8d35b8179e120a60a6bc01562ac740c401c876fdb2fbffc599ac9ffbc85f7e6087855fd40e12b23888718e381c93de2614cce5bdfb1108ff72a73b85d4a6556dad6578a59bfa0e61b93c775cb1c1924f20c40fd992b9d776da1d2da390915e743f4213ce8a6db862b6354cccbcb3bf8b4edd14562cb546cbbcb3e2d096c9e527d4ee7879c5bbfd01d9a2bb00a1986820e1c320c40fcf3d76b809078a872970bd3bde5836589a7f62660098bbfb82a1cb470b75cc480d527a3829de89c897bacc6c29dfc3e4b84368aa1f69c9bce704fb3399fc04e","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"txid":"0xd24f8ab43462f8a2f788fa9073b8b32b4417aeddea19dfcc8dfb543d68e90de5","size":577,"version":0,"nonce":2,"sender":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","sys_fee":"0","net_fee":"0.0087835","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":1}],"script":"0218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b5238","vin":[],"vout":[],"scripts":[{"invocation":"0c4015b24aa11983bbf098384be4ebf5c283c3b4b2d2f6bf17b1a2a32f59627c7eb6370748b231acce2eb1ab5db3cdbf6943084634d12f69bf7b0aa447f96ebaf7d40c40a6777566463018e3f82fbbd370eef65b8454e50978401d1d97df05d9eda3b2bd5f5f882a94f8adbeaa2a55f64bd4ec23398fca26e8b364e5f57c771d71b3ec3d0c4032f5b1e3af669f4f14c2579de8f7ce46e30b35f7ed70892389251d50779477d2506b330dc9dd47f8c406218656d60b669479433bd9f762888091dc94415de5690c404b6ca943c34be335466c25ed442d17abc24d6a78db407a8c2a2c43fb911f0cbdbb28ec791b5f9de01b2db26105aa35193113a3a382c1ed02fbaf82b42468853a","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}]},{"txid":"0xf3304d751f7f6e871a610644529aa4f8fcc60a31592352eea54b86be472614f1","size":581,"version":0,"nonce":3,"sender":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","sys_fee":"0","net_fee":"0.0088235","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":1}],"script":"0300e87648170000000c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5238","vin":[],"vout":[],"scripts":[{"invocation":"0c4001c81faa8317a390c211d5e03451ed852ff02ec6c401c450558bc90c60a01912c3e1018de98e7458f6f96c68aad71ffb0e92327fcdfa4aabb5ea5834ec7afbf10c40a84e9bc36ffbf18a1ee79254182d2939dd3a7b9278e13893856f187ab44dec259c8a15d4f449a21aa6970234d79d81fee0aca1807341b977ff66d503a78c82920c4085495bb8d86b95a4afc1659ce1d993e1a7843e3dcb19696ce0422294dadddc73438780d5bc629560f576e0e75cff062c03e237d4e609dfaa9869135e23a487650c40ae25f2c563b978f931c3a8b5a18854d9bb77abf48c7554f2008f97696caca180cd40d4d0e47b3f4d86082e631547d1a4729c7ed60dda64fab9cd9a2270dd1132","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}]}]}}`
|
||||
const b1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"size":1681,"nextblockhash":"0xf2afe371a27c9dbac4f4a8ad8eba750898b7c04aa298e64fe9e488e947976045","confirmations":4,"hash":"0xbd178d8d4a28ec082c034f817ce2423221281a31e7e00014dbf732c4053033d2","version":0,"previousblockhash":"0x8f3b2c628ae86f037a735ec477932c81b8ae54b7abb3dd58cd87c97f90e762e8","merkleroot":"0x95831633b36588acf0e69f37f73d1a84cb0c5329790e837072206ba8842e1f30","time":1591366176001,"index":1,"nextconsensus":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","witnesses":[{"invocation":"0c40ab634ce91590e77b246cee8b204e8a270268ee1ef32434cece73f425a7dbc90f1bed1dbe914edcaa2653167ad170ae10e16a9b2c6b7e0af1f711fb848fbb1b7f0c40232de6ad07ee3846bafa96302d37602349501a556df575e7df0743e45b076d6a0c6c6dd4cad3898f9e8848dd054abd303b229fd12984042f241f0e668f39a0fb0c408b4af43057df189a9d471010b5150bab442040403147c5e502bda38cde3ff8bce803f01245e07e2bfb95d57349c55dcc27e3710b82f2735d0f40eb4342908e330c40cda66f743d4ed8d856f5376953f9169581c668a9370245aef16202ebef9bb3f7f81234be62ec287d701ad7d8bf5042648019af9fe5baa0a8e05d279bfdb1d4c9","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"txid":"0x8af9ccb8e7e0f0a73e77b78dc52750e77c50f78b09ecc2f0669c0b459cc7dd89","size":575,"version":0,"nonce":2,"sender":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","sys_fee":"0","net_fee":"0.0087635","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":1}],"script":"0218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b5238","scripts":[{"invocation":"0c402d96d8fde4ba266f89bc71ef117361967e0d11ed84cd60942a27bc99994dc58adf36a0e74ce976aac657a87a3c19c38e8ca450c67420046b81d98c60fd8feb040c40b3c15d5d23e0403a36cf559caee2979ca6ef00fe255df0e5c3daac4da051016b41eba42668934cd3308359451bafdd5419d059179fd40859684a3b91388bf9d80c407ac048cf8540b091955a374a0f36dae560c92c0134886507a589edf58b9dfbb4e3dbd5450be34e269d2e5454eb14eb7d6280d6101b4529410f829d37634849be0c403bba4113a687ff8507c1753f8519557531cf9df51ecc20deeb2c2b003ec5a1f7588cdd50b99e40b4f8039bb56c5df7ec9e7d6ea4b02fe23792510da21c7557f3","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}]},{"txid":"0xe7cff9e4820e53232dae619a3e6f57a9430dc240b5ed7b5c0ea2cfee3e90c985","size":579,"version":0,"nonce":3,"sender":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","sys_fee":"0","net_fee":"0.0088035","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":1}],"script":"0300e87648170000000c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5238","scripts":[{"invocation":"0c4063fb12aa9f3fb83f6324ea9c7ec11fa9e995b51140f480409d68cf4d625e598b0632d6610602984bfa2f5e5ea9bcc62a0e6d818dd271b38530c0d1b8a71b4e0c0c4013e091eac6f304668d647c5c032fd1020597ea5204545e21c38655a6343d58492118f1231ede91af848af7e1d987d1a8816966f5fc1a7821c6c6f62734267bde0c40daadd04a7a4141d96c58de2d373e672ca071e2b82138ef52df016ac522710385db2ac73743d2fe73061fa5d6cb0ff73a7ec7f0667e4c8bff6aa0d5783128d36e0c40dab85cd87d3f92be9532292bdc6f420b0ecbf2f877c70c6a9921ee0fc900dfc53998cf020a51fa9af3d0608f6a2b9048cea3c0b586485802bbd278b261eee8a4","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}]}]}}`
|
||||
|
||||
const hexHeader1 = "000000001409900d6f330df2624b9ad49e4e68c04bab6757b684b4ae46e995ec9278002d6aa5da3b3922db2ae0fb51e6058d123d441650db8a187e7b7a34c55927fae217b91274847201000001000000abec5362f11e75b6e02e407bb98d63675d14384101fd08010c40bac8992a77bd21ba670f2bf5097387a42b227878b361c69bfeeeb55c4b68bdff7db5c93b8f6d84876e473bba84bf27dc7de8d35b8179e120a60a6bc01562ac740c401c876fdb2fbffc599ac9ffbc85f7e6087855fd40e12b23888718e381c93de2614cce5bdfb1108ff72a73b85d4a6556dad6578a59bfa0e61b93c775cb1c1924f20c40fd992b9d776da1d2da390915e743f4213ce8a6db862b6354cccbcb3bf8b4edd14562cb546cbbcb3e2d096c9e527d4ee7879c5bbfd01d9a2bb00a1986820e1c320c40fcf3d76b809078a872970bd3bde5836589a7f62660098bbfb82a1cb470b75cc480d527a3829de89c897bacc6c29dfc3e4b84368aa1f69c9bce704fb3399fc04e94130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb00"
|
||||
const hexHeader1 = "00000000e862e7907fc987cd58ddb3abb754aeb8812c9377c45e737a036fe88a622c3b8f301f2e84a86b207270830e7929530ccb841a3df7379fe6f0ac8865b33316839501cdd0847201000001000000abec5362f11e75b6e02e407bb98d63675d14384101fd08010c40ab634ce91590e77b246cee8b204e8a270268ee1ef32434cece73f425a7dbc90f1bed1dbe914edcaa2653167ad170ae10e16a9b2c6b7e0af1f711fb848fbb1b7f0c40232de6ad07ee3846bafa96302d37602349501a556df575e7df0743e45b076d6a0c6c6dd4cad3898f9e8848dd054abd303b229fd12984042f241f0e668f39a0fb0c408b4af43057df189a9d471010b5150bab442040403147c5e502bda38cde3ff8bce803f01245e07e2bfb95d57349c55dcc27e3710b82f2735d0f40eb4342908e330c40cda66f743d4ed8d856f5376953f9169581c668a9370245aef16202ebef9bb3f7f81234be62ec287d701ad7d8bf5042648019af9fe5baa0a8e05d279bfdb1d4c994130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb00"
|
||||
|
||||
const header1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"hash":"0xc826db8aa7a05fbd336eb0fca66efea7330cdc09cc8cf8e3cd9a3d9a87b751ac","size":518,"version":0,"previousblockhash":"0x2d007892ec95e946aeb484b65767ab4bc0684e9ed49a4b62f20d336f0d900914","merkleroot":"0x17e2fa2759c5347a7b7e188adb5016443d128d05e651fbe02adb22393bdaa56a","time":1591360099001,"index":1,"nextconsensus":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","witnesses":[{"invocation":"0c40bac8992a77bd21ba670f2bf5097387a42b227878b361c69bfeeeb55c4b68bdff7db5c93b8f6d84876e473bba84bf27dc7de8d35b8179e120a60a6bc01562ac740c401c876fdb2fbffc599ac9ffbc85f7e6087855fd40e12b23888718e381c93de2614cce5bdfb1108ff72a73b85d4a6556dad6578a59bfa0e61b93c775cb1c1924f20c40fd992b9d776da1d2da390915e743f4213ce8a6db862b6354cccbcb3bf8b4edd14562cb546cbbcb3e2d096c9e527d4ee7879c5bbfd01d9a2bb00a1986820e1c320c40fcf3d76b809078a872970bd3bde5836589a7f62660098bbfb82a1cb470b75cc480d527a3829de89c897bacc6c29dfc3e4b84368aa1f69c9bce704fb3399fc04e","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}],"confirmations":6,"nextblockhash":"0x14a61952de5bf47e2550bb97439de44ea60f79e052419e8b263b1b41d7065b0f"}}`
|
||||
const header1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"hash":"0xbd178d8d4a28ec082c034f817ce2423221281a31e7e00014dbf732c4053033d2","size":518,"version":0,"previousblockhash":"0x8f3b2c628ae86f037a735ec477932c81b8ae54b7abb3dd58cd87c97f90e762e8","merkleroot":"0x95831633b36588acf0e69f37f73d1a84cb0c5329790e837072206ba8842e1f30","time":1591366176001,"index":1,"nextconsensus":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","witnesses":[{"invocation":"0c40ab634ce91590e77b246cee8b204e8a270268ee1ef32434cece73f425a7dbc90f1bed1dbe914edcaa2653167ad170ae10e16a9b2c6b7e0af1f711fb848fbb1b7f0c40232de6ad07ee3846bafa96302d37602349501a556df575e7df0743e45b076d6a0c6c6dd4cad3898f9e8848dd054abd303b229fd12984042f241f0e668f39a0fb0c408b4af43057df189a9d471010b5150bab442040403147c5e502bda38cde3ff8bce803f01245e07e2bfb95d57349c55dcc27e3710b82f2735d0f40eb4342908e330c40cda66f743d4ed8d856f5376953f9169581c668a9370245aef16202ebef9bb3f7f81234be62ec287d701ad7d8bf5042648019af9fe5baa0a8e05d279bfdb1d4c9","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}],"confirmations":6,"nextblockhash":"0xf2afe371a27c9dbac4f4a8ad8eba750898b7c04aa298e64fe9e488e947976045"}}`
|
||||
|
||||
const txMoveNeoVerbose = `{"id":5,"jsonrpc":"2.0","result":{"blockhash":"0xc826db8aa7a05fbd336eb0fca66efea7330cdc09cc8cf8e3cd9a3d9a87b751ac","confirmations":6,"blocktime":1591360099001,"txid":"0xd24f8ab43462f8a2f788fa9073b8b32b4417aeddea19dfcc8dfb543d68e90de5","size":577,"version":0,"nonce":2,"sender":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","sys_fee":"0","net_fee":"0.0087835","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":1}],"script":"0218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b5238","vin":[],"vout":[],"scripts":[{"invocation":"0c4015b24aa11983bbf098384be4ebf5c283c3b4b2d2f6bf17b1a2a32f59627c7eb6370748b231acce2eb1ab5db3cdbf6943084634d12f69bf7b0aa447f96ebaf7d40c40a6777566463018e3f82fbbd370eef65b8454e50978401d1d97df05d9eda3b2bd5f5f882a94f8adbeaa2a55f64bd4ec23398fca26e8b364e5f57c771d71b3ec3d0c4032f5b1e3af669f4f14c2579de8f7ce46e30b35f7ed70892389251d50779477d2506b330dc9dd47f8c406218656d60b669479433bd9f762888091dc94415de5690c404b6ca943c34be335466c25ed442d17abc24d6a78db407a8c2a2c43fb911f0cbdbb28ec791b5f9de01b2db26105aa35193113a3a382c1ed02fbaf82b42468853a","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}]}}`
|
||||
const txMoveNeoVerbose = `{"id":5,"jsonrpc":"2.0","result":{"blockhash":"0xbd178d8d4a28ec082c034f817ce2423221281a31e7e00014dbf732c4053033d2","confirmations":6,"blocktime":1591366176001,"txid":"0x8af9ccb8e7e0f0a73e77b78dc52750e77c50f78b09ecc2f0669c0b459cc7dd89","size":575,"version":0,"nonce":2,"sender":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","sys_fee":"0","net_fee":"0.0087635","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":1}],"script":"0218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b5238","scripts":[{"invocation":"0c402d96d8fde4ba266f89bc71ef117361967e0d11ed84cd60942a27bc99994dc58adf36a0e74ce976aac657a87a3c19c38e8ca450c67420046b81d98c60fd8feb040c40b3c15d5d23e0403a36cf559caee2979ca6ef00fe255df0e5c3daac4da051016b41eba42668934cd3308359451bafdd5419d059179fd40859684a3b91388bf9d80c407ac048cf8540b091955a374a0f36dae560c92c0134886507a589edf58b9dfbb4e3dbd5450be34e269d2e5454eb14eb7d6280d6101b4529410f829d37634849be0c403bba4113a687ff8507c1753f8519557531cf9df51ecc20deeb2c2b003ec5a1f7588cdd50b99e40b4f8039bb56c5df7ec9e7d6ea4b02fe23792510da21c7557f3","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}]}}`
|
||||
|
||||
// getResultBlock1 returns data for block number 1 which is used by several tests.
|
||||
func getResultBlock1() *result.Block {
|
||||
|
@ -54,14 +54,14 @@ func getResultBlock1() *result.Block {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
b2Hash, err := util.Uint256DecodeStringLE("14a61952de5bf47e2550bb97439de44ea60f79e052419e8b263b1b41d7065b0f")
|
||||
b2Hash, err := util.Uint256DecodeStringLE("f2afe371a27c9dbac4f4a8ad8eba750898b7c04aa298e64fe9e488e947976045")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &result.Block{
|
||||
Block: b,
|
||||
BlockMetadata: result.BlockMetadata{
|
||||
Size: 1685,
|
||||
Size: 1681,
|
||||
NextBlockHash: &b2Hash,
|
||||
Confirmations: 4,
|
||||
},
|
||||
|
|
|
@ -117,8 +117,8 @@ func TestWSClientEvents(t *testing.T) {
|
|||
var events = []string{
|
||||
`{"jsonrpc":"2.0","method":"transaction_executed","params":[{"txid":"0xe1cd5e57e721d2a2e05fb1f08721b12057b25ab1dd7fd0f33ee1639932fdfad7","executions":[{"trigger":"Application","contract":"0x0000000000000000000000000000000000000000","vmstate":"HALT","gas_consumed":"2.291","stack":[],"notifications":[{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","state":{"type":"Array","value":[{"type":"ByteArray","value":"636f6e74726163742063616c6c"},{"type":"ByteArray","value":"7472616e73666572"},{"type":"Array","value":[{"type":"ByteArray","value":"769162241eedf97c2481652adf1ba0f5bf57431b"},{"type":"ByteArray","value":"316e851039019d39dfc2c37d6c3fee19fd580987"},{"type":"Integer","value":"1000"}]}]}},{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","state":{"type":"Array","value":[{"type":"ByteArray","value":"7472616e73666572"},{"type":"ByteArray","value":"769162241eedf97c2481652adf1ba0f5bf57431b"},{"type":"ByteArray","value":"316e851039019d39dfc2c37d6c3fee19fd580987"},{"type":"Integer","value":"1000"}]}}]}]}]}`,
|
||||
`{"jsonrpc":"2.0","method":"notification_from_execution","params":[{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","state":{"type":"Array","value":[{"type":"ByteArray","value":"636f6e74726163742063616c6c"},{"type":"ByteArray","value":"7472616e73666572"},{"type":"Array","value":[{"type":"ByteArray","value":"769162241eedf97c2481652adf1ba0f5bf57431b"},{"type":"ByteArray","value":"316e851039019d39dfc2c37d6c3fee19fd580987"},{"type":"Integer","value":"1000"}]}]}}]}`,
|
||||
`{"jsonrpc":"2.0","method":"transaction_added","params":[{"txid":"0xe1e0200a69f3096010c75dd003653e5bd945e414d0b0a3b95e3dfe291534c8e5","size":267,"version":0,"nonce":9,"sender":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","sys_fee":"0","net_fee":"0.0036721","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x870958fd19ee3f6c7dc3c2df399d013910856e31","scopes":1}],"script":"007b0c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14769162241eedf97c2481652adf1ba0f5bf57431b41627d5b5238","vin":[],"vout":[],"scripts":[{"invocation":"0c40b61d89290451852143490194e09a547d79b8b6cda05691c085160cc0ddebc6e3a508defae7ac04e015c85af6b6fcb487a954c2a884d4cc147c9959f776844b3c","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}]}]}`,
|
||||
`{"jsonrpc":"2.0","method":"block_added","params":[{"hash":"0x73f4157bc918f0af115522380b13dadf505773f91e818288edb0de4fcce104b4","version":0,"previousblockhash":"0x3c664cf156f2f418e92fd24621ac3b56da4a4c1e56c443fe360d52324d8f060f","merkleroot":"0xfa9e8e7a506c1a38fa1f090237156debd055c8cf41d743ae4f37fe3c278ff2e0","time":1591360099006,"index":6,"nextconsensus":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","witnesses":[{"invocation":"0c40d7690b2335898b192d408f3565fb8da0f994a305999f1b7b87000934831f76d680442ea2c44200c2b154c12a0ee587046eeec45f749b73ce3bc97ac172487f350c400835fd349d184a691087b24a827d82bad3026ffa0e7f2841e85fb91f5c9549e6a1430883586cbbfba3a450a0198608e19c0b1630677bbd9495e3f48c8c4835c40c400fd2d4dfee36f99de6df35b1c857eb7c8b9b26574a781114081b066b806a1bcae129d05329298b0e6b4728da90928d4a375157dd7472fb37351a20d1a84996790c404b58d5f6374bf6c2ab12f51e6f78a07852e027279879a5fb25b5e6371ad9d89749127e7b8f8eae3df45bfe187ae226aa1aeead061006da7c4e665d97950c9a3e","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"txid":"0xe1e0200a69f3096010c75dd003653e5bd945e414d0b0a3b95e3dfe291534c8e5","size":267,"version":0,"nonce":9,"sender":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","sys_fee":"0","net_fee":"0.0036721","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x870958fd19ee3f6c7dc3c2df399d013910856e31","scopes":1}],"script":"007b0c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14769162241eedf97c2481652adf1ba0f5bf57431b41627d5b5238","vin":[],"vout":[],"scripts":[{"invocation":"0c40b61d89290451852143490194e09a547d79b8b6cda05691c085160cc0ddebc6e3a508defae7ac04e015c85af6b6fcb487a954c2a884d4cc147c9959f776844b3c","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}]}]}]}`,
|
||||
`{"jsonrpc":"2.0","method":"transaction_added","params":[{"txid":"0x1c615d4043c98fc0e285c2f40cc3601cf4ebe1cf9d2b404dfc67c9cd085444ec","size":265,"version":0,"nonce":9,"sender":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","sys_fee":"0","net_fee":"0.0036521","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x870958fd19ee3f6c7dc3c2df399d013910856e31","scopes":1}],"script":"007b0c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14769162241eedf97c2481652adf1ba0f5bf57431b41627d5b5238","scripts":[{"invocation":"0c4034d02f3b97a220ffe79640e482b887ec0e44dcc95e719f5e2b43b29987f0c9822b9af0499d90094c6ad3ba191e434a3df5dd378d3b73318cf47c9f2d6d801cc8","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}]}]}`,
|
||||
`{"jsonrpc":"2.0","method":"block_added","params":[{"hash":"0x765ea65b4de6addfee29b1c90ac922d1901c8d7ab7f2366da9a8ad3dd71ca703","version":0,"previousblockhash":"0xbdeed527a43ab72d5d8cecf1dc6ee142112ff8a8eaaaebc7206d3df3bf3c1169","merkleroot":"0xa1b321f59b127cddd23b0cd47fc9ec7920647d30d7ab23318a106597b9c9abad","time":1591366176006,"index":6,"nextconsensus":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","witnesses":[{"invocation":"0c40da1f4b546a8a60e96596351234d7709391866bb3590a290133bc0c45837f1dac6351ee32506a7e0bbf6fcbcc3ec01222ccfe84bc1d4071221f4c432ebf569b620c40ee5906328012a8a4a411e7fa23aa8ba21fedb81b11581e5a287cad961fa36d2a20b2069549a5a14860d9e9ae3640ea20f9191d60ab7c2aeddf43edd6dabe558c0c40f5391e79e7d62f7ccaa900511d530f89de183fa51bc4af744bda81f763e14ddd7fb953e69b0901660d4752f240d5269344d0b64b50b124d1a316ad72486da15e0c40012f773faef2aee4af59e083b443ebe6cf404d12f49d32966c5f48f2c203e284429615aa2d34c827356d55c3be1612f67a5b725f6ff49b9b95b1f60306a72b71","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"txid":"0x1c615d4043c98fc0e285c2f40cc3601cf4ebe1cf9d2b404dfc67c9cd085444ec","size":265,"version":0,"nonce":9,"sender":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","sys_fee":"0","net_fee":"0.0036521","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x870958fd19ee3f6c7dc3c2df399d013910856e31","scopes":1}],"script":"007b0c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14769162241eedf97c2481652adf1ba0f5bf57431b41627d5b5238","scripts":[{"invocation":"0c4034d02f3b97a220ffe79640e482b887ec0e44dcc95e719f5e2b43b29987f0c9822b9af0499d90094c6ad3ba191e434a3df5dd378d3b73318cf47c9f2d6d801cc8","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}]}]}]}`,
|
||||
`{"jsonrpc":"2.0","method":"event_missed","params":[]}`,
|
||||
}
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
package result
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// TransactionOutput is a wrapper to represent transaction's output.
|
||||
type TransactionOutput struct {
|
||||
N int `json:"n"`
|
||||
Asset string `json:"asset"`
|
||||
Value util.Fixed8 `json:"value"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
// NewTxOutput converts out to a TransactionOutput.
|
||||
func NewTxOutput(out *transaction.Output) *TransactionOutput {
|
||||
addr := address.Uint160ToString(out.ScriptHash)
|
||||
|
||||
return &TransactionOutput{
|
||||
N: out.Position,
|
||||
Asset: "0x" + out.AssetID.String(),
|
||||
Value: out.Amount,
|
||||
Address: addr,
|
||||
}
|
||||
}
|
|
@ -28,10 +28,6 @@ type TransactionMetadata struct {
|
|||
func NewTransactionOutputRaw(tx *transaction.Transaction, header *block.Header, chain blockchainer.Blockchainer) TransactionOutputRaw {
|
||||
// confirmations formula
|
||||
confirmations := int(chain.BlockHeight() - header.Base.Index + 1)
|
||||
// set index position
|
||||
for i, o := range tx.Outputs {
|
||||
o.Position = i
|
||||
}
|
||||
return TransactionOutputRaw{
|
||||
Transaction: tx,
|
||||
TransactionMetadata: TransactionMetadata{
|
||||
|
|
|
@ -55,12 +55,12 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
"getapplicationlog": {
|
||||
{
|
||||
name: "positive",
|
||||
params: `["2a6bf372fb96d05735eeb685805396818ac3f21e748528f4d6ebdecfd07ddce4"]`,
|
||||
params: `["5878052c7e9843786d64a9aeab16e74fabffd5abad9a0404aaf4f4bf2b6213e9"]`,
|
||||
result: func(e *executor) interface{} { return &result.ApplicationLog{} },
|
||||
check: func(t *testing.T, e *executor, acc interface{}) {
|
||||
res, ok := acc.(*result.ApplicationLog)
|
||||
require.True(t, ok)
|
||||
expectedTxHash, err := util.Uint256DecodeStringLE("2a6bf372fb96d05735eeb685805396818ac3f21e748528f4d6ebdecfd07ddce4")
|
||||
expectedTxHash, err := util.Uint256DecodeStringLE("5878052c7e9843786d64a9aeab16e74fabffd5abad9a0404aaf4f4bf2b6213e9")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedTxHash, res.TxHash)
|
||||
assert.Equal(t, 1, len(res.Executions))
|
||||
|
@ -484,7 +484,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
"gettransactionheight": {
|
||||
{
|
||||
name: "positive",
|
||||
params: `["2a6bf372fb96d05735eeb685805396818ac3f21e748528f4d6ebdecfd07ddce4"]`,
|
||||
params: `["5878052c7e9843786d64a9aeab16e74fabffd5abad9a0404aaf4f4bf2b6213e9"]`,
|
||||
result: func(e *executor) interface{} {
|
||||
h := 0
|
||||
return &h
|
||||
|
@ -678,7 +678,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
"sendrawtransaction": {
|
||||
{
|
||||
name: "positive",
|
||||
params: `["000a000000316e851039019d39dfc2c37d6c3fee19fd5809870000000000000000c2b5050000000000b00400000001316e851039019d39dfc2c37d6c3fee19fd580987015d0300e87648170000000c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b5238000001420c4070b2b74b193b574fede47432a9bfa192726603a7f1ef6f6589f192943eb5978f47d25a7834ae7f42a0ebf4e013d1d1c2d1bb61417890335e9804ef26a94d2c00290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"]`,
|
||||
params: `["000a000000316e851039019d39dfc2c37d6c3fee19fd5809870000000000000000f2ad050000000000b00400000001316e851039019d39dfc2c37d6c3fee19fd580987015d0300e87648170000000c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801420c40df953141271169421cebab5d27a0163e294d7c7f2d0525b4498745344814fd3d6c5c591c9b1723d05d42856f409adb084cf67acc921cfafc629133a5eb5e7a7e290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"]`,
|
||||
result: func(e *executor) interface{} {
|
||||
v := true
|
||||
return &v
|
||||
|
@ -686,7 +686,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
},
|
||||
{
|
||||
name: "negative",
|
||||
params: `["000a000000316e851039019d39dfc2c37d6c3fee19fd5809870000000000000000c2b5050000000000b00400000001316e851039019d39dfc2c37d6c3fee19fd580987015d0300e87648170000000c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b5238000001420c4070b2b74b193b574fede47432a9bfa192726603a7f1ef6f6589f192943eb5978f47d25a7834ae7f42a0ebf4e013d1d1c2d1bb61417890335e9804ef26a94d2c00290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906aff"]`,
|
||||
params: `["000a000000316e851039019d39dfc2c37d6c3fee19fd5809870000000000000000f2ad050000000000b00400000001316e851039019d39dfc2c37d6c3fee19fd580987015d0300e87648170000000c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801420c40df953141271169421cebab5d27a0163e294d7c7f2d0525b4498745344814fd3d6c5c591c9b1723d05d42856f409adb084cf67acc921cfafc629133a5eb5e7a7e290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906aff"]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
|
|
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
Binary file not shown.
|
@ -166,15 +166,6 @@ func newParam(typ smartcontract.ParamType, name string) wallet.ContractParam {
|
|||
|
||||
func getContractTx() *transaction.Transaction {
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.AddInput(&transaction.Input{
|
||||
PrevHash: util.Uint256{1, 2, 3, 4},
|
||||
PrevIndex: 5,
|
||||
})
|
||||
tx.AddOutput(&transaction.Output{
|
||||
AssetID: util.Uint256{7, 8, 9},
|
||||
Amount: 10,
|
||||
ScriptHash: util.Uint160{11, 12},
|
||||
})
|
||||
tx.Attributes = make([]transaction.Attribute, 0)
|
||||
tx.Scripts = make([]transaction.Witness, 0)
|
||||
tx.Hash()
|
||||
|
|
Loading…
Reference in a new issue