Introduction
I'm developing a .NET 6 environment that has an API for transferring ether from one wallet to another wallet. It uses the Nethereum library for that.
Issue
I'm getting the Transaction Hash of the ether transfer. But when I'm trying to see it on Etherscan (see here) it doesn't appear. In addition to that, in my wallet, the value isn't transferred.
Workflow
The controller calls Transfer method, which calls TransferWaitingTxHashAsync method.
public bool Transfer(int ecommerceId, EtherWallet senderWallet, TransferRequestVO transferRequest)
{
string transactionHash = null;
bool thrownError = false;
try
{
transactionHash = _hdWalletManager.TransferWaitingTxHashAsync(senderWallet, transferRequest).Result;
}
catch (Exception)
{
thrownError = true;
}
if(thrownError || transactionHash == null)
return false;
EthereumTransactionReceipt pendingTransactionReceipt = new()
{
From = senderWallet.Address,
To = transferRequest.AddressDestination,
EcommerceId = ecommerceId,
TransactionHash = transactionHash,
Status = EthereumTransactionReceiptAvailableStatus.Pending.Value
};
_ethereumTransactionReceiptRepository.Insert(pendingTransactionReceipt);
bool saveChanges = _ethereumTransactionReceiptRepository.SaveChanges();
return saveChanges;
}
As you can see below, I'm doubling both Gas and GasPrice.
The Nonce is being summed to avoid transaction replacement underpriced (changing it on the debugger each time I run).
public Task<string> TransferWaitingTxHashAsync(EtherWallet senderWallet, TransferRequestVO transferRequest)
{
NethereumAccount senderAccount = new(senderWallet.PrivateKey);
Web3 web3 = new(senderAccount, _httpAddress);
HexBigInteger gasPrice = web3.Eth.GasPrice.SendRequestAsync().Result;
decimal gasPriceDecimal = Math.Round(gasPrice.ToLong() * 2m);
gasPriceDecimal = Web3.Convert.FromWei(BigInteger.Parse(gasPriceDecimal.ToString()), UnitConversion.EthUnit.Gwei);
BigInteger gas = web3.Eth.GetEtherTransferService()
.EstimateGasAsync(transferRequest.AddressDestination,
transferRequest.EtherAmount)
.Result;
gas = BigInteger.Add(gas, gas);
BigInteger nonce = senderAccount.NonceService.GetNextNonceAsync().Result;
BigInteger nextNonce = nonce + 131;
return web3.Eth.GetEtherTransferService()
.TransferEtherAsync(transferRequest.AddressDestination,
transferRequest.EtherAmount,
gasPriceDecimal,
gas,
nextNonce);
}
Environment
I'm running it locally using Infura as the provider (https://goerli.infura.io/v3/YOUR-API-KEY) for Goerli network.
Summarized
How can I fix it, and successfully complete my transaction or either see it on Etherscan (Goerli)?
Related
I recently created a Console app which held the context of Pkcs11Interop library, along with HSM dll. It worked fine, however I needed to rewrite the code to Windows Service (I hosted it as gRPC service since it's .NET Core). After hosting WS, it ocurred that the slot list on factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, libraryPath, AppType.MultiThreaded).GetSlotList(SlotsType.WithOrWithoutTokenPresent).Find(slot => slot.SlotId == slotId) returns an empty list of slots, even though it returned list of 3 elements in console app.
public Pkcs11Signature(string libraryPath, ulong slotId)
{
Pkcs11InteropFactories factories = new Pkcs11InteropFactories();
_pkcs11Library = factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, libraryPath, AppType.MultiThreaded);
_slot = _pkcs11Library.GetSlotList(SlotsType.WithOrWithoutTokenPresent).Find(slot => slot.SlotId == slotId);
}
public Pkcs11Signature Select(string alias, string certLabel, string pin, bool login)
{
List<CKA> pkAttributeKeys = new List<CKA>();
pkAttributeKeys.Add(CKA.CKA_KEY_TYPE);
pkAttributeKeys.Add(CKA.CKA_LABEL);
pkAttributeKeys.Add(CKA.CKA_ID);
List<CKA> certAttributeKeys = new List<CKA>();
certAttributeKeys.Add(CKA.CKA_VALUE);
certAttributeKeys.Add(CKA.CKA_LABEL);
//CloseSession();
_session = _slot.OpenSession(SessionType.ReadWrite);
if (login)
_session.Login(CKU.CKU_USER, pin);
ObjectAttributeFactory objectAttributeFactory = new ObjectAttributeFactory();
List<IObjectAttribute> attributes = new List<IObjectAttribute>();
attributes.Add(objectAttributeFactory.Create(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY));
List<IObjectHandle> keys = _session.FindAllObjects(attributes);
bool found = false;
foreach (IObjectHandle key in keys)
{
List<IObjectAttribute> keyAttributes = _session.GetAttributeValue(key, pkAttributeKeys);
ulong type = keyAttributes[0].GetValueAsUlong();
string encryptionAlgorithm;
switch (type)
{
case (ulong)CKK.CKK_RSA:
encryptionAlgorithm = "RSA";
break;
case (ulong)CKK.CKK_DSA:
encryptionAlgorithm = "DSA";
break;
case (ulong)CKK.CKK_ECDSA:
encryptionAlgorithm = "ECDSA";
break;
default:
continue;
}
string thisAlias = keyAttributes[1].GetValueAsString();
if (thisAlias == null || thisAlias.Length == 0)
thisAlias = keyAttributes[2].GetValueAsString();
if (alias != null && !alias.Equals(thisAlias))
continue;
attributes.Clear();
attributes.Add(objectAttributeFactory.Create(CKA.CKA_CLASS, CKO.CKO_CERTIFICATE));
attributes.Add(objectAttributeFactory.Create(CKA.CKA_CERTIFICATE_TYPE, CKC.CKC_X_509));
if (certLabel == null && thisAlias != null && thisAlias.Length > 0)
certLabel = thisAlias;
if (certLabel != null)
attributes.Add(objectAttributeFactory.Create(CKA.CKA_LABEL, certLabel));
List<IObjectHandle> certificates =_session.FindAllObjects(attributes);
if (certificates.Count != 1)
continue;
IObjectHandle certificate = certificates[0];
List<IObjectAttribute> certificateAttributes =_session.GetAttributeValue(certificate, certAttributeKeys);
X509Certificate x509Certificate =
new X509Certificate(X509CertificateStructure.GetInstance(certificateAttributes[0].GetValueAsByteArray()));
List<X509Certificate> x509Certificates = new List<X509Certificate>();
x509Certificates.Add(x509Certificate);
attributes.Clear();
attributes.Add(objectAttributeFactory.Create(CKA.CKA_CLASS, CKO.CKO_CERTIFICATE));
attributes.Add(objectAttributeFactory.Create(CKA.CKA_CERTIFICATE_TYPE, CKC.CKC_X_509));
List<IObjectHandle> otherCertificates =_session.FindAllObjects(attributes);
foreach (IObjectHandle otherCertificate in otherCertificates)
{
if (!certificate.ObjectId.Equals(otherCertificate.ObjectId))
{
certificateAttributes =_session.GetAttributeValue(otherCertificate, certAttributeKeys);
X509Certificate otherX509Certificate =
new X509Certificate(X509CertificateStructure.GetInstance(certificateAttributes[0].GetValueAsByteArray()));
x509Certificates.Add(otherX509Certificate);
}
}
found = true;
_alias = thisAlias;
_encryptionAlgorithm = encryptionAlgorithm;
_privateKeyHandle = key;
_chain = x509Certificates.ToArray();
break;
}
if (!found)
{
Console.WriteLine("Havent found");
_alias = null;
_encryptionAlgorithm = null;
_privateKeyHandle = null;
_chain = null;
}
return this;
}
Executing the library:
using (var signature = new Pkcs11Signature(#"C:\Program Files (x86)\hsm\hsm.dll", 3).
Select(null, "CERT LABEL", "PIN", true)
{
(...DO THE WORK HERE...)
}
I've seen this, but changing the "Log On As" to "Local Service" for Windows Service gives no effect:
C_GetSlotList Failing when called from IIS but not from IIS express
Pkcs11Interop returns slots received by calling C_GetSlotList function of unmanaged PKCS#11 library. So if you get 0 slots then C_GetSlotList returned 0 slots. You need to discuss this situation with the vendor of your PKCS#11 library who might know why their library does not see any slots.
I'm using BouncyCastle (BC) API for C#.
After Hours going through this API hell i figured out how to sign/verify and encrypt/decrypt using CMS with BC.
I have to encrypt the symmetric key for the CMSEnvelope with RSA-OAEP (SHA256 + MGF1 with SHA256) as key encryption algorithm.
On default BC obviously uses RSA.
In RecipientInfo structure of the CMS envelope I have to use KeyTransRecipientInfo.
I can't figure out how to set the RSA-OAEP-algorithm to the recipientInfo:
//Create Encrypted cms envelope
X509Certificate otherscert = new X509CertificateParser().ReadCertificate(myCertBytes)
CmsEnvelopedDataGenerator envDataGen = new CmsEnvelopedDataGenerator();
envDataGen.AddKeyTransRecipient(othersCert); //setting Cert, but how the key encr. algorithm?
CmsProcessableByteArray sData = new CmsProcessableByteArray(signedData.GetEncoded());
var envData = envDataGen.Generate(sData, CmsEnvelopedDataGenerator.Aes256Cbc); //generate the envelope
Now in the recipientInfo KeyEncryptionAlgOid is 1.2.840.113549.1.1.1 (RSA)
I need it to be 1.2.840.113549.1.1.7 (RSA-OAEP) with the mentioned key encryption parameters.
Any idea how to do that in BC's C#-API?
EDIT:
Now I got the Source from Git and did the following edits.
The problem was not just setting the keyEncryptionAlgorithm to the recipient.
BC's logic takes the certificates SubjectPublicKeyInfo-algorithm for key encryption.
in CMSEnvelopedGenerator.cs I added
public void AddKeyTransRecipientRsaOaep(
X509Certificate cert, DerObjectIdentifier digest, DerObjectIdentifier mgf1digest, AlgorithmIdentifier pSource = null)
{
if (pSource == null)
{
pSource = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdPSpecified, new DerOctetString(new byte[0]));
}
AlgorithmIdentifier hash = new AlgorithmIdentifier(digest, DerNull.Instance);
AlgorithmIdentifier mask = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, mgf1digest);
var rsaOaepParams = new RsaesOaepParameters(hash, mask, pSource);
KeyTransRecipientInfoGenerator ktrig = new KeyTransRecipientInfoGenerator();
ktrig.RecipientCert = cert;
ktrig.AlgorithmId = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdRsaesOaep, rsaOaepParams);
recipientInfoGenerators.Add(ktrig);
}
In KeyTransrecipientInfoGenerator.cs I added
private AlgorithmIdentifier keyEncryptionAlg;
...
internal AlgorithmIdentifier AlgorithmId
{
set
{
keyEncryptionAlg = value;
}
}
...
protected virtual AlgorithmIdentifier AlgorithmDetails
{
get
{
if (keyEncryptionAlg == null)
{
return info.AlgorithmID;
}
else
{
return keyEncryptionAlg;
}
}
}
//In the Generate() method I changed
keyEncryptionAlgorithm = AlgorithmDetails;
//to
AlgorithmIdentifier keyEncryptionAlgorithm;
if (this.keyEncryptionAlg != null)
{
keyEncryptionAlgorithm = this.keyEncryptionAlg;
}
else
{
keyEncryptionAlgorithm = AlgorithmDetails;
}
Now the keyEncryption algorithm and parameters are set correctly and it encrypts without exception:
CmsEnvelopedDataGenerator envDataGen = new CmsEnvelopedDataGenerator();
envDataGen.AddKeyTransRecipientRsaOaep(othersCert, NistObjectIdentifiers.IdSha256, NistObjectIdentifiers.IdSha256);
CmsProcessableByteArray sData = new CmsProcessableByteArray(signedData.GetEncoded());
var envData = envDataGen.Generate(sData, CmsEnvelopedDataGenerator.Aes256Cbc);
Reading the Envelope with BC or NetCore3 works fine.
The RecipientInfos are set as intended (keyEncryptionAlgorithm, params).
But Decryption fails on both... :(
I have a custom PXbutton called UploadRecords, when I click this button I should populate the grid with records and release the records.
Release Action is pressed in the UploadRecords action delegate. The problem I get with this code is, the code here function properly for less records by release action but when passes thousands of records to release, it takes huge time(> 30 min.) and show the error like Execution timeout.
suggest me to avoid more execution time and release the records fastly.
namespace PX.Objects.AR
{
public class ARPriceWorksheetMaint_Extension : PXGraphExtension<ARPriceWorksheetMaint>
{
//public class string_R112 : Constant<string>
//{
// public string_R112()
// : base("4E5CCAFC-0957-4DB3-A4DA-2A24EA700047")
// {
// }
//}
public class string_R112 : Constant<string>
{
public string_R112()
: base("EA")
{
}
}
public PXSelectJoin<InventoryItem, InnerJoin<CSAnswers, On<InventoryItem.noteID, Equal<CSAnswers.refNoteID>>,
LeftJoin<INItemCost, On<InventoryItem.inventoryID, Equal<INItemCost.inventoryID>>>>,
Where<InventoryItem.salesUnit, Equal<string_R112>>> records;
public PXAction<ARPriceWorksheet> uploadRecord;
[PXUIField(DisplayName = "Upload Records", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
[PXButton]
public IEnumerable UploadRecord(PXAdapter adapter)
{
using (PXTransactionScope ts = new PXTransactionScope())
{
foreach (PXResult<InventoryItem, CSAnswers, INItemCost> res in records.Select())
{
InventoryItem invItem = (InventoryItem)res;
INItemCost itemCost = (INItemCost)res;
CSAnswers csAnswer = (CSAnswers)res;
ARPriceWorksheetDetail gridDetail = new ARPriceWorksheetDetail();
gridDetail.PriceType = PriceTypeList.CustomerPriceClass;
gridDetail.PriceCode = csAnswer.AttributeID;
gridDetail.AlternateID = "";
gridDetail.InventoryID = invItem.InventoryID;
gridDetail.Description = invItem.Descr;
gridDetail.UOM = "EA";
gridDetail.SiteID = 6;
InventoryItemExt invExt = PXCache<InventoryItem>.GetExtension<InventoryItemExt>(invItem);
decimal y;
if (decimal.TryParse(csAnswer.Value, out y))
{
y = decimal.Parse(csAnswer.Value);
}
else
y = decimal.Parse(csAnswer.Value.Replace(" ", ""));
gridDetail.CurrentPrice = y; //(invExt.UsrMarketCost ?? 0m) * (Math.Round(y / 100, 2));
gridDetail.PendingPrice = y; // (invExt.UsrMarketCost ?? 0m)* (Math.Round( y/ 100, 2));
gridDetail.TaxID = null;
Base.Details.Update(gridDetail);
}
ts.Complete();
}
Base.Document.Current.Hold = false;
using (PXTransactionScope ts = new PXTransactionScope())
{
Base.Release.Press();
ts.Complete();
}
List<ARPriceWorksheet> lst = new List<ARPriceWorksheet>
{
Base.Document.Current
};
return lst;
}
protected void ARPriceWorksheet_RowSelected(PXCache cache, PXRowSelectedEventArgs e, PXRowSelected InvokeBaseHandler)
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (ARPriceWorksheet)e.Row;
uploadRecord.SetEnabled(row.Status != SPWorksheetStatus.Released);
}
}
}
First, Do you need them all to be in a single transaction scope? This would revert all changes if there is an exception in any. If you need to have them all committed without any errors rather than each record, you would have to perform the updates this way.
I would suggest though moving your process to a custom processing screen. This way you can load the records, select one or many, and use the processing engine built into Acumatica to handle the process, rather than a single button click action. Here is an example: https://www.acumatica.com/blog/creating-custom-processing-screens-in-acumatica/
Based on the feedback that it must be all in a single transaction scope and thousands of records, I can only see two optimizations that may assist. First is increasing the Timeout as explained in this blog post. https://acumaticaclouderp.blogspot.com/2017/12/acumatica-snapshots-uploading-and.html
Next I would load all records into memory first and then loop through them with a ToList(). That might save you time as it should pull all records at once rather than once for each record.
going from
foreach (PXResult<InventoryItem, CSAnswers, INItemCost> res in records.Select())
to
var recordList = records.Select().ToList();
foreach (PXResult<InventoryItem, CSAnswers, INItemCost> res in recordList)
I am trying to retrieve a value from a Zebra printer by interrogating it with this code:
public static string GetSettingFromPrinter(string cmd)
{
string setting = string.Empty;
try
{
BasicPortSettings bps = new BasicPortSettings();
bps.BaudRate = BaudRates.CBR_19200;
bps.Parity = OpenNETCF.IO.Serial.Parity.none;
bps.StopBits = OpenNETCF.IO.Serial.StopBits.one;
Port serialPort = new Port("COM1:", bps);
serialPort.Open();
byte[] sendBytes = Encoding.ASCII.GetBytes(cmd);
MessageBox.Show(Encoding.ASCII.GetString(sendBytes, 0, sendBytes.Length));
serialPort.Output = sendBytes;
serialPort.Query(); // <= this is new
byte[] responseBytes = serialPort.Input;
setting = GetString(responseBytes);
serialPort.Close();
return setting;
}
catch (Exception x)
{
MessageBox.Show(x.ToString());
return setting;
}
}
However, I don't see where the Output is actually sent, or how to do that. My best guess was calling the Port.Query() method, but that doesn't work, either - at least there is nothing in setting / the Port.Input value after doing so.
I have successfully passed commands to the printer using the older SerialPort class:
public static bool SendCommandToPrinter(string cmd)
{
bool success; // init'd to false by default
try
{
SerialPort serialPort = new SerialPort();
serialPort.BaudRate = 19200;
serialPort.Handshake = Handshake.XOnXOff;
serialPort.Open();
serialPort.Write(cmd);
serialPort.Close();
success = true;
}
catch // may not need a try/catch block, as success defaults to false
{
success = false;
}
return success;
}
...but was advised not to use that due to its longness of tooth.
I would revert back to this snaggletooth if I knew how to read from the old SerialPort class. Does anybody know what I need to do to send sendBytes (and receive responseBytes)?
UPDATE
I tested "COM1" instead of "COM1:" (I used the latter because there is a post that says the colon is necessary (<= not medical advice, although that is doubtless true in that sense, too), but sans the ":" made no noticeable difference.
I then tried "string.Empty" in place of giving it a name, and got, "OpenNETCF.IO.Serial.CommPortException: CreateFileFailed 2 ..."
Onward...or is it Sideward...
FWIW, setting the Output property immediately sends the data on the wire. No additional call is necessary.
I want to create an application wide feed on my ASP.net 3.5 web site using the application cache. The data that I am using to populate the cache is slow to obtain, maybe up to 10 seconds (from a remote server's data feed). My question/confusion is, what is the best way to structure the cache management.
private const string CacheKey = "MyCachedString";
private static string lockString = "";
public string GetCachedString()
{
string data = (string)Cache[CacheKey];
string newData = "";
if (data == null)
{
// A - Should this method call go here?
newData = SlowResourceMethod();
lock (lockString)
{
data = (string)Cache[CacheKey];
if (data != null)
{
return data;
}
// B - Or here, within the lock?
newData = SlowResourceMethod();
Cache[CacheKey] = data = newData;
}
}
return data;
}
The actual method would be presented by and HttpHandler (.ashx).
If I collect the data at point 'A', I keep the lock time short, but might end up calling the external resource many times (from web pages all trying to reference the feed). If I put it at point 'B', the lock time will be long, which I am assuming is a bad thing.
What is the best approach, or is there a better pattern that I could use?
Any advice would be appreciated.
I add the comments on the code.
private const string CacheKey = "MyCachedString";
private static readonly object syncLock = new object();
public string GetCachedString()
{
string data = (string)Cache[CacheKey];
string newData = "";
// start to check if you have it on cache
if (data == null)
{
// A - Should this method call go here?
// absolut not here
// newData = SlowResourceMethod();
// we are now here and wait for someone else to make it or not
lock (syncLock)
{
// now lets see if some one else make it...
data = (string)Cache[CacheKey];
// we have it, send it
if (data != null)
{
return data;
}
// not have it, now is the time to look for it.
// B - Or here, within the lock?
newData = SlowResourceMethod();
// set it on cache
Cache[CacheKey] = data = newData;
}
}
return data;
}
Better for me is to use mutex and lock depended on the name CacheKey and not lock all resource and the non relative one. With mutex one basic simple example will be:
private const string CacheKey = "MyCachedString";
public string GetCachedString()
{
string data = (string)Cache[CacheKey];
string newData = "";
// start to check if you have it on cache
if (data == null)
{
// lock it base on resource key
// (note that not all chars are valid for name)
var mut = new Mutex(true, CacheKey);
try
{
// Wait until it is safe to enter.
// but also add 30 seconds max
mut.WaitOne(30000);
// now lets see if some one else make it...
data = (string)Cache[CacheKey];
// we have it, send it
if (data != null)
{
return data;
}
// not have it, now is the time to look for it.
// B - Or here, within the lock?
newData = SlowResourceMethod();
// set it on cache
Cache[CacheKey] = data = newData;
}
finally
{
// Release the Mutex.
mut.ReleaseMutex();
}
}
return data;
}
You can also read
Image caching issue by using files in ASP.NET