.Net Core library to assist smooth development on VeChain for developers and hobbyists.
See ReleaseNotes.md
using Org.VeChain.Thor.Devkit;
using Org.VeChain.Thor.Devkit.Extension;
A Special shout out to following projects:
- Hashing
byte[] blake2bHash = Cry.Blake2b.CalculateHash("hello world");
Console.log(blake2bHash.ToHexString());
// 0x256c83b297114d201b30179f3f0ef0cace9783622da5974326b436178aeef610
byte[] keccack256Hash = Cry.Keccack256.CalculateHash("hello world");
Console.log(keccack256Hash.ToHexString());
// 0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad
- HDNode
var works = new string[12]{"mouse","brave","fun","viable","utility","veteran","luggage","area","bike","myself","target","thunder"};
var node = new HDNode(works);
var sonNode = node.Derive(0);
var grandNode = sonNode.Derive(1);
Console.log(node.privateKey.ToHexString());
Console.log(sonNode.privateKey.ToHexString());
Console.log(grandNode.privateKey.ToHexString());
// 0x546498ba394789f18b5b4b62227572fe033f8f7f2597a747ada3d8ec8f20f650
// 0x12ddf96bb7f2c031ee9b776e2b236f2fc46de6f90fb9cd48a82e0c849327d251
// 0xa7af80364059211616e47394cf49240b40f7a87cea54ec99a6aa1815b586d74e
var seed = "0x403d3e5f3646f517d3d6f51bd98f90493d502d259506abbfe46d5af2196960531edf6030aef3876fe3432f457c9cb9d6f312c538f19d3f30e731022d309683c2".ToBytes();
var node = new HDNode(seed);
Console.log(node.privateKey.ToHexString());
// 0x546498ba394789f18b5b4b62227572fe033f8f7f2597a747ada3d8ec8f20f650
var privateKey = "0x546498ba394789f18b5b4b62227572fe033f8f7f2597a747ada3d8ec8f20f650".ToBytes();
var chainCode = "0xc86826253a925f5958a838756e20ba33e71566da60b9e274f97342492c578c10".ToBytes();
IHDNode node = new HDNode(privateKey,chainCode);
Console.log(node.privateKey.ToHexString());
// 0x546498ba394789f18b5b4b62227572fe033f8f7f2597a747ada3d8ec8f20f650
- Mnemonic & Keystore
var works = new string[12]{"mouse","brave","fun","viable","utility","veteran","luggage","area","bike","myself","target","thunder"};
var priKey = Mnemonic.DerivePrivateKey(works);
Console.log(priKey.ToHexString());
// 0x12ddf96bb7f2c031ee9b776e2b236f2fc46de6f90fb9cd48a82e0c849327d251
var priKey = "0xdce1443bd2ef0c2631adc1c67e5c93f13dc23a41c18b536effbbdcbcdb96fb65";
var keystore = Keystore.EncryptToJson(priKey.ToBytes(),"123456789");
var recoveredPriKey = Keystore.DecryptFromJson(keystore,"123456789");
Console.log(recoveredPriKey.SequenceEqual(priKey.ToBytes()));
// True
- Secp256K1
var priKey = Secp256k1.GeneratePrivateKey();
var pubKey = Secp256k1.DerivePublicKey(priKey);
var msgHash = Keccack256.CalculateHash("hello world");
var signature = Secp256k1.Sign(msgHash,priKey);
var recoveredPubKey = Secp256k1.RecoverPublickey(Keccack256.CalculateHash("hello world"),signature);
Console.log(pubKey.SequenceEqual(recoveredPubKey));
// True
- SimpleWallet
var priKey = "0xdce1443bd2ef0c2631adc1c67e5c93f13dc23a41c18b536effbbdcbcdb96fb65".ToBytes();
var vechainKey = new SimpleWallet(priKey);
Console.log(vechainKey.address);
Console.log(SimpleWallet.ToChecksumAddress(vechainKey.address));
// 0x7567d83b7b8d80addcb281a71d54fc7b3364ffed
// 0x7567D83b7b8d80ADdCb281A71d54Fc7B3364ffed
- ABI Parames Builder
string abiJson = "[{\"name\":\"to\",\"type\":\"address\"},{\"name\":\"numProposals\",\"type\":\"uint8\"}]";
IAbiParameterDefinition[] parames = (new AbiParameterBuilder()).Builder(abiJson);
Console.log(parames.Length);
Console.log(parames[0].Name);
Console.log(parames[0].ABIType);
Console.log(parames[1].Name);
Console.log(parames[1].ABIType);
// 2
// to
// address
// numProposals
// uint8
- ABI Function Encode
string abiJson = "{\"constant\": false,\"inputs\": [{\"name\": \"a1\",\"type\": \"uint256\"},{\"name\": \"a2\",\"type\": \"string\"}],\"name\": \"f1\",\"outputs\": [{\"name\": \"r1\",\"type\": \"address\"},{\"name\": \"r2\",\"type\": \"bytes\"}],\"payable\": false,\"stateMutability\": \"nonpayable\",\"type\": \"function\"}";
AbiFuncationCoder coder = new AbiFuncationCoder(abiJson);
var encodeData = coder.Encode(1,"foo");
var data = "0x27fcbb2f000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000003666f6f0000000000000000000000000000000000000000000000000000000000";
Console.log(encodeData.SequenceEqual(data.ToBytes()));
// True
- ABI Function Decode
string outputData = "0x0000000000000000000000004c6f3ca686c053354a83c030e80d2ee0a000b0cf000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c073850000000000000000000000000000000000000000000000000000000005c073850000000000000000000000000000000000000000000000000000000005c073850";
string abiJson = "{\"constant\": true,\"inputs\": [{\"name\": \"_tokenId\",\"type\": \"uint256\"}],\"name\": \"getMetadata\",\"outputs\": [{\"name\": \"\",\"type\": \"address\"},{\"name\": \"\",\"type\": \"uint8\"},{\"name\": \"\",\"type\": \"bool\"},{\"name\": \"\",\"type\": \"bool\"},{\"name\": \"\",\"type\": \"uint64\"},{\"name\": \"\",\"type\": \"uint64\"},{\"name\": \"\",\"type\": \"uint64\"}],\"payable\": false,\"stateMutability\": \"view\",\"type\": \"function\"}";
AbiFuncationCoder coder = new AbiFuncationCoder(abiJson);
var output = coder.Decode(outputData.ToBytes());
Console.log((output[0].Result as string).Equals("0x4c6f3ca686c053354a83c030e80d2ee0a000b0cf"));
Console.log(((BigInteger)output[1].Result).Equals(new BigInteger(5)));
Console.log(((bool)output[2].Result).Equals(false));
Console.log(((bool)output[3].Result).Equals(false));
Console.log(((BigInteger)output[4].Result).Equals(new BigInteger(1543977040)));
Console.log(((BigInteger)output[5].Result).Equals(new BigInteger(1543977040)));
Console.log(((BigInteger)output[6].Result).Equals(new BigInteger(1543977040)));
// True
- ABI Event Encode (Due to build event filter)
string abiJson = "{\"anonymous\": false,\"inputs\": [{\"indexed\": true,\"name\": \"_from\",\"type\": \"address\"},{\"indexed\": true,\"name\": \"_to\",\"type\": \"address\"},{\"indexed\": false,\"name\": \"_value\",\"type\": \"uint256\"}],\"name\": \"Transfer\",\"type\": \"event\"}";
AbiEventCoder coder = new AbiEventCoder(abiJson);
Dictionary<string,dynamic> indexed = new Dictionary<string,dynamic>();
indexed.Add("_from","0xe4aea9f855d6960d56190fb26e32d0ec2ab40d82");
indexed.Add("_to","0xf43a84be55e162034f4c13de65294a3875f15bc9");
byte[][] filters = coder.EncodeFilter(indexed);
Console.log(filters[0].SequenceEqual("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef".ToBytes()));
Console.log(filters[1].SequenceEqual("0x000000000000000000000000e4aea9f855d6960d56190fb26e32d0ec2ab40d82".ToBytes()));
Console.log(filters[2].SequenceEqual("0x000000000000000000000000f43a84be55e162034f4c13de65294a3875f15bc9".ToBytes()));
// True
- ABI Event Decode
tring abiJson = "{\"anonymous\": false,\"inputs\": [{\"indexed\": true,\"name\": \"_from\",\"type\": \"address\"},{\"indexed\": true,\"name\": \"_to\",\"type\": \"address\"},{\"indexed\": false,\"name\": \"_value\",\"type\": \"uint256\"}],\"name\": \"Transfer\",\"type\": \"event\"}";
AbiEventCoder coder = new AbiEventCoder(abiJson);
List<byte[]> topics = new List<byte[]>();
topics.Add("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef".ToBytes());
topics.Add("0x000000000000000000000000e4aea9f855d6960d56190fb26e32d0ec2ab40d82".ToBytes());
topics.Add("0x000000000000000000000000f43a84be55e162034f4c13de65294a3875f15bc9".ToBytes());
byte[] data = "0x0000000000000000000000000000000000000000000000056bc75e2d63100000".ToBytes();
AbiEventTopic[] decode = coder.DecodeTopics(topics.ToArray(),data);
console.log(decode.Length == 3);
console.log(decode[0].Definition.Name == "_from" && decode[0].Definition.Indexed == true && (decode[0].Result as string).Equals("0xe4aea9f855d6960d56190fb26e32d0ec2ab40d82"));
console.log(decode[1].Definition.Name == "_to" && decode[1].Definition.Indexed == true && (decode[1].Result as string).Equals("0xf43a84be55e162034f4c13de65294a3875f15bc9"));
console.log(decode[2].Definition.Name == "_value" && decode[2].Definition.Indexed == false && decode[2].Result.ToString().Equals("100000000000000000000"));
- Scalar RLP Encode & Decode
var bigInterger = BigInteger.Parse("102030405060708090A0B0C0D0E0F2",NumberStyles.AllowHexSpecifier);
byte[] encode = (new RlpBigIntegerKind()).EncodeToRlp(bigInterger).Encode();
// 0x8F102030405060708090A0B0C0D0E0F2
var decode = (new RlpBigIntegerKind()).DecodeFromRlp(new RlpItem(encode));
- Array RLP Encode & Decode
var array = new int[3]{1,2,3};
byte[] encode = (new RlpArrayKind(new RlpIntKind(true)).EncodeToRlp(array).Encode());
// 0xC3010203
var decode = (new RlpArrayKind()).DecodeFromRlp(new RlpArray(encode));
// {1,2,3}
- Transaction (MTT Multi-task transaction)
var txbody = new Body();
txbody.ChainTag = 74;
txbody.BlockRef = "0x005d64da8e7321bd";
txbody.Expiration = 18;
txbody.GasPriceCoef = 0;
txbody.Gas = 21000;
txbody.DependsOn = "";
txbody.Nonce = "0xd6846cde87878603";
txbody.Reserved = null;
txbody.Clauses.Add(new Clause("0xa4aDAfAef9Ec07BC4Dc6De146934C7119341eE25",new BigInteger(100000),"0x2398479812734981"));
txbody.Clauses.Add(new Clause("0xa4aDAfAef9Ec07BC4Dc6De146934C7119341eE25",new BigInteger(100000),"0x2398479812734981"));
txbody.Clauses.Add(new Clause("0xa4aDAfAef9Ec07BC4Dc6De146934C7119341eE25",new BigInteger(100000),"0x2398479812734981"));
var transaction = new Transaction.Transaction(txbody);
transaction.Signature = Cry.Secp256k1.Sign(transaction.SigningHash(),"0xdce1443bd2ef0c2631adc1c67e5c93f13dc23a41c18b536effbbdcbcdb96fb65".ToBytes());
var rlp = (Transaction.Transaction.UnsignedRlpDefinition() as RlpStructKind).EncodeToRlp(transaction.Body);
- Transaction (VIP-191)
var delegated_body = new Body();
delegated_body.ChainTag = 74;
delegated_body.BlockRef = "0x005d64da8e7321bd";
delegated_body.Expiration = 18;
delegated_body.GasPriceCoef = 0;
delegated_body.Gas = 21000;
delegated_body.DependsOn = "";
delegated_body.Nonce = "0xd6846cde87878603";
delegated_body.Reserved = new Reserved();
delegated_body.Reserved.Features = 1;
delegated_body.Clauses.Add(new Clause("0xa4aDAfAef9Ec07BC4Dc6De146934C7119341eE25",new BigInteger(100000),"0x2398479812734981"));
delegated_body.Clauses.Add(new Clause("0xa4aDAfAef9Ec07BC4Dc6De146934C7119341eE25",new BigInteger(100000),"0x2398479812734981"));
delegated_body.Clauses.Add(new Clause("0xa4aDAfAef9Ec07BC4Dc6De146934C7119341eE25",new BigInteger(100000),"0x2398479812734981"));
var transaction = new Transaction.Transaction(delegated_body);
Assert.True(transaction.IsDelegated);
var senderPrikey = "0xdce1443bd2ef0c2631adc1c67e5c93f13dc23a41c18b536effbbdcbcdb96fb65";
var senderAddr = "0x7567d83b7b8d80addcb281a71d54fc7b3364ffed";
var gasPayerPrikey = "0x321d6443bc6177273b5abf54210fe806d451d6b7973bccc2384ef78bbcd0bf51";
var geaPayerAddr = "0xd3ae78222beadb038203be21ed5ce7c9b1bff602";
var senderSigningHash = transaction.SigningHash();
var gasPayerSigningHash = transaction.SigningHash(senderAddr);
var senderSignature = Cry.Secp256k1.Sign(senderSigningHash,senderPrikey.ToBytes());
var gasPayerSignature = Cry.Secp256k1.Sign(gasPayerSigningHash,gasPayerPrikey.ToBytes());
transaction.AddVIP191Signature(senderSignature,gasPayerSignature);
- Certificate
Certificate.Certificate info = new Certificate.Certificate();
info.Purpose = "identification";
info.Payload = new CertificatePayload();
info.Payload.Type = "text";
info.Payload.Content = "fyi";
info.Domain = "localhost";
info.Timestamp = 1545035330;
info.Signer = "0x7567d83b7b8d80addcb281a71d54fc7b3364ffed";
byte[] msgHash = CertificateCoder.SigningHash(info);
var signature = Cry.Secp256k1.Sign(msgHash,"0xdce1443bd2ef0c2631adc1c67e5c93f13dc23a41c18b536effbbdcbcdb96fb65".ToBytes());
info.Signature = signature;
CertificateCoder.Verify(info);
// True
dotnet test ./Org.VeChain.Thor/Org.VeChain.Thor.Devkit.UnitTest