diff --git a/backups/devnet/config.json b/backups/devnet/config.json new file mode 100644 index 0000000..4309372 --- /dev/null +++ b/backups/devnet/config.json @@ -0,0 +1,17 @@ +{ + "RelayJsonRpcUrl": "http://138.91.6.226:40336", + "RelayChainID": 0, + "WalletFile": "./poly_test.dat", + "NeoMagic": 6543221, + "NeoWalletFile": "./neo3_dev.json", + "NeoJsonRpcUrl": "http://seed1t.neo.org:21332", + "NeoChainID": 220, + "NeoCCMC": "0x7c842f3900ceba5bcdeeeec05c9b41ba63ad6cd2", + "SpecificContract": "", + "ScanInterval": 2, + "RetryInterval": 2, + "DBPath": "boltdb", + "ChangeBookkeeper": true, + "PolyStartHeight": 1196919, + "NeoStartHeight": 67140 +} \ No newline at end of file diff --git a/backups/devnet/neo3_dev.json b/backups/devnet/neo3_dev.json new file mode 100644 index 0000000..7d7fbe1 --- /dev/null +++ b/backups/devnet/neo3_dev.json @@ -0,0 +1 @@ +{"name":null,"version":"1.0","scrypt":{"n":16384,"r":8,"p":8},"accounts":[{"address":"NWZPTRojhAQ1DZ8wGZYVSpX87oNEajLZ5Y","label":null,"isdefault":false,"lock":false,"key":"6PYURwh7xSUBA6yEbBsnYCGESd7UNHi3iWHsDweCxwzEH8xPBSphb1z7ss","contract":{"script":"DCEDBZF107jxpjbIR22sox21V6K7aZvg9/N2wIgp7txBLIJBVuezJw==","parameters":[{"name":"signature","type":"Signature"}],"deployed":false},"extra":null}],"extra":null} \ No newline at end of file diff --git a/backups/devnet/pwd.go b/backups/devnet/pwd.go new file mode 100644 index 0000000..89a99d4 --- /dev/null +++ b/backups/devnet/pwd.go @@ -0,0 +1,3 @@ +package devnet + +// --neopwd poly --relaypwd 4cUYqGj2yib718E7ZmGQc, for neo3_dev.json diff --git a/backups/devnet/qian.json b/backups/devnet/qian.json new file mode 100644 index 0000000..186c61f --- /dev/null +++ b/backups/devnet/qian.json @@ -0,0 +1 @@ +{"name":null,"version":"1.0","scrypt":{"n":16384,"r":8,"p":8},"accounts":[{"address":"NbEa6FJ1wz5Tqqk4WzGGZkrktzVzm4uQYo","label":null,"isdefault":false,"lock":false,"key":"6PYPcZavziPGCxkt9358xdMUM5penKuEGvXrFwJ1QTaE45qnYZ2fuk8JVd","contract":{"script":"DCECOf1zcDTs2F1xI/VaKOSyVRKLEHf0XFmJXWIVZSmh\u002B6dBdHR2qg==","parameters":[{"name":"signature","type":"Signature"}],"deployed":false},"extra":null}],"extra":null} \ No newline at end of file diff --git a/config.json b/config.json index 39d497b..fa777dd 100644 --- a/config.json +++ b/config.json @@ -7,11 +7,14 @@ "NeoJsonRpcUrl": "http://seed1t4.neo.org:20332", "NeoChainID": 88, "NeoCCMC": "0x618d44dc3af16c6120dbf65402024f40a04f772a", - "SpecificContract": "", - "ScanInterval": 2, - "RetryInterval": 2, + "NtorContract": "", + "RtonContract": "", + "ScanInterval": 15, + "RetryInterval": 5, "DBPath": "boltdb", "ChangeBookkeeper": true, - "PolyStartHeight": 0, - "NeoStartHeight": 0 + "Neo2RpcUrl": "http://seed1.ngd.network:20332", + "Neo2Wrapper": "0x7997ac991b66ca3810602639a2f2c1bd985e8b5a", + "PolyStartHeight": 12665830, + "NeoStartHeight": 48950 } \ No newline at end of file diff --git a/config/config.go b/config/config.go index c6e226e..ec2df4c 100644 --- a/config/config.go +++ b/config/config.go @@ -17,23 +17,21 @@ type Config struct { RelayJsonRpcUrl string WalletFile string RelayAccountsPath string - - NeoMagic uint32 - NeoWalletFile string - NeoJsonRpcUrl string - NeoChainID uint64 - NeoCCMC string // little endian string - SpecificContract string // the specific contract which is monitored - //NeoSysFee float64 - //NeoNetFee float64 - - ScanInterval uint64 - RetryInterval uint64 - DBPath string - ChangeBookkeeper bool - - PolyStartHeight uint32 - NeoStartHeight uint32 + NeoMagic uint32 + NeoWalletFile string + NeoJsonRpcUrl string + NeoChainID uint64 + NeoCCMC string // big endian string, like 0x1234567812345678123456781234567812345678 + NtorContract string + RtonContract string + ScanInterval uint64 + RetryInterval uint64 + DBPath string + ChangeBookkeeper bool + Neo2RpcUrl string + Neo2Wrapper string + PolyStartHeight uint32 + NeoStartHeight uint32 } //Default config instance diff --git a/db/db.go b/db/db.go index 991646f..568bdd7 100644 --- a/db/db.go +++ b/db/db.go @@ -1,10 +1,11 @@ package db + // db not used import ( "encoding/hex" "fmt" "github.com/boltdb/bolt" - "github.com/joeqian10/neo3-gogogo/helper" + //"github.com/joeqian10/neo3-gogogo/helper" "github.com/polynetwork/neo3-relayer/log" "path" "strings" @@ -14,19 +15,20 @@ import ( const MAX_NUM = 1000 var ( - BKTCheck = []byte("Check") - BKTRetry = []byte("Retry") + BKTCheck = []byte("Check") + BKTRetry = []byte("Retry") BKTPolyPublicKeysSorted = []byte("PPKS") + BKTNeoCheck = []byte("NeoCheck") BKTNeoRetry = []byte("NeoRetry") - BKTHeader = []byte("Header") // bucket header + BKTHeader = []byte("Header") // bucket header BKTHeightList = []byte("HeightList") // bucket header height list ) type BoltDB struct { - rwLock *sync.RWMutex - db *bolt.DB + rwLock *sync.RWMutex + db *bolt.DB filePath string } @@ -72,6 +74,16 @@ func NewBoltDB(filePath string) (*BoltDB, error) { }); err != nil { return nil, err } + // neo check + if err = db.Update(func(btx *bolt.Tx) error { + _, err := btx.CreateBucketIfNotExists(BKTNeoCheck) + if err != nil { + return err + } + return nil + }); err != nil { + return nil, err + } // neo retry if err = db.Update(func(btx *bolt.Tx) error { _, err := btx.CreateBucketIfNotExists(BKTNeoRetry) @@ -106,7 +118,6 @@ func NewBoltDB(filePath string) (*BoltDB, error) { return w, nil } - //func (w *BoltDB) PutPolyHeight(height uint32) error { // w.rwLock.Lock() // defer w.rwLock.Unlock() @@ -143,7 +154,71 @@ func NewBoltDB(filePath string) (*BoltDB, error) { // return height //} +func (w *BoltDB) PutNeoCheck(neoTxHash string, v []byte) error { + w.rwLock.Lock() + defer w.rwLock.Unlock() + + k, err := hex.DecodeString(neoTxHash) + if err != nil { + return err + } + return w.db.Update(func(btx *bolt.Tx) error { + bucket := btx.Bucket(BKTNeoCheck) + err := bucket.Put(k, v) + if err != nil { + return err + } + + return nil + }) +} + +func (w *BoltDB) DeleteNeoCheck(neoTxHash string) error { + w.rwLock.Lock() + defer w.rwLock.Unlock() + k, err := hex.DecodeString(neoTxHash) + if err != nil { + return err + } + return w.db.Update(func(tx *bolt.Tx) error { + bucket := tx.Bucket(BKTNeoCheck) + err := bucket.Delete(k) + if err != nil { + return err + } + return nil + }) +} + +func (w *BoltDB) GetNeoAllCheck() (map[string][]byte, error) { + w.rwLock.Lock() + defer w.rwLock.Unlock() + + checkMap := make(map[string][]byte) + err := w.db.Update(func(tx *bolt.Tx) error { + bw := tx.Bucket(BKTNeoCheck) + err := bw.ForEach(func(k, v []byte) error { + _k := make([]byte, len(k)) + _v := make([]byte, len(v)) + copy(_k, k) + copy(_v, v) + checkMap[hex.EncodeToString(_k)] = _v + if len(checkMap) >= MAX_NUM { + return fmt.Errorf("max num") + } + return nil + }) + if err != nil { + log.Errorf("GetAllCheck err: %s", err) + } + return nil + }) + if err != nil { + return nil, err + } + return checkMap, nil +} func (w *BoltDB) PutNeoRetry(k []byte) error { w.rwLock.Lock() @@ -201,8 +276,6 @@ func (w *BoltDB) GetAllNeoRetry() ([][]byte, error) { return retryList, nil } - - func (w *BoltDB) PutCheck(txHash string, v []byte) error { w.rwLock.Lock() defer w.rwLock.Unlock() @@ -362,166 +435,166 @@ func (w *BoltDB) GetPPKS() ([]byte, error) { return v, nil } -func (w *BoltDB) PutHeader(height uint32, rawHeader []byte) error { - w.rwLock.Lock() - defer w.rwLock.Unlock() - - key := helper.UInt32ToBytes(height) - // check if exists - var v []byte = nil - err := w.db.View(func(tx *bolt.Tx) error { - _v := tx.Bucket(BKTHeader).Get(key) - v = make([]byte, len(_v)) - copy(v, _v) - return nil - }) - if err != nil { - return err - } - if v == nil { - return nil - } - - return w.db.Update(func(btx *bolt.Tx) error { - bucket := btx.Bucket(BKTHeader) - err := bucket.Put(key, rawHeader) - if err != nil { - return err - } - - return nil - }) -} - -func (w *BoltDB) GetHeader(height uint32) ([]byte, error) { - w.rwLock.Lock() - defer w.rwLock.Unlock() - - key := helper.UInt32ToBytes(height) - var v []byte = nil - err := w.db.View(func(tx *bolt.Tx) error { - _v := tx.Bucket(BKTHeader).Get(key) - v = make([]byte, len(_v)) - copy(v, _v) - return nil - }) - if err != nil { - return nil, err - } - return v, nil -} - -func (w *BoltDB) GetHeadersByRange(low uint32, high uint32) (map[uint32][]byte, error) { - if low > high { - return nil, fmt.Errorf("invalid parameters") - } - - heightList, err := w.GetHeightList() - if err != nil { - return nil,err - } - if heightList == nil { - return nil, nil - } - - var i = 0 - var j = len(heightList)-1 - for heightList[i] <= low { // exclude low - i++ - } - for heightList[j] > high { // include high - j-- - } - if i > j { - return nil, nil - } - - w.rwLock.Lock() - defer w.rwLock.Unlock() - - var headersMap = make(map[uint32][]byte) - for k := i; k <= j; k++ { - key := helper.UInt32ToBytes(heightList[k]) - var v []byte = nil - err := w.db.View(func(tx *bolt.Tx) error { - _v := tx.Bucket(BKTHeader).Get(key) - v = make([]byte, len(_v)) - copy(v, _v) - return nil - }) - if err != nil { - return nil, err - } - headersMap[heightList[k]] = v - } - - return headersMap, nil -} - -func (w *BoltDB) PutHeightList(heights []uint32) error { - w.rwLock.Lock() - defer w.rwLock.Unlock() - - var v []byte - for _, height := range heights { - v = append(v, helper.UInt32ToBytes(height)...) - } - - return w.db.Update(func(btx *bolt.Tx) error { - bucket := btx.Bucket(BKTHeightList) - err := bucket.Put(BKTHeightList, v) - if err != nil { - return err - } - - return nil - }) -} - -func (w *BoltDB) GetHeightList() ([]uint32, error) { - w.rwLock.Lock() - defer w.rwLock.Unlock() - - var v []byte - err := w.db.View(func(tx *bolt.Tx) error { - _v := tx.Bucket(BKTHeightList).Get(BKTHeightList) - v = make([]byte, len(_v)) - copy(v, _v) - return nil - }) - if err != nil { - return nil, err - } - - results := make([]uint32, len(v)/4) - for i := 0; i < len(results); i++ { - results[i] = helper.BytesToUInt32(v[4*i:4*i+4]) - } - return results, nil -} - -func (w *BoltDB) PutValueInHeightList(height uint32) error { - heightList, err := w.GetHeightList() - if err != nil { - return err - } - - newHeightList := make([]uint32, len(heightList)+1) - // find the position to insert - i := 0 - for height < heightList[i] { - i++ - } - copy(newHeightList[:i], heightList[:i]) - newHeightList[i] = height - copy(newHeightList[i+1:], heightList[i:]) - - err = w.PutHeightList(newHeightList) - if err != nil { - return err - } - return nil -} +//func (w *BoltDB) PutHeader(height uint32, rawHeader []byte) error { +// w.rwLock.Lock() +// defer w.rwLock.Unlock() +// +// key := helper.UInt32ToBytes(height) +// // check if exists +// var v []byte = nil +// err := w.db.View(func(tx *bolt.Tx) error { +// _v := tx.Bucket(BKTHeader).Get(key) +// v = make([]byte, len(_v)) +// copy(v, _v) +// return nil +// }) +// if err != nil { +// return err +// } +// if v == nil { +// return nil +// } +// +// return w.db.Update(func(btx *bolt.Tx) error { +// bucket := btx.Bucket(BKTHeader) +// err := bucket.Put(key, rawHeader) +// if err != nil { +// return err +// } +// +// return nil +// }) +//} +// +//func (w *BoltDB) GetHeader(height uint32) ([]byte, error) { +// w.rwLock.Lock() +// defer w.rwLock.Unlock() +// +// key := helper.UInt32ToBytes(height) +// var v []byte = nil +// err := w.db.View(func(tx *bolt.Tx) error { +// _v := tx.Bucket(BKTHeader).Get(key) +// v = make([]byte, len(_v)) +// copy(v, _v) +// return nil +// }) +// if err != nil { +// return nil, err +// } +// return v, nil +//} +// +//func (w *BoltDB) GetHeadersByRange(low uint32, high uint32) (map[uint32][]byte, error) { +// if low > high { +// return nil, fmt.Errorf("invalid parameters") +// } +// +// heightList, err := w.GetHeightList() +// if err != nil { +// return nil,err +// } +// if heightList == nil { +// return nil, nil +// } +// +// var i = 0 +// var j = len(heightList)-1 +// for heightList[i] <= low { // exclude low +// i++ +// } +// for heightList[j] > high { // include high +// j-- +// } +// if i > j { +// return nil, nil +// } +// +// w.rwLock.Lock() +// defer w.rwLock.Unlock() +// +// var headersMap = make(map[uint32][]byte) +// for k := i; k <= j; k++ { +// key := helper.UInt32ToBytes(heightList[k]) +// var v []byte = nil +// err := w.db.View(func(tx *bolt.Tx) error { +// _v := tx.Bucket(BKTHeader).Get(key) +// v = make([]byte, len(_v)) +// copy(v, _v) +// return nil +// }) +// if err != nil { +// return nil, err +// } +// headersMap[heightList[k]] = v +// } +// +// return headersMap, nil +//} +// +//func (w *BoltDB) PutHeightList(heights []uint32) error { +// w.rwLock.Lock() +// defer w.rwLock.Unlock() +// +// var v []byte +// for _, height := range heights { +// v = append(v, helper.UInt32ToBytes(height)...) +// } +// +// return w.db.Update(func(btx *bolt.Tx) error { +// bucket := btx.Bucket(BKTHeightList) +// err := bucket.Put(BKTHeightList, v) +// if err != nil { +// return err +// } +// +// return nil +// }) +//} +// +//func (w *BoltDB) GetHeightList() ([]uint32, error) { +// w.rwLock.Lock() +// defer w.rwLock.Unlock() +// +// var v []byte +// err := w.db.View(func(tx *bolt.Tx) error { +// _v := tx.Bucket(BKTHeightList).Get(BKTHeightList) +// v = make([]byte, len(_v)) +// copy(v, _v) +// return nil +// }) +// if err != nil { +// return nil, err +// } +// +// results := make([]uint32, len(v)/4) +// for i := 0; i < len(results); i++ { +// results[i] = helper.BytesToUInt32(v[4*i:4*i+4]) +// } +// return results, nil +//} +// +//func (w *BoltDB) PutValueInHeightList(height uint32) error { +// heightList, err := w.GetHeightList() +// if err != nil { +// return err +// } +// +// newHeightList := make([]uint32, len(heightList)+1) +// // find the position to insert +// i := 0 +// for height < heightList[i] { +// i++ +// } +// copy(newHeightList[:i], heightList[:i]) +// newHeightList[i] = height +// copy(newHeightList[i+1:], heightList[i:]) +// +// err = w.PutHeightList(newHeightList) +// if err != nil { +// return err +// } +// return nil +//} func (w *BoltDB) Close() { w.rwLock.Lock() diff --git a/go.mod b/go.mod index da4632a..6b308ef 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,8 @@ require ( github.com/boltdb/bolt v1.3.1 github.com/btcsuite/btcd v0.20.1-beta github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e // indirect - github.com/joeqian10/neo3-gogogo v0.3.8 + github.com/joeqian10/neo-gogogo v1.3.0 + github.com/joeqian10/neo3-gogogo v1.0.0 github.com/ontio/ontology-crypto v1.0.9 github.com/polynetwork/poly v0.0.0-20200722075529-eea88acb37b2 github.com/polynetwork/poly-go-sdk v0.0.0-20200722030827-6875b6018b93 diff --git a/go.sum b/go.sum index d9cba37..e2856c7 100644 --- a/go.sum +++ b/go.sum @@ -320,8 +320,10 @@ github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/joeqian10/neo-gogogo v0.0.0-20200611102831-c17de5e1f0f8 h1:C+PIS6p7oQ4DwT+1IJkIvS+BMLn9TodglcqcGzhE+fQ= github.com/joeqian10/neo-gogogo v0.0.0-20200611102831-c17de5e1f0f8/go.mod h1:1fVDp4U1ROZQBRIooecbGNHHJpfs3bG9528sqlZ096g= -github.com/joeqian10/neo3-gogogo v0.3.8 h1:oOAcdUeIFjE4g+93Fsgf/fjYkZdOPt3PNefE223Mi/A= -github.com/joeqian10/neo3-gogogo v0.3.8/go.mod h1:k0wb1hcBjjspDpyHtEXIpDUEXAw5SfX7coi5AkNtxoU= +github.com/joeqian10/neo-gogogo v1.3.0 h1:wHN++OOZONic0tkIk8+Qk6EvHJh+YHcIHVhQLSxjzao= +github.com/joeqian10/neo-gogogo v1.3.0/go.mod h1:1fVDp4U1ROZQBRIooecbGNHHJpfs3bG9528sqlZ096g= +github.com/joeqian10/neo3-gogogo v1.0.0 h1:KXNJ3ya2H6JU445hMEs04jd0g74ia4v4uAqEoF4bfcc= +github.com/joeqian10/neo3-gogogo v1.0.0/go.mod h1:k0wb1hcBjjspDpyHtEXIpDUEXAw5SfX7coi5AkNtxoU= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= diff --git a/main.go b/main.go index c1c3375..979fe1e 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + rpc2 "github.com/joeqian10/neo-gogogo/rpc" "github.com/joeqian10/neo3-gogogo/helper" "github.com/joeqian10/neo3-gogogo/rpc" "github.com/joeqian10/neo3-gogogo/wallet" @@ -109,8 +110,11 @@ func startSync(ctx *cli.Context) { } wh := wallet.NewWalletHelperFromWallet(neoRpcClient, w) + // add neo2 sdk + neo2RpcClient := rpc2.NewClient(config.DefConfig.Neo2RpcUrl) + //Start syncing - syncService := service.NewSyncService(account, relaySdk, wh, neoRpcClient) + syncService := service.NewSyncService(account, relaySdk, wh, neoRpcClient, neo2RpcClient) syncService.Run() waitToExit() diff --git a/service/ntorMethods.go b/service/ntorMethods.go index 56e2ce6..ad2476b 100644 --- a/service/ntorMethods.go +++ b/service/ntorMethods.go @@ -122,7 +122,7 @@ func (this *SyncService) syncProofToRelay(key string, height uint32) error { buff := io.NewBufBinaryWriter() stateRoot.Serialize(buff.BinaryWriter) crossChainMsg := buff.Bytes() - log.Infof("stateroot: %s", helper.BytesToHex(crossChainMsg)) + //log.Infof("stateroot: %s", helper.BytesToHex(crossChainMsg)) //msg := this.GetStateRootMsg(stateRoot) //log.Infof("message: %s", helper.BytesToHex(msg)) @@ -139,7 +139,7 @@ func (this *SyncService) syncProofToRelay(key string, height uint32) error { if err != nil { return fmt.Errorf("[syncProofToRelay] decode proof error: %s", err) } - log.Info("proof: %s", helper.BytesToHex(proof)) + //log.Info("proof: %s", helper.BytesToHex(proof)) // following for testing only //id, k, proofs, err := mpt.ResolveProof(proof) diff --git a/service/ntorService.go b/service/ntorService.go index 74eb257..3af92d7 100644 --- a/service/ntorService.go +++ b/service/ntorService.go @@ -109,11 +109,11 @@ func (this *SyncService) neoToRelay(m, n uint32) error { return fmt.Errorf("[neoToRelay] notification.State.Value error: Wrong length of states") } // when empty, relay everything - if this.config.SpecificContract != "" { + if this.config.NtorContract != "" { // this loop check it is for this specific contract for index, ntf := range notifications { v, _ := helper.UInt160FromString(ntf.Contract) - if "0x"+v.String() != this.config.SpecificContract { + if "0x"+v.String() != this.config.NtorContract { if index < len(notifications)-1 { continue } diff --git a/service/rtonMethods.go b/service/rtonMethods.go index 116e134..a4070d6 100644 --- a/service/rtonMethods.go +++ b/service/rtonMethods.go @@ -9,6 +9,8 @@ import ( "errors" "fmt" "github.com/btcsuite/btcd/btcec" + helper2 "github.com/joeqian10/neo-gogogo/helper" + models2 "github.com/joeqian10/neo-gogogo/rpc/models" "github.com/joeqian10/neo3-gogogo/crypto" "github.com/joeqian10/neo3-gogogo/helper" "github.com/joeqian10/neo3-gogogo/rpc/models" @@ -22,7 +24,6 @@ import ( "github.com/polynetwork/neo3-relayer/log" "github.com/polynetwork/poly/common" "github.com/polynetwork/poly/core/types" - "strconv" "strings" "time" @@ -136,6 +137,8 @@ func (this *SyncService) changeBookKeeper(block *types.Block) error { return fmt.Errorf("[changeBookKeeper] sc.MakeScript error: %s", err) } + log.Infof("script: " + crypto.Base64Encode(script)) + balancesGas, err := this.nwh.GetAccountAndBalance(tx.GasToken) if err != nil { return fmt.Errorf("[changeBookKeeper] WalletHelper.GetAccountAndBalance error: %s", err) @@ -195,7 +198,7 @@ func (this *SyncService) syncProofToNeo(key string, txHeight, lastSynced uint32) Type: sc.ByteArray, Value: path, } - log.Infof("txProof: " + helper.BytesToHex(path)) + //log.Infof("txProof: " + helper.BytesToHex(path)) // get the next block header since it has the stateroot for the cross chain tx blockHeightToBeVerified := txHeight + 1 @@ -207,10 +210,10 @@ func (this *SyncService) syncProofToNeo(key string, txHeight, lastSynced uint32) Type: sc.ByteArray, Value: headerToBeVerified.GetMessage(), } - log.Infof("txProofHeader: " + helper.BytesToHex(headerToBeVerified.GetMessage())) + //log.Infof("txProofHeader: " + helper.BytesToHex(headerToBeVerified.GetMessage())) // check constraints - if this.config.SpecificContract != "" { // if empty, relay everything + if this.config.RtonContract != "" { // if empty, relay everything stateRootValue, err := MerkleProve(path, headerToBeVerified.CrossStateRoot.ToArray()) if err != nil { return fmt.Errorf("[syncProofToNeo] MerkleProve error: %s", err) @@ -220,7 +223,7 @@ func (this *SyncService) syncProofToNeo(key string, txHeight, lastSynced uint32) if err != nil { return fmt.Errorf("[syncProofToNeo] DeserializeMerkleValue error: %s", err) } - if helper.BytesToHex(toMerkleValue.TxParam.ToContract) != this.config.SpecificContract { + if helper.BytesToHex(toMerkleValue.TxParam.ToContract) != this.config.RtonContract { log.Infof(helper.BytesToHex(toMerkleValue.TxParam.ToContract)) log.Infof("This cross chain tx is not for this specific contract.") return nil @@ -267,15 +270,14 @@ func (this *SyncService) syncProofToNeo(key string, txHeight, lastSynced uint32) Type: sc.ByteArray, Value: headerProofBytes, } - log.Infof("headerProof: " + helper.BytesToHex(headerProofBytes)) + //log.Infof("headerProof: " + helper.BytesToHex(headerProofBytes)) currentHeader := sc.ContractParameter{ Type: sc.ByteArray, Value: currentHeaderBytes, } - log.Infof("currentHeader: " + helper.BytesToHex(currentHeaderBytes)) - - log.Infof("headerHash: 0x" + headerHash.ToHexString()) + //log.Infof("currentHeader: " + helper.BytesToHex(currentHeaderBytes)) + //log.Infof("headerHash: 0x" + headerHash.ToHexString()) hasher := goc.SHA256.New() hasher.Write(headerHash.ToArray()) @@ -289,7 +291,7 @@ func (this *SyncService) syncProofToNeo(key string, txHeight, lastSynced uint32) Type: sc.ByteArray, Value: signListBytes, } - log.Infof("signList: " + helper.BytesToHex(signListBytes)) + //log.Infof("signList: " + helper.BytesToHex(signListBytes)) stateRootValue, err := MerkleProve(path, headerToBeVerified.CrossStateRoot.ToArray()) if err != nil { @@ -299,15 +301,15 @@ func (this *SyncService) syncProofToNeo(key string, txHeight, lastSynced uint32) if err != nil { return fmt.Errorf("[syncProofToNeo] DeserializeMerkleValue error: %s", err) } - log.Infof("fromChainId: " + strconv.Itoa(int(toMerkleValue.FromChainID))) - log.Infof("polyTxHash: " + helper.BytesToHex(toMerkleValue.TxHash)) - log.Infof("fromContract: " + helper.BytesToHex(toMerkleValue.TxParam.FromContract)) - log.Infof("toChainId: " + strconv.Itoa(int(toMerkleValue.TxParam.ToChainID))) - log.Infof("sourceTxHash: " + helper.BytesToHex(toMerkleValue.TxParam.TxHash)) - log.Infof("toContract: " + helper.BytesToHex(toMerkleValue.TxParam.ToContract)) - log.Infof("method: " + helper.BytesToHex(toMerkleValue.TxParam.Method)) - log.Infof("TxParamArgs: " + helper.BytesToHex(toMerkleValue.TxParam.Args)) + //log.Infof("fromChainId: " + strconv.Itoa(int(toMerkleValue.FromChainID))) + //log.Infof("polyTxHash: " + helper.BytesToHex(toMerkleValue.TxHash)) + //log.Infof("fromContract: " + helper.BytesToHex(toMerkleValue.TxParam.FromContract)) + //log.Infof("toChainId: " + strconv.Itoa(int(toMerkleValue.TxParam.ToChainID))) + //log.Infof("sourceTxHash: " + helper.BytesToHex(toMerkleValue.TxParam.TxHash)) + //log.Infof("toContract: " + helper.BytesToHex(toMerkleValue.TxParam.ToContract)) + //log.Infof("method: " + helper.BytesToHex(toMerkleValue.TxParam.Method)) + //log.Infof("TxParamArgs: " + helper.BytesToHex(toMerkleValue.TxParam.Args)) //toAssetHash, toAddress, amount, err := DeserializeArgs(toMerkleValue.TxParam.Args) //if err != nil { // return fmt.Errorf("[syncProofToNeo] DeserializeArgs error: %s", err) @@ -316,6 +318,18 @@ func (this *SyncService) syncProofToNeo(key string, txHeight, lastSynced uint32) //log.Infof("toAddress: " + helper.BytesToHex(toAddress)) //log.Infof("amount: " + amount.String()) + // check if source hash app log includes wrapper contract + sourceTxHash := helper.UInt256FromBytes(toMerkleValue.TxParam.TxHash) + txId := sourceTxHash.String() + res := this.neo2Sdk.GetApplicationLog(txId) + if res.HasError() { + return fmt.Errorf("[syncProofToNeo] this.neo2Sdk.GetApplicationLog error: %s", res.GetErrorInfo()) + } + if !this.checkIsNeo2Wrapper(res.Result) { + log.Infof("[syncProofToNeo] this tx 0x%s is not from neo2 wrapper", txId) + return nil + } + // build script scriptHash, err := helper.UInt160FromString(this.config.NeoCCMC) if err != nil { @@ -325,7 +339,7 @@ func (this *SyncService) syncProofToNeo(key string, txHeight, lastSynced uint32) if err != nil { return fmt.Errorf("[syncProofToNeo] sc.MakeScript error: %s", err) } - log.Infof("script: " + helper.BytesToHex(script)) + //log.Infof("script: " + helper.BytesToHex(script)) balancesGas, err := this.nwh.GetAccountAndBalance(tx.GasToken) if err != nil { return fmt.Errorf("[syncProofToNeo] WalletHelper.GetAccountAndBalance error: %s", err) @@ -337,15 +351,16 @@ func (this *SyncService) syncProofToNeo(key string, txHeight, lastSynced uint32) } sink := common.NewZeroCopySink(nil) retry.Serialization(sink) + v := sink.Bytes() trx, err := this.nwh.MakeTransaction(script, nil, []tx.ITransactionAttribute{}, balancesGas) if err != nil { if strings.Contains(err.Error(), "insufficient GAS") { - err = this.db.PutNeoRetry(sink.Bytes()) + err = this.db.PutNeoRetry(v) // this tx is not ready thus will not cost extra gas, so put it into retry if err != nil { return fmt.Errorf("[syncProofToNeo] this.db.PutNeoRetry error: %s", err) } - log.Infof("[syncProofToNeo] put tx into retry db, height %d, key %s, db key %s", txHeight, key, helper.BytesToHex(sink.Bytes())) + log.Infof("[syncProofToNeo] put tx into retry db, height %d, key %s, db key %s", txHeight, key, helper.BytesToHex(v)) return nil } return fmt.Errorf("[syncProofToNeo] WalletHelper.MakeTransaction error: %s", err) @@ -358,20 +373,30 @@ func (this *SyncService) syncProofToNeo(key string, txHeight, lastSynced uint32) } rawTxString := crypto.Base64Encode(trx.ToByteArray()) - log.Infof("rawTxString: " + rawTxString) + //log.Infof("rawTxString: " + rawTxString) // send the raw transaction response := this.neoSdk.SendRawTransaction(rawTxString) if response.HasError() { - err = this.db.PutNeoRetry(sink.Bytes()) - if err != nil { - return fmt.Errorf("[syncProofToNeo] this.db.PutNeoRetry error: %s", err) - } - log.Errorf("[syncProofToNeo] put tx into retry db, height %d, key %s, db key %s", txHeight, key, helper.BytesToHex(sink.Bytes())) - return fmt.Errorf("[syncProofToNeo] SendRawTransaction error: %s, path(cp1): %s, cp2: %d, syncProofToNeo RawTransactionString: %s", - response.ErrorResponse.Error.Message, helper.BytesToHex(path), int64(blockHeightReliable), rawTxString) + return fmt.Errorf("[syncProofToNeo] SendRawTransaction error: %s, "+ + "tx height: %d, "+ + "key hex string: %s, "+ + "block height reliable: %d"+ + "script hex string: %s, "+ + "raw tx string: %s", + response.ErrorResponse.Error.Message, + txHeight, + key, + blockHeightReliable, + helper.BytesToHex(script), + rawTxString) + } + txHash := trx.GetHash().String() + log.Infof("[syncProofToNeo] syncProofToNeo txHash is: %s", txHash) + err = this.db.PutNeoCheck(txHash, v) + if err != nil { + return fmt.Errorf("[syncProofToNeo] this.db.PutNeoCheck error: %s", err) } - log.Infof("[syncProofToNeo] syncProofToNeo txHash is: %s", trx.GetHash().String()) return nil } @@ -437,7 +462,7 @@ func (this *SyncService) retrySyncProofToNeo(v []byte, lastSynced uint32) error if err != nil { return fmt.Errorf("[retrySyncProofToNeo] merkleProof DecodeString error: %s", err) } - log.Infof("headerPath: " + helper.BytesToHex(headerProofBytes)) + //log.Infof("headerPath: " + helper.BytesToHex(headerProofBytes)) // get the raw current header headerReliable, err := this.relaySdk.GetHeaderByHeight(blockHeightReliable) @@ -476,6 +501,27 @@ func (this *SyncService) retrySyncProofToNeo(v []byte, lastSynced uint32) error } //log.Infof("signList: " + helper.BytesToHex(signListBytes)) + stateRootValue, err := MerkleProve(path, headerToBeVerified.CrossStateRoot.ToArray()) + if err != nil { + return fmt.Errorf("[retrySyncProofToNeo] MerkleProve error: %s", err) + } + toMerkleValue, err := DeserializeMerkleValue(stateRootValue) + if err != nil { + return fmt.Errorf("[retrySyncProofToNeo] DeserializeMerkleValue error: %s", err) + } + + // check if source hash app log includes wrapper contract + sourceTxHash := helper.UInt256FromBytes(toMerkleValue.TxParam.TxHash) + txId := sourceTxHash.String() + res := this.neo2Sdk.GetApplicationLog(txId) + if res.HasError() { + return fmt.Errorf("[retrySyncProofToNeo] this.neo2Sdk.GetApplicationLog error: %s", res.GetErrorInfo()) + } + if !this.checkIsNeo2Wrapper(res.Result) { + log.Infof("[retrySyncProofToNeo] this tx 0x%s is not from neo2 wrapper", txId) + return nil + } + // build script scriptHash, err := helper.UInt160FromString(this.config.NeoCCMC) // hex string in little endian if err != nil { @@ -485,7 +531,7 @@ func (this *SyncService) retrySyncProofToNeo(v []byte, lastSynced uint32) error if err != nil { return fmt.Errorf("[syncProofToNeo] sc.MakeScript error: %s", err) } - log.Infof("script: " + helper.BytesToHex(script)) + //log.Infof("script: " + helper.BytesToHex(script)) balancesGas, err := this.nwh.GetAccountAndBalance(tx.GasToken) if err != nil { return fmt.Errorf("[syncProofToNeo] WalletHelper.GetAccountAndBalance error: %s", err) @@ -501,35 +547,92 @@ func (this *SyncService) retrySyncProofToNeo(v []byte, lastSynced uint32) error return fmt.Errorf("[syncProofToNeo] WalletHelper.SignTransaction error: %s", err) } rawTxString := crypto.Base64Encode(trx.ToByteArray()) - log.Infof("rawTxString: " + rawTxString) + //log.Infof("rawTxString: " + rawTxString) // send the raw transaction response := this.neoSdk.SendRawTransaction(rawTxString) if response.HasError() { - if strings.Contains(response.ErrorResponse.Error.Message, "Block or transaction validation failed") { - log.Infof("[retrySyncProofToNeo] remain tx in retry db, SendRawTransaction: height %d, key %s, db key %s", txHeight, key, helper.BytesToHex(v)) - return nil - } - - err := this.db.DeleteNeoRetry(v) - if err != nil { - return fmt.Errorf("[retrySyncProofToNeo] this.db.DeleteNeoRetry error: %s", err) - } - log.Infof("[retrySyncProofToNeo] delete tx from retry db, height %d, key %s, db key %s", txHeight, key, helper.BytesToHex(v)) - return fmt.Errorf("[retrySyncProofToNeo] SendRawTransaction error: %s, path(cp1): %s, cp2: %d, syncProofToNeo RawTransactionString: %s", - response.ErrorResponse.Error.Message, helper.BytesToHex(path), int64(blockHeightReliable), rawTxString) + return fmt.Errorf("[retrySyncProofToNeo] SendRawTransaction error: %s, "+ + "tx height: %d, "+ + "key hex string: %s, "+ + "block height reliable: %d"+ + "script hex string: %s, "+ + "raw tx string: %s", + response.ErrorResponse.Error.Message, + txHeight, + key, + blockHeightReliable, + helper.BytesToHex(script), + rawTxString) + } + txHash := trx.GetHash().String() + log.Infof("[retrySyncProofToNeo] syncProofToNeo txHash is: %s", txHash) + err = this.db.PutNeoCheck(txHash, v) + if err != nil { + return fmt.Errorf("[retrySyncProofToNeo] this.db.PutNeoCheck error: %s", err) } - log.Infof("[retrySyncProofToNeo] syncProofToNeo txHash is: %s", trx.GetHash().String()) - err = this.db.DeleteNeoRetry(v) if err != nil { - err := this.db.DeleteNeoRetry(v) - log.Infof("[retrySyncProofToNeo] delete tx from retry db, height %d, key %s, db key %s", txHeight, key, helper.BytesToHex(v)) return fmt.Errorf("[retrySyncProofToNeo] this.db.DeleteNeoRetry error: %s", err) } return nil } +func (this *SyncService) neoCheckTx() error { + checkMap, err := this.db.GetNeoAllCheck() + if err != nil { + return fmt.Errorf("[neoCheckTx] this.db.GetNeoAllCheck error: %s", err) + } + for k, v := range checkMap { + retry := new(db.Retry) + err := retry.Deserialization(common.NewZeroCopySource(v)) + if err != nil { + return fmt.Errorf("[neoCheckTx] retry.Deserialization error: %s", err) + } + // start check tx + res := this.neoSdk.GetApplicationLog(k) + if res.HasError() { + info := res.GetErrorInfo() + if !strings.Contains(info, "Unknown transaction/blockhash") { + log.Errorf("[neoCheckTx] this.neoSdk.GetApplicationLog error: %s, txHash: %s", res.GetErrorInfo(), k) + } + continue + } + // can delete check now + err = this.db.DeleteNeoCheck(k) + if err != nil { + return fmt.Errorf("[neoCheckTx] this.db.DeleteNeoCheck error: %s", err) + } + appLog := res.Result + if len(appLog.Executions) < 1 { + log.Errorf("[neoCheckTx] this.neoSdk.GetApplicationLog error: no executions, txHash: %s", k) + continue + } + exec := appLog.Executions[0] + if exec.VMState == "FAULT" { + log.Errorf("[neoCheckTx] tx engine faulted, height: %d, key: %s, exception: %s", retry.Height, retry.Key, exec.Exception) + continue + } + if len(exec.Stack) < 1 { + log.Errorf("[neoCheckTx] this.neoSdk.GetApplicationLog error: no stack result, txHash: %s", k) + continue + } + stack := exec.Stack[0] + if stack.Type == "Boolean" { + b := stack.Value.(bool) + if b == false { + notifications := exec.Notifications + if !appLogNotificationContains(notifications, this.config.NeoCCMC, "Transaction has been executed") { // if executed, skip + log.Errorf("[neoCheckTx] tx stack result is false, height: %d, key: %s, check app log details and retry", retry.Height, retry.Key) + } + continue + } + } + log.Infof("[neoCheckTx] tx is successful, hash: %s, height: %d", k, retry.Height) + } + return nil +} + func (this *SyncService) neoRetryTx() error { retryList, err := this.db.GetAllNeoRetry() if err != nil { @@ -633,13 +736,30 @@ func (this *SyncService) sortSignatures(sigs [][]byte, hash []byte) ([]byte, err return sortSignatures(this.relayPubKeys, sigs, hash) } +func (this *SyncService) checkIsNeo2Wrapper(applicationLog models2.RpcApplicationLog) bool { + for _, execution := range applicationLog.Executions { + if execution.VMState == "FAULT" { + return false + } + notifications := execution.Notifications + for _, notification := range notifications { + u, _ := helper2.UInt160FromString(notification.Contract) + s := "0x"+u.String() + if s == this.config.Neo2Wrapper { + return true + } + } + } + return false +} + func sortSignatures(pubKeys, sigs [][]byte, hash []byte) ([]byte, error) { // sig length should >= 2/3 * len(pubKeys) + 1 if len(sigs) < len(pubKeys)*2/3+1 { return nil, fmt.Errorf("[sortSignatures] not enough signatures") } - sortedSigs := make([][]byte, len(pubKeys)) // len(relayPubKeys)? - log.Infof("before sorting sig: ") + sortedSigs := make([][]byte, len(pubKeys)) + //log.Infof("before sorting sig: ") for _, sig := range sigs { //log.Infof(helper.BytesToHex(sig)) pubKey, err := recoverPublicKeyFromSignature(sig, hash) // sig in BTC format @@ -769,3 +889,29 @@ func recoverPublicKeyFromSignature1(sig, hash []byte) ([]byte, error) { } return pubKey.SerializeCompressed(), nil // length in 65 } + +func appLogNotificationContains(notifications []models.RpcNotification, contract string, msg string) bool { + if len(notifications) != 0 { + for _, notif := range notifications { + if contract != "" { + if notif.Contract != contract { + continue + } + } + if notif.State.Type == "Array" { + notif.State.Convert() + results := notif.State.Value.([]models.InvokeStack) + for _, result := range results { + if result.Type == "ByteString" { + s := result.Value.(string) + bs, _ := crypto.Base64Decode(s) + if string(bs) == msg { + return true + } + } + } + } + } + } + return false +} diff --git a/service/rtonMethods_test.go b/service/rtonMethods_test.go index c25b4c7..0c3f2c5 100644 --- a/service/rtonMethods_test.go +++ b/service/rtonMethods_test.go @@ -166,8 +166,8 @@ func TestConvertPublicKey(t *testing.T) { } func Test666(t *testing.T) { - c := rpc.NewClient("http://seed1t.neo.org:21332") - r1 := c.GetRawTransaction("0x98fc9f6231fc53637ed01d777723e30190ec69ab489df048605a86ae06a017c7") + c := rpc.NewClient("http://seed1t.neo.org:20332") + r1 := c.GetRawTransaction("0x58f6b0fe73e1b6b68bd604ab6f2d06b816a6cfb96540db8b3b87b0987df99e1d") blockHash := r1.Result.BlockHash r2 := c.GetBlockHeader(blockHash) index := r2.Result.Index diff --git a/service/rtonService.go b/service/rtonService.go index 8217b40..3b08b68 100644 --- a/service/rtonService.go +++ b/service/rtonService.go @@ -29,12 +29,27 @@ func (this *SyncService) relayToNeo(m, n uint32) error { for i := m; i < n; i++ { log.Infof("[relayToNeo] start parse block %d", i) - // sync cross chain info - events, err := this.relaySdk.GetSmartContractEventByBlock(i) + block, err := this.relaySdk.GetBlockByHeight(i) if err != nil { - return fmt.Errorf("[relayToNeo] relaySdk.GetSmartContractEventByBlock error:%s", err) + return fmt.Errorf("[relayToNeo] GetBlockByHeight error: %s", err) } - for _, event := range events { + txs := block.Transactions + // sync cross chain info + //events, err := this.relaySdk.GetSmartContractEventByBlock(i) + //if err != nil { + // return fmt.Errorf("[relayToNeo] relaySdk.GetSmartContractEventByBlock error:%s", err) + //} + for _, tx := range txs { + //payer := tx.Payer.ToBase58() + //log.Infof(payer) + //for _, s := range tx.SignedAddr { + // log.Infof(s.ToBase58()) + //} + txHash := tx.Hash() + event, err := this.relaySdk.GetSmartContractEvent(txHash.ToHexString()) + if err != nil { + return fmt.Errorf("[relayToNeo] relaySdk.GetSmartContractEvent error:%s", err) + } for _, notify := range event.Notify { states, ok := notify.States.([]interface{}) if !ok { @@ -97,12 +112,16 @@ func (this *SyncService) relayToNeo(m, n uint32) error { return nil } -func (this *SyncService) RelayToNeoRetry() { +func (this *SyncService) RelayToNeoCheckAndRetry() { for { - err := this.neoRetryTx() + time.Sleep(time.Duration(this.config.ScanInterval) * time.Second) // 15 seconds a block + err := this.neoCheckTx() if err != nil { - log.Errorf("[RelayToNeoRetry] this.neoRetryTx error:%s", err) + log.Errorf("[RelayToNeoCheckAndRetry] this.neoCheckTx error: %s", err) + } + err = this.neoRetryTx() + if err != nil { + log.Errorf("[RelayToNeoCheckAndRetry] this.neoRetryTx error: %s", err) } - time.Sleep(time.Duration(this.config.ScanInterval) * time.Second) } } diff --git a/service/syncService.go b/service/syncService.go index 279bb31..3b4868a 100644 --- a/service/syncService.go +++ b/service/syncService.go @@ -1,6 +1,7 @@ package service import ( + rpc2 "github.com/joeqian10/neo-gogogo/rpc" "github.com/joeqian10/neo3-gogogo/rpc" "github.com/joeqian10/neo3-gogogo/wallet" "github.com/polynetwork/neo3-relayer/config" @@ -23,12 +24,14 @@ type SyncService struct { neoNextConsensus string neoStateRootHeight uint32 + neo2Sdk *rpc2.RpcClient + db *db.BoltDB config *config.Config } // NewSyncService ... -func NewSyncService(acct *rsdk.Account, relaySdk *rsdk.PolySdk, neoAccount *wallet.WalletHelper, client *rpc.RpcClient) *SyncService { +func NewSyncService(acct *rsdk.Account, relaySdk *rsdk.PolySdk, neoAccount *wallet.WalletHelper, client *rpc.RpcClient, client2 *rpc2.RpcClient) *SyncService { if !checkIfExist(config.DefConfig.DBPath) { os.Mkdir(config.DefConfig.DBPath, os.ModePerm) } @@ -43,6 +46,7 @@ func NewSyncService(acct *rsdk.Account, relaySdk *rsdk.PolySdk, neoAccount *wall neoSdk: client, neoStateRootHeight: 0, nwh: neoAccount, + neo2Sdk: client2, db: boltDB, config: config.DefConfig, } @@ -52,7 +56,7 @@ func NewSyncService(acct *rsdk.Account, relaySdk *rsdk.PolySdk, neoAccount *wall // Run ... func (this *SyncService) Run() { go this.RelayToNeo() - go this.RelayToNeoRetry() + go this.RelayToNeoCheckAndRetry() go this.NeoToRelay() go this.NeoToRelayCheckAndRetry() }