github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/services/oracle/transaction.go (about)

     1  package oracle
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	"github.com/nspcc-dev/neo-go/pkg/config/netmode"
     8  	"github.com/nspcc-dev/neo-go/pkg/core/state"
     9  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    10  	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
    11  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    12  	"github.com/nspcc-dev/neo-go/pkg/io"
    13  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    14  	"github.com/nspcc-dev/neo-go/pkg/vm/emit"
    15  )
    16  
    17  type (
    18  	incompleteTx struct {
    19  		sync.RWMutex
    20  		// isSent is true if tx has been already broadcasted.
    21  		isSent bool
    22  		// attempts is how many times the request was processed.
    23  		attempts int
    24  		// time is the time when the request was last processed.
    25  		time time.Time
    26  		// request is an oracle request.
    27  		request *state.OracleRequest
    28  		// tx is an oracle response transaction.
    29  		tx *transaction.Transaction
    30  		// sigs contains a signature from every oracle node.
    31  		sigs map[string]*txSignature
    32  		// backupTx is a backup transaction.
    33  		backupTx *transaction.Transaction
    34  		// backupSigs contains signatures of backup tx.
    35  		backupSigs map[string]*txSignature
    36  	}
    37  
    38  	txSignature struct {
    39  		// pub is a cached public key.
    40  		pub *keys.PublicKey
    41  		// ok is true if the signature was verified.
    42  		ok bool
    43  		// sig is tx signature.
    44  		sig []byte
    45  	}
    46  )
    47  
    48  func newIncompleteTx() *incompleteTx {
    49  	return &incompleteTx{
    50  		sigs:       make(map[string]*txSignature),
    51  		backupSigs: make(map[string]*txSignature),
    52  	}
    53  }
    54  
    55  func (t *incompleteTx) reverifyTx(net netmode.Magic) {
    56  	txHash := hash.NetSha256(uint32(net), t.tx)
    57  	backupHash := hash.NetSha256(uint32(net), t.backupTx)
    58  	for pub, sig := range t.sigs {
    59  		if !sig.ok {
    60  			sig.ok = sig.pub.Verify(sig.sig, txHash.BytesBE())
    61  			if !sig.ok && sig.pub.Verify(sig.sig, backupHash.BytesBE()) {
    62  				t.backupSigs[pub] = &txSignature{
    63  					pub: sig.pub,
    64  					ok:  true,
    65  					sig: sig.sig,
    66  				}
    67  			}
    68  		}
    69  	}
    70  }
    71  
    72  func (t *incompleteTx) addResponse(pub *keys.PublicKey, sig []byte, isBackup bool) {
    73  	tx, sigs := t.tx, t.sigs
    74  	if isBackup {
    75  		tx, sigs = t.backupTx, t.backupSigs
    76  	}
    77  	sigs[string(pub.Bytes())] = &txSignature{
    78  		pub: pub,
    79  		ok:  tx != nil,
    80  		sig: sig,
    81  	}
    82  }
    83  
    84  // finalize checks if either main or backup tx has sufficient number of signatures and returns
    85  // tx and bool value indicating if it is ready to be broadcasted.
    86  func (t *incompleteTx) finalize(oracleNodes keys.PublicKeys, backupOnly bool) (*transaction.Transaction, bool) {
    87  	if !backupOnly && finalizeTx(oracleNodes, t.tx, t.sigs) {
    88  		return t.tx, true
    89  	}
    90  	return t.backupTx, finalizeTx(oracleNodes, t.backupTx, t.backupSigs)
    91  }
    92  
    93  func finalizeTx(oracleNodes keys.PublicKeys, tx *transaction.Transaction, txSigs map[string]*txSignature) bool {
    94  	if tx == nil {
    95  		return false
    96  	}
    97  	m := smartcontract.GetDefaultHonestNodeCount(len(oracleNodes))
    98  	sigs := make([][]byte, 0, m)
    99  	for _, pub := range oracleNodes {
   100  		sig, ok := txSigs[string(pub.Bytes())]
   101  		if ok && sig.ok {
   102  			sigs = append(sigs, sig.sig)
   103  			if len(sigs) == m {
   104  				break
   105  			}
   106  		}
   107  	}
   108  	if len(sigs) != m {
   109  		return false
   110  	}
   111  
   112  	w := io.NewBufBinWriter()
   113  	for i := range sigs {
   114  		emit.Bytes(w.BinWriter, sigs[i])
   115  	}
   116  	tx.Scripts[1].InvocationScript = w.Bytes()
   117  	return true
   118  }