Cannot broadcast bitcoin transaction Golang - go

I've encouraged an error broadcasting my bitcoin transaction here.
Error validating transaction: Error running script for input 0 referencing 78b6df775e4132f747d8d15909cfe61834e6ff1d3b1e42219ace64aae7151e18 at 0: Script was NOT verified successfully..
I've tried to put decoded Asm PkScript instead of PubKey in TxIn. I've tried to find similar error on sof but their steps didn't work for me. Can anyone find out what is wrong with this tx? because my vm didn't find any troubles.
Hex transaction:
0100000001181e15e7aa64ce9a21421e3b1dffe63418e6cf0959d1d847f732415e77dfb678000000008a47304402200aa339f80c705a22180c8edd4acc4baa5bc76c9e8f64852eb3cb507f027aee9c02206da34700e76df568ef018fe346bb5b9c4484506ee84740916d6c3b454c2e1d3f0141042a1b4d9533ff37cd290b95016056cacbdbbf99b5975345dc5837a95f698f8a26d74f726e2a1e62552107953595502c8078a84311690c61ace3f22644ae33a0abffffffff02d0070000000000001976a91437383464376cccaf2ae4c8a121805f45bf544e4488acc8320000000000001976a914a404271e971d58240add530813e514b0ffe45f3588ac00000000
Decoded transaction:
{
"addresses": [
"mvUC64ETwYLbTjH38ZVigDwMBtCS9TbuT1",
"mkYvnmm3KUBkvVqUAYsG6A6amt5Dva4jzX"
],
"block_height": -1,
"block_index": -1,
"confirmations": 0,
"double_spend": false,
"fees": 5000,
"hash": "410f8119e9c225fc197a21ede7f9e6902f85a36010a2776dd3fdc2663072a36c",
"inputs": [
{
"addresses": [
"mvUC64ETwYLbTjH38ZVigDwMBtCS9TbuT1"
],
"age": 2345496,
"output_index": 0,
"output_value": 20000,
"prev_hash": "78b6df775e4132f747d8d15909cfe61834e6ff1d3b1e42219ace64aae7151e18",
"script": "47304402200aa339f80c705a22180c8edd4acc4baa5bc76c9e8f64852eb3cb507f027aee9c02206da34700e76df568ef018fe346bb5b9c4484506ee84740916d6c3b454c2e1d3f0141042a1b4d9533ff37cd290b95016056cacbdbbf99b5975345dc5837a95f698f8a26d74f726e2a1e62552107953595502c8078a84311690c61ace3f22644ae33a0ab",
"script_type": "pay-to-pubkey-hash",
"sequence": 4294967295
}
],
"outputs": [
{
"addresses": [
"mkYvnmm3KUBkvVqUAYsG6A6amt5Dva4jzX"
],
"script": "76a91437383464376cccaf2ae4c8a121805f45bf544e4488ac",
"script_type": "pay-to-pubkey-hash",
"value": 2000
},
{
"addresses": [
"mvUC64ETwYLbTjH38ZVigDwMBtCS9TbuT1"
],
"script": "76a914a404271e971d58240add530813e514b0ffe45f3588ac",
"script_type": "pay-to-pubkey-hash",
"value": 13000
}
],
"preference": "low",
"received": "2022-09-06T14:10:50.749940874Z",
"relayed_by": "44.204.10.232",
"size": 257,
"total": 15000,
"ver": 1,
"vin_sz": 1,
"vout_sz": 2,
"vsize": 257
}
func main() {
connCfg := &rpcclient.ConnConfig{ //smth here
}
client, err := rpcclient.New(connCfg, nil)
if err != nil {
log.Fatal(err)
}
defer client.Shutdown()
utxoMap := make(map[string]string)
utxoMap["mvUC64ETwYLbTjH38ZVigDwMBtCS9TbuT1"] = "78b6df775e4132f747d8d15909cfe61834e6ff1d3b1e42219ace64aae7151e18"
rawTx, err := CreateRawTx("mvUC64ETwYLbTjH38ZVigDwMBtCS9TbuT1", "mkYvnmm3KUBkvVqUAYsG6A6amt5Dva4jzX", 2000, utxoMap, client)
if err != nil {
println(err.Error())
}
signedTx, err := Sign(rawTx, "private key here")
if err != nil {
println(err.Error())
}
fmt.Println(signedTx)
}
type UTXO struct {
previousTxID string
pubKeyScript string
Asm string
Amount decimal.Decimal
}
func GetUTXO(utxoMap map[string]string, address string, client *rpcclient.Client) (utxo UTXO, err error) {
// unmarshal it, and extract necessary data
hash, _ := chainhash.NewHashFromStr(utxoMap[address])
tx, err := client.GetRawTransactionVerbose(hash)
if err != nil {
return utxo, errors.Wrapf(err, "GetUTXO.GetRawTransactionVerbose")
}
pubKeyScript := tx.Vout[0].ScriptPubKey.Hex
previousTxID := utxoMap[address]
amount := decimal.NewFromFloat(tx.Vout[0].Value)
asm := tx.Vout[0].ScriptPubKey.Hex
return UTXO{
previousTxID: previousTxID,
pubKeyScript: pubKeyScript,
Asm: asm,
Amount: amount,
}, nil
}
func GetPayToAddrScript(address string) []byte {
rcvAddress, _ := btcutil.DecodeAddress(address, &chaincfg.TestNet3Params)
rcvScript, _ := txscript.PayToAddrScript(rcvAddress)
return rcvScript
}
type TXRef struct {
TXHash string
TXOutputN int
}
// get private key
func getKeyAddressFromPrivateKey(privateKey string, conf *chaincfg.Params) (*btcec.PrivateKey, string, error) {
newWif, err := btcutil.DecodeWIF(privateKey)
if err != nil {
return nil, "", err
}
publicKey := newWif.PrivKey.PubKey().SerializeUncompressed()
address, err := btcutil.NewAddressPubKeyHash(btcutil.Hash160(publicKey), conf)
if err != nil {
return nil, "", err
}
return newWif.PrivKey, address.EncodeAddress(), nil
}
// debug signing function
func Sign(rawTx, privateKey string) (string, error) {
var signTx wire.MsgTx
err := json.Unmarshal([]byte(rawTx), &signTx)
if err != nil {
return "", err
}
myPrivateKey, address, err := getKeyAddressFromPrivateKey(privateKey, &chaincfg.TestNet3Params)
fmt.Println("address ", address)
if err != nil {
return "", err
}
txScript := GetPayToAddrScript(address)
for i := 0; i < len(signTx.TxIn); i++ {
sig, err := txscript.SignatureScript(
&signTx, // The tx to be signed.
i, // The index of the txin the signature is for.
txScript, // The other half of the script from the PubKeyHash.
txscript.SigHashAll, // The signature flags that indicate what the sig covers.
myPrivateKey, // The key to generate the signature with.
false) // The compress sig flag. This saves space on the blockchain.
if err != nil {
return "", err
}
//refer from this link for sign multiple inputs https://bitcoin.stackexchange.com/questions/41209/how-to-sign-a-transaction-with-multiple-inputs
signTx.TxIn[i].SignatureScript = sig
//Validate signature
flags := txscript.StandardVerifyFlags
vm, err := txscript.NewEngine(txScript, &signTx, i, flags, nil, nil, signTx.TxOut[0].Value, nil)
if err != nil {
return "", err
}
if err := vm.Execute(); err != nil {
return "", err
}
}
// return signed tx
var signedTx bytes.Buffer
signTx.Serialize(&signedTx)
hexSignedTx := hex.EncodeToString(signedTx.Bytes())
return hexSignedTx, nil
}
// CreateBtcRawTx create raw tx for sending btc
func CreateRawTx(from string, to string, amount int64, utxoMap map[string]string, client *rpcclient.Client) (string, error) {
amountInt := amount
var feeBitcoin int64 = 5000
spendAmount := amountInt + feeBitcoin
utxo, err := GetUTXO(utxoMap, from, client)
changeAmount := utxo.Amount.Shift(8).IntPart() - spendAmount
// create new empty transaction
redemTx := wire.NewMsgTx(wire.TxVersion)
// create multiple txIns
hash, err := chainhash.NewHashFromStr(utxo.previousTxID)
if err != nil {
fmt.Printf("could not get hash from transaction ID: %v", err)
return "", err
}
outPoint := wire.NewOutPoint(hash, 0)
txIn := wire.NewTxIn(outPoint, nil, nil)
redemTx.AddTxIn(txIn)
// create TxOut
rcvScript := GetPayToAddrScript(to)
txOut := wire.NewTxOut(amountInt, rcvScript)
redemTx.AddTxOut(txOut)
// create TxOut for change address, in this case, change address is sender itself
if changeAmount > 0 {
// return change BTC to its own address
rcvChangeAddressScript := GetPayToAddrScript(from)
txOut := wire.NewTxOut(changeAmount, rcvChangeAddressScript)
redemTx.AddTxOut(txOut)
}
encodedTx, err := json.Marshal(redemTx)
if err != nil {
return "", err
}
// return raw tx in hex format
return string(encodedTx), nil
}
my bitcoin wallet: mvUC64ETwYLbTjH38ZVigDwMBtCS9TbuT1
last transaction on the wallet (hash): 78b6df775e4132f747d8d15909cfe61834e6ff1d3b1e42219ace64aae7151e18

Related

I get an error in golang test and would like to know how to improve it

sta9_test.go:124: Cannot convert id to numeric, got = id
sta9_test.go:124: Cannot convert id to numericCannot convert id to numeric, got = id
sta9_test.go:145: It is a value we do not expect.
map[string]any{
- "UpdatedAt": string("2022-10-27T15:19:10Z"),
- "createdAenter code heret": string("2022-10-27T15:19:10Z"),
"description": string(""),}
func TestStation9(t *testing.T) {
dbPath := "./temp_test.db"
if err := os.Setenv("DB_PATH", dbPath); err != nil {
t.Error("dbPathのセットに失敗しました。", err)
return
}
t.Cleanup(func() {
if err := os.Remove(dbPath); err != nil {
t.Errorf("テスト用のDBファイルの削除に失敗しました: %v", err)
return
}
})
todoDB, err := db.NewDB(dbPath)
if err != nil {
t.Error("DBの作成に失敗しました。", err)
return
}
defer func(todoDB *sql.DB) {
err := todoDB.Close()
if err != nil {
t.Error("DBのクローズに失敗しました.", err)
}
}(todoDB)
r := router.NewRouter(todoDB)
srv := httptest.NewServer(r)
defer srv.Close()
testcases := map[string]struct {
Subject string
Description string
WantHTTPStatusCode int
}{
"Subject is empty": {
WantHTTPStatusCode: http.StatusBadRequest,
},
"Description is empty": {
Subject: "todo subject",
WantHTTPStatusCode: http.StatusOK,
},
"Subject and Description is not empty": {
Subject: "todo subject",
Description: "todo description",
WantHTTPStatusCode: http.StatusOK,
},
}
for name, tc := range testcases {
name := name
tc := tc
t.Run(name, func(t *testing.T) {
resp, err := http.Post(srv.URL+"/todos", "application/json",
bytes.NewBufferString(fmt.Sprintf(`{"subject":"%s","description":"%s"}`, tc.Subject, tc.Description)))
if err != nil {
t.Error("リクエストの送信に失敗しました。", err)
return
}
defer func() {
if err := resp.Body.Close(); err != nil {
t.Error("レスポンスのクローズに失敗しました。", err)
return
}
}()
if resp.StatusCode != tc.WantHTTPStatusCode {
t.Errorf("期待していない HTTP status code です, got = %d, want = %d", resp.StatusCode, tc.WantHTTPStatusCode)
return
}
if tc.WantHTTPStatusCode != http.StatusOK {
return
}
var m map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&m); err != nil {
t.Error("レスポンスのデコードに失敗しました。", err)
return
}
v, ok := m["todo"]
if !ok {
t.Error("レスポンスの中にtodoがありません。")
return
}
got, ok := v.(map[string]interface{})
if !ok {
t.Error("レスポンスの中のtodoがmapではありません。")
return
}
want := map[string]interface{}{
"subject": tc.Subject,
"description": tc.Description,
}
now := time.Now().UTC()
fmt.Println(got)
fmt.Println(want)
diff := cmp.Diff(got, want, cmpopts.IgnoreMapEntries(func(k string, v interface{}) bool {
switch k {
case "id":
if vv, _ := v.(float64); vv == 0 {
t.Errorf("id を数値に変換できません, got = %s", k)
}
return true
case "created_at", "updated_at":
vv, ok := v.(string)
if !ok {
t.Errorf("日付が文字列に変換できません, got = %+v", k)
return true
}
if tt, err := time.Parse(time.RFC3339, vv); err != nil {
t.Errorf("日付が期待しているフォーマットではありません, got = %s", k)
} else if now.Before(tt) {
t.Errorf("日付が未来の日付になっています, got = %s", tt)
}
return true
}
return false
}))
if diff != "" {
t.Error("期待していない値です\n", diff)
}
})
}
}
type TODOHandler struct {
svc *service.TODOService
}
// NewTODOHandler returns TODOHandler based http.Handler.
func NewTODOHandler(svc *service.TODOService) *TODOHandler {
return &TODOHandler{
svc: svc,
}
}
func (h *TODOHandler)ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost{
var todo model.CreateTODORequest
if err := json.NewDecoder(r.Body).Decode(&todo); err != nil {
log.Fatal(err)
}
if todo.Subject == ""{
w.WriteHeader(400)
}else{
createdTodo, _ := h.Create(r.Context(),&todo)
e := json.NewEncoder(w)
if err:= e.Encode(createdTodo); err != nil{
log.Fatal("ListenAndServe:", err)
}
}
}
}
// Create handles the endpoint that creates the TODO.
func (h *TODOHandler) Create(ctx context.Context, req *model.CreateTODORequest) (*model.CreateTODOResponse, error) {
createtodo, err := h.svc.CreateTODO(ctx, req.Subject, req.Description)
if err != nil{
log.Fatal(err)
}
return &model.CreateTODOResponse{TODO: *createtodo}, nil
}
type TODOService struct {
db *sql.DB
}
// NewTODOService returns new TODOService.
func NewTODOService(db *sql.DB) *TODOService {
return &TODOService{
db: db,
}
}
// CreateTODO creates a TODO on DB.
func (s *TODOService) CreateTODO(ctx context.Context, subject, description string) (*model.TODO, error) {
const (
insert = `INSERT INTO todos(subject, description) VALUES(?, ?)`
confirm = `SELECT subject, description, created_at, updated_at FROM todos WHERE id = ?`
)
var todo model.TODO
if subject == ""{
msg := "subjectがありません"
return &todo,fmt.Errorf("err %s", msg)
}
stmt,err := s.db.PrepareContext(ctx,insert)
if err != nil{
log.Fatal("server/todo.go s.db.ExecContext(ctx,insert,subject,description)",err)
}
defer stmt.Close()
res, err := stmt.ExecContext(ctx, subject,description)
if err != nil {
return nil, err
}
insert_id,err := res.LastInsertId()
if err != nil{
log.Fatal("server/todo.go stmt.RowsAffected()",err)
}
err = s.db.QueryRowContext(ctx,confirm,insert_id).Scan(&todo.Subject,&todo.Description,&todo.CreatedAt,&todo.UpdatedAt)
if err != nil{
log.Fatal("server/todo.go s.db.QueryRowContext(ctx,confirm,insert_id).Scan(&todo.Subject,&todo.Description,&todo.CreatedAt,&todo.UpdatedAt)",err)
}
return &todo, err
}
I am building a TODO application with TDD and have a question. I get the error at the top. What kind of error is this and how can I improve it?

How to unmarshal/parse a request using Go correctly?

How to parse a curl request response in Golang correctly? I have tried the following where I send a request to an api and its response is:
{
"Certificates": [
{
"Name": "some-name.here",
.......
}
],
"DataRange": "Certificates 1 - 1",
"TotalCount": 1
}
Now I want to use the Name in the Certificates in a string variable. i.e match. Before even I get to the looping through of the response, I get the error: json: cannot unmarshal object into Go value of type []program.Item. This error is coming from json.Unmarshal function where I pass my []bytes and the struct to use the bytes. Am I doing this correctly?
type Item struct {
Certificates []CertificatesInfo
}
type CertificatesInfo struct {
Name string
}
func main() {
url := .....
req, err := http.NewRequest("GET", url, nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
// handle err
continue
}
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
continue
}
var ItemInfo []Item
if err := json.Unmarshal(bodyBytes, &ItemInfo); err != nil {
return nil, fmt.Errorf("failed to parse %v", err)
}
for _, infos := range ItemInfo {
for _, names := range infos.Certificates {
infraId:= names.Name
match:= display_name
}
}
}
There's a mismatch between your sample JSON response data (a single object containing a list of certificates), and your code, which is expecting a list of objects, each of which contain a list of certificates).
Assuming that your JSON example is correct, this bit:
var ItemInfo []Item
if err := json.Unmarshal(bodyBytes, &ItemInfo); err != nil {
. . .
should probably be
var itemInfo Item
if err := json.Unmarshal(bodyBytes, &ItemInfo); err != nil {
. . .
Works on my machine:
https://go.dev/play/p/8FqIif1zzsQ
package main
import (
"encoding/json"
"fmt"
)
func main() {
resp := Item{}
err := json.Unmarshal([]byte(data), &resp)
if err != nil {
fmt.Print(err)
panic(err)
}
fmt.Printf("resp.DataRange: %s\n", resp.DataRange)
fmt.Printf("resp.TotalCount: %d\n", resp.TotalCount)
fmt.Printf("len(resp.Certificates: %d\n", len(resp.Certificates))
for i, c := range resp.Certificates {
fmt.Printf("resp.Certificates[%d].Name: %s\n", i, c.Name)
}
}
type Item struct {
Certificates []CertificatesInfo
DataRange string
TotalCount int
}
type CertificatesInfo struct {
Name string
}
const data = `
{
"Certificates": [
{ "Name": "alpha" },
{ "Name": "bravo" },
{ "Name": "charlie" }
],
"DataRange": "Certificates 1 - 1",
"TotalCount": 3
}
`

Failed Unmarshal for xxx invalid character '{' after top-level value

Failed Unmarshal for LP name invalid character '{' after top-level value
package main
import (...)
type hostInfo struct {
Name string
}
var b bytes.Buffer
var c bytes.Buffer
var tmphostInfo hostInfo
var result map[string]interface{}
func main() {
keypath := os.Args[1]
hostiplist := []string {"192.168.1.150","192.168.1.151","192.168.1.152","192.168.1.153"}
port := "22"
key,err := ioutil.ReadFile(keypath)
if err != nil {
log.Fatalf("Unable to read private key: %v", err)
}
signer,err:=ssh.ParsePrivateKey(key)
if err != nil {
log.Fatalf("Unable to parse private key: %v",err)
}
var conn *ssh.Client
config := &ssh.ClientConfig{
User: "support",
Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: 5*time.Second,
}
for _,hostip := range hostiplist {
conn,err = ssh.Dial("tcp",net.JoinHostPort(hostip,port),config)
if err != nil {
log.Fatalf("unable to connect: %v",err)
}
defer conn.Close()
session1,err:=conn.NewSession()
if err != nil {
log.Fatalf("unable to connect: %v",err)
}
defer session1.Close()
session1.Stdout = &b
if err := session1.Run("some mongo query"); err != nil {
log.Fatal("Failed to run:sess1 " + err.Error())
}
tmphostInfo=hostInfo{}
fmt.Printf("tmphostInfo: %v , ip : %s",tmphostInfo.Name,hostip)
err1 := json.Unmarshal([]byte(""),&tmphostInfo)
if err1 != nil {
log.Fatalf("Failed Unmarshal for LP name %v for IP %s",err1,hostip)
}
session2,err:=conn.NewSession()
session2.Stdout = &c
if err := session2.Run("another mongo query"); err != nil {
log.Fatal("Failed to run:sess2 " + err.Error())
}
err2 := json.Unmarshal([]byte(c.String()),&result)
if err2 != nil {
log.Fatalf("Failed Unmarshal for Customer name %v",err2)
}
//fmt.Println(c.String())
custmap := result["customer"].(map[string]interface{})
//fmt.Println(birds)
var LPname string
for key, value := range custmap {
// Each value is an interface{} type, that is type asserted as a string
//fmt.Println(key, value.(string))
if key == "name" {
LPname=value.(string)
}
}
fmt.Println(hostip,tmphostInfo.Name,LPname)
f, err := os.OpenFile("/tmp/supportip.csv", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
if _, err := f.Write([]byte(hostip+","+tmphostInfo.Name+","+LPname+"\n")); err != nil {
log.Fatal(err)
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
}
}
go run scan.go /path/to/key results in
tmphostInfo: {} , ip : 192.168.1.150 10.45.9.141 servername1 DFIDorg
tmphostInfo: {} , ip : 192.168.1.151 2021/12/10 15:07:36 Failed Unmarshal for LP name invalid character '{' after top-level value for 192.168.1.151
exit status 1
The unmarshal for both queries result for first array element "192.168.1.150" is successful but fails on second and rest of items
The json string for first query looks like below
{ "name" : "john doe" }
result of second query looks like
{
"customer" : {
"address1" : "ktm",
"secret_key" : "12237918f0d441d25fb",
"address2" : "",
"name" : "KUKL Limited",
"phone" : "12345"
}
}
I had to reset the bytes.Buffer variable right after the for loop start, the issue was that the result pulled from ssh execution had been appended to buffer which could not be deserialized
for _,hostip := range hostiplist {
b.Reset()
c.Reset()

Getting More than 1 push notification using go lang cron

I am creating GO rest APIs. We are using AWS server.
I want to send push notification to mobile. Then I used
https://pkg.go.dev/github.com/robfig/cron (https://github.com/robfig/cron )
for creating cron job.
We are using 2 version of API, V1(old one) and V1.1(new one)
we have more than 1 environment dev,QA,preproduction,production
in our go lang code I created a cron job for sending push notification to mobile. and the function called inside main().
But we are getting 2 notification each interval.
I didn't understand why 2 one is getting at a time
I am attaching my code.
const title = "This Week’s Activity"
func NotifyWeeklyActivity(db *sql.DB, logger *zap.Logger) {
logger.Info("NotifyWeeklyActivity- start")
c := cron.New()
c.AddFunc("*/5 * * * *", func() {
lastweekTime := CurrentUTC().AddDate(0, 0, -7)
type PostCount struct {
HiveID uint64 `json:"hive_id"`
Post uint64 `json:"post"`
NotificationTopicArn null.String `json:"notification_topic_arn"`
}
var posts []PostCount
err := queries.Raw(`
select count(post_id) as post , post.hive_id as hive_id , hive.notification_topic_arn
from post
join hive on post.hive_id=hive.hive_id and hive.deleted_at is null
where post.deleted_at is null
and hive.deleted_at is null
and post.created_at between ? and ?
group by hive_id
having count(post_id)>3 ;
`, lastweekTime, CurrentUTC()).Bind(context.TODO(), db, &posts)
if err != nil {
logger.Error("error while fetching data ", zap.Error(err))
// return err
}
cfg, _ := config.GetImpart()
if cfg.Env != config.Local {
notification := NewImpartNotificationService(db, string(cfg.Env), cfg.Region, cfg.IOSNotificationARN, logger)
logger.Info("Notification- fetching complted")
for _, hive := range posts {
pushNotification := Alert{
Title: aws.String(title),
Body: aws.String(
fmt.Sprintf("Check out %d new posts in your Hive this week", hive.Post),
),
}
additionalData := NotificationData{
EventDatetime: CurrentUTC(),
HiveID: hive.HiveID,
}
Logger.Info("Notification",
zap.Any("pushNotification", pushNotification),
zap.Any("additionalData", additionalData),
zap.Any("hive", hive),
)
err = notification.NotifyTopic(context.Background(), additionalData, pushNotification, hive.NotificationTopicArn.String)
if err != nil {
logger.Error("error sending notification to topic", zap.Error(err))
}
}
}
})
c.Start()
}
func NewImpartNotificationService(db *sql.DB, stage, region, platformApplicationARN string, logger *zap.Logger) NotificationService {
//SNS not available in us-east-2
if strings.EqualFold(region, "us-east-2") {
region = "us-east-1"
}
sess, err := session.NewSession(&aws.Config{
Region: aws.String(region),
HTTPClient: NewHttpClient(10 * time.Second),
})
if err != nil {
logger.Fatal("unable to create aws session", zap.Error(err))
}
snsAppleNotificationService := &snsAppleNotificationService{
stage: stage,
Logger: logger,
SNS: sns.New(sess),
platformApplicationARN: platformApplicationARN,
db: db,
}
logger.Debug("created new NotificationService",
zap.String("stage", stage),
zap.String("arn", platformApplicationARN))
return snsAppleNotificationService
}
Why I am getting 2 notification at a time ?
How can I Solve this
func (ns *snsAppleNotificationService) NotifyTopic(ctx context.Context, data NotificationData, alert Alert, topicARN string) error {
var b []byte
var err error
if strings.TrimSpace(topicARN) == "" {
return nil
}
ns.Logger.Debug("sending push notification",
zap.Any("data", data),
zap.Any("msg", alert),
zap.String("platformEndpoint", topicARN),
zap.String("arn", ns.platformApplicationARN))
if b, err = json.Marshal(apnsMessageWrapper{
APNSData: APNSMessage{
Alert: alert,
Sound: aws.String("default"),
Data: data,
Badge: aws.Int(0),
},
}); err != nil {
return err
}
msg := awsSNSMessage{Default: *alert.Body}
msg.APNS = string(b)
msg.APNSSandbox = string(b)
if b, err = json.Marshal(msg); err != nil {
return err
}
input := &sns.PublishInput{
Message: aws.String(string(b)),
MessageStructure: aws.String("json"),
TopicArn: aws.String(topicARN),
}
// print()
_, err = ns.Publish(input)
if err != nil {
ns.Logger.Error("push-notification : After publish input",
zap.Any("topicARN", topicARN),
zap.Error(err),
)
}
return err
}
main fuction
func main() {
logger, err := zap.NewProduction()
if err != nil {
log.Fatal(err)
}
cfg, err := config.GetImpart()
if err != nil {
logger.Fatal("error parsing config", zap.Error(err))
}
if cfg == nil {
logger.Fatal("nil config")
return
}
if cfg.Debug {
gin.SetMode(gin.DebugMode)
//boil.DebugMode = true
boil.WithDebugWriter(context.TODO(), &config.ZapBoilWriter{Logger: logger})
logger, _ = zap.NewDevelopment()
if cfg.Env == config.Local || cfg.Env == config.Development {
logger.Debug("config startup", zap.Any("config", *cfg))
}
} else {
gin.SetMode(gin.ReleaseMode)
}
//init the sentry logger ,either debug
logger, err = impart.InitSentryLogger(cfg, logger, cfg.Debug)
if err != nil {
logger.Error("error on sentry init", zap.Any("error", err))
}
migrationDB, err := cfg.GetMigrationDBConnection()
if err != nil {
logger.Fatal("unable to connect to DB", zap.Error(err))
}
//Trap sigterm during migraitons
migrationsDoneChan := make(chan bool)
shutdownMigrationsChan := make(chan bool)
sigc := make(chan os.Signal, 1)
signal.Notify(sigc,
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGQUIT)
go func() {
select {
case <-sigc:
logger.Info("received a shutdown request during migrations, sending shutdown signal")
shutdownMigrationsChan <- true
case <-migrationsDoneChan:
logger.Info("migrations complete, no longer waiting for sig int")
return
}
}()
err = migrater.RunMigrationsUp(migrationDB, cfg.MigrationsPath, logger, shutdownMigrationsChan)
if err != nil {
logger.Fatal("error running migrations", zap.Error(err))
}
migrationsDoneChan <- true
if err := migrationDB.Close(); err != nil {
logger.Fatal("error closing migrations DB connection", zap.Error(err))
}
boil.SetLocation(time.UTC)
db, err := cfg.GetDBConnection()
if err != nil {
logger.Fatal("unable to connect to DB", zap.Error(err))
}
defer db.Close()
defer logger.Sync()
// if err := migrater.BootStrapAdminUsers(db, cfg.Env, logger); err != nil {
// logger.Fatal("unable to bootstrap user", zap.Error(err))
// }
// if err := migrater.BootStrapTopicHive(db, cfg.Env, logger); err != nil {
// logger.Fatal("unable to bootstrap user", zap.Error(err))
// }
// initiate global profanity detector
impart.InitProfanityDetector(db, logger)
impart.NotifyWeeklyActivity(db, logger)
services := setupServices(cfg, db, logger)
r := gin.New()
r.Use(CORS)
r.Use(secure.Secure(secure.Options{
//AllowedHosts: []string{"*"},
// AllowedHosts: []string{"localhost:3000", "ssl.example.com"},
//SSLRedirect: true,
// SSLHost: "*",
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"},
STSIncludeSubdomains: true,
FrameDeny: true,
ContentTypeNosniff: true,
BrowserXssFilter: true,
ContentSecurityPolicy: "default-src 'self'",
}))
r.RedirectTrailingSlash = true
r.Use(ginzap.RecoveryWithZap(logger, true)) // panics don't stop server
r.Use(ginzap.Ginzap(logger, time.RFC3339, true)) // logs all requests
r.NoRoute(noRouteFunc)
r.GET("/ping", func(ctx *gin.Context) {
_, err := dbmodels.Pings(dbmodels.PingWhere.Ok.EQ(true)).One(ctx, db)
if err != nil {
ctx.AbortWithStatus(http.StatusInternalServerError)
}
ctx.String(http.StatusOK, "pong")
})
var v1Route string
var v2Route string
if cfg.Env == config.Production || cfg.Env == config.Local {
v1Route = "v1"
v2Route = "v1.1"
} else {
v1Route = fmt.Sprintf("%s/v1", cfg.Env)
v2Route = fmt.Sprintf("%s/v1.1", cfg.Env)
}
err = mailchimp.SetKey(impart.MailChimpApiKey)
if err != nil {
logger.Info("Error connecting Mailchimp", zap.Error(err),
zap.Any("MailchimpApikey", cfg.MailchimpApikey))
}
v1 := r.Group(v1Route)
setRouter(v1, services, logger, db)
v2 := r.Group(v2Route)
setRouter(v2, services, logger, db)
server := cfg.GetHttpServer()
server.Handler = r
logger.Info("Impart backend started.", zap.Int("port", cfg.Port), zap.String("env", string(cfg.Env)))
if err := graceful.Graceful(server.ListenAndServe, server.Shutdown); err != nil {
logger.Fatal("error serving", zap.Error(err))
}
logger.Info("done serving")
}
publish
// See also, https://docs.aws.amazon.com/goto/WebAPI/sns-2010-03-31/Publish
func (c *SNS) Publish(input *PublishInput) (*PublishOutput, error) {
req, out := c.PublishRequest(input)
return out, req.Send()
}

Multi WATCH in Golang using go-redis/redis

I am trying to do this 4 redis calls in one transaction using https://github.com/go-redis/redis :
func getHost(key) host, score, error{
numberOfHosts, err := rdb.ZCard(key).Result()
if err != nil {
return nil, 0, err
}
host, err := rdb.ZRangeByScoreWithScores(key, redis.ZRangeBy{
Min: "0",
Max: strconv.Itoa(maxScore),
Offset: numberOfHosts - 1,
Count: 1,
}).Result()
if err != nil {
return nil, 0, err
}
score := host[0].Score
member, err := json.Marshal(host[0].Member)
if err != nil {
return nil, 0, err
}
if int(score) < maxCapacity {
rdb.ZIncrBy(key, score+1, string(member))
}
host, err := rdb.GetHost(string(member))
if err != nil {
return nil, 0, err
}
return host, int(score), nil
}
I found this https://godoc.org/github.com/go-redis/redis#Client.Watch in the doc and I am trying to do something with it but it is a bit difficult at the moment.
Is anybody already done this and can provide advice ?
EDIT :
I tried to do this :
count := func(key string) error {
txf := func(tx *redis.Tx) error {
numberOfHosts, err := tx.ZCard(key).Result()
if err != nil {
return err
}
numberOfHosts--
_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
getHostKey := func(key string, numberOfHosts int) error {
txf := func(tx *redis.Tx) error {
host, err := tx.ZRangeByScoreWithScores(key, redis.ZRangeBy{
Min: "0",
Max: strconv.Itoa(maxCapacity),
Offset: numberOfHosts,
Count: 1,
}).Result()
if err != nil {
return err
}
if len(host) == 0 {
return fmt.Errorf("Get Host did not return a host for the key: %v", key)
}
score := host[0].Score
member, err := json.Marshal(host[0].Member)
if err != nil {
return err
}
score++
_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
if int(score) < maxCapacity {
incScore := func(key string, score int, member string) (*Host, int, error) {
txf := func(tx *redis.Tx) error {
tx.ZIncrBy(key, score, string(member))
host, err := rc.GetHost(string(member))
if err != nil {
return nil, 0, err
}
return host, int(score), nil
}
}
err := rc.Client.Watch(txf, key)
if err != redis.TxFailedErr {
return err
}
}
}
})
}
err := rc.Client.Watch(txf, key)
if err != redis.TxFailedErr {
return err
}
}
if err := getHostKey(key, numberOfHosts); err != nil {
return err
}
})
}
err := rc.Client.Watch(txf, key)
if err != redis.TxFailedErr {
return err
}
}
if err := count(key); err != nil {
return nil, 0, err
But the compiler is not happy with all the variable coming from the previous watch.
I will look into LUA script

Resources