Example #1
0
 public Message(MessageArgs args)
 {
     this.header          = args.header;
     this.accountKeys     = args.accountKeys.ToList().Select(account => new PublicKey(account)).ToArray();
     this.recentBlockhash = args.recentBlockhash;
     this.instructions    = args.instructions;
 }
Example #2
0
        //  /**
        //   * Construct an empty Transaction
        //   */
        //  constructor(opts?: TransactionCtorFields)
        //        {
        //            opts && Object.assign(this, opts);
        //        }

        //        /**
        //         * Add one or more instructions to this Transaction
        //         */
        //        add(
        //    ...items: Array<
        //            Transaction | TransactionInstruction | TransactionInstructionCtorFields
        //          >

        //        ) : Transaction {
        //    if (items.length === 0) {
        //      throw new Error('No instructions');
        //    }

        //    items.forEach((item: any) => {
        //      if ('instructions' in item) {
        //        this.instructions = this.instructions.concat(item.instructions);
        //      } else if ('data' in item && 'programId' in item && 'keys' in item) {
        //        this.instructions.push(item);
        //      } else {
        //        this.instructions.push(new TransactionInstruction(item));
        //      }
        //    });
        //return this;
        //  }

        /// <summary>
        /// Compile transaction data
        /// </summary>
        /// <returns></returns>
        public Message compileMessage()
        {
            var nonceInfo = this.nonceInfo;

            if (nonceInfo != null && this.instructions[0] != nonceInfo.nonceInstruction)
            {
                this.recentBlockhash = nonceInfo.nonce;
                this.instructions.Insert(0, nonceInfo.nonceInstruction);
            }
            var recentBlockhash = this.recentBlockhash;

            if (recentBlockhash.Length < 1)
            {
                throw new Exception("Transaction recentBlockhash required");
            }

            if (this.instructions.Count < 1)
            {
                throw new Exception("No instructions provided");
            }

            PublicKey feePayer;

            if (this.feePayer != null)
            {
                feePayer = this.feePayer;
            }
            else if (signatures.Count > 0 && signatures[0].publicKey != null)
            {
                // Use implicit fee payer
                feePayer = this.signatures[0].publicKey;
            }
            else
            {
                throw new Exception("Transaction fee payer required");
            }

            for (var i = 0; i < this.instructions.Count(); i++)
            {
                if (this.instructions[i].programId == null)
                {
                    throw new Exception(string.Format("Transaction instruction index {0} has undefined program id", i));
                }
            }

            List <string>      programIds   = new List <string>();
            List <AccountMeta> accountMetas = new List <AccountMeta>();

            this.instructions.ForEach(instruction => {
                instruction.keys.ForEach(accountMeta =>
                {
                    accountMetas.Add(accountMeta);
                });

                var programId = instruction.programId.ToString();
                if (!programIds.Contains(programId))
                {
                    programIds.Add(programId);
                }
            });

            // Append programID account metas
            programIds.ForEach(programId => {
                accountMetas.Add(new AccountMeta()
                {
                    pubkey     = new PublicKey(programId),
                    isSigner   = false,
                    isWritable = false,
                });
            });

            // Sort. Prioritizing first by signer, then by writable
            accountMetas.Sort((x, y) => {
                var checkSigner   = x.isSigner == y.isSigner ? 0 : x.isSigner ? -1 : 1;
                var checkWritable = x.isWritable == y.isWritable ? 0 : x.isWritable ? -1 : 1;
                return(Convert.ToBoolean(checkSigner) ? checkSigner : checkWritable);
            });

            // Cull duplicate account metas
            List <AccountMeta> uniqueMetas = new List <AccountMeta>();

            accountMetas.ForEach(accountMeta =>
            {
                var pubkeyString = accountMeta.pubkey.ToString();
                var uniqueIndex  = uniqueMetas.FindIndex(x =>
                {
                    return(x.pubkey.ToString() == pubkeyString);
                });
                if (uniqueIndex > -1)
                {
                    var meta                 = uniqueMetas[uniqueIndex];
                    meta.isWritable          = meta.isWritable || accountMeta.isWritable;
                    uniqueMetas[uniqueIndex] = meta;
                }
                else
                {
                    uniqueMetas.Add(accountMeta);
                }
            });

            // Move fee payer to the front
            var feePayerIndex = uniqueMetas.FindIndex(x =>
            {
                return(x.pubkey.Equals(feePayer));
            });

            if (feePayerIndex > -1)
            {
                var payerMeta = uniqueMetas[feePayerIndex];
                payerMeta.isSigner   = true;
                payerMeta.isWritable = true;
                uniqueMetas.Insert(0, payerMeta);
            }
            else
            {
                uniqueMetas.Insert(0, new AccountMeta()
                {
                    pubkey     = feePayer,
                    isSigner   = true,
                    isWritable = true,
                });
            }

            // Disallow unknown signers
            foreach (var signature in this.signatures)
            {
                var uniqueIndex = uniqueMetas.FindIndex(x =>
                {
                    return(x.pubkey.Equals(signature.publicKey));
                });
                if (uniqueIndex > -1)
                {
                    if (!uniqueMetas[uniqueIndex].isSigner)
                    {
                        var meta = uniqueMetas[uniqueIndex];
                        meta.isSigner            = true;
                        uniqueMetas[uniqueIndex] = meta;
                        //console.warn(
                        //  'Transaction references a signature that is unnecessary, ' +
                        //    'only the fee payer and instruction signer accounts should sign a transaction. ' +
                        //    'This behavior is deprecated and will throw an error in the next major version release.',
                        //);
                    }
                }
                else
                {
                    throw new Exception(string.Format("unknown signer: {0}", signature.publicKey.ToString()));
                }
            }

            var numRequiredSignatures       = 0;
            var numReadonlySignedAccounts   = 0;
            var numReadonlyUnsignedAccounts = 0;

            // Split out signing from non-signing keys and count header values
            List <string> signedKeys   = new List <string>();
            List <string> unsignedKeys = new List <string>();

            uniqueMetas.ForEach(meta => {
                if (meta.isSigner)
                {
                    signedKeys.Add(meta.pubkey.ToString());
                    numRequiredSignatures += 1;
                    if (!meta.isWritable)
                    {
                        numReadonlySignedAccounts += 1;
                    }
                }
                else
                {
                    unsignedKeys.Add(meta.pubkey.ToString());
                    if (!meta.isWritable)
                    {
                        numReadonlyUnsignedAccounts += 1;
                    }
                }
            });

            signedKeys.AddRange(unsignedKeys);
            var accountKeys  = signedKeys;
            var instructions = this.instructions.Select(
                instruction =>
            {
                var data      = instruction.data;
                var programId = instruction.programId;
                //const { data, programId} = instruction;
                var cin            = new CompiledInstruction();
                cin.programIdIndex = accountKeys.IndexOf(programId.ToString());
                cin.accounts       = instruction.keys.Select(meta => accountKeys.IndexOf(meta.pubkey.ToString())).ToArray();
                cin.data           = Base58CheckEncoding.EncodePlain(data);
                return(cin);
            }).ToList();

            instructions.ForEach(instruction =>
            {
                if (!(instruction.programIdIndex >= 0))
                {
                    throw new Exception("instruction.programIdIndex error");
                }
                foreach (var keyIndex in instruction.accounts)
                {
                    if (keyIndex < 0)
                    {
                        throw new Exception("instruction.accounts error");
                    }
                }
            });
            var args = new MessageArgs()
            {
                header = new MessageHeader()
                {
                    numRequiredSignatures       = numRequiredSignatures,
                    numReadonlySignedAccounts   = numReadonlySignedAccounts,
                    numReadonlyUnsignedAccounts = numReadonlyUnsignedAccounts
                },
                accountKeys     = accountKeys.ToArray(),
                recentBlockhash = recentBlockhash,
                instructions    = instructions.ToArray()
            };

            return(new Message(args));
        }