func (b *FBlock) MarshalTrans() ([]byte, error) { var out bytes.Buffer var periodMark = 0 var i int var trans fct.ITransaction for i, trans = range b.Transactions { for periodMark < len(b.endOfPeriod) && b.endOfPeriod[periodMark] > 0 && // Ignore if markers are not set i == b.endOfPeriod[periodMark] { out.WriteByte(fct.MARKER) periodMark++ } data, err := trans.MarshalBinary() if err != nil { return nil, err } out.Write(data) if err != nil { return nil, err } } for periodMark < len(b.endOfPeriod) { out.WriteByte(fct.MARKER) periodMark++ } return out.Bytes(), nil }
func FactoidAddOutput(trans fct.ITransaction, key string, address fct.IAddress, amount uint64) error { ok := Utility.IsValidKey(key) if !ok { return fmt.Errorf("Invalid name for transaction") } // First look if this is really an update for _, output := range trans.GetOutputs() { if output.GetAddress().IsSameAs(address) { output.SetAmount(amount) return nil } } // Add our new Output err := factoidState.GetWallet().AddOutput(trans, address, uint64(amount)) if err != nil { return fmt.Errorf("Failed to add output") } // Update our map with our new transaction to the same key. Otherwise, all // of our work will go away! factoidState.GetDB().PutRaw([]byte(fct.DB_BUILD_TRANS), []byte(key), trans) return nil }
func HandleGetFee(ctx *web.Context, k string) { var trans fct.ITransaction var err error key := ctx.Params["key"] fmt.Println("getfee", key) if len(key) > 0 { trans, err = getTransaction(ctx, key) if err != nil { reportResults(ctx, "Failure to locate the transaction", false) return } } fee, err := Wallet.GetFee() if err != nil { reportResults(ctx, err.Error(), false) return } if trans != nil { ufee, _ := trans.CalculateFee(uint64(fee)) fee = int64(ufee) } reportResults(ctx, fmt.Sprintf("%s", strings.TrimSpace(fct.ConvertDecimal(uint64(fee)))), true) }
func (w *SCWallet) AddECOutput(trans fct.ITransaction, address fct.IAddress, amount uint64) error { _, adr, err := w.getWalletEntry([]byte(fct.W_RCD_ADDRESS_HASH), address) if err != nil { adr = address } trans.AddECOutput(fct.CreateAddress(adr), amount) return nil }
func FactoidAddFee(trans fct.ITransaction, key string, address fct.IAddress, name string) (uint64, error) { { ins, err := trans.TotalInputs() if err != nil { return 0, err } outs, err := trans.TotalOutputs() if err != nil { return 0, err } ecs, err := trans.TotalECs() if err != nil { return 0, err } if ins != outs+ecs { return 0, fmt.Errorf("Inputs and outputs don't add up") } } ok := Utility.IsValidKey(key) if !ok { return 0, fmt.Errorf("Invalid name for transaction") } fee, err := GetFee() if err != nil { return 0, err } transfee, err := trans.CalculateFee(uint64(fee)) if err != nil { return 0, err } adr, err := factoidState.GetWallet().GetAddressHash(address) if err != nil { return 0, err } for _, input := range trans.GetInputs() { if input.GetAddress().IsSameAs(adr) { amt, err := fct.ValidateAmounts(input.GetAmount(), transfee) if err != nil { return 0, err } input.SetAmount(amt) return transfee, nil } } return 0, fmt.Errorf("%s is not an input to the transaction.", key) }
func (w *SCWallet) SignInputs(trans fct.ITransaction) (bool, error) { data, err := trans.MarshalBinarySig() // Get the part of the transaction we sign if err != nil { return false, err } var numSigs int = 0 inputs := trans.GetInputs() rcds := trans.GetRCDs() for i, rcd := range rcds { rcd1, ok := rcd.(*fct.RCD_1) if ok { pub := rcd1.GetPublicKey() we := w.db.GetRaw([]byte(fct.W_ADDRESS_PUB_KEY), pub).(*WalletEntry) if we != nil { var pri [fct.SIGNATURE_LENGTH]byte copy(pri[:], we.private[0]) bsig := ed25519.Sign(&pri, data) sig := new(fct.Signature) sig.SetSignature(bsig[:]) sigblk := new(fct.SignatureBlock) sigblk.AddSignature(sig) trans.SetSignatureBlock(i, sigblk) numSigs += 1 } } } return numSigs == len(inputs), nil }
// FactoidSubmit assumes the caller has already validated and signed // the transaction. No checking is done, it is just submitted to Factom. func FactoidSubmit(trans fct.ITransaction) (err error) { var data []byte if data, err = trans.MarshalBinary(); err != nil { return err } transdata := string(hex.EncodeToString(data)) s := struct{ Transaction string }{transdata} var js []byte if js, err = json.Marshal(s); err != nil { return err } resp, err := http.Post( fmt.Sprintf("http://%s/v1/factoid-submit/", ipaddressFD+portNumberFD), "application/json", bytes.NewBuffer(js)) if err != nil { return err } body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } resp.Body.Close() type rtnStruct struct { Response string Success bool } rtn := new(rtnStruct) if err := json.Unmarshal(body, rtn); err != nil { return err } if !rtn.Success { return fmt.Errorf(rtn.Response) } return nil }
// Checks the transaction timestamp for validity in being included in the current block. // No node has any responsiblity to forward on transactions that do not fall within // the timeframe around a block defined by TRANSACTION_PRIOR_LIMIT and TRANSACTION_POST_LIMIT func (fs *FactoidState) ValidateTransactionAge(trans fct.ITransaction) error { tsblk := fs.GetCurrentBlock().GetCoinbaseTimestamp() if tsblk < 0 { return fmt.Errorf("Block has no coinbase transaction at this time") } tstrans := int64(trans.GetMilliTimestamp()) if tsblk-tstrans > fct.TRANSACTION_PRIOR_LIMIT { return fmt.Errorf("Transaction is too old to be included in the current block") } if tstrans-tsblk > fct.TRANSACTION_POST_LIMIT { return fmt.Errorf("Transaction is dated too far in the future to be included in the current block") } return nil }
// Assumes validation has already been done. func (fs *AssetState) UpdateTransaction(trans fct.ITransaction) error { for _, input := range trans.GetInputs() { fs.UpdateBalance(input.GetAddress(), -int64(input.GetAmount())) } for _, output := range trans.GetOutputs() { fs.UpdateBalance(output.GetAddress(), int64(output.GetAmount())) } fs.numTransactions++ cp.CP.AddUpdate( "transprocessed", // tag "status", // Category fmt.Sprintf("Factoid Transactions Processed: %d", fs.numTransactions), // Title "", // Message 0) // When to expire the message; 0 is never return nil }
// Returns an error message about what is wrong with the transaction if it is // invalid, otherwise you are good to go. func (fs *FactoidState) Validate(index int, trans fct.ITransaction) error { err := fs.currentBlock.ValidateTransaction(index, trans) if err != nil { return err } var sums = make(map[[32]byte]uint64, 10) // Look at the sum of an address's inputs for _, input := range trans.GetInputs() { // to a transaction. bal, err := fct.ValidateAmounts(sums[input.GetAddress().Fixed()], input.GetAmount()) if err != nil { return err } if bal > fs.GetBalance(input.GetAddress()) { return fmt.Errorf("Not enough funds in input addresses for the transaction") } sums[input.GetAddress().Fixed()] = bal } return nil }
func (w *SCWallet) UpdateInput(trans fct.ITransaction, index int, address fct.IAddress, amount uint64) error { we, adr, err := w.getWalletEntry([]byte(fct.W_RCD_ADDRESS_HASH), address) if err != nil { return err } in, err := trans.GetInput(index) if err != nil { return err } trans.GetRCDs()[index] = we.GetRCD() // The RCD must match the (possibly) new input in.SetAddress(adr) in.SetAmount(amount) return nil }
// Returns an error message about what is wrong with the transaction if it is // invalid, otherwise you are good to go. func (fs *AssetState) Validate(trans fct.ITransaction) error { if err := trans.ValidateSignatures(); err != nil { return err } var sums = make(map[fct.IAddress]uint64, 10) for _, input := range trans.GetInputs() { bal, err := fct.ValidateAmounts( sums[input.GetAddress()], // Will be zero the first time around input.GetAmount()) // Get this amount, check against bounds if err != nil { return err } if bal > fs.GetBalance(input.GetAddress()) { return fmt.Errorf("Not enough funds in input addresses for the transaction") } sums[input.GetAddress()] = bal } return nil }
func (w *SCWallet) AddInput(trans fct.ITransaction, address fct.IAddress, amount uint64) error { // Check if this is an address we know. we, adr, err := w.getWalletEntry([]byte(fct.W_RCD_ADDRESS_HASH), address) // If it isn't, we assume the user knows what they are doing. if we == nil || err != nil { rcd := fct.NewRCD_1(address.Bytes()) trans.AddRCD(rcd) adr, err := rcd.GetAddress() if err != nil { return err } trans.AddInput(fct.CreateAddress(adr), amount) } else { trans.AddRCD(we.GetRCD()) trans.AddInput(fct.CreateAddress(adr), amount) } return nil }
func PrtTrans(t fct.ITransaction) { fmt.Println("Transaction") for _, input := range t.GetInputs() { fmt.Println("in ", input.GetAddress(), input.GetAmount(), fs.GetBalance(input.GetAddress())) } for _, output := range t.GetOutputs() { fmt.Println("out", output.GetAddress(), output.GetAmount(), fs.GetBalance(output.GetAddress())) } for _, ecoutput := range t.GetECOutputs() { fmt.Println("ec ", ecoutput.GetAddress(), ecoutput.GetAmount(), fs.GetECBalance(ecoutput.GetAddress())) } }
func isReasonableFee(trans fct.ITransaction) error { feeRate, getErr := GetFee() if getErr != nil { return getErr } reqFee, err := trans.CalculateFee(uint64(feeRate)) if err != nil { return err } sreqFee := int64(reqFee) tin, err := trans.TotalInputs() if err != nil { return err } tout, err := trans.TotalOutputs() if err != nil { return err } tec, err := trans.TotalECs() if err != nil { return err } cfee := int64(tin) - int64(tout) - int64(tec) if cfee >= (sreqFee * 10) { return fmt.Errorf("Unbalanced transaction (fee too high). Fee should be less than 10x the required fee.") } if cfee < sreqFee { return fmt.Errorf("Insufficient fee") } return nil }
// Add the first transaction of a block. This transaction makes the // payout to the servers, so it has no inputs. This transaction must // be deterministic so that all servers will know and expect its output. func (b *FBlock) AddCoinbase(trans fct.ITransaction) error { b.BodyMR = nil if len(b.Transactions) != 0 { return fmt.Errorf("The coinbase transaction must be the first transaction") } if len(trans.GetInputs()) != 0 { return fmt.Errorf("The coinbase transaction cannot have any inputs") } if len(trans.GetECOutputs()) != 0 { return fmt.Errorf("The coinbase transaction cannot buy Entry Credits") } if len(trans.GetRCDs()) != 0 { return fmt.Errorf("The coinbase transaction cannot have anyRCD blocks") } if len(trans.GetSignatureBlocks()) != 0 { return fmt.Errorf("The coinbase transaction is not signed") } // TODO Add check here for the proper payouts. b.Transactions = append(b.Transactions, trans) return nil }
func (w *SCWallet) SignInputs(trans fct.ITransaction) (bool, error) { data, err := trans.MarshalBinarySig() // Get the part of the transaction we sign if err != nil { return false, err } var errMsg []byte rcds := trans.GetRCDs() for i, rcd := range rcds { rcd1, ok := rcd.(*fct.RCD_1) if ok { pub := rcd1.GetPublicKey() we, ok := w.db.GetRaw([]byte(fct.W_ADDRESS_PUB_KEY), pub).(*WalletEntry) if ok { var pri [fct.SIGNATURE_LENGTH]byte copy(pri[:], we.private[0]) bsig := ed25519.Sign(&pri, data) sig := new(fct.Signature) sig.SetSignature(bsig[:]) sigblk := new(fct.SignatureBlock) sigblk.AddSignature(sig) trans.SetSignatureBlock(i, sigblk) } else { errMsg = append(errMsg, []byte("Do not have the private key for: "+ fct.ConvertFctAddressToUserStr(fct.NewAddress(pub))+"\n")...) } } } if errMsg != nil { return false, fmt.Errorf("%s", string(errMsg)) } return true, nil }
func (b FBlock) ValidateTransaction(index int, trans fct.ITransaction) error { // Calculate the fee due. { err := trans.Validate(index) if err != nil { return err } } //Ignore coinbase transaction's signatures if len(b.Transactions) > 0 { err := trans.ValidateSignatures() if err != nil { return err } } fee, err := trans.CalculateFee(b.ExchRate) if err != nil { return err } tin, err := trans.TotalInputs() if err != nil { return err } tout, err := trans.TotalOutputs() if err != nil { return err } tec, err := trans.TotalECs() if err != nil { return err } sum, err := fct.ValidateAmounts(tout, tec, fee) if err != nil { return err } if tin < sum { return fmt.Errorf("The inputs %s do not cover the outputs %s,\n"+ "the Entry Credit outputs %s, and the required fee %s", strings.TrimSpace(fct.ConvertDecimal(tin)), strings.TrimSpace(fct.ConvertDecimal(tout)), strings.TrimSpace(fct.ConvertDecimal(tec)), strings.TrimSpace(fct.ConvertDecimal(fee))) } return nil }
func filtertransaction(trans fct.ITransaction, addresses [][]byte) bool { if addresses == nil || len(addresses) == 0 { return true } if len(trans.GetInputs()) == 0 && len(trans.GetOutputs()) == 0 { return false } if len(addresses) == 1 && bytes.Equal(addresses[0], trans.GetSigHash().Bytes()) { return true } Search: for _, adr := range addresses { for _, in := range trans.GetInputs() { if bytes.Equal(adr, in.GetAddress().Bytes()) { continue Search } } for _, out := range trans.GetOutputs() { if bytes.Equal(adr, out.GetAddress().Bytes()) { continue Search } } for _, ec := range trans.GetECOutputs() { if bytes.Equal(adr, ec.GetAddress().Bytes()) { continue Search } } return false } return true }
func (w *SCWallet) ValidateSignatures(trans fct.ITransaction) error { if trans == nil { return fmt.Errorf("Missing Transaction") } return trans.ValidateSignatures() }
func (w *SCWallet) Validate(index int, trans fct.ITransaction) error { err := trans.Validate(index) return err }