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 }